Skip to content

Commit

Permalink
ComponentSerializer services
Browse files Browse the repository at this point in the history
  • Loading branch information
kashike committed Apr 27, 2021
1 parent b909022 commit b6ba19f
Show file tree
Hide file tree
Showing 11 changed files with 460 additions and 43 deletions.
64 changes: 64 additions & 0 deletions api/src/main/java/net/kyori/adventure/util/Services.java
@@ -0,0 +1,64 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2021 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.util;

import java.util.Iterator;
import java.util.Optional;
import java.util.ServiceLoader;
import org.checkerframework.checker.nullness.qual.NonNull;

/**
* Tools for working with {@link ServiceLoader}s.
*
* @since 4.8.0
*/
public final class Services {
private Services() {
}

/**
* Locates a service provider.
*
* @param type the service provider type
* @param <P> the service provider type
* @return a service
* @since 4.8.0
*/
public static <P> Optional<P> service(final @NonNull Class<P> type) {
final ServiceLoader<P> loader = Services0.loader(type);
final Iterator<P> it = loader.iterator();
while(it.hasNext()) {
try {
final P provider = it.next();
if(it.hasNext()) {
throw new IllegalStateException("Expected to find one " + type + " service provider, found multiple");
}
return Optional.of(provider);
} catch(final Throwable t) {
// ignored
}
}
return Optional.empty();
}
}
35 changes: 35 additions & 0 deletions api/src/main/java/net/kyori/adventure/util/Services0.java
@@ -0,0 +1,35 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2021 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.util;

import java.util.ServiceLoader;

final class Services0 {
private Services0() {
}

static <S> ServiceLoader<S> loader(final Class<S> type) {
return ServiceLoader.load(type, type.getClassLoader());
}
}
Expand Up @@ -32,6 +32,7 @@
import net.kyori.adventure.util.Buildable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.ApiStatus;

/**
* A gson component serializer.
Expand Down Expand Up @@ -153,4 +154,30 @@ interface Builder extends Buildable.Builder<GsonComponentSerializer> {
@Override
@NonNull GsonComponentSerializer build();
}

/**
* A {@link GsonComponentSerializer} service provider.
*
* @since 4.8.0
*/
@ApiStatus.Internal
interface Provider {
/**
* Provides a standard {@link GsonComponentSerializer}.
*
* @return a {@link GsonComponentSerializer}
* @since 4.8.0
*/
@ApiStatus.Internal
@NonNull GsonComponentSerializer gson();

/**
* Provides a legacy {@link GsonComponentSerializer}.
*
* @return a {@link GsonComponentSerializer}
* @since 4.8.0
*/
@ApiStatus.Internal
@NonNull GsonComponentSerializer gsonLegacy();
}
}
Expand Up @@ -26,6 +26,7 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import java.util.Optional;
import java.util.function.UnaryOperator;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.BlockNBTComponent;
Expand All @@ -35,12 +36,18 @@
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.util.Services;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

final class GsonComponentSerializerImpl implements GsonComponentSerializer {
static final GsonComponentSerializer INSTANCE = new GsonComponentSerializerImpl(false, null, false);
static final GsonComponentSerializer LEGACY_INSTANCE = new GsonComponentSerializerImpl(true, null, true);
private static final Optional<Provider> SERVICE = Services.service(Provider.class);
static final GsonComponentSerializer INSTANCE = SERVICE
.map(Provider::gson)
.orElseGet(() -> new GsonComponentSerializerImpl(false, null, false));
static final GsonComponentSerializer LEGACY_INSTANCE = SERVICE
.map(Provider::gsonLegacy)
.orElseGet(() -> new GsonComponentSerializerImpl(true, null, true));

private final Gson serializer;
private final UnaryOperator<GsonBuilder> populator;
Expand Down
Expand Up @@ -23,17 +23,19 @@
*/
package net.kyori.adventure.text.serializer.legacy;

import java.util.function.Consumer;
import java.util.regex.Pattern;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.util.Buildable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.ApiStatus;

/**
* A legacy component serializer.
Expand Down Expand Up @@ -261,4 +263,39 @@ interface Builder extends Buildable.Builder<LegacyComponentSerializer> {
@Override
@NonNull LegacyComponentSerializer build();
}

/**
* A {@link LegacyComponentSerializer} service provider.
*
* @since 4.8.0
*/
@ApiStatus.Internal
interface Provider {
/**
* Provides a {@link LegacyComponentSerializer} using {@link #AMPERSAND_CHAR}.
*
* @return a {@link LegacyComponentSerializer}
* @since 4.8.0
*/
@ApiStatus.Internal
@NonNull LegacyComponentSerializer legacyAmpersand();

/**
* Provides a {@link LegacyComponentSerializer} using {@link #SECTION_CHAR}.
*
* @return a {@link LegacyComponentSerializer}
* @since 4.8.0
*/
@ApiStatus.Internal
@NonNull LegacyComponentSerializer legacySection();

/**
* Completes the building process of {@link Builder}.
*
* @return a {@link Consumer}
* @since 4.8.0
*/
@ApiStatus.Internal
@NonNull Consumer<Builder> legacy();
}
}
Expand Up @@ -30,7 +30,9 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
Expand All @@ -43,6 +45,7 @@
import net.kyori.adventure.text.format.TextFormat;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.flattener.FlattenerListener;
import net.kyori.adventure.util.Services;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

Expand Down Expand Up @@ -95,8 +98,18 @@ final class LegacyComponentSerializerImpl implements LegacyComponentSerializer {
}
}

static final LegacyComponentSerializer SECTION_SERIALIZER = new LegacyComponentSerializerImpl(SECTION_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic());
static final LegacyComponentSerializer AMPERSAND_SERIALIZER = new LegacyComponentSerializerImpl(AMPERSAND_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic());
private static final Optional<Provider> SERVICE = Services.service(Provider.class);
static final LegacyComponentSerializer SECTION_SERIALIZER = SERVICE
.map(Provider::legacySection)
.orElseGet(() -> new LegacyComponentSerializerImpl(SECTION_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic()));
static final LegacyComponentSerializer AMPERSAND_SERIALIZER = SERVICE
.map(Provider::legacyAmpersand)
.orElseGet(() -> new LegacyComponentSerializerImpl(AMPERSAND_CHAR, HEX_CHAR, null, false, false, ComponentFlattener.basic()));
static final Consumer<Builder> BUILDER = SERVICE
.map(Provider::legacy)
.orElseGet(() -> builder -> {
// NOOP
});

private final char character;
private final char hexCharacter;
Expand Down Expand Up @@ -471,6 +484,7 @@ static final class BuilderImpl implements Builder {
private ComponentFlattener flattener = ComponentFlattener.basic();

BuilderImpl() {
BUILDER.accept(this); // let service provider touch the builder before anybody else touches it
}

BuilderImpl(final @NonNull LegacyComponentSerializerImpl serializer) {
Expand Down

0 comments on commit b6ba19f

Please sign in to comment.