From b38758439f7e53dfedfa10806f14189c65ada28c Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 31 May 2026 11:20:11 +0200 Subject: [PATCH 01/11] Remove Container#mappings This has never been supported and is no longer relevant --- .../spongepowered/plugin/metadata/Container.java | 14 -------------- .../plugin/metadata/builtin/MetadataContainer.java | 12 ------------ 2 files changed, 26 deletions(-) diff --git a/src/main/java/org/spongepowered/plugin/metadata/Container.java b/src/main/java/org/spongepowered/plugin/metadata/Container.java index 4caa528..372b34e 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/Container.java +++ b/src/main/java/org/spongepowered/plugin/metadata/Container.java @@ -24,7 +24,6 @@ */ package org.spongepowered.plugin.metadata; -import org.apache.maven.artifact.Artifact; import org.spongepowered.plugin.metadata.model.ContainerLoader; import java.util.Optional; @@ -59,19 +58,6 @@ public interface Container { */ String license(); - /** - * Gets the {@link Artifact mappings} that code within this container might be written in. - *

- * The format of this string should be in maven dependency format (group:artifact:version). - *

- * Consult the vendor for how this field is used. As an example, that entity could use this - * purely for information purposes or go farther and perform artifact remapping from these - * mappings to another. - * - * @return The mappings or {@link Optional#empty()} otherwise - */ - Optional mappings(); - /** * @return The {@link Inheritable global metadata} or {@link Optional#empty()} otherwise */ diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index 6896a56..6a2715d 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -57,7 +57,6 @@ public final class MetadataContainer implements Container { private final String license; - @Nullable private final String mappings; private final StandardContainerLoader loader; @Nullable private final Inheritable globalMetadata; private final Set metadata = new LinkedHashSet<>(); @@ -66,7 +65,6 @@ public final class MetadataContainer implements Container { private MetadataContainer(final Builder builder) { this.loader = builder.loader; this.license = builder.license; - this.mappings = builder.mappings; this.globalMetadata = builder.globalMetadata; this.metadata.addAll(builder.metadata); for (final StandardPluginMetadata pm : this.metadata) { @@ -85,11 +83,6 @@ public String license() { return this.license; } - @Override - public Optional mappings() { - return Optional.ofNullable(this.mappings); - } - @Override public Optional globalMetadata() { return Optional.ofNullable(this.globalMetadata); @@ -110,7 +103,6 @@ public String toString() { return new StringJoiner(", ", MetadataContainer.class.getSimpleName() + "[", "]") .add("loader=" + this.loader) .add("license=" + this.license) - .add("mappings=" + this.mappings) .add("globalMetadata=" + this.globalMetadata) .toString(); } @@ -119,7 +111,6 @@ public MetadataContainer.Builder toBuilder() { final Builder builder = new Builder(); builder.loader = this.loader; builder.license = this.license; - builder.mappings = this.mappings; builder.globalMetadata = this.globalMetadata; builder.metadata.addAll(this.metadata); return builder; @@ -208,8 +199,6 @@ public MetadataContainer deserialize(final JsonElement element, final Type type, .loader(Adapters.Deserializers.CONTAINER_LOADER.fromJsonTree(obj.get("loader")).build()) .license(obj.get("license").getAsString()); - GsonUtils.consumeIfPresent(obj, "mappings", e -> builder.mappings(e.getAsString())); - final JsonElement globalElement = obj.get("global"); @Nullable StandardInheritable inheritable = null; if (globalElement instanceof JsonObject) { @@ -253,7 +242,6 @@ public JsonElement serialize(final MetadataContainer value, final Type type, fin final JsonObject obj = new JsonObject(); obj.add("loader", Adapters.Serializers.CONTAINER_LOADER.toJsonTree(value.loader)); obj.addProperty("license", value.license); - GsonUtils.writeIfPresent(obj, "mappings", value.mappings()); final JsonArray plugins = new JsonArray(); for (final PluginMetadata metadata : value.metadata) { From d081530c2bc98c67877a46fb5c8b73fcd4ee0c83 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 31 May 2026 21:50:10 +0200 Subject: [PATCH 02/11] Cleanup models and builders --- .../plugin/metadata/Container.java | 6 +- .../plugin/metadata/Inheritable.java | 8 +- .../metadata/builtin/MetadataContainer.java | 82 +++++------ .../plugin/metadata/builtin/NullVersion.java | 2 + .../metadata/builtin/StandardInheritable.java | 132 +++++++++++------- .../builtin/StandardPluginMetadata.java | 35 +++-- .../model/StandardContainerLoader.java | 41 +++--- .../builtin/model/StandardPluginBranding.java | 22 +-- .../model/StandardPluginContributor.java | 27 ++-- .../model/StandardPluginDependency.java | 48 ++++--- .../builtin/model/StandardPluginLinks.java | 23 +-- 11 files changed, 236 insertions(+), 190 deletions(-) diff --git a/src/main/java/org/spongepowered/plugin/metadata/Container.java b/src/main/java/org/spongepowered/plugin/metadata/Container.java index 372b34e..a7b6445 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/Container.java +++ b/src/main/java/org/spongepowered/plugin/metadata/Container.java @@ -26,8 +26,8 @@ import org.spongepowered.plugin.metadata.model.ContainerLoader; +import java.util.Collection; import java.util.Optional; -import java.util.Set; /** * A container joins together {@link Inheritable global metadata} with specific one or more @@ -74,7 +74,7 @@ public interface Container { Optional metadata(String id); /** - * @return All of the {@link PluginMetadata plugin metadata} as an unmodifiable {@link Set}. + * @return All of the {@link PluginMetadata plugin metadata} as an unmodifiable {@link Collection}. */ - Set metadata(); + Collection metadata(); } diff --git a/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java b/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java index ac35191..cd6669d 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java +++ b/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java @@ -30,10 +30,10 @@ import org.spongepowered.plugin.metadata.model.PluginDependency; import org.spongepowered.plugin.metadata.model.PluginLinks; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; /** * Represents metadata that is meant to be inherited/overwritten, per contract. @@ -61,7 +61,7 @@ public interface Inheritable { /** * @return The {@link PluginContributor contributors} as an unmodifiable {@link List}. */ - List contributors(); + List contributors(); /** * Gets the {@link PluginDependency plugin dependency} by {@link String id}. @@ -73,9 +73,9 @@ public interface Inheritable { Optional dependency(String id); /** - * @return The {@link PluginDependency dependencies} as an unmodifiable {@link Set}. + * @return The {@link PluginDependency dependencies} as an unmodifiable {@link Collection}. */ - Set dependencies(); + Collection dependencies(); /** * Gets the {@link T property} by {@link String key}. diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index 6a2715d..a11b593 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -32,26 +32,23 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Container; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.builtin.model.Adapters; import org.spongepowered.plugin.metadata.builtin.model.StandardContainerLoader; -import org.spongepowered.plugin.metadata.util.GsonUtils; import java.lang.reflect.Type; -import java.util.Collections; +import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.StringJoiner; public final class MetadataContainer implements Container { @@ -59,17 +56,16 @@ public final class MetadataContainer implements Container { private final String license; private final StandardContainerLoader loader; @Nullable private final Inheritable globalMetadata; - private final Set metadata = new LinkedHashSet<>(); - private final Map metadataById = new LinkedHashMap<>(); + private final Map metadata; private MetadataContainer(final Builder builder) { this.loader = builder.loader; this.license = builder.license; this.globalMetadata = builder.globalMetadata; - this.metadata.addAll(builder.metadata); - for (final StandardPluginMetadata pm : this.metadata) { - this.metadataById.put(pm.id(), pm); - pm.setContainer(this); + this.metadata = Map.copyOf(builder.metadata); + + for (final StandardPluginMetadata element : this.metadata.values()) { + element.setContainer(this); } } @@ -90,12 +86,12 @@ public Optional globalMetadata() { @Override public Optional metadata(final String id) { - return Optional.ofNullable(this.metadataById.get(Objects.requireNonNull(id, "id"))); + return Optional.ofNullable(this.metadata.get(Objects.requireNonNull(id, "id"))); } @Override - public Set metadata() { - return Collections.unmodifiableSet(this.metadata); + public Collection metadata() { + return this.metadata.values(); } @Override @@ -108,20 +104,15 @@ public String toString() { } public MetadataContainer.Builder toBuilder() { - final Builder builder = new Builder(); - builder.loader = this.loader; - builder.license = this.license; - builder.globalMetadata = this.globalMetadata; - builder.metadata.addAll(this.metadata); - return builder; + return new MetadataContainer.Builder().from(this); } public static final class Builder { - final Set metadata = new LinkedHashSet<>(); - @Nullable String license, mappings; - @Nullable StandardContainerLoader loader; - @Nullable Inheritable globalMetadata; + private final Map metadata = new LinkedHashMap<>(); + private @MonotonicNonNull String license; + private @MonotonicNonNull StandardContainerLoader loader; + private @Nullable Inheritable globalMetadata; public Builder loader(final StandardContainerLoader loader) { this.loader = Objects.requireNonNull(loader, "loader"); @@ -133,37 +124,36 @@ public Builder license(final String license) { return this; } - public Builder mappings(@Nullable final String mappings) { - - if (mappings != null) { - final String[] elements = mappings.split("\\.", 3); - // Triggers their sanity checks - ArtifactUtils.key(elements[0], elements[1], elements[2]); - } - - this.mappings = mappings; - return this; - } - - public Builder globalMetadata(final Inheritable globalMetadata) { - this.globalMetadata = Objects.requireNonNull(globalMetadata, "globalMetadata"); + public Builder globalMetadata(final @Nullable Inheritable globalMetadata) { + this.globalMetadata = globalMetadata; return this; } - public Builder metadata(final List metadata) { + public Builder metadata(final Collection metadata) { Objects.requireNonNull(metadata, "metadata"); this.metadata.clear(); - this.metadata.addAll(metadata); - return this; + return this.addMetadata(metadata); } - public Builder addMetadata(final List metadata) { - this.metadata.addAll(Objects.requireNonNull(metadata, "metadata")); + public Builder addMetadata(final Collection metadata) { + for (final StandardPluginMetadata element : Objects.requireNonNull(metadata, "metadata")) { + this.metadata.put(Objects.requireNonNull(element, "element").id(), element); + } return this; } public Builder addMetadata(final StandardPluginMetadata metadata) { - this.metadata.add(Objects.requireNonNull(metadata, "metadata")); + this.metadata.put(Objects.requireNonNull(metadata, "metadata").id(), metadata); + return this; + } + + public Builder from(final MetadataContainer value) { + Objects.requireNonNull(value, "value"); + this.loader = value.loader; + this.license = value.license; + this.globalMetadata = value.globalMetadata; + this.metadata.clear(); + this.metadata.putAll(value.metadata); return this; } @@ -224,7 +214,7 @@ public MetadataContainer deserialize(final JsonElement element, final Type type, for (final JsonObject pluginObject : pluginObjects) { final StandardPluginMetadata.Builder pluginBuilder = context.deserialize(pluginObject, StandardPluginMetadata.Builder.class); if (inheritable != null) { - pluginBuilder.from(inheritable); + pluginBuilder.merge(inheritable); } builder.addMetadata(pluginBuilder.build()); @@ -244,7 +234,7 @@ public JsonElement serialize(final MetadataContainer value, final Type type, fin obj.addProperty("license", value.license); final JsonArray plugins = new JsonArray(); - for (final PluginMetadata metadata : value.metadata) { + for (final PluginMetadata metadata : value.metadata()) { plugins.add(context.serialize(metadata, StandardPluginMetadata.class)); } obj.add("plugins", plugins); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java index 3571187..1fdb841 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java @@ -34,6 +34,8 @@ public static NullVersion instance() { return NullVersion.INSTANCE; } + private NullVersion() {} + @Override public int getMajorVersion() { throw new RuntimeException("This is a null version and should not be used!"); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java index d542ab2..4bf5d22 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java @@ -42,13 +42,11 @@ import org.spongepowered.plugin.metadata.builtin.model.StandardPluginContributor; import org.spongepowered.plugin.metadata.builtin.model.StandardPluginDependency; import org.spongepowered.plugin.metadata.builtin.model.StandardPluginLinks; -import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; import org.spongepowered.plugin.metadata.util.GsonUtils; import java.lang.reflect.Type; import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -56,7 +54,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.StringJoiner; import java.util.stream.Collectors; @@ -64,26 +61,20 @@ public class StandardInheritable implements Inheritable { protected final ArtifactVersion version; - protected final String rawVersion; protected final StandardPluginBranding branding; protected final StandardPluginLinks links; - protected final List contributors = new LinkedList<>(); - protected final Set dependencies = new LinkedHashSet<>(); - protected final Map properties = new LinkedHashMap<>(); - private final Map dependenciesById = new LinkedHashMap<>(); + protected final List contributors; + protected final Map dependencies; + protected final Map properties; @SuppressWarnings("rawtypes") protected StandardInheritable(final AbstractBuilder builder) { this.version = builder.version; - this.rawVersion = builder.rawVersion; this.branding = builder.branding; this.links = builder.links; - this.contributors.addAll(builder.contributors); - this.dependencies.addAll(builder.dependencies); - for (final StandardPluginDependency dependency : this.dependencies) { - this.dependenciesById.put(dependency.id(), dependency); - } - this.properties.putAll(builder.properties); + this.contributors = List.copyOf(builder.contributors); + this.dependencies = Map.copyOf(builder.dependencies); + this.properties = Map.copyOf(builder.properties); } @Override @@ -102,18 +93,18 @@ public StandardPluginLinks links() { } @Override - public List contributors() { - return Collections.unmodifiableList(this.contributors); + public List contributors() { + return this.contributors; } @Override public Optional dependency(final String id) { - return Optional.ofNullable(this.dependenciesById.get(Objects.requireNonNull(id, "id"))); + return Optional.ofNullable(this.dependencies.get(Objects.requireNonNull(id, "id"))); } @Override - public Set dependencies() { - return Collections.unmodifiableSet(this.dependencies); + public Collection dependencies() { + return this.dependencies.values(); } @SuppressWarnings("unchecked") @@ -124,7 +115,7 @@ public Optional property(final String key) { @Override public Map properties() { - return Collections.unmodifiableMap(this.properties); + return this.properties; } @Override @@ -134,7 +125,7 @@ public String toString() { protected StringJoiner stringJoiner() { return new StringJoiner(", ", StandardInheritable.class.getSimpleName() + "[", "]") - .add("version=" + this.rawVersion) + .add("version=" + this.version) .add("branding=" + this.branding) .add("links=" + this.links) .add("contributors=" + this.contributors) @@ -144,27 +135,25 @@ protected StringJoiner stringJoiner() { } @SuppressWarnings("unchecked") - public abstract static class AbstractBuilder> { + public abstract static class AbstractBuilder> { - final List contributors = new LinkedList<>(); - final Set dependencies = new LinkedHashSet<>(); - final Map properties = new LinkedHashMap<>(); - ArtifactVersion version = NullVersion.instance(); - String rawVersion = "null"; - StandardPluginBranding branding = StandardPluginBranding.none(); - StandardPluginLinks links = StandardPluginLinks.none(); + protected final List contributors = new LinkedList<>(); + protected final Map dependencies = new LinkedHashMap<>(); + protected final Map properties = new LinkedHashMap<>(); + protected ArtifactVersion version = NullVersion.instance(); + protected StandardPluginBranding branding = StandardPluginBranding.none(); + protected StandardPluginLinks links = StandardPluginLinks.none(); protected AbstractBuilder() { } public B version(final @Nullable String version) { - if (version == null) { - this.version = NullVersion.instance(); - this.rawVersion = "null"; - } else { - this.version = new DefaultArtifactVersion(Objects.requireNonNull(version, "version")); - this.rawVersion = version; - } + this.version = version == null ? NullVersion.instance() : new DefaultArtifactVersion(version); + return (B) this; + } + + public B version(final @Nullable ArtifactVersion version) { + this.version = version == null ? NullVersion.instance() : version; return (B) this; } @@ -178,7 +167,14 @@ public B links(final StandardPluginLinks links) { return (B) this; } - public B contributors(final Collection contributors) { + public B contributors(final Collection contributors) { + Objects.requireNonNull(contributors, "contributors"); + this.contributors.clear(); + this.contributors.addAll(contributors); + return (B) this; + } + + public B addContributors(final Collection contributors) { this.contributors.addAll(Objects.requireNonNull(contributors, "contributors")); return (B) this; } @@ -189,47 +185,77 @@ public B addContributor(final StandardPluginContributor contributor) { } public B dependencies(final Collection dependencies) { - this.dependencies.addAll(Objects.requireNonNull(dependencies, "dependencies")); + Objects.requireNonNull(dependencies, "dependencies"); + this.dependencies.clear(); + return this.addDependencies(dependencies); + } + + public B addDependencies(final Collection dependencies) { + for (final StandardPluginDependency element : Objects.requireNonNull(dependencies, "dependencies")) { + this.dependencies.put(Objects.requireNonNull(element, "element").id(), element); + } return (B) this; } public B addDependency(final StandardPluginDependency dependency) { - this.dependencies.add(Objects.requireNonNull(dependency, "dependency")); + this.dependencies.put(Objects.requireNonNull(dependency, "dependency").id(), dependency); return (B) this; } public B properties(final Map properties) { + Objects.requireNonNull(properties, "properties"); + this.properties.clear(); + this.properties.putAll(properties); + return (B) this; + } + + public B addProperties(final Map properties) { this.properties.putAll(Objects.requireNonNull(properties, "properties")); return (B) this; } - public B property(final String key, final Object value) { + public B addProperty(final String key, final Object value) { this.properties.put(Objects.requireNonNull(key, "key"), Objects.requireNonNull(value, "value")); return (B) this; } - public B from(final StandardInheritable value) { - // TODO If we have "partial" entries, do we want to do deep merging? May start to get out of control... - - // Inheritable + public B merge(final StandardInheritable value) { + Objects.requireNonNull(value, "value"); if (this.version == NullVersion.instance()) { - this.version = value.version(); - this.rawVersion = value.rawVersion; + this.version = value.version; } if (this.branding == StandardPluginBranding.none()) { - this.branding = value.branding(); + this.branding = value.branding; } if (this.links == StandardPluginLinks.none()) { this.links = value.links; } - this.contributors.addAll(value.contributors); - this.dependencies.addAll(value.dependencies); - for (final Map.Entry entry : value.properties().entrySet()) { + if (this.contributors.isEmpty()) { + this.contributors.addAll(value.contributors); + } + for (final Map.Entry entry : value.dependencies.entrySet()) { + this.dependencies.putIfAbsent(entry.getKey(), entry.getValue()); + } + for (final Map.Entry entry : value.properties.entrySet()) { this.properties.putIfAbsent(entry.getKey(), entry.getValue()); } return (B) this; } + public B from(final T value) { + Objects.requireNonNull(value, "value"); + this.version = value.version; + this.branding = value.branding; + this.links = value.links; + this.contributors.clear(); + this.contributors.addAll(value.contributors); + this.dependencies.clear(); + this.dependencies.putAll(value.dependencies); + this.properties.clear(); + this.properties.putAll(value.properties); + return (B) this; + } + public final T build() { Objects.requireNonNull(this.version, "version"); @@ -268,11 +294,11 @@ public StandardInheritable deserialize(final JsonElement element, final Type typ @Override public JsonElement serialize(final StandardInheritable value, final Type type, final JsonSerializationContext context) { final JsonObject obj = new JsonObject(); - GsonUtils.applyIfValid(obj, value, p -> p.version != NullVersion.instance(), (o, v) -> o.addProperty("version", v.rawVersion)); + GsonUtils.applyIfValid(obj, value, p -> p.version != NullVersion.instance(), (o, v) -> o.addProperty("version", v.version.toString())); GsonUtils.applyIfValid(obj, value, p -> p.branding != StandardPluginBranding.none(), (o, v) -> o.add("branding", Adapters.Serializers.PLUGIN_BRANDING.toJsonTree(v.branding))); GsonUtils.applyIfValid(obj, value, p -> p.links != StandardPluginLinks.none(), (o, v) -> o.add("links", Adapters.Serializers.PLUGIN_LINKS.toJsonTree(v.links))); GsonUtils.applyIfValid(obj, value, p -> !p.contributors.isEmpty(), (o, v) -> o.add("contributors", GsonUtils.write(Adapters.Serializers.PLUGIN_CONTRIBUTOR, v.contributors))); - GsonUtils.applyIfValid(obj, value, p -> !p.dependencies.isEmpty(), (o, v) -> o.add("dependencies", GsonUtils.write(Adapters.Serializers.PLUGIN_DEPENDENCY, v.dependencies))); + GsonUtils.applyIfValid(obj, value, p -> !p.dependencies.isEmpty(), (o, v) -> o.add("dependencies", GsonUtils.write(Adapters.Serializers.PLUGIN_DEPENDENCY, v.dependencies()))); GsonUtils.applyIfValid(obj, value, p -> !p.properties.isEmpty(), (o, v) -> o.add("properties", GsonUtils.write(e -> new JsonPrimitive(e.toString()), v.properties))); return obj; } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java index ad0f8d9..886f983 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java @@ -31,6 +31,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Constants; import org.spongepowered.plugin.metadata.Container; @@ -66,6 +67,10 @@ public Container container() { return this.container; } + void setContainer(final MetadataContainer container) { + this.container = container; + } + @Override public String id() { return this.id; @@ -86,10 +91,6 @@ public Optional description() { return Optional.ofNullable(this.description); } - void setContainer(final MetadataContainer container) { - this.container = container; - } - @Override public int hashCode() { return Objects.hashCode(this.id); @@ -120,9 +121,14 @@ public String toString() { return joiner.toString(); } + public StandardPluginMetadata.Builder toBuilder() { + return new StandardPluginMetadata.Builder().from(this); + } + public static final class Builder extends StandardInheritable.AbstractBuilder { - @Nullable String id, entrypoint, name, description; + private @MonotonicNonNull String id, entrypoint; + private @Nullable String name, description; public Builder() { } @@ -137,16 +143,25 @@ public Builder entrypoint(final String entrypoint) { return this; } - public Builder name(final String name) { - this.name = Objects.requireNonNull(name, "name"); + public Builder name(final @Nullable String name) { + this.name = name; return this; } - public Builder description(final String description) { - this.description = Objects.requireNonNull(description, "description"); + public Builder description(final @Nullable String description) { + this.description = description; return this; } + @Override + public Builder from(final StandardPluginMetadata value) { + this.id = value.id; + this.entrypoint = value.entrypoint; + this.name = value.name; + this.description = value.description; + return super.from(value); + } + @Override protected StandardPluginMetadata build0() { if (!Constants.VALID_ID_PATTERN.matcher(Objects.requireNonNull(this.id, "id")).matches()) { @@ -180,7 +195,7 @@ public StandardPluginMetadata.Builder deserialize(final JsonElement element, fin .entrypoint(obj.get("entrypoint").getAsString()); GsonUtils.consumeIfPresent(obj, "name", e -> builder.name(e.getAsString())); GsonUtils.consumeIfPresent(obj, "description", e -> builder.description(e.getAsString())); - builder.from(context.deserialize(element, StandardInheritable.class)); + builder.merge(context.deserialize(element, StandardInheritable.class)); return builder; } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java index 7e5908c..757b133 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java @@ -30,7 +30,7 @@ import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.plugin.metadata.model.ContainerLoader; import java.io.IOException; @@ -42,13 +42,11 @@ public final class StandardContainerLoader implements ContainerLoader { private final String name; - private final String rawVersion; private final VersionRange version; private StandardContainerLoader(final Builder builder) { this.name = builder.name; this.version = builder.version; - this.rawVersion = builder.rawVersion; } public static Builder builder() { @@ -65,15 +63,6 @@ public VersionRange version() { return this.version; } - public StandardContainerLoader.Builder toBuilder() { - final Builder builder = new Builder(); - builder.name = this.name; - builder.version = this.version; - builder.rawVersion = this.rawVersion; - - return builder; - } - @Override public int hashCode() { return Objects.hashCode(this.name); @@ -95,33 +84,47 @@ public boolean equals(final Object o) { public String toString() { return new StringJoiner(", ", StandardContainerLoader.class.getSimpleName() + "[", "]") .add("name=" + this.name) - .add("version=" + this.rawVersion) + .add("version=" + this.version) .toString(); } + public StandardContainerLoader.Builder toBuilder() { + return new Builder().from(this); + } + public static final class Builder { - @Nullable String name, rawVersion; - @Nullable VersionRange version; + private @MonotonicNonNull String name; + private @MonotonicNonNull VersionRange version; private Builder() { } public Builder name(final String name) { - this.name = name; + this.name = Objects.requireNonNull(name, "name"); return this; } public Builder version(final String version) { this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); - this.rawVersion = version; + return this; + } + + public Builder version(final VersionRange version) { + this.version = Objects.requireNonNull(version, "version"); + return this; + } + + public Builder from(final StandardContainerLoader value) { + Objects.requireNonNull(value, "value"); + this.name = value.name; + this.version = value.version; return this; } public StandardContainerLoader build() { Objects.requireNonNull(this.name, "name"); Objects.requireNonNull(this.version, "version"); - return new StandardContainerLoader(this); } } @@ -178,7 +181,7 @@ public void write(final JsonWriter out, final StandardContainerLoader value) thr out.beginObject(); out.name("name").value(value.name); - out.name("version").value(value.rawVersion); + out.name("version").value(value.version.toString()); out.endObject(); } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java index d7d5b97..65fe1e1 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java @@ -74,14 +74,6 @@ public Optional icon() { return Optional.ofNullable(this.icon); } - public StandardPluginBranding.Builder toBuilder() { - final Builder builder = new Builder(); - builder.logo = this.logo; - builder.icon = this.icon; - - return builder; - } - @Override public String toString() { return new StringJoiner(", ", StandardPluginBranding.class.getSimpleName() + "[", "]") @@ -90,9 +82,12 @@ public String toString() { .toString(); } - public static final class Builder { + public StandardPluginBranding.Builder toBuilder() { + return new Builder().from(this); + } - @Nullable String logo, icon; + public static final class Builder { + private @Nullable String logo, icon; private Builder() { } @@ -107,6 +102,13 @@ public Builder icon(@Nullable final String icon) { return this; } + public Builder from(final StandardPluginBranding value) { + Objects.requireNonNull(value, "value"); + this.logo = value.logo; + this.icon = value.icon; + return this; + } + public StandardPluginBranding build() { return new StandardPluginBranding(this); } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java index 75b8ddd..ea8be6d 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java @@ -29,6 +29,7 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.util.GsonUtils; @@ -64,14 +65,6 @@ public Optional description() { return Optional.ofNullable(this.description); } - public StandardPluginContributor.Builder toBuilder() { - final Builder builder = new Builder(); - builder.name = this.name; - builder.description = this.description; - - return builder; - } - @Override public String toString() { return new StringJoiner(", ", StandardPluginContributor.class.getSimpleName() + "[", "]") @@ -80,15 +73,19 @@ public String toString() { .toString(); } - public static final class Builder { + public StandardPluginContributor.Builder toBuilder() { + return new Builder().from(this); + } - @Nullable String name, description; + public static final class Builder { + private @MonotonicNonNull String name; + private @Nullable String description; private Builder() { } public Builder name(final String name) { - this.name = Objects.requireNonNull(name); + this.name = Objects.requireNonNull(name, "name"); return this; } @@ -97,9 +94,15 @@ public Builder description(@Nullable final String description) { return this; } + public Builder from(final StandardPluginContributor value) { + Objects.requireNonNull(value, "value"); + this.name = value.name; + this.description = value.description; + return this; + } + public StandardPluginContributor build() { Objects.requireNonNull(this.name, "name"); - return new StandardPluginContributor(this); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java index 1fc302c..78bee3d 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java @@ -30,7 +30,7 @@ import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.plugin.metadata.Constants; import org.spongepowered.plugin.metadata.model.PluginDependency; @@ -46,14 +46,12 @@ public final class StandardPluginDependency implements PluginDependency { private final String id; private final VersionRange version; - private final String rawVersion; private final LoadOrder loadOrder; private final boolean optional; private StandardPluginDependency(final Builder builder) { this.id = builder.id; this.version = builder.version; - this.rawVersion = builder.rawVersion; this.loadOrder = builder.loadOrder; this.optional = builder.optional; } @@ -87,17 +85,6 @@ public boolean optional() { return this.optional; } - public StandardPluginDependency.Builder toBuilder() { - final Builder builder = new Builder(); - builder.id = this.id; - builder.version = this.version; - builder.rawVersion = this.rawVersion; - builder.loadOrder = this.loadOrder; - builder.optional = this.optional; - - return builder; - } - @Override public int hashCode() { return Objects.hashCode(this.id); @@ -119,19 +106,21 @@ public boolean equals(final Object o) { public String toString() { return new StringJoiner(", ", StandardPluginDependency.class.getSimpleName() + "[", "]") .add("id=" + this.id) - .add("version=" + this.rawVersion) + .add("version=" + this.version) .add("loadOrder=" + this.loadOrder) .add("optional=" + this.optional) .toString(); } - public static final class Builder { + public StandardPluginDependency.Builder toBuilder() { + return new Builder().from(this); + } - @Nullable String id; - @Nullable VersionRange version; - @Nullable String rawVersion; - LoadOrder loadOrder = LoadOrder.UNDEFINED; - boolean optional = false; + public static final class Builder { + private @MonotonicNonNull String id; + private @MonotonicNonNull VersionRange version; + private LoadOrder loadOrder = LoadOrder.UNDEFINED; + private boolean optional = false; private Builder() { } @@ -143,7 +132,11 @@ public Builder id(final String id) { public Builder version(final String version) { this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); - this.rawVersion = version; + return this; + } + + public Builder version(final VersionRange version) { + this.version = Objects.requireNonNull(version, "version"); return this; } @@ -157,6 +150,15 @@ public Builder optional(final boolean optional) { return this; } + public Builder from(final StandardPluginDependency value) { + Objects.requireNonNull(value, "value"); + this.id = value.id; + this.version = value.version; + this.loadOrder = value.loadOrder; + this.optional = value.optional; + return this; + } + public StandardPluginDependency build() { Objects.requireNonNull(this.id, "id"); if (!Constants.VALID_ID_PATTERN.matcher(this.id).matches()) { @@ -230,7 +232,7 @@ public void write(final JsonWriter out, final StandardPluginDependency value) th out.beginObject(); out.name("id").value(value.id()); - out.name("version").value(value.rawVersion); + out.name("version").value(value.version().toString()); out.name("load-order").value(value.loadOrder().name().toLowerCase(Locale.ROOT)); out.name("optional").value(value.optional()); out.endObject(); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java index 4e5aed3..d0f5fe9 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java @@ -82,15 +82,6 @@ public Optional issues() { return Optional.ofNullable(this.issues); } - public StandardPluginLinks.Builder toBuilder() { - final Builder builder = new Builder(); - builder.homepage = this.homepage; - builder.source = this.source; - builder.issues = this.issues; - - return builder; - } - @Override public String toString() { return new StringJoiner(", ", StandardPluginLinks.class.getSimpleName() + "[", "]") @@ -100,9 +91,13 @@ public String toString() { .toString(); } + public StandardPluginLinks.Builder toBuilder() { + return new Builder().from(this); + } + public static final class Builder { - @Nullable URL homepage, source, issues; + private @Nullable URL homepage, source, issues; private Builder() { } @@ -122,6 +117,14 @@ public Builder issues(@Nullable final URL issues) { return this; } + public Builder from(final StandardPluginLinks value) { + Objects.requireNonNull(value, "value"); + this.homepage = value.homepage; + this.source = value.source; + this.issues = value.issues; + return this; + } + public StandardPluginLinks build() { return new StandardPluginLinks(this); } From 05fa4651dc50c08529d58b5faff43472eefa3f52 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 31 May 2026 22:11:14 +0200 Subject: [PATCH 03/11] Use new Java features --- .../MissingRequiredFieldException.java | 3 +++ .../builtin/StandardPluginMetadata.java | 3 +-- .../plugin/metadata/util/GsonUtils.java | 20 +++++++------------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MissingRequiredFieldException.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MissingRequiredFieldException.java index 02b8981..402e16f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MissingRequiredFieldException.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MissingRequiredFieldException.java @@ -26,8 +26,11 @@ import com.google.gson.JsonParseException; +import java.io.Serial; + public final class MissingRequiredFieldException extends JsonParseException { + @Serial private static final long serialVersionUID = 223908766837278356L; public MissingRequiredFieldException(final String field) { diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java index 886f983..5213413 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java @@ -102,11 +102,10 @@ public boolean equals(final Object o) { return true; } - if (!(o instanceof StandardPluginMetadata)) { + if (!(o instanceof StandardPluginMetadata other)) { return false; } - final StandardPluginMetadata other = (StandardPluginMetadata) o; return this.id.equals(other.id); } diff --git a/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java b/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java index 85ec3be..dd64264 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java +++ b/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java @@ -88,19 +88,13 @@ public static void writeIfPresent(final JsonWriter out, final String name, f public static void writeIfPresent(final JsonObject out, final String key, final Optional value) { final @Nullable T val = value.orElse(null); - if (val == null) { - return; - } - if (val instanceof JsonElement) { - out.add(key, (JsonElement) val); - } else if (val instanceof String) { - out.addProperty(key, (String) val); - } else if (val instanceof Number) { - out.addProperty(key, (Number) val); - } else if (val instanceof Boolean) { - out.addProperty(key, (Boolean) val); - } else if (val instanceof Character) { - out.addProperty(key, (Character) val); + switch (val) { + case JsonElement jsonElement -> out.add(key, jsonElement); + case String s -> out.addProperty(key, s); + case Number number -> out.addProperty(key, number); + case Boolean b -> out.addProperty(key, b); + case Character c -> out.addProperty(key, c); + case null, default -> {} } } From 562c1b4a66353451f2d2e2cff3d38b4b6ac2042c Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 31 May 2026 22:13:58 +0200 Subject: [PATCH 04/11] Use URIs instead of URLs URL constructor is deprecated --- .../builtin/model/StandardPluginLinks.java | 24 +++++++++---------- .../plugin/metadata/model/PluginLinks.java | 14 +++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java index d0f5fe9..99b097f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java @@ -34,7 +34,7 @@ import org.spongepowered.plugin.metadata.util.GsonUtils; import java.io.IOException; -import java.net.URL; +import java.net.URI; import java.util.HashSet; import java.util.Objects; import java.util.Optional; @@ -45,7 +45,7 @@ public final class StandardPluginLinks implements PluginLinks { private static final StandardPluginLinks NONE = new StandardPluginLinks(); - private final @Nullable URL homepage, source, issues; + private final @Nullable URI homepage, source, issues; private StandardPluginLinks(final Builder builder) { this.homepage = builder.homepage; @@ -68,17 +68,17 @@ public static StandardPluginLinks none() { } @Override - public Optional homepage() { + public Optional homepage() { return Optional.ofNullable(this.homepage); } @Override - public Optional source() { + public Optional source() { return Optional.ofNullable(this.source); } @Override - public Optional issues() { + public Optional issues() { return Optional.ofNullable(this.issues); } @@ -97,22 +97,22 @@ public StandardPluginLinks.Builder toBuilder() { public static final class Builder { - private @Nullable URL homepage, source, issues; + private @Nullable URI homepage, source, issues; private Builder() { } - public Builder homepage(@Nullable final URL homepage) { + public Builder homepage(@Nullable final URI homepage) { this.homepage = homepage; return this; } - public Builder source(@Nullable final URL source) { + public Builder source(@Nullable final URI source) { this.source = source; return this; } - public Builder issues(@Nullable final URL issues) { + public Builder issues(@Nullable final URI issues) { this.issues = issues; return this; } @@ -156,13 +156,13 @@ public StandardPluginLinks.Builder read(final JsonReader in) throws IOException } switch (key) { case "homepage": - builder.homepage(new URL(in.nextString())); + builder.homepage(URI.create(in.nextString())); break; case "source": - builder.source(new URL(in.nextString())); + builder.source(URI.create(in.nextString())); break; case "issues": - builder.issues(new URL(in.nextString())); + builder.issues(URI.create(in.nextString())); break; } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java index 9ceb08a..ab74ce1 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java @@ -28,7 +28,7 @@ import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.builtin.model.StandardPluginLinks; -import java.net.URL; +import java.net.URI; import java.util.Optional; /** @@ -41,17 +41,17 @@ public interface PluginLinks { /** - * @return The {@link URL homepage} or {@link Optional#empty()} otherwise + * @return The {@link URI homepage} or {@link Optional#empty()} otherwise */ - Optional homepage(); + Optional homepage(); /** - * @return The {@link URL source} or {@link Optional#empty()} otherwise + * @return The {@link URI source} or {@link Optional#empty()} otherwise */ - Optional source(); + Optional source(); /** - * @return The {@link URL issues} or {@link Optional#empty()} otherwise + * @return The {@link URI issues} or {@link Optional#empty()} otherwise */ - Optional issues(); + Optional issues(); } From d69ab4cf38ead0bdd7668360ca5f942a07ac4add Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 31 May 2026 22:57:57 +0200 Subject: [PATCH 05/11] Migrate models to records when applicable --- src/main/java/module-info.java | 2 +- .../plugin/metadata/Inheritable.java | 4 +- .../metadata/builtin/MetadataContainer.java | 12 +- .../metadata/builtin/StandardInheritable.java | 65 +++-- .../metadata/builtin/adapter/Adapters.java | 70 +++++ .../adapter/ContainerLoaderAdapter.java | 102 ++++++++ .../PluginBrandingAdapter.java} | 93 +------ .../PluginContributorAdapter.java} | 86 +----- .../adapter/PluginDependencyAdapter.java | 115 ++++++++ .../PluginLinksAdapter.java} | 108 +------- .../metadata/builtin/model/Adapters.java | 65 ----- .../model/StandardContainerLoader.java | 193 -------------- .../model/StandardPluginDependency.java | 246 ------------------ .../metadata/model/ContainerLoader.java | 63 ++++- .../plugin/metadata/model/PluginBranding.java | 77 ++++-- .../metadata/model/PluginContributor.java | 58 ++++- .../metadata/model/PluginDependency.java | 113 +++++--- .../plugin/metadata/model/PluginLinks.java | 79 ++++-- 18 files changed, 650 insertions(+), 901 deletions(-) create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java rename src/main/java/org/spongepowered/plugin/metadata/builtin/{model/StandardPluginBranding.java => adapter/PluginBrandingAdapter.java} (54%) rename src/main/java/org/spongepowered/plugin/metadata/builtin/{model/StandardPluginContributor.java => adapter/PluginContributorAdapter.java} (54%) create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java rename src/main/java/org/spongepowered/plugin/metadata/builtin/{model/StandardPluginLinks.java => adapter/PluginLinksAdapter.java} (52%) delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/model/Adapters.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 3b6d94c..354aa4e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,7 +1,7 @@ module org.spongepowered.plugin.metadata { exports org.spongepowered.plugin.metadata; exports org.spongepowered.plugin.metadata.builtin; - exports org.spongepowered.plugin.metadata.builtin.model; + exports org.spongepowered.plugin.metadata.builtin.adapter; exports org.spongepowered.plugin.metadata.model; exports org.spongepowered.plugin.metadata.util; diff --git a/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java b/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java index cd6669d..1de121e 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java +++ b/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java @@ -61,7 +61,7 @@ public interface Inheritable { /** * @return The {@link PluginContributor contributors} as an unmodifiable {@link List}. */ - List contributors(); + List contributors(); /** * Gets the {@link PluginDependency plugin dependency} by {@link String id}. @@ -75,7 +75,7 @@ public interface Inheritable { /** * @return The {@link PluginDependency dependencies} as an unmodifiable {@link Collection}. */ - Collection dependencies(); + Collection dependencies(); /** * Gets the {@link T property} by {@link String key}. diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index a11b593..166df4e 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -38,8 +38,8 @@ import org.spongepowered.plugin.metadata.Container; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.Adapters; -import org.spongepowered.plugin.metadata.builtin.model.StandardContainerLoader; +import org.spongepowered.plugin.metadata.builtin.adapter.Adapters; +import org.spongepowered.plugin.metadata.model.ContainerLoader; import java.lang.reflect.Type; import java.util.Collection; @@ -54,7 +54,7 @@ public final class MetadataContainer implements Container { private final String license; - private final StandardContainerLoader loader; + private final ContainerLoader loader; @Nullable private final Inheritable globalMetadata; private final Map metadata; @@ -70,7 +70,7 @@ private MetadataContainer(final Builder builder) { } @Override - public StandardContainerLoader loader() { + public ContainerLoader loader() { return this.loader; } @@ -111,10 +111,10 @@ public static final class Builder { private final Map metadata = new LinkedHashMap<>(); private @MonotonicNonNull String license; - private @MonotonicNonNull StandardContainerLoader loader; + private @MonotonicNonNull ContainerLoader loader; private @Nullable Inheritable globalMetadata; - public Builder loader(final StandardContainerLoader loader) { + public Builder loader(final ContainerLoader loader) { this.loader = Objects.requireNonNull(loader, "loader"); return this; } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java index 4bf5d22..e04462b 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java @@ -37,12 +37,11 @@ import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; -import org.spongepowered.plugin.metadata.builtin.model.Adapters; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginBranding; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginContributor; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginDependency; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginLinks; +import org.spongepowered.plugin.metadata.builtin.adapter.Adapters; +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; import org.spongepowered.plugin.metadata.util.GsonUtils; import java.lang.reflect.Type; @@ -61,10 +60,10 @@ public class StandardInheritable implements Inheritable { protected final ArtifactVersion version; - protected final StandardPluginBranding branding; - protected final StandardPluginLinks links; - protected final List contributors; - protected final Map dependencies; + protected final PluginBranding branding; + protected final PluginLinks links; + protected final List contributors; + protected final Map dependencies; protected final Map properties; @SuppressWarnings("rawtypes") @@ -83,17 +82,17 @@ public ArtifactVersion version() { } @Override - public StandardPluginBranding branding() { + public PluginBranding branding() { return this.branding; } @Override - public StandardPluginLinks links() { + public PluginLinks links() { return this.links; } @Override - public List contributors() { + public List contributors() { return this.contributors; } @@ -103,7 +102,7 @@ public Optional dependency(final String id) { } @Override - public Collection dependencies() { + public Collection dependencies() { return this.dependencies.values(); } @@ -137,12 +136,12 @@ protected StringJoiner stringJoiner() { @SuppressWarnings("unchecked") public abstract static class AbstractBuilder> { - protected final List contributors = new LinkedList<>(); - protected final Map dependencies = new LinkedHashMap<>(); + protected final List contributors = new LinkedList<>(); + protected final Map dependencies = new LinkedHashMap<>(); protected final Map properties = new LinkedHashMap<>(); protected ArtifactVersion version = NullVersion.instance(); - protected StandardPluginBranding branding = StandardPluginBranding.none(); - protected StandardPluginLinks links = StandardPluginLinks.none(); + protected PluginBranding branding = PluginBranding.none(); + protected PluginLinks links = PluginLinks.none(); protected AbstractBuilder() { } @@ -157,47 +156,47 @@ public B version(final @Nullable ArtifactVersion version) { return (B) this; } - public B branding(final StandardPluginBranding branding) { + public B branding(final PluginBranding branding) { this.branding = Objects.requireNonNull(branding, "branding"); return (B) this; } - public B links(final StandardPluginLinks links) { + public B links(final PluginLinks links) { this.links = Objects.requireNonNull(links, "links"); return (B) this; } - public B contributors(final Collection contributors) { + public B contributors(final Collection contributors) { Objects.requireNonNull(contributors, "contributors"); this.contributors.clear(); this.contributors.addAll(contributors); return (B) this; } - public B addContributors(final Collection contributors) { + public B addContributors(final Collection contributors) { this.contributors.addAll(Objects.requireNonNull(contributors, "contributors")); return (B) this; } - public B addContributor(final StandardPluginContributor contributor) { + public B addContributor(final PluginContributor contributor) { this.contributors.add(Objects.requireNonNull(contributor, "contributor")); return (B) this; } - public B dependencies(final Collection dependencies) { + public B dependencies(final Collection dependencies) { Objects.requireNonNull(dependencies, "dependencies"); this.dependencies.clear(); return this.addDependencies(dependencies); } - public B addDependencies(final Collection dependencies) { - for (final StandardPluginDependency element : Objects.requireNonNull(dependencies, "dependencies")) { + public B addDependencies(final Collection dependencies) { + for (final PluginDependency element : Objects.requireNonNull(dependencies, "dependencies")) { this.dependencies.put(Objects.requireNonNull(element, "element").id(), element); } return (B) this; } - public B addDependency(final StandardPluginDependency dependency) { + public B addDependency(final PluginDependency dependency) { this.dependencies.put(Objects.requireNonNull(dependency, "dependency").id(), dependency); return (B) this; } @@ -224,16 +223,16 @@ public B merge(final StandardInheritable value) { if (this.version == NullVersion.instance()) { this.version = value.version; } - if (this.branding == StandardPluginBranding.none()) { + if (this.branding == PluginBranding.none()) { this.branding = value.branding; } - if (this.links == StandardPluginLinks.none()) { + if (this.links == PluginLinks.none()) { this.links = value.links; } if (this.contributors.isEmpty()) { this.contributors.addAll(value.contributors); } - for (final Map.Entry entry : value.dependencies.entrySet()) { + for (final Map.Entry entry : value.dependencies.entrySet()) { this.dependencies.putIfAbsent(entry.getKey(), entry.getValue()); } for (final Map.Entry entry : value.properties.entrySet()) { @@ -284,8 +283,8 @@ public StandardInheritable deserialize(final JsonElement element, final Type typ builder.version(GsonUtils.get(obj, "version", JsonElement::getAsString).orElse(null)); GsonUtils.consumeIfPresent(obj, "branding", e -> builder.branding(Adapters.Deserializers.PLUGIN_BRANDING.fromJsonTree(e).build())); GsonUtils.consumeIfPresent(obj, "links", e -> builder.links(Adapters.Deserializers.PLUGIN_LINKS.fromJsonTree(e).build())); - GsonUtils.consumeIfPresent(obj, "contributors", e -> builder.contributors(GsonUtils.read((JsonArray) e, Adapters.Deserializers.PLUGIN_CONTRIBUTOR, LinkedList::new).stream().map(StandardPluginContributor.Builder::build).collect(Collectors.toList()))); - GsonUtils.consumeIfPresent(obj, "dependencies", e -> builder.dependencies(GsonUtils.read((JsonArray) e, Adapters.Deserializers.PLUGIN_DEPENDENCY, LinkedHashSet::new).stream().map(StandardPluginDependency.Builder::build).collect(Collectors.toList()))); + GsonUtils.consumeIfPresent(obj, "contributors", e -> builder.contributors(GsonUtils.read((JsonArray) e, Adapters.Deserializers.PLUGIN_CONTRIBUTOR, LinkedList::new).stream().map(PluginContributor.Builder::build).collect(Collectors.toList()))); + GsonUtils.consumeIfPresent(obj, "dependencies", e -> builder.dependencies(GsonUtils.read((JsonArray) e, Adapters.Deserializers.PLUGIN_DEPENDENCY, LinkedHashSet::new).stream().map(PluginDependency.Builder::build).collect(Collectors.toList()))); GsonUtils.consumeIfPresent(obj, "properties", e -> builder.properties(GsonUtils.read((JsonObject) e, JsonElement::getAsString, LinkedHashMap::new))); return builder.build(); @@ -295,8 +294,8 @@ public StandardInheritable deserialize(final JsonElement element, final Type typ public JsonElement serialize(final StandardInheritable value, final Type type, final JsonSerializationContext context) { final JsonObject obj = new JsonObject(); GsonUtils.applyIfValid(obj, value, p -> p.version != NullVersion.instance(), (o, v) -> o.addProperty("version", v.version.toString())); - GsonUtils.applyIfValid(obj, value, p -> p.branding != StandardPluginBranding.none(), (o, v) -> o.add("branding", Adapters.Serializers.PLUGIN_BRANDING.toJsonTree(v.branding))); - GsonUtils.applyIfValid(obj, value, p -> p.links != StandardPluginLinks.none(), (o, v) -> o.add("links", Adapters.Serializers.PLUGIN_LINKS.toJsonTree(v.links))); + GsonUtils.applyIfValid(obj, value, p -> p.branding != PluginBranding.none(), (o, v) -> o.add("branding", Adapters.Serializers.PLUGIN_BRANDING.toJsonTree(v.branding))); + GsonUtils.applyIfValid(obj, value, p -> p.links != PluginLinks.none(), (o, v) -> o.add("links", Adapters.Serializers.PLUGIN_LINKS.toJsonTree(v.links))); GsonUtils.applyIfValid(obj, value, p -> !p.contributors.isEmpty(), (o, v) -> o.add("contributors", GsonUtils.write(Adapters.Serializers.PLUGIN_CONTRIBUTOR, v.contributors))); GsonUtils.applyIfValid(obj, value, p -> !p.dependencies.isEmpty(), (o, v) -> o.add("dependencies", GsonUtils.write(Adapters.Serializers.PLUGIN_DEPENDENCY, v.dependencies()))); GsonUtils.applyIfValid(obj, value, p -> !p.properties.isEmpty(), (o, v) -> o.add("properties", GsonUtils.write(e -> new JsonPrimitive(e.toString()), v.properties))); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java new file mode 100644 index 0000000..bba1472 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java @@ -0,0 +1,70 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.TypeAdapter; +import org.spongepowered.plugin.metadata.model.ContainerLoader; +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; + +public final class Adapters { + + private Adapters() { + } + + public static final class Deserializers { + + public static final TypeAdapter CONTAINER_LOADER = new ContainerLoaderAdapter.Deserializer(); + + public static final TypeAdapter PLUGIN_BRANDING = new PluginBrandingAdapter.Deserializer(); + + public static final TypeAdapter PLUGIN_CONTRIBUTOR = new PluginContributorAdapter.Deserializer(); + + public static final TypeAdapter PLUGIN_DEPENDENCY = new PluginDependencyAdapter.Deserializer(); + + public static final TypeAdapter PLUGIN_LINKS = new PluginLinksAdapter.Deserializer(); + + private Deserializers() { + } + } + + public static final class Serializers { + + public static final TypeAdapter CONTAINER_LOADER = new ContainerLoaderAdapter.Serializer(); + + public static final TypeAdapter PLUGIN_BRANDING = new PluginBrandingAdapter.Serializer(); + + public static final TypeAdapter PLUGIN_CONTRIBUTOR = new PluginContributorAdapter.Serializer(); + + public static final TypeAdapter PLUGIN_DEPENDENCY = new PluginDependencyAdapter.Serializer(); + + public static final TypeAdapter PLUGIN_LINKS = new PluginLinksAdapter.Serializer(); + + private Serializers() { + } + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java new file mode 100644 index 0000000..1dc07c8 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java @@ -0,0 +1,102 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.spongepowered.plugin.metadata.model.ContainerLoader; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public final class ContainerLoaderAdapter { + + public static final class Deserializer extends TypeAdapter { + + @Override + public void write(final JsonWriter out, final ContainerLoader.Builder builder) throws IOException { + throw new UnsupportedOperationException("This adapter is for reading only"); + } + + @Override + public ContainerLoader.Builder read(final JsonReader in) throws IOException { + Objects.requireNonNull(in, "in"); + + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + in.beginObject(); + final Set processedKeys = new HashSet<>(); + final ContainerLoader.Builder builder = ContainerLoader.builder(); + while (in.hasNext()) { + final String key = in.nextName(); + if (!processedKeys.add(key)) { + throw new JsonParseException(String.format("Duplicate id key '%s' in %s", key, in)); + } + switch (key) { + case "name": + builder.name(in.nextString()); + break; + case "version": + builder.version(in.nextString()); + break; + } + } + in.endObject(); + + return builder; + } + } + + public static final class Serializer extends TypeAdapter { + + @Override + public void write(final JsonWriter out, final ContainerLoader value) throws IOException { + Objects.requireNonNull(out, "out"); + + if (value == null) { + out.nullValue(); + return; + } + + out.beginObject(); + out.name("name").value(value.name()); + out.name("version").value(value.version().toString()); + out.endObject(); + } + + @Override + public ContainerLoader read(final JsonReader in) throws IOException { + throw new UnsupportedOperationException("This adapter is for writing only"); + } + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java similarity index 54% rename from src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java rename to src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java index 65fe1e1..393e676 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginBranding.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java @@ -22,107 +22,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin.metadata.builtin.model; +package org.spongepowered.plugin.metadata.builtin.adapter; import com.google.gson.JsonParseException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.model.PluginBranding; import org.spongepowered.plugin.metadata.util.GsonUtils; import java.io.IOException; import java.util.HashSet; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import java.util.StringJoiner; -public final class StandardPluginBranding implements PluginBranding { +public final class PluginBrandingAdapter { - private static final StandardPluginBranding NONE = new StandardPluginBranding(); - - private final @Nullable String logo, icon; - - private StandardPluginBranding(final Builder builder) { - this.logo = builder.logo; - this.icon = builder.icon; - } - - private StandardPluginBranding() { - this.logo = null; - this.icon = null; - } - - public static Builder builder() { - return new Builder(); - } - - public static StandardPluginBranding none() { - return StandardPluginBranding.NONE; - } - - @Override - public Optional logo() { - return Optional.ofNullable(this.logo); - } - - @Override - public Optional icon() { - return Optional.ofNullable(this.icon); - } - - @Override - public String toString() { - return new StringJoiner(", ", StandardPluginBranding.class.getSimpleName() + "[", "]") - .add("logo=" + this.logo) - .add("icon=" + this.icon) - .toString(); - } - - public StandardPluginBranding.Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private @Nullable String logo, icon; - - private Builder() { - } - - public Builder logo(@Nullable final String logo) { - this.logo = logo; - return this; - } - - public Builder icon(@Nullable final String icon) { - this.icon = icon; - return this; - } - - public Builder from(final StandardPluginBranding value) { - Objects.requireNonNull(value, "value"); - this.logo = value.logo; - this.icon = value.icon; - return this; - } - - public StandardPluginBranding build() { - return new StandardPluginBranding(this); - } - } - - public static final class Deserializer extends TypeAdapter { + public static final class Deserializer extends TypeAdapter { @Override - public void write(final JsonWriter out, final Builder builder) throws IOException { + public void write(final JsonWriter out, final PluginBranding.Builder builder) throws IOException { throw new UnsupportedOperationException("This adapter is for reading only"); } @Override - public Builder read(final JsonReader in) throws IOException { + public PluginBranding.Builder read(final JsonReader in) throws IOException { Objects.requireNonNull(in, "in"); if (in.peek() == JsonToken.NULL) { @@ -132,7 +57,7 @@ public Builder read(final JsonReader in) throws IOException { in.beginObject(); final Set processedKeys = new HashSet<>(); - final StandardPluginBranding.Builder builder = StandardPluginBranding.builder(); + final PluginBranding.Builder builder = PluginBranding.builder(); while (in.hasNext()) { final String key = in.nextName(); if (!processedKeys.add(key)) { @@ -153,10 +78,10 @@ public Builder read(final JsonReader in) throws IOException { } } - public static final class Serializer extends TypeAdapter { + public static final class Serializer extends TypeAdapter { @Override - public void write(final JsonWriter out, final StandardPluginBranding value) throws IOException { + public void write(final JsonWriter out, final PluginBranding value) throws IOException { Objects.requireNonNull(out, "out"); if (value == null) { @@ -171,7 +96,7 @@ public void write(final JsonWriter out, final StandardPluginBranding value) thro } @Override - public StandardPluginBranding read(final JsonReader in) throws IOException { + public PluginBranding read(final JsonReader in) throws IOException { throw new UnsupportedOperationException("This adapter is for writing only"); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java similarity index 54% rename from src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java rename to src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java index ea8be6d..4083eb1 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginContributor.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java @@ -22,100 +22,32 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin.metadata.builtin.model; +package org.spongepowered.plugin.metadata.builtin.adapter; import com.google.gson.JsonParseException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.util.GsonUtils; import java.io.IOException; import java.util.HashSet; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import java.util.StringJoiner; -public final class StandardPluginContributor implements PluginContributor { +public final class PluginContributorAdapter { - private final String name; - @Nullable private final String description; - - private StandardPluginContributor(final Builder builder) { - this.name = builder.name; - this.description = builder.description; - } - - public static Builder builder() { - return new Builder(); - } - - @Override - public String name() { - return this.name; - } - - @Override - public Optional description() { - return Optional.ofNullable(this.description); - } - - @Override - public String toString() { - return new StringJoiner(", ", StandardPluginContributor.class.getSimpleName() + "[", "]") - .add("name=" + this.name) - .add("description=" + this.description) - .toString(); - } - - public StandardPluginContributor.Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private @MonotonicNonNull String name; - private @Nullable String description; - - private Builder() { - } - - public Builder name(final String name) { - this.name = Objects.requireNonNull(name, "name"); - return this; - } - - public Builder description(@Nullable final String description) { - this.description = description; - return this; - } - - public Builder from(final StandardPluginContributor value) { - Objects.requireNonNull(value, "value"); - this.name = value.name; - this.description = value.description; - return this; - } - - public StandardPluginContributor build() { - Objects.requireNonNull(this.name, "name"); - return new StandardPluginContributor(this); - } - } - - public static final class Deserializer extends TypeAdapter { + public static final class Deserializer extends TypeAdapter { @Override - public void write(final JsonWriter out, final Builder builder) throws IOException { + public void write(final JsonWriter out, final PluginContributor.Builder builder) throws IOException { throw new UnsupportedOperationException("This adapter is for reading only"); } @Override - public Builder read(final JsonReader in) throws IOException { + public PluginContributor.Builder read(final JsonReader in) throws IOException { Objects.requireNonNull(in, "in"); if (in.peek() == JsonToken.NULL) { @@ -125,7 +57,7 @@ public Builder read(final JsonReader in) throws IOException { in.beginObject(); final Set processedKeys = new HashSet<>(); - final StandardPluginContributor.Builder builder = StandardPluginContributor.builder(); + final PluginContributor.Builder builder = PluginContributor.builder(); while (in.hasNext()) { final String key = in.nextName(); if (!processedKeys.add(key)) { @@ -145,10 +77,10 @@ public Builder read(final JsonReader in) throws IOException { } } - public static final class Serializer extends TypeAdapter { + public static final class Serializer extends TypeAdapter { @Override - public void write(final JsonWriter out, final StandardPluginContributor value) throws IOException { + public void write(final JsonWriter out, final PluginContributor value) throws IOException { Objects.requireNonNull(out, "out"); if (value == null) { @@ -163,7 +95,7 @@ public void write(final JsonWriter out, final StandardPluginContributor value) t } @Override - public StandardPluginContributor read(final JsonReader in) throws IOException { + public PluginContributor read(final JsonReader in) throws IOException { throw new UnsupportedOperationException("This adapter is for writing only"); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java new file mode 100644 index 0000000..c4049fc --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java @@ -0,0 +1,115 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import org.spongepowered.plugin.metadata.model.PluginDependency; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; + + +public final class PluginDependencyAdapter { + + public static final class Deserializer extends TypeAdapter { + + @Override + public void write(final JsonWriter out, final PluginDependency.Builder builder) throws IOException { + throw new UnsupportedOperationException("This adapter is for reading only"); + } + + @Override + public PluginDependency.Builder read(final JsonReader in) throws IOException { + Objects.requireNonNull(in, "in"); + + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + + in.beginObject(); + final Set processedKeys = new HashSet<>(); + final PluginDependency.Builder builder = PluginDependency.builder(); + while (in.hasNext()) { + final String key = in.nextName(); + if (!processedKeys.add(key)) { + throw new JsonParseException(String.format("Duplicate dependency key '%s' in %s", key, in)); + } + switch (key) { + case "id": + builder.id(in.nextString()); + break; + case "version": + builder.version(in.nextString()); + break; + case "optional": + builder.optional(in.nextBoolean()); + break; + case "load-order": + try { + builder.loadOrder(PluginDependency.LoadOrder.valueOf(in.nextString().toUpperCase())); + } catch (final Exception ex) { + throw new JsonParseException(String.format("Invalid load order found in '%s'", in), ex); + } + break; + } + } + in.endObject(); + return builder; + } + } + + public static final class Serializer extends TypeAdapter { + + @Override + public void write(final JsonWriter out, final PluginDependency value) throws IOException { + Objects.requireNonNull(out, "out"); + + if (value == null) { + out.nullValue(); + return; + } + + out.beginObject(); + out.name("id").value(value.id()); + out.name("version").value(value.version().toString()); + out.name("load-order").value(value.loadOrder().name().toLowerCase(Locale.ROOT)); + out.name("optional").value(value.optional()); + out.endObject(); + } + + @Override + public PluginDependency read(final JsonReader in) throws IOException { + throw new UnsupportedOperationException("This adapter is for writing only"); + } + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java similarity index 52% rename from src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java rename to src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java index 99b097f..294ee4e 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java @@ -22,14 +22,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package org.spongepowered.plugin.metadata.builtin.model; +package org.spongepowered.plugin.metadata.builtin.adapter; import com.google.gson.JsonParseException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.model.PluginLinks; import org.spongepowered.plugin.metadata.util.GsonUtils; @@ -37,108 +36,19 @@ import java.net.URI; import java.util.HashSet; import java.util.Objects; -import java.util.Optional; import java.util.Set; -import java.util.StringJoiner; -public final class StandardPluginLinks implements PluginLinks { +public final class PluginLinksAdapter { - private static final StandardPluginLinks NONE = new StandardPluginLinks(); - - private final @Nullable URI homepage, source, issues; - - private StandardPluginLinks(final Builder builder) { - this.homepage = builder.homepage; - this.source = builder.source; - this.issues = builder.issues; - } - - private StandardPluginLinks() { - this.homepage = null; - this.source = null; - this.issues = null; - } - - public static Builder builder() { - return new Builder(); - } - - public static StandardPluginLinks none() { - return StandardPluginLinks.NONE; - } - - @Override - public Optional homepage() { - return Optional.ofNullable(this.homepage); - } - - @Override - public Optional source() { - return Optional.ofNullable(this.source); - } - - @Override - public Optional issues() { - return Optional.ofNullable(this.issues); - } - - @Override - public String toString() { - return new StringJoiner(", ", StandardPluginLinks.class.getSimpleName() + "[", "]") - .add("homepage=" + this.homepage) - .add("source=" + this.source) - .add("issues=" + this.issues) - .toString(); - } - - public StandardPluginLinks.Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - - private @Nullable URI homepage, source, issues; - - private Builder() { - } - - public Builder homepage(@Nullable final URI homepage) { - this.homepage = homepage; - return this; - } - - public Builder source(@Nullable final URI source) { - this.source = source; - return this; - } - - public Builder issues(@Nullable final URI issues) { - this.issues = issues; - return this; - } - - public Builder from(final StandardPluginLinks value) { - Objects.requireNonNull(value, "value"); - this.homepage = value.homepage; - this.source = value.source; - this.issues = value.issues; - return this; - } - - public StandardPluginLinks build() { - return new StandardPluginLinks(this); - } - } - - public static final class Deserializer extends TypeAdapter { + public static final class Deserializer extends TypeAdapter { @Override - public void write(final JsonWriter in, final Builder builder) throws IOException { + public void write(final JsonWriter in, final PluginLinks.Builder builder) throws IOException { throw new UnsupportedOperationException("This adapter is for reading only"); } @Override - public StandardPluginLinks.Builder read(final JsonReader in) throws IOException { + public PluginLinks.Builder read(final JsonReader in) throws IOException { Objects.requireNonNull(in, "in"); if (in.peek() == JsonToken.NULL) { @@ -148,7 +58,7 @@ public StandardPluginLinks.Builder read(final JsonReader in) throws IOException in.beginObject(); final Set processedKeys = new HashSet<>(); - final StandardPluginLinks.Builder builder = StandardPluginLinks.builder(); + final PluginLinks.Builder builder = PluginLinks.builder(); while (in.hasNext()) { final String key = in.nextName(); if (!processedKeys.add(key)) { @@ -172,10 +82,10 @@ public StandardPluginLinks.Builder read(final JsonReader in) throws IOException } } - public static final class Serializer extends TypeAdapter { + public static final class Serializer extends TypeAdapter { @Override - public void write(final JsonWriter out, final StandardPluginLinks value) throws IOException { + public void write(final JsonWriter out, final PluginLinks value) throws IOException { Objects.requireNonNull(out, "out"); if (value == null) { @@ -191,7 +101,7 @@ public void write(final JsonWriter out, final StandardPluginLinks value) throws } @Override - public StandardPluginLinks read(final JsonReader out) throws IOException { + public PluginLinks read(final JsonReader out) throws IOException { throw new UnsupportedOperationException("This adapter is for writing only"); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/Adapters.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/Adapters.java deleted file mode 100644 index 812914a..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/Adapters.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.model; - -import com.google.gson.TypeAdapter; - -public final class Adapters { - - private Adapters() { - } - - public static final class Deserializers { - - public static final TypeAdapter CONTAINER_LOADER = new StandardContainerLoader.Deserializer(); - - public static final TypeAdapter PLUGIN_BRANDING = new StandardPluginBranding.Deserializer(); - - public static final TypeAdapter PLUGIN_CONTRIBUTOR = new StandardPluginContributor.Deserializer(); - - public static final TypeAdapter PLUGIN_DEPENDENCY = new StandardPluginDependency.Deserializer(); - - public static final TypeAdapter PLUGIN_LINKS = new StandardPluginLinks.Deserializer(); - - private Deserializers() { - } - } - - public static final class Serializers { - - public static final TypeAdapter CONTAINER_LOADER = new StandardContainerLoader.Serializer(); - - public static final TypeAdapter PLUGIN_BRANDING = new StandardPluginBranding.Serializer(); - - public static final TypeAdapter PLUGIN_CONTRIBUTOR = new StandardPluginContributor.Serializer(); - - public static final TypeAdapter PLUGIN_DEPENDENCY = new StandardPluginDependency.Serializer(); - - public static final TypeAdapter PLUGIN_LINKS = new StandardPluginLinks.Serializer(); - - private Serializers() { - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java deleted file mode 100644 index 757b133..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardContainerLoader.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.model; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.spongepowered.plugin.metadata.model.ContainerLoader; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; - -public final class StandardContainerLoader implements ContainerLoader { - - private final String name; - private final VersionRange version; - - private StandardContainerLoader(final Builder builder) { - this.name = builder.name; - this.version = builder.version; - } - - public static Builder builder() { - return new Builder(); - } - - @Override - public String name() { - return this.name; - } - - @Override - public VersionRange version() { - return this.version; - } - - @Override - public int hashCode() { - return Objects.hashCode(this.name); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof StandardContainerLoader)) { - return false; - } - final StandardContainerLoader that = (StandardContainerLoader) o; - return this.name.equals(that.name); - } - - @Override - public String toString() { - return new StringJoiner(", ", StandardContainerLoader.class.getSimpleName() + "[", "]") - .add("name=" + this.name) - .add("version=" + this.version) - .toString(); - } - - public StandardContainerLoader.Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - - private @MonotonicNonNull String name; - private @MonotonicNonNull VersionRange version; - - private Builder() { - } - - public Builder name(final String name) { - this.name = Objects.requireNonNull(name, "name"); - return this; - } - - public Builder version(final String version) { - this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); - return this; - } - - public Builder version(final VersionRange version) { - this.version = Objects.requireNonNull(version, "version"); - return this; - } - - public Builder from(final StandardContainerLoader value) { - Objects.requireNonNull(value, "value"); - this.name = value.name; - this.version = value.version; - return this; - } - - public StandardContainerLoader build() { - Objects.requireNonNull(this.name, "name"); - Objects.requireNonNull(this.version, "version"); - return new StandardContainerLoader(this); - } - } - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public StandardContainerLoader.Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final StandardContainerLoader.Builder builder = StandardContainerLoader.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate id key '%s' in %s", key, in)); - } - switch (key) { - case "name": - builder.name(in.nextString()); - break; - case "version": - builder.version(in.nextString()); - break; - } - } - in.endObject(); - - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final StandardContainerLoader value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - out.name("name").value(value.name); - out.name("version").value(value.version.toString()); - out.endObject(); - } - - @Override - public StandardContainerLoader read(final JsonReader in) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java deleted file mode 100644 index 78bee3d..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/model/StandardPluginDependency.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.model; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.spongepowered.plugin.metadata.Constants; -import org.spongepowered.plugin.metadata.model.PluginDependency; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; -import java.util.StringJoiner; - - -public final class StandardPluginDependency implements PluginDependency { - - private final String id; - private final VersionRange version; - private final LoadOrder loadOrder; - private final boolean optional; - - private StandardPluginDependency(final Builder builder) { - this.id = builder.id; - this.version = builder.version; - this.loadOrder = builder.loadOrder; - this.optional = builder.optional; - } - - /** - * Returns a new {@link Builder} for creating a PluginDependency. - * - * @return A builder - */ - public static Builder builder() { - return new Builder(); - } - - @Override - public String id() { - return this.id; - } - - @Override - public VersionRange version() { - return this.version; - } - - @Override - public LoadOrder loadOrder() { - return this.loadOrder; - } - - @Override - public boolean optional() { - return this.optional; - } - - @Override - public int hashCode() { - return Objects.hashCode(this.id); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof StandardPluginDependency)) { - return false; - } - final StandardPluginDependency that = (StandardPluginDependency) o; - return this.id.equals(that.id); - } - - @Override - public String toString() { - return new StringJoiner(", ", StandardPluginDependency.class.getSimpleName() + "[", "]") - .add("id=" + this.id) - .add("version=" + this.version) - .add("loadOrder=" + this.loadOrder) - .add("optional=" + this.optional) - .toString(); - } - - public StandardPluginDependency.Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private @MonotonicNonNull String id; - private @MonotonicNonNull VersionRange version; - private LoadOrder loadOrder = LoadOrder.UNDEFINED; - private boolean optional = false; - - private Builder() { - } - - public Builder id(final String id) { - this.id = Objects.requireNonNull(id, "id"); - return this; - } - - public Builder version(final String version) { - this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); - return this; - } - - public Builder version(final VersionRange version) { - this.version = Objects.requireNonNull(version, "version"); - return this; - } - - public Builder loadOrder(final LoadOrder loadOrder) { - this.loadOrder = Objects.requireNonNull(loadOrder, "load order"); - return this; - } - - public Builder optional(final boolean optional) { - this.optional = optional; - return this; - } - - public Builder from(final StandardPluginDependency value) { - Objects.requireNonNull(value, "value"); - this.id = value.id; - this.version = value.version; - this.loadOrder = value.loadOrder; - this.optional = value.optional; - return this; - } - - public StandardPluginDependency build() { - Objects.requireNonNull(this.id, "id"); - if (!Constants.VALID_ID_PATTERN.matcher(this.id).matches()) { - throw new IllegalStateException(String.format("Dependency with supplied ID '{%s}' is invalid. %s", this.id, - Constants.INVALID_ID_REQUIREMENTS_MESSAGE)); - } - Objects.requireNonNull(this.version, "version"); - - return new StandardPluginDependency(this); - } - } - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final StandardPluginDependency.Builder builder = StandardPluginDependency.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate dependency key '%s' in %s", key, in)); - } - switch (key) { - case "id": - builder.id(in.nextString()); - break; - case "version": - builder.version(in.nextString()); - break; - case "optional": - builder.optional(in.nextBoolean()); - break; - case "load-order": - try { - builder.loadOrder(StandardPluginDependency.LoadOrder.valueOf(in.nextString().toUpperCase())); - } catch (final Exception ex) { - throw new JsonParseException(String.format("Invalid load order found in '%s'", in), ex); - } - break; - } - } - in.endObject(); - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final StandardPluginDependency value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - out.name("id").value(value.id()); - out.name("version").value(value.version().toString()); - out.name("load-order").value(value.loadOrder().name().toLowerCase(Locale.ROOT)); - out.name("optional").value(value.optional()); - out.endObject(); - } - - @Override - public StandardPluginDependency read(final JsonReader in) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java b/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java index 9b7a948..fe68a99 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java @@ -25,24 +25,65 @@ package org.spongepowered.plugin.metadata.model; import org.apache.maven.artifact.versioning.VersionRange; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.plugin.metadata.Container; -import org.spongepowered.plugin.metadata.builtin.model.StandardContainerLoader; + +import java.util.Objects; /** * Specification for an entity representing the "loader" of a {@link Container container}. *

* Consult the vendor for further information on how this is used. - * @see StandardContainerLoader StandardContainerLoader, for a generic implementation + * + * @param name The {@link String name} + * @param version The {@link VersionRange version}, as a maven range. */ -public interface ContainerLoader { +public record ContainerLoader(String name, VersionRange version) { + + public ContainerLoader { + Objects.requireNonNull(name, "name"); + Objects.requireNonNull(version, "version"); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().from(this); + } + + public static final class Builder { + private @MonotonicNonNull String name; + private @MonotonicNonNull VersionRange version; + + private Builder() { + } + + public Builder name(final String name) { + this.name = Objects.requireNonNull(name, "name"); + return this; + } + + public Builder version(final String version) { + this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); + return this; + } + + public Builder version(final VersionRange version) { + this.version = Objects.requireNonNull(version, "version"); + return this; + } - /** - * @return The {@link String name} - */ - String name(); + public Builder from(final ContainerLoader value) { + Objects.requireNonNull(value, "value"); + this.name = value.name(); + this.version = value.version(); + return this; + } - /** - * @return The {@link VersionRange version}, as a maven range. - */ - VersionRange version(); + public ContainerLoader build() { + return new ContainerLoader(this.name, this.version); + } + } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java index 2874221..d859eeb 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java @@ -24,10 +24,11 @@ */ package org.spongepowered.plugin.metadata.model; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginBranding; +import java.util.Objects; import java.util.Optional; /** @@ -35,29 +36,55 @@ * or {@link PluginMetadata plugin metadata}. *

* Consult the vendor for further information on how this is used. - * @see StandardPluginBranding StandardPluginBranding, for a generic implementation + * + * @param icon The {@link String} that represents the location of the icon. + * @param logo The {@link String} that represents the location of the logo. */ -public interface PluginBranding { - - /** - * Gets the {@link String} that represents the location of the icon. - *

- * Consult the vendor on the composition of this value. For example, it - * could be an absolute path or relative path (commonly fed to a {@link java.io.File} or - * {@link java.nio.file.Path}), or a serialized {@link java.net.URI}. - * - * @return The icon or {@link Optional#empty()} otherwise - */ - Optional icon(); - - /** - * Gets the {@link String} that represents the location of the logo. - *

- * Consult the vendor on the composition of this value. For example, it - * could be an absolute path or relative path (commonly fed to a {@link java.io.File} or - * {@link java.nio.file.Path}), or a serialized {@link java.net.URI}. - * - * @return The icon or {@link Optional#empty()} otherwise - */ - Optional logo(); +public record PluginBranding(Optional icon, Optional logo) { + private static final PluginBranding NONE = new PluginBranding(Optional.empty(), Optional.empty()); + + public PluginBranding { + Objects.requireNonNull(icon, "icon"); + Objects.requireNonNull(logo, "logo"); + } + + public static PluginBranding none() { + return PluginBranding.NONE; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().from(this); + } + + public static final class Builder { + private Optional logo = Optional.empty(), icon = Optional.empty(); + + private Builder() { + } + + public Builder logo(@Nullable final String logo) { + this.logo = Optional.ofNullable(logo); + return this; + } + + public Builder icon(@Nullable final String icon) { + this.icon = Optional.ofNullable(icon); + return this; + } + + public Builder from(final PluginBranding value) { + Objects.requireNonNull(value, "value"); + this.logo = value.logo(); + this.icon = value.icon(); + return this; + } + + public PluginBranding build() { + return new PluginBranding(this.icon, this.logo); + } + } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java index 871e4e9..3e5a234 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java @@ -24,10 +24,12 @@ */ package org.spongepowered.plugin.metadata.model; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginContributor; +import java.util.Objects; import java.util.Optional; /** @@ -35,17 +37,51 @@ * or {@link PluginMetadata plugin metadata}. *

* Consult the vendor for further information on how this is used. - * @see StandardPluginContributor StandardPluginContributor, for a generic implementation + * + * @param name The {@link String name} + * @param description The {@link String description} or {@link Optional#empty()} otherwise */ -public interface PluginContributor { +public record PluginContributor(String name, Optional description) { + + public PluginContributor { + Objects.requireNonNull(name, "name"); + Objects.requireNonNull(description, "description"); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().from(this); + } + + public static final class Builder { + private @MonotonicNonNull String name; + private Optional description = Optional.empty(); + + private Builder() { + } + + public Builder name(final String name) { + this.name = Objects.requireNonNull(name, "name"); + return this; + } + + public Builder description(@Nullable final String description) { + this.description = Optional.ofNullable(description); + return this; + } - /** - * @return The {@link String name} - */ - String name(); + public Builder from(final PluginContributor value) { + Objects.requireNonNull(value, "value"); + this.name = value.name; + this.description = value.description; + return this; + } - /** - * @return The {@link String description} or {@link Optional#empty()} otherwise - */ - Optional description(); + public PluginContributor build() { + return new PluginContributor(this.name, this.description); + } + } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java index fe20e54..1a8131f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java @@ -25,48 +25,101 @@ package org.spongepowered.plugin.metadata.model; import org.apache.maven.artifact.versioning.VersionRange; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.spongepowered.plugin.metadata.Constants; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginDependency; + +import java.util.Objects; /** * Specification for an entity considered to be a "dependency" for an {@link Inheritable inheritable} * or {@link PluginMetadata plugin metadata}. *

* Consult the vendor for further information on how this is used. - * @see StandardPluginDependency StandardPluginDependency, for a generic implementation + * + *

Ids must conform to the following requirements:

+ * + * + * + * @param id The {@link String id} + * @param version The {@link VersionRange version}, as a maven range. + * @param loadOrder The {@link LoadOrder load order} + * @param optional Whether this dependency is optional */ -public interface PluginDependency { +public record PluginDependency(String id, VersionRange version, LoadOrder loadOrder, boolean optional) { - /** - * Gets the {@link String id}. - * - *

Ids must conform to the following requirements:

- * - * - * @return The id - */ - String id(); + public PluginDependency { + Objects.requireNonNull(id, "id"); + Objects.requireNonNull(version, "version"); + Objects.requireNonNull(loadOrder, "loadOrder"); - /** - * @return The {@link VersionRange version}, as a maven range. - */ - VersionRange version(); + if (!Constants.VALID_ID_PATTERN.matcher(id).matches()) { + throw new IllegalStateException(String.format("Dependency with supplied ID '{%s}' is invalid. %s", id, + Constants.INVALID_ID_REQUIREMENTS_MESSAGE)); + } + } - /** - * @return The {@link LoadOrder load order} - */ - LoadOrder loadOrder(); + public static Builder builder() { + return new Builder(); + } - /** - * @return True if optional, false if not - */ - boolean optional(); + public Builder toBuilder() { + return new Builder().from(this); + } + + public static final class Builder { + private @MonotonicNonNull String id; + private @MonotonicNonNull VersionRange version; + private LoadOrder loadOrder = LoadOrder.UNDEFINED; + private boolean optional = false; + + private Builder() { + } + + public Builder id(final String id) { + this.id = Objects.requireNonNull(id, "id"); + return this; + } + + public Builder version(final String version) { + this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); + return this; + } + + public Builder version(final VersionRange version) { + this.version = Objects.requireNonNull(version, "version"); + return this; + } + + public Builder loadOrder(final LoadOrder loadOrder) { + this.loadOrder = Objects.requireNonNull(loadOrder, "load order"); + return this; + } + + public Builder optional(final boolean optional) { + this.optional = optional; + return this; + } + + public Builder from(final PluginDependency value) { + Objects.requireNonNull(value, "value"); + this.id = value.id; + this.version = value.version; + this.loadOrder = value.loadOrder; + this.optional = value.optional; + return this; + } + + public PluginDependency build() { + return new PluginDependency(this.id, this.version, this.loadOrder, this.optional); + } + } /** * Represents the ordering of how dependencies are loaded versus others. @@ -75,7 +128,7 @@ public interface PluginDependency { * documented here. It is recommended to consult with that entity on any * further behavioral changes. */ - enum LoadOrder { + public enum LoadOrder { /** * The plugin can be loaded regardless of when the dependency is loaded. */ diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java index ab74ce1..59ea140 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java @@ -24,11 +24,12 @@ */ package org.spongepowered.plugin.metadata.model; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.model.StandardPluginLinks; import java.net.URI; +import java.util.Objects; import java.util.Optional; /** @@ -36,22 +37,64 @@ * or {@link PluginMetadata plugin metadata}. *

* Consult the vendor for further information on how this is used. - * @see StandardPluginLinks StandardPluginLinks, for a generic implementation + * + * @param homepage The {@link URI homepage} + * @param source The {@link URI source} + * @param issues The {@link URI issues} */ -public interface PluginLinks { - - /** - * @return The {@link URI homepage} or {@link Optional#empty()} otherwise - */ - Optional homepage(); - - /** - * @return The {@link URI source} or {@link Optional#empty()} otherwise - */ - Optional source(); - - /** - * @return The {@link URI issues} or {@link Optional#empty()} otherwise - */ - Optional issues(); +public record PluginLinks(Optional homepage, Optional source, Optional issues) { + private static final PluginLinks NONE = new PluginLinks(Optional.empty(), Optional.empty(), Optional.empty()); + + public PluginLinks { + Objects.requireNonNull(homepage, "homepage"); + Objects.requireNonNull(source, "source"); + Objects.requireNonNull(issues, "issues"); + } + + public static PluginLinks none() { + return PluginLinks.NONE; + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + return new Builder().from(this); + } + + public static final class Builder { + + private Optional homepage = Optional.empty(), source = Optional.empty(), issues = Optional.empty(); + + private Builder() { + } + + public Builder homepage(@Nullable final URI homepage) { + this.homepage = Optional.ofNullable(homepage); + return this; + } + + public Builder source(@Nullable final URI source) { + this.source = Optional.ofNullable(source); + return this; + } + + public Builder issues(@Nullable final URI issues) { + this.issues = Optional.ofNullable(issues); + return this; + } + + public Builder from(final PluginLinks value) { + Objects.requireNonNull(value, "value"); + this.homepage = value.homepage; + this.source = value.source; + this.issues = value.issues; + return this; + } + + public PluginLinks build() { + return new PluginLinks(this.homepage, this.source, this.issues); + } + } } From 3308f377e110fdcc6227d7ef522d23f2ec3b298a Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Mon, 1 Jun 2026 23:41:09 +0200 Subject: [PATCH 06/11] Simplify (de)serialization --- src/main/java/module-info.java | 7 +- .../metadata/builtin/MetadataContainer.java | 86 ------------- .../metadata/builtin/MetadataParser.java | 50 ++++++-- .../metadata/builtin/StandardInheritable.java | 48 +------ .../builtin/StandardPluginMetadata.java | 52 -------- .../metadata/builtin/adapter/Adapters.java | 70 ---------- .../adapter/ContainerLoaderAdapter.java | 102 --------------- .../adapter/MetadataContainerAdapter.java | 103 +++++++++++++++ .../adapter/PluginBrandingAdapter.java | 103 --------------- .../adapter/PluginContributorAdapter.java | 102 --------------- .../adapter/PluginDependencyAdapter.java | 115 ----------------- .../builtin/adapter/PluginLinksAdapter.java | 108 ---------------- .../adapter/StandardInheritableAdapter.java | 77 +++++++++++ ...dardPluginMetadataBuilderDeserializer.java | 46 +++++++ .../StandardPluginMetadataSerializer.java | 48 +++++++ .../adapter/model/ContainerLoaderAdapter.java | 58 +++++++++ .../adapter/model/PluginBrandingAdapter.java | 57 +++++++++ .../model/PluginContributorAdapter.java | 57 +++++++++ .../model/PluginDependencyAdapter.java | 58 +++++++++ .../adapter/model/PluginLinksAdapter.java | 63 +++++++++ .../builtin/adapter/util/GsonUtils.java | 86 +++++++++++++ .../version/ArtifactVersionAdapter.java} | 22 +++- .../adapter/version/VersionRangeAdapter.java | 49 +++++++ .../metadata/model/ContainerLoader.java | 43 ------- .../plugin/metadata/model/PluginBranding.java | 37 ------ .../metadata/model/PluginContributor.java | 39 ------ .../metadata/model/PluginDependency.java | 57 --------- .../plugin/metadata/model/PluginLinks.java | 44 ------- .../plugin/metadata/util/GsonUtils.java | 120 ------------------ 29 files changed, 763 insertions(+), 1144 deletions(-) delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginBrandingAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginContributorAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLinksAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java rename src/main/java/org/spongepowered/plugin/metadata/{util/package-info.java => builtin/adapter/version/ArtifactVersionAdapter.java} (60%) create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/VersionRangeAdapter.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 354aa4e..c97f14e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,11 +1,12 @@ module org.spongepowered.plugin.metadata { exports org.spongepowered.plugin.metadata; + exports org.spongepowered.plugin.metadata.model; exports org.spongepowered.plugin.metadata.builtin; exports org.spongepowered.plugin.metadata.builtin.adapter; - exports org.spongepowered.plugin.metadata.model; - exports org.spongepowered.plugin.metadata.util; + exports org.spongepowered.plugin.metadata.builtin.adapter.model; + exports org.spongepowered.plugin.metadata.builtin.adapter.version; requires transitive com.google.gson; requires static transitive org.checkerframework.checker.qual; requires static transitive maven.artifact; // Generated module name, may change? -} \ No newline at end of file +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index 166df4e..27eaf2f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -24,28 +24,16 @@ */ package org.spongepowered.plugin.metadata.builtin; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Container; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.builtin.adapter.Adapters; import org.spongepowered.plugin.metadata.model.ContainerLoader; -import java.lang.reflect.Type; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -168,78 +156,4 @@ public MetadataContainer build() throws IllegalStateException, InvalidVersionSpe return new MetadataContainer(this); } } - - public static final class Serializer implements JsonSerializer, JsonDeserializer { - - @Override - public MetadataContainer deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) - throws JsonParseException { - final JsonObject obj = element.getAsJsonObject(); - if (!obj.has("loader")) { - throw new MissingRequiredFieldException("loader"); - } - if (!obj.has("license")) { - throw new MissingRequiredFieldException("license"); - } - if (!obj.has("plugins")) { - throw new MissingRequiredFieldException("plugins"); - } - - final Builder builder = new Builder() - .loader(Adapters.Deserializers.CONTAINER_LOADER.fromJsonTree(obj.get("loader")).build()) - .license(obj.get("license").getAsString()); - - final JsonElement globalElement = obj.get("global"); - @Nullable StandardInheritable inheritable = null; - if (globalElement instanceof JsonObject) { - inheritable = context.deserialize(globalElement, StandardInheritable.class); - builder.globalMetadata(inheritable); - } - - final JsonElement pluginsElement = obj.get("plugins"); - final List pluginObjects = new LinkedList<>(); - - if (pluginsElement.isJsonArray()) { - for (final JsonElement pluginElement : ((JsonArray) pluginsElement)) { - if (pluginElement.isJsonObject()) { - pluginObjects.add((JsonObject) pluginElement); - } - } - } - - if (pluginObjects.isEmpty()) { - throw new JsonParseException("No plugin metadata has been specified for the 'plugins' tag!"); - } - - for (final JsonObject pluginObject : pluginObjects) { - final StandardPluginMetadata.Builder pluginBuilder = context.deserialize(pluginObject, StandardPluginMetadata.Builder.class); - if (inheritable != null) { - pluginBuilder.merge(inheritable); - } - - builder.addMetadata(pluginBuilder.build()); - } - - try { - return builder.build(); - } catch (final InvalidVersionSpecificationException e) { - throw new JsonParseException(e); - } - } - - @Override - public JsonElement serialize(final MetadataContainer value, final Type type, final JsonSerializationContext context) { - final JsonObject obj = new JsonObject(); - obj.add("loader", Adapters.Serializers.CONTAINER_LOADER.toJsonTree(value.loader)); - obj.addProperty("license", value.license); - - final JsonArray plugins = new JsonArray(); - for (final PluginMetadata metadata : value.metadata()) { - plugins.add(context.serialize(metadata, StandardPluginMetadata.class)); - } - obj.add("plugins", plugins); - - return obj; - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java index 05f624c..7a05a84 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java @@ -28,6 +28,16 @@ import com.google.gson.GsonBuilder; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.builtin.adapter.MetadataContainerAdapter; +import org.spongepowered.plugin.metadata.builtin.adapter.StandardInheritableAdapter; +import org.spongepowered.plugin.metadata.builtin.adapter.StandardPluginMetadataBuilderDeserializer; +import org.spongepowered.plugin.metadata.builtin.adapter.StandardPluginMetadataSerializer; +import org.spongepowered.plugin.metadata.builtin.adapter.model.*; +import org.spongepowered.plugin.metadata.builtin.adapter.version.ArtifactVersionAdapter; +import org.spongepowered.plugin.metadata.builtin.adapter.version.VersionRangeAdapter; +import org.spongepowered.plugin.metadata.model.*; import java.io.BufferedReader; import java.io.IOException; @@ -38,35 +48,47 @@ import java.util.Objects; public final class MetadataParser { + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(ContainerLoader.class, new ContainerLoaderAdapter()) + .registerTypeAdapter(PluginBranding.class, new PluginBrandingAdapter()) + .registerTypeAdapter(PluginContributor.class, new PluginContributorAdapter()) + .registerTypeAdapter(PluginDependency.class, new PluginDependencyAdapter()) + .registerTypeAdapter(PluginLinks.class, new PluginLinksAdapter()) + .registerTypeAdapter(ArtifactVersion.class, new ArtifactVersionAdapter()) + .registerTypeAdapter(VersionRange.class, new VersionRangeAdapter()) + .registerTypeAdapter(MetadataContainer.class, new MetadataContainerAdapter()) + .registerTypeAdapter(StandardInheritable.class, new StandardInheritableAdapter()) + .registerTypeAdapter(StandardPluginMetadata.Builder.class, new StandardPluginMetadataBuilderDeserializer()) + .registerTypeAdapter(StandardPluginMetadata.class, new StandardPluginMetadataSerializer()) + .create(); private MetadataParser() { } + public static Gson gson() { + return MetadataParser.GSON; + } + public static GsonBuilder gsonBuilder() { - return new GsonBuilder() - .registerTypeAdapter(MetadataContainer.class, new MetadataContainer.Serializer()) - .registerTypeAdapter(StandardInheritable.class, new StandardInheritable.Serializer()) - .registerTypeAdapter(StandardPluginMetadata.Builder.class, new StandardPluginMetadata.Deserializer()) - .registerTypeAdapter(StandardPluginMetadata.class, new StandardPluginMetadata.Serializer()) - ; + return MetadataParser.GSON.newBuilder(); } /** * Reads a {@link MetadataContainer container} from a given {@link Path path} using the default {@link Gson deserializer} - * (retrieved from {@link #gsonBuilder()}). + * (retrieved from {@link #gson()}). * * @param path The path * @return The container * @throws IOException if the container fails to be read */ public static MetadataContainer read(final Path path) throws IOException { - return MetadataParser.read(path, MetadataParser.gsonBuilder().create()); + return MetadataParser.read(path, MetadataParser.gson()); } /** * Reads a {@link MetadataContainer container} from a given {@link Path path} with configured {@link Gson deserializer}. *

- * To get a standard deserializer, {@link MetadataParser#gsonBuilder()} is available. + * To get a standard deserializer, {@link MetadataParser#gson()} is available. * @param path The path * @param gson The deserializer * @return The container @@ -83,20 +105,20 @@ public static MetadataContainer read(final Path path, final Gson gson) throws IO /** * Reads a {@link MetadataContainer container} from a given {@link Reader reader} using the default {@link Gson deserializer} - * (retrieved from {@link #gsonBuilder()}). + * (retrieved from {@link #gson()}). * * @param reader The reader * @return The container * @throws IOException if the container fails to be read */ public static MetadataContainer read(final Reader reader) throws IOException { - return MetadataParser.read(reader, MetadataParser.gsonBuilder().create()); + return MetadataParser.read(reader, MetadataParser.gson()); } /** * Reads a {@link MetadataContainer container} from a given {@link Reader reader} with configured {@link Gson deserializer}. *

- * To get a standard deserializer, {@link MetadataParser#gsonBuilder()} is available. + * To get a standard deserializer, {@link MetadataParser#gson()} is available. * @param reader The reader * @param gson The deserializer * @return The container @@ -114,7 +136,7 @@ public static MetadataContainer read(final Reader reader, final Gson gson) throw /** * Writes a {@link MetadataContainer container} to the given {@link Path path} using the configured {@link Gson serializer}. *

- * To get a standard serializer, {@link MetadataParser#gsonBuilder()} is available. + * To get a standard serializer, {@link MetadataParser#gson()} is available. * @param path The path * @param container The container * @param gson The serializer @@ -137,7 +159,7 @@ public static void write(final Path path, final MetadataContainer container, fin /** * Writes a {@link MetadataContainer container} to the given {@link Path path} using the configured {@link Gson serializer}. *

- * To get a standard serializer, {@link MetadataParser#gsonBuilder()} is available. + * To get a standard serializer, {@link MetadataParser#gson()} is available. * @param writer The writer * @param container The container * @param gson The serializer diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java index e04462b..eff1db1 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java @@ -24,39 +24,24 @@ */ package org.spongepowered.plugin.metadata.builtin; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; -import org.spongepowered.plugin.metadata.builtin.adapter.Adapters; import org.spongepowered.plugin.metadata.model.PluginBranding; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; import org.spongepowered.plugin.metadata.model.PluginLinks; -import org.spongepowered.plugin.metadata.util.GsonUtils; -import java.lang.reflect.Type; import java.util.Collection; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; -import java.util.stream.Collectors; -@SuppressWarnings("unchecked") public class StandardInheritable implements Inheritable { protected final ArtifactVersion version; @@ -66,7 +51,7 @@ public class StandardInheritable implements Inheritable { protected final Map dependencies; protected final Map properties; - @SuppressWarnings("rawtypes") + @SuppressWarnings({"rawtypes", "unchecked"}) protected StandardInheritable(final AbstractBuilder builder) { this.version = builder.version; this.branding = builder.branding; @@ -271,35 +256,4 @@ protected StandardInheritable build0() { return new StandardInheritable(this); } } - - public static final class Serializer implements JsonSerializer, JsonDeserializer { - - @Override - public StandardInheritable deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) - throws JsonParseException { - - final JsonObject obj = element.getAsJsonObject(); - final Builder builder = new Builder(); - builder.version(GsonUtils.get(obj, "version", JsonElement::getAsString).orElse(null)); - GsonUtils.consumeIfPresent(obj, "branding", e -> builder.branding(Adapters.Deserializers.PLUGIN_BRANDING.fromJsonTree(e).build())); - GsonUtils.consumeIfPresent(obj, "links", e -> builder.links(Adapters.Deserializers.PLUGIN_LINKS.fromJsonTree(e).build())); - GsonUtils.consumeIfPresent(obj, "contributors", e -> builder.contributors(GsonUtils.read((JsonArray) e, Adapters.Deserializers.PLUGIN_CONTRIBUTOR, LinkedList::new).stream().map(PluginContributor.Builder::build).collect(Collectors.toList()))); - GsonUtils.consumeIfPresent(obj, "dependencies", e -> builder.dependencies(GsonUtils.read((JsonArray) e, Adapters.Deserializers.PLUGIN_DEPENDENCY, LinkedHashSet::new).stream().map(PluginDependency.Builder::build).collect(Collectors.toList()))); - GsonUtils.consumeIfPresent(obj, "properties", e -> builder.properties(GsonUtils.read((JsonObject) e, JsonElement::getAsString, LinkedHashMap::new))); - - return builder.build(); - } - - @Override - public JsonElement serialize(final StandardInheritable value, final Type type, final JsonSerializationContext context) { - final JsonObject obj = new JsonObject(); - GsonUtils.applyIfValid(obj, value, p -> p.version != NullVersion.instance(), (o, v) -> o.addProperty("version", v.version.toString())); - GsonUtils.applyIfValid(obj, value, p -> p.branding != PluginBranding.none(), (o, v) -> o.add("branding", Adapters.Serializers.PLUGIN_BRANDING.toJsonTree(v.branding))); - GsonUtils.applyIfValid(obj, value, p -> p.links != PluginLinks.none(), (o, v) -> o.add("links", Adapters.Serializers.PLUGIN_LINKS.toJsonTree(v.links))); - GsonUtils.applyIfValid(obj, value, p -> !p.contributors.isEmpty(), (o, v) -> o.add("contributors", GsonUtils.write(Adapters.Serializers.PLUGIN_CONTRIBUTOR, v.contributors))); - GsonUtils.applyIfValid(obj, value, p -> !p.dependencies.isEmpty(), (o, v) -> o.add("dependencies", GsonUtils.write(Adapters.Serializers.PLUGIN_DEPENDENCY, v.dependencies()))); - GsonUtils.applyIfValid(obj, value, p -> !p.properties.isEmpty(), (o, v) -> o.add("properties", GsonUtils.write(e -> new JsonPrimitive(e.toString()), v.properties))); - return obj; - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java index 5213413..cc5f7f5 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java @@ -24,22 +24,12 @@ */ package org.spongepowered.plugin.metadata.builtin; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Constants; import org.spongepowered.plugin.metadata.Container; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.util.GsonUtils; -import java.lang.reflect.Type; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; @@ -176,46 +166,4 @@ protected StandardPluginMetadata build0() { } } - public static final class Deserializer implements JsonDeserializer { - - @Override - public StandardPluginMetadata.Builder deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) - throws JsonParseException { - final JsonObject obj = element.getAsJsonObject(); - if (!obj.has("id")) { - throw new MissingRequiredFieldException("id"); - } - if (!obj.has("entrypoint")) { - throw new MissingRequiredFieldException("entrypoint"); - } - final StandardPluginMetadata.Builder builder = new StandardPluginMetadata.Builder(); - builder - .id(obj.get("id").getAsString()) - .entrypoint(obj.get("entrypoint").getAsString()); - GsonUtils.consumeIfPresent(obj, "name", e -> builder.name(e.getAsString())); - GsonUtils.consumeIfPresent(obj, "description", e -> builder.description(e.getAsString())); - builder.merge(context.deserialize(element, StandardInheritable.class)); - - return builder; - } - } - - public static final class Serializer implements JsonSerializer { - - @Override - public JsonElement serialize(final StandardPluginMetadata value, final Type type, final JsonSerializationContext context) { - final JsonObject obj = new JsonObject(); - obj.addProperty("id", value.id); - obj.addProperty("entrypoint", value.entrypoint); - GsonUtils.writeIfPresent(obj, "name", value.name()); - GsonUtils.writeIfPresent(obj, "description", value.description()); - - final JsonObject inheritableElement = (JsonObject) context.serialize(value, StandardInheritable.class); - // TODO Sort this? - for (final Map.Entry entry : inheritableElement.entrySet()) { - obj.add(entry.getKey(), entry.getValue()); - } - return obj; - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java deleted file mode 100644 index bba1472..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/Adapters.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.adapter; - -import com.google.gson.TypeAdapter; -import org.spongepowered.plugin.metadata.model.ContainerLoader; -import org.spongepowered.plugin.metadata.model.PluginBranding; -import org.spongepowered.plugin.metadata.model.PluginContributor; -import org.spongepowered.plugin.metadata.model.PluginDependency; -import org.spongepowered.plugin.metadata.model.PluginLinks; - -public final class Adapters { - - private Adapters() { - } - - public static final class Deserializers { - - public static final TypeAdapter CONTAINER_LOADER = new ContainerLoaderAdapter.Deserializer(); - - public static final TypeAdapter PLUGIN_BRANDING = new PluginBrandingAdapter.Deserializer(); - - public static final TypeAdapter PLUGIN_CONTRIBUTOR = new PluginContributorAdapter.Deserializer(); - - public static final TypeAdapter PLUGIN_DEPENDENCY = new PluginDependencyAdapter.Deserializer(); - - public static final TypeAdapter PLUGIN_LINKS = new PluginLinksAdapter.Deserializer(); - - private Deserializers() { - } - } - - public static final class Serializers { - - public static final TypeAdapter CONTAINER_LOADER = new ContainerLoaderAdapter.Serializer(); - - public static final TypeAdapter PLUGIN_BRANDING = new PluginBrandingAdapter.Serializer(); - - public static final TypeAdapter PLUGIN_CONTRIBUTOR = new PluginContributorAdapter.Serializer(); - - public static final TypeAdapter PLUGIN_DEPENDENCY = new PluginDependencyAdapter.Serializer(); - - public static final TypeAdapter PLUGIN_LINKS = new PluginLinksAdapter.Serializer(); - - private Serializers() { - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java deleted file mode 100644 index 1dc07c8..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/ContainerLoaderAdapter.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.adapter; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.spongepowered.plugin.metadata.model.ContainerLoader; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -public final class ContainerLoaderAdapter { - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final ContainerLoader.Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public ContainerLoader.Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final ContainerLoader.Builder builder = ContainerLoader.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate id key '%s' in %s", key, in)); - } - switch (key) { - case "name": - builder.name(in.nextString()); - break; - case "version": - builder.version(in.nextString()); - break; - } - } - in.endObject(); - - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final ContainerLoader value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - out.name("name").value(value.name()); - out.name("version").value(value.version().toString()); - out.endObject(); - } - - @Override - public ContainerLoader read(final JsonReader in) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java new file mode 100644 index 0000000..d30f482 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java @@ -0,0 +1,103 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.*; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.builtin.MetadataContainer; +import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; +import org.spongepowered.plugin.metadata.model.ContainerLoader; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; +import java.util.LinkedList; +import java.util.List; + +public final class MetadataContainerAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public MetadataContainer deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) + throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + + final MetadataContainer.Builder builder = new MetadataContainer.Builder() + .loader(context.deserialize(GsonUtils.require(obj, "loader"), ContainerLoader.class)) + .license(GsonUtils.require(obj, "license").getAsString()); + + final JsonElement globalElement = obj.get("global"); + @Nullable StandardInheritable inheritable = null; + if (globalElement instanceof JsonObject) { + inheritable = context.deserialize(globalElement, StandardInheritable.class); + builder.globalMetadata(inheritable); + } + + final JsonElement pluginsElement = GsonUtils.require(obj, "plugins"); + final List pluginObjects = new LinkedList<>(); + + if (pluginsElement.isJsonArray()) { + for (final JsonElement pluginElement : ((JsonArray) pluginsElement)) { + if (pluginElement.isJsonObject()) { + pluginObjects.add((JsonObject) pluginElement); + } + } + } + + if (pluginObjects.isEmpty()) { + throw new JsonParseException("No plugin metadata has been specified for the 'plugins' tag!"); + } + + for (final JsonObject pluginObject : pluginObjects) { + final StandardPluginMetadata.Builder pluginBuilder = context.deserialize(pluginObject, StandardPluginMetadata.Builder.class); + if (inheritable != null) { + pluginBuilder.merge(inheritable); + } + builder.addMetadata(pluginBuilder.build()); + } + + try { + return builder.build(); + } catch (final InvalidVersionSpecificationException e) { + throw new JsonParseException(e); + } + } + + @Override + public JsonElement serialize(final MetadataContainer value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + obj.add("loader", context.serialize(value.loader(), ContainerLoader.class)); + obj.addProperty("license", value.license()); + + final JsonArray plugins = new JsonArray(); + for (final PluginMetadata metadata : value.metadata()) { + plugins.add(context.serialize(metadata, StandardPluginMetadata.class)); + } + obj.add("plugins", plugins); + + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java deleted file mode 100644 index 393e676..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginBrandingAdapter.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.adapter; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.spongepowered.plugin.metadata.model.PluginBranding; -import org.spongepowered.plugin.metadata.util.GsonUtils; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -public final class PluginBrandingAdapter { - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginBranding.Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public PluginBranding.Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final PluginBranding.Builder builder = PluginBranding.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate branding key '%s' in %s", key, in)); - } - switch (key) { - case "logo": - builder.logo(in.nextString()); - break; - case "icon": - builder.icon(in.nextString()); - break; - } - } - in.endObject(); - - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginBranding value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - GsonUtils.writeIfPresent(out, "logo", value.logo()); - GsonUtils.writeIfPresent(out, "icon", value.icon()); - out.endObject(); - } - - @Override - public PluginBranding read(final JsonReader in) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java deleted file mode 100644 index 4083eb1..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginContributorAdapter.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.adapter; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.spongepowered.plugin.metadata.model.PluginContributor; -import org.spongepowered.plugin.metadata.util.GsonUtils; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -public final class PluginContributorAdapter { - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginContributor.Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public PluginContributor.Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final PluginContributor.Builder builder = PluginContributor.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate contributor key '%s' in %s", key, in)); - } - switch (key) { - case "name": - builder.name(in.nextString()); - break; - case "description": - builder.description(in.nextString()); - break; - } - } - in.endObject(); - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginContributor value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - out.name("name").value(value.name()); - GsonUtils.writeIfPresent(out, "description", value.description()); - out.endObject(); - } - - @Override - public PluginContributor read(final JsonReader in) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java deleted file mode 100644 index c4049fc..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginDependencyAdapter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.adapter; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.spongepowered.plugin.metadata.model.PluginDependency; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; - - -public final class PluginDependencyAdapter { - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginDependency.Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public PluginDependency.Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final PluginDependency.Builder builder = PluginDependency.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate dependency key '%s' in %s", key, in)); - } - switch (key) { - case "id": - builder.id(in.nextString()); - break; - case "version": - builder.version(in.nextString()); - break; - case "optional": - builder.optional(in.nextBoolean()); - break; - case "load-order": - try { - builder.loadOrder(PluginDependency.LoadOrder.valueOf(in.nextString().toUpperCase())); - } catch (final Exception ex) { - throw new JsonParseException(String.format("Invalid load order found in '%s'", in), ex); - } - break; - } - } - in.endObject(); - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginDependency value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - out.name("id").value(value.id()); - out.name("version").value(value.version().toString()); - out.name("load-order").value(value.loadOrder().name().toLowerCase(Locale.ROOT)); - out.name("optional").value(value.optional()); - out.endObject(); - } - - @Override - public PluginDependency read(final JsonReader in) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java deleted file mode 100644 index 294ee4e..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/PluginLinksAdapter.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin.adapter; - -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import org.spongepowered.plugin.metadata.model.PluginLinks; -import org.spongepowered.plugin.metadata.util.GsonUtils; - -import java.io.IOException; -import java.net.URI; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -public final class PluginLinksAdapter { - - public static final class Deserializer extends TypeAdapter { - - @Override - public void write(final JsonWriter in, final PluginLinks.Builder builder) throws IOException { - throw new UnsupportedOperationException("This adapter is for reading only"); - } - - @Override - public PluginLinks.Builder read(final JsonReader in) throws IOException { - Objects.requireNonNull(in, "in"); - - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - - in.beginObject(); - final Set processedKeys = new HashSet<>(); - final PluginLinks.Builder builder = PluginLinks.builder(); - while (in.hasNext()) { - final String key = in.nextName(); - if (!processedKeys.add(key)) { - throw new JsonParseException(String.format("Duplicate links key '%s' in %s", key, in)); - } - switch (key) { - case "homepage": - builder.homepage(URI.create(in.nextString())); - break; - case "source": - builder.source(URI.create(in.nextString())); - break; - case "issues": - builder.issues(URI.create(in.nextString())); - break; - } - } - in.endObject(); - - return builder; - } - } - - public static final class Serializer extends TypeAdapter { - - @Override - public void write(final JsonWriter out, final PluginLinks value) throws IOException { - Objects.requireNonNull(out, "out"); - - if (value == null) { - out.nullValue(); - return; - } - - out.beginObject(); - GsonUtils.writeIfPresent(out, "homepage", value.homepage()); - GsonUtils.writeIfPresent(out, "source", value.source()); - GsonUtils.writeIfPresent(out, "issues", value.issues()); - out.endObject(); - } - - @Override - public PluginLinks read(final JsonReader out) throws IOException { - throw new UnsupportedOperationException("This adapter is for writing only"); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java new file mode 100644 index 0000000..5d71243 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java @@ -0,0 +1,77 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.*; +import org.spongepowered.plugin.metadata.builtin.NullVersion; +import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; +import java.util.LinkedHashMap; + +public final class StandardInheritableAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public StandardInheritable deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new StandardInheritable.Builder() + .version(GsonUtils.optional(obj, "version").map(JsonElement::getAsString).orElse(null)) + .branding(GsonUtils.optional(obj, "branding").map(v -> context.deserialize(v, PluginBranding.class)).orElseGet(PluginBranding::none)) + .links(GsonUtils.optional(obj, "links").map(v -> context.deserialize(v, PluginLinks.class)).orElseGet(PluginLinks::none)) + .contributors(GsonUtils.stream(obj, "contributors").map(v -> context.deserialize(v, PluginContributor.class)).toList()) + .dependencies(GsonUtils.stream(obj, "dependencies").map(v -> context.deserialize(v, PluginDependency.class)).toList()) + .properties(GsonUtils.deserializeMap(obj.get("properties"), JsonElement::getAsString, LinkedHashMap::new)) + .build(); + } + + @Override + public JsonElement serialize(final StandardInheritable value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + if (value.version() != NullVersion.instance()) { + obj.add("version", context.serialize(value.version(), ArtifactVersion.class)); + } + if (value.branding() != PluginBranding.none()) { + obj.add("branding", context.serialize(value.branding(), PluginBranding.class)); + } + if (value.links() != PluginLinks.none()) { + obj.add("links", context.serialize(value.links(), PluginLinks.class)); + } + if (!value.contributors().isEmpty()) { + obj.add("contributors", GsonUtils.toArray(value.contributors().stream().map(v -> context.serialize(v, PluginContributor.class)))); + } + if (!value.dependencies().isEmpty()) { + obj.add("dependencies", GsonUtils.toArray(value.dependencies().stream().map(v -> context.serialize(v, PluginDependency.class)))); + } + if (!value.properties().isEmpty()) { + obj.add("properties", GsonUtils.serializeMap(value.properties(), v -> new JsonPrimitive(v.toString()))); + } + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java new file mode 100644 index 0000000..af8ed67 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java @@ -0,0 +1,46 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.*; +import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; + +public final class StandardPluginMetadataBuilderDeserializer implements JsonDeserializer { + + @Override + public StandardPluginMetadata.Builder deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new StandardPluginMetadata.Builder() + .id(GsonUtils.require(obj, "id").getAsString()) + .entrypoint(GsonUtils.require(obj, "entrypoint").getAsString()) + .name(GsonUtils.optional(obj, "name").map(JsonElement::getAsString).orElse(null)) + .description(GsonUtils.optional(obj, "description").map(JsonElement::getAsString).orElse(null)) + .merge(context.deserialize(element, StandardInheritable.class)); + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java new file mode 100644 index 0000000..2d3393e --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java @@ -0,0 +1,48 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; + +import java.lang.reflect.Type; + +public final class StandardPluginMetadataSerializer implements JsonSerializer { + + @Override + public JsonElement serialize(final StandardPluginMetadata value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + obj.addProperty("id", value.id()); + obj.addProperty("entrypoint", value.entrypoint()); + value.name().ifPresent(v -> obj.addProperty("name", v)); + value.description().ifPresent(v -> obj.addProperty("description", v)); + obj.asMap().putAll(((JsonObject) context.serialize(value, StandardInheritable.class)).asMap()); + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java new file mode 100644 index 0000000..bf2d451 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java @@ -0,0 +1,58 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.model; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.model.ContainerLoader; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; + +public final class ContainerLoaderAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public ContainerLoader deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new ContainerLoader( + GsonUtils.require(obj, "name").getAsString(), + context.deserialize(GsonUtils.require(obj, "version"), VersionRange.class) + ); + } + + @Override + public JsonElement serialize(final ContainerLoader value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + obj.addProperty("name", value.name()); + obj.add("version", context.serialize(value.version(), VersionRange.class)); + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginBrandingAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginBrandingAdapter.java new file mode 100644 index 0000000..0e22b70 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginBrandingAdapter.java @@ -0,0 +1,57 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.model; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; + +public final class PluginBrandingAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public PluginBranding deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new PluginBranding( + GsonUtils.optional(obj, "logo").map(JsonElement::getAsString), + GsonUtils.optional(obj, "icon").map(JsonElement::getAsString) + ); + } + + @Override + public JsonElement serialize(final PluginBranding value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + value.logo().ifPresent(v -> obj.addProperty("logo", v)); + value.icon().ifPresent(v -> obj.addProperty("icon", v)); + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginContributorAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginContributorAdapter.java new file mode 100644 index 0000000..8b89a36 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginContributorAdapter.java @@ -0,0 +1,57 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.model; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; + +public final class PluginContributorAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public PluginContributor deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new PluginContributor( + GsonUtils.require(obj, "name").getAsString(), + GsonUtils.optional(obj, "description").map(JsonElement::getAsString) + ); + } + + @Override + public JsonElement serialize(final PluginContributor value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + obj.addProperty("name", value.name()); + value.description().ifPresent(v -> obj.addProperty("description", v)); + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java new file mode 100644 index 0000000..efe340e --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java @@ -0,0 +1,58 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.model; + +import com.google.gson.*; +import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; + +public final class PluginDependencyAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public PluginDependency deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new PluginDependency( + GsonUtils.require(obj, "id").getAsString(), + context.deserialize(GsonUtils.require(obj, "version"), VersionRange.class), + GsonUtils.optional(obj, "load-order") + .map(v -> context.deserialize(v, PluginDependency.LoadOrder.class)) + .orElse(PluginDependency.LoadOrder.UNDEFINED), + GsonUtils.optional(obj, "optional").map(JsonElement::getAsBoolean).orElse(false) + ); + } + + @Override + public JsonElement serialize(final PluginDependency value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + obj.addProperty("id", value.id()); + obj.add("version", context.serialize(value.version(), VersionRange.class)); + obj.add("load-order", context.serialize(value.loadOrder(), PluginDependency.LoadOrder.class)); + obj.addProperty("optional", value.optional()); + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLinksAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLinksAdapter.java new file mode 100644 index 0000000..d3725a0 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLinksAdapter.java @@ -0,0 +1,63 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.model; + +import com.google.gson.*; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; + +import java.lang.reflect.Type; +import java.net.URI; +import java.util.Optional; + +public final class PluginLinksAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public PluginLinks deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new PluginLinks( + PluginLinksAdapter.deserialize(obj, "homepage", context), + PluginLinksAdapter.deserialize(obj, "source", context), + PluginLinksAdapter.deserialize(obj, "issues", context) + ); + } + + @Override + public JsonElement serialize(final PluginLinks value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + PluginLinksAdapter.serialize(obj, "homepage", context, value.homepage()); + PluginLinksAdapter.serialize(obj, "source", context, value.source()); + PluginLinksAdapter.serialize(obj, "issues", context, value.issues()); + return obj; + } + + private static Optional deserialize(final JsonObject obj, final String name, final JsonDeserializationContext context) throws JsonParseException { + return GsonUtils.optional(obj, name).map(v -> context.deserialize(v, URI.class)); + } + + private static void serialize(final JsonObject obj, final String name, final JsonSerializationContext context, final Optional value) throws JsonParseException { + value.ifPresent(v -> obj.add(name, context.serialize(v, URI.class))); + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java new file mode 100644 index 0000000..858a41a --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java @@ -0,0 +1,86 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.util; + +import com.google.gson.*; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.plugin.metadata.builtin.MissingRequiredFieldException; + +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public final class GsonUtils { + + public static JsonElement require(final JsonObject obj, final String name) { + final JsonElement element = obj.get(name); + if (element == null || element.isJsonNull()) { + throw new MissingRequiredFieldException(name); + } + return element; + } + + public static Optional optional(final JsonObject obj, final String name) { + final JsonElement element = obj.get(name); + if (element == null || element.isJsonNull()) { + return Optional.empty(); + } + return Optional.of(element); + } + + public static Stream stream(final JsonObject obj, final String name) { + final JsonElement element = obj.get(name); + if (element == null || element.isJsonNull()) { + return Stream.empty(); + } + return element.getAsJsonArray().asList().stream(); + } + + public static JsonArray toArray(final Stream stream) { + final JsonArray array = new JsonArray(); + stream.forEach(array::add); + return array; + } + + public static Map deserializeMap(final @Nullable JsonElement element, final Function valFunc, final Supplier> collector) { + final Map map = collector.get(); + if (element instanceof JsonObject obj) { + for (final Map.Entry entry : obj.entrySet()) { + map.put(entry.getKey(), valFunc.apply(entry.getValue())); + } + } + return map; + } + + public static JsonObject serializeMap(final Map map, final Function valFunc) { + final JsonObject obj = new JsonObject(); + for (final Map.Entry entry : map.entrySet()) { + obj.add(entry.getKey(), valFunc.apply(entry.getValue())); + } + return obj; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/util/package-info.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/ArtifactVersionAdapter.java similarity index 60% rename from src/main/java/org/spongepowered/plugin/metadata/util/package-info.java rename to src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/ArtifactVersionAdapter.java index 74da21b..3fce99a 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/util/package-info.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/ArtifactVersionAdapter.java @@ -22,5 +22,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -@org.checkerframework.framework.qual.DefaultQualifier(org.checkerframework.checker.nullness.qual.NonNull.class) -package org.spongepowered.plugin.metadata.util; +package org.spongepowered.plugin.metadata.builtin.adapter.version; + +import com.google.gson.*; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; + +import java.lang.reflect.Type; + +public final class ArtifactVersionAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public ArtifactVersion deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + return new DefaultArtifactVersion(element.getAsString()); + } + + @Override + public JsonElement serialize(final ArtifactVersion value, final Type type, final JsonSerializationContext context) { + return new JsonPrimitive(value.toString()); + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/VersionRangeAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/VersionRangeAdapter.java new file mode 100644 index 0000000..71f6145 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/version/VersionRangeAdapter.java @@ -0,0 +1,49 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.version; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import org.apache.maven.artifact.versioning.VersionRange; + +import java.lang.reflect.Type; + +public final class VersionRangeAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public VersionRange deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + return VersionRange.createFromVersion(element.getAsString()); + } + + @Override + public JsonElement serialize(final VersionRange value, final Type type, final JsonSerializationContext context) { + return new JsonPrimitive(value.toString()); + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java b/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java index fe68a99..fa82377 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java @@ -25,7 +25,6 @@ package org.spongepowered.plugin.metadata.model; import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.plugin.metadata.Container; import java.util.Objects; @@ -44,46 +43,4 @@ public record ContainerLoader(String name, VersionRange version) { Objects.requireNonNull(name, "name"); Objects.requireNonNull(version, "version"); } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private @MonotonicNonNull String name; - private @MonotonicNonNull VersionRange version; - - private Builder() { - } - - public Builder name(final String name) { - this.name = Objects.requireNonNull(name, "name"); - return this; - } - - public Builder version(final String version) { - this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); - return this; - } - - public Builder version(final VersionRange version) { - this.version = Objects.requireNonNull(version, "version"); - return this; - } - - public Builder from(final ContainerLoader value) { - Objects.requireNonNull(value, "value"); - this.name = value.name(); - this.version = value.version(); - return this; - } - - public ContainerLoader build() { - return new ContainerLoader(this.name, this.version); - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java index d859eeb..702f052 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java @@ -24,7 +24,6 @@ */ package org.spongepowered.plugin.metadata.model; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -51,40 +50,4 @@ public record PluginBranding(Optional icon, Optional logo) { public static PluginBranding none() { return PluginBranding.NONE; } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private Optional logo = Optional.empty(), icon = Optional.empty(); - - private Builder() { - } - - public Builder logo(@Nullable final String logo) { - this.logo = Optional.ofNullable(logo); - return this; - } - - public Builder icon(@Nullable final String icon) { - this.icon = Optional.ofNullable(icon); - return this; - } - - public Builder from(final PluginBranding value) { - Objects.requireNonNull(value, "value"); - this.logo = value.logo(); - this.icon = value.icon(); - return this; - } - - public PluginBranding build() { - return new PluginBranding(this.icon, this.logo); - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java index 3e5a234..c3fe7a0 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java @@ -24,8 +24,6 @@ */ package org.spongepowered.plugin.metadata.model; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -47,41 +45,4 @@ public record PluginContributor(String name, Optional description) { Objects.requireNonNull(name, "name"); Objects.requireNonNull(description, "description"); } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private @MonotonicNonNull String name; - private Optional description = Optional.empty(); - - private Builder() { - } - - public Builder name(final String name) { - this.name = Objects.requireNonNull(name, "name"); - return this; - } - - public Builder description(@Nullable final String description) { - this.description = Optional.ofNullable(description); - return this; - } - - public Builder from(final PluginContributor value) { - Objects.requireNonNull(value, "value"); - this.name = value.name; - this.description = value.description; - return this; - } - - public PluginContributor build() { - return new PluginContributor(this.name, this.description); - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java index 1a8131f..745521c 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java @@ -25,7 +25,6 @@ package org.spongepowered.plugin.metadata.model; import org.apache.maven.artifact.versioning.VersionRange; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.spongepowered.plugin.metadata.Constants; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -65,62 +64,6 @@ public record PluginDependency(String id, VersionRange version, LoadOrder loadOr } } - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - private @MonotonicNonNull String id; - private @MonotonicNonNull VersionRange version; - private LoadOrder loadOrder = LoadOrder.UNDEFINED; - private boolean optional = false; - - private Builder() { - } - - public Builder id(final String id) { - this.id = Objects.requireNonNull(id, "id"); - return this; - } - - public Builder version(final String version) { - this.version = VersionRange.createFromVersion(Objects.requireNonNull(version, "version")); - return this; - } - - public Builder version(final VersionRange version) { - this.version = Objects.requireNonNull(version, "version"); - return this; - } - - public Builder loadOrder(final LoadOrder loadOrder) { - this.loadOrder = Objects.requireNonNull(loadOrder, "load order"); - return this; - } - - public Builder optional(final boolean optional) { - this.optional = optional; - return this; - } - - public Builder from(final PluginDependency value) { - Objects.requireNonNull(value, "value"); - this.id = value.id; - this.version = value.version; - this.loadOrder = value.loadOrder; - this.optional = value.optional; - return this; - } - - public PluginDependency build() { - return new PluginDependency(this.id, this.version, this.loadOrder, this.optional); - } - } - /** * Represents the ordering of how dependencies are loaded versus others. *

diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java index 59ea140..5e8b77f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java @@ -24,7 +24,6 @@ */ package org.spongepowered.plugin.metadata.model; -import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -54,47 +53,4 @@ public record PluginLinks(Optional homepage, Optional source, Optional public static PluginLinks none() { return PluginLinks.NONE; } - - public static Builder builder() { - return new Builder(); - } - - public Builder toBuilder() { - return new Builder().from(this); - } - - public static final class Builder { - - private Optional homepage = Optional.empty(), source = Optional.empty(), issues = Optional.empty(); - - private Builder() { - } - - public Builder homepage(@Nullable final URI homepage) { - this.homepage = Optional.ofNullable(homepage); - return this; - } - - public Builder source(@Nullable final URI source) { - this.source = Optional.ofNullable(source); - return this; - } - - public Builder issues(@Nullable final URI issues) { - this.issues = Optional.ofNullable(issues); - return this; - } - - public Builder from(final PluginLinks value) { - Objects.requireNonNull(value, "value"); - this.homepage = value.homepage; - this.source = value.source; - this.issues = value.issues; - return this; - } - - public PluginLinks build() { - return new PluginLinks(this.homepage, this.source, this.issues); - } - } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java b/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java deleted file mode 100644 index dd64264..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/util/GsonUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.util; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonWriter; -import org.checkerframework.checker.nullness.qual.Nullable; - -import java.io.IOException; -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -public final class GsonUtils { - - public static > V read(final JsonArray in, final TypeAdapter adapter, final Supplier collector) { - final V parsed = collector.get(); - - for (final JsonElement element : in) { - parsed.add(adapter.fromJsonTree(element)); - } - - return parsed; - } - - public static Map read(final JsonObject in, final Function valFunc, final Supplier> collector) { - final Map parsed = collector.get(); - - for (final Map.Entry entry : in.entrySet()) { - parsed.put(entry.getKey(), valFunc.apply(entry.getValue())); - } - - return parsed; - } - - public static JsonArray write(final TypeAdapter adapter, final Collection value) { - final JsonArray array = new JsonArray(); - for (final T val : value) { - array.add(adapter.toJsonTree(val)); - } - return array; - } - - public static JsonObject write(final Function valFunc, final Map value) { - final JsonObject obj = new JsonObject(); - for (final Map.Entry entry : value.entrySet()) { - obj.add(entry.getKey(), valFunc.apply(entry.getValue())); - } - - return obj; - } - - public static void writeIfPresent(final JsonWriter out, final String name, final Optional value) throws IOException { - if (value.isPresent()) { - out.name(name).value(value.get().toString()); - } - } - - public static void writeIfPresent(final JsonObject out, final String key, final Optional value) { - final @Nullable T val = value.orElse(null); - switch (val) { - case JsonElement jsonElement -> out.add(key, jsonElement); - case String s -> out.addProperty(key, s); - case Number number -> out.addProperty(key, number); - case Boolean b -> out.addProperty(key, b); - case Character c -> out.addProperty(key, c); - case null, default -> {} - } - } - - @SuppressWarnings("unchecked") - public static void consumeIfPresent(final JsonObject obj, final String key, final Consumer consumer) { - if (obj.has(key)) { - consumer.accept((T) obj.get(key)); - } - } - - public static void applyIfValid(final JsonObject obj, final T value, final Predicate validator, final BiConsumer consumer) { - if (validator.test(value)) { - consumer.accept(obj, value); - } - } - - public static Optional get(final JsonObject obj, final String key, final Function valFunc) { - if (!obj.has(key)) { - return Optional.empty(); - } - return Optional.of(valFunc.apply(obj.get(key))); - } -} From 4373094a9235463603951a37c63987eb1be0b9e8 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Tue, 2 Jun 2026 22:37:04 +0200 Subject: [PATCH 07/11] Add unit tests to prevent parser regressions --- .../metadata/builtin/MetadataContainer.java | 9 +- .../metadata/builtin/MetadataParser.java | 30 ++++- .../builtin/StandardPluginMetadata.java | 11 +- .../adapter/MetadataContainerAdapter.java | 9 +- .../adapter/StandardInheritableAdapter.java | 1 + ...dardPluginMetadataBuilderDeserializer.java | 2 +- .../StandardPluginMetadataSerializer.java | 2 +- .../plugin/metadata/model/PluginBranding.java | 5 + .../metadata/model/PluginContributor.java | 5 + .../plugin/metadata/model/PluginLinks.java | 5 + .../metadata/builtin/MetadataParserTest.java | 110 ++++++++++++++++++ src/test/resources/valid/container1.json | 35 ++++++ 12 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java create mode 100644 src/test/resources/valid/container1.json diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index 27eaf2f..90c822a 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -24,7 +24,6 @@ */ package org.spongepowered.plugin.metadata.builtin; -import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Container; @@ -95,6 +94,10 @@ public MetadataContainer.Builder toBuilder() { return new MetadataContainer.Builder().from(this); } + public static MetadataContainer.Builder builder() { + return new MetadataContainer.Builder(); + } + public static final class Builder { private final Map metadata = new LinkedHashMap<>(); @@ -102,6 +105,8 @@ public static final class Builder { private @MonotonicNonNull ContainerLoader loader; private @Nullable Inheritable globalMetadata; + private Builder() {} + public Builder loader(final ContainerLoader loader) { this.loader = Objects.requireNonNull(loader, "loader"); return this; @@ -145,7 +150,7 @@ public Builder from(final MetadataContainer value) { return this; } - public MetadataContainer build() throws IllegalStateException, InvalidVersionSpecificationException { + public MetadataContainer build() throws IllegalStateException { Objects.requireNonNull(this.license, "license"); Objects.requireNonNull(this.loader, "loader"); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java index 7a05a84..9aed9bf 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java @@ -69,10 +69,6 @@ public static Gson gson() { return MetadataParser.GSON; } - public static GsonBuilder gsonBuilder() { - return MetadataParser.GSON.newBuilder(); - } - /** * Reads a {@link MetadataContainer container} from a given {@link Path path} using the default {@link Gson deserializer} * (retrieved from {@link #gson()}). @@ -133,6 +129,19 @@ public static MetadataContainer read(final Reader reader, final Gson gson) throw } } + /** + * Writes a {@link MetadataContainer container} to the given {@link Path path} using the default {@link Gson deserializer} + * (retrieved from {@link #gson()}). + * + * @param path The path + * @param container The container + * @param indent True to indent (pretty print) the resulting JSON, false if not + * @throws IOException If the container fails to serialize + */ + public static void write(final Path path, final MetadataContainer container, final boolean indent) throws IOException { + MetadataParser.write(path, container, MetadataParser.gson(), indent); + } + /** * Writes a {@link MetadataContainer container} to the given {@link Path path} using the configured {@link Gson serializer}. *

@@ -156,6 +165,19 @@ public static void write(final Path path, final MetadataContainer container, fin } } + /** + * Writes a {@link MetadataContainer container} to the given {@link Path path} using the default {@link Gson deserializer} + * (retrieved from {@link #gson()}). + * + * @param writer The writer + * @param container The container + * @param indent True to indent (pretty print) the resulting JSON, false if not + * @throws IOException If the container fails to serialize + */ + public static void write(final Writer writer, final MetadataContainer container, final boolean indent) throws IOException { + MetadataParser.write(writer, container, MetadataParser.gson(), indent); + } + /** * Writes a {@link MetadataContainer container} to the given {@link Path path} using the configured {@link Gson serializer}. *

diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java index cc5f7f5..a4d7093 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java @@ -48,10 +48,6 @@ private StandardPluginMetadata(final Builder builder) { this.description = builder.description; } - public static StandardPluginMetadata.Builder builder() { - return new Builder(); - } - @Override public Container container() { return this.container; @@ -114,13 +110,16 @@ public StandardPluginMetadata.Builder toBuilder() { return new StandardPluginMetadata.Builder().from(this); } + public static StandardPluginMetadata.Builder builder() { + return new StandardPluginMetadata.Builder(); + } + public static final class Builder extends StandardInheritable.AbstractBuilder { private @MonotonicNonNull String id, entrypoint; private @Nullable String name, description; - public Builder() { - } + private Builder() {} public Builder id(final String id) { this.id = Objects.requireNonNull(id, "id"); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java index d30f482..cbbfceb 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java @@ -25,7 +25,6 @@ package org.spongepowered.plugin.metadata.builtin.adapter; import com.google.gson.*; -import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.builtin.MetadataContainer; @@ -45,7 +44,7 @@ public MetadataContainer deserialize(final JsonElement element, final Type type, throws JsonParseException { final JsonObject obj = element.getAsJsonObject(); - final MetadataContainer.Builder builder = new MetadataContainer.Builder() + final MetadataContainer.Builder builder = MetadataContainer.builder() .loader(context.deserialize(GsonUtils.require(obj, "loader"), ContainerLoader.class)) .license(GsonUtils.require(obj, "license").getAsString()); @@ -79,11 +78,7 @@ public MetadataContainer deserialize(final JsonElement element, final Type type, builder.addMetadata(pluginBuilder.build()); } - try { - return builder.build(); - } catch (final InvalidVersionSpecificationException e) { - throw new JsonParseException(e); - } + return builder.build(); } @Override diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java index 5d71243..b66775b 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java @@ -25,6 +25,7 @@ package org.spongepowered.plugin.metadata.builtin.adapter; import com.google.gson.*; +import org.apache.maven.artifact.versioning.ArtifactVersion; import org.spongepowered.plugin.metadata.builtin.NullVersion; import org.spongepowered.plugin.metadata.builtin.StandardInheritable; import org.spongepowered.plugin.metadata.model.PluginBranding; diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java index af8ed67..8f361a0 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java @@ -36,7 +36,7 @@ public final class StandardPluginMetadataBuilderDeserializer implements JsonDese @Override public StandardPluginMetadata.Builder deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { final JsonObject obj = element.getAsJsonObject(); - return new StandardPluginMetadata.Builder() + return StandardPluginMetadata.builder() .id(GsonUtils.require(obj, "id").getAsString()) .entrypoint(GsonUtils.require(obj, "entrypoint").getAsString()) .name(GsonUtils.optional(obj, "name").map(JsonElement::getAsString).orElse(null)) diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java index 2d3393e..eadd9ab 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java @@ -39,9 +39,9 @@ public final class StandardPluginMetadataSerializer implements JsonSerializer obj.addProperty("name", v)); value.description().ifPresent(v -> obj.addProperty("description", v)); + obj.addProperty("entrypoint", value.entrypoint()); obj.asMap().putAll(((JsonObject) context.serialize(value, StandardInheritable.class)).asMap()); return obj; } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java index 702f052..44fc592 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java @@ -24,6 +24,7 @@ */ package org.spongepowered.plugin.metadata.model; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -47,6 +48,10 @@ public record PluginBranding(Optional icon, Optional logo) { Objects.requireNonNull(logo, "logo"); } + public PluginBranding(@Nullable String icon, @Nullable String logo) { + this(Optional.ofNullable(icon), Optional.ofNullable(logo)); + } + public static PluginBranding none() { return PluginBranding.NONE; } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java index c3fe7a0..9ad109f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java @@ -24,6 +24,7 @@ */ package org.spongepowered.plugin.metadata.model; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -45,4 +46,8 @@ public record PluginContributor(String name, Optional description) { Objects.requireNonNull(name, "name"); Objects.requireNonNull(description, "description"); } + + public PluginContributor(String name, @Nullable String description) { + this(name, Optional.ofNullable(description)); + } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java index 5e8b77f..0ccd846 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java @@ -24,6 +24,7 @@ */ package org.spongepowered.plugin.metadata.model; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; @@ -50,6 +51,10 @@ public record PluginLinks(Optional homepage, Optional source, Optional Objects.requireNonNull(issues, "issues"); } + public PluginLinks(@Nullable URI homepage, @Nullable URI source, @Nullable URI issues) { + this(Optional.ofNullable(homepage), Optional.ofNullable(source), Optional.ofNullable(issues)); + } + public static PluginLinks none() { return PluginLinks.NONE; } diff --git a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java new file mode 100644 index 0000000..4f91bb7 --- /dev/null +++ b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java @@ -0,0 +1,110 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin; + +import org.apache.maven.artifact.versioning.VersionRange; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.spongepowered.plugin.metadata.PluginMetadata; +import org.spongepowered.plugin.metadata.model.ContainerLoader; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; + +import java.io.*; +import java.net.URI; +import java.util.List; +import java.util.Objects; + +public class MetadataParserTest { + + private static Reader resourceReader(final String path) { + return new InputStreamReader(Objects.requireNonNull(MetadataParserTest.class.getResourceAsStream(path))); + } + + private static List readLines(final String path) throws IOException { + try (final BufferedReader reader = new BufferedReader(MetadataParserTest.resourceReader(path))) { + return reader.lines().toList(); + } + } + + private static MetadataContainer readContainer(final String path) throws IOException { + try (final Reader reader = MetadataParserTest.resourceReader(path)) { + return MetadataParser.read(reader); + } + } + + private static List writeContainer(final MetadataContainer container) throws IOException { + final StringWriter writer = new StringWriter(); + MetadataParser.write(writer, container, true); + return List.of(writer.toString().split("\n")); + } + + private static final MetadataContainer container1 = MetadataContainer.builder() + .loader(new ContainerLoader("java_plain", VersionRange.createFromVersion("1.0"))) + .license("some_license") + .addMetadata(StandardPluginMetadata.builder() + .id("test_plugin") + .name("TestPlugin") + .version("1.2.3") + .entrypoint("my.test.package.MyTestPlugin") + .description("Some test plugin") + .links(new PluginLinks( + URI.create("https://spongepowered.org/"), + URI.create("https://github.com/SpongePowered/Sponge"), + URI.create("https://github.com/SpongePowered/Sponge/issues") + )) + .addContributor(new PluginContributor("Spongie", "Mascot")) + .addDependency(new PluginDependency("spongeapi", VersionRange.createFromVersion("17.0.0"), PluginDependency.LoadOrder.AFTER, false)) + .build()) + .build(); + + @Test + public void readValidContainer1() throws IOException { + // TODO MetadataContainer#equals ? + final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/container1.json"); + Assertions.assertEquals(container1.loader(), parsed.loader()); + Assertions.assertEquals(container1.license(), parsed.license()); + Assertions.assertEquals(1, parsed.metadata().size()); + + final PluginMetadata plugin1 = container1.metadata().iterator().next(); + final PluginMetadata parsedPlugin = parsed.metadata().iterator().next(); + Assertions.assertEquals(plugin1.id(), parsedPlugin.id()); + Assertions.assertEquals(plugin1.name(), parsedPlugin.name()); + Assertions.assertEquals(plugin1.version(), parsedPlugin.version()); + Assertions.assertEquals(plugin1.entrypoint(), parsedPlugin.entrypoint()); + Assertions.assertEquals(plugin1.description(), parsedPlugin.description()); + Assertions.assertEquals(plugin1.links(), parsedPlugin.links()); + Assertions.assertIterableEquals(plugin1.contributors(), parsedPlugin.contributors()); + Assertions.assertIterableEquals(plugin1.dependencies(), parsedPlugin.dependencies()); + } + + @Test + public void writeContainer1() throws IOException { + final List expected = MetadataParserTest.readLines("/valid/container1.json"); + final List result = MetadataParserTest.writeContainer(container1); + Assertions.assertLinesMatch(expected, result); + } +} diff --git a/src/test/resources/valid/container1.json b/src/test/resources/valid/container1.json new file mode 100644 index 0000000..24547c2 --- /dev/null +++ b/src/test/resources/valid/container1.json @@ -0,0 +1,35 @@ +{ + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "some_license", + "plugins": [ + { + "id": "test_plugin", + "name": "TestPlugin", + "description": "Some test plugin", + "entrypoint": "my.test.package.MyTestPlugin", + "version": "1.2.3", + "links": { + "homepage": "https://spongepowered.org/", + "source": "https://github.com/SpongePowered/Sponge", + "issues": "https://github.com/SpongePowered/Sponge/issues" + }, + "contributors": [ + { + "name": "Spongie", + "description": "Mascot" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "17.0.0", + "load-order": "AFTER", + "optional": false + } + ] + } + ] +} From 009bea956f656c0361d063cdf206c11539e86fc4 Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Thu, 4 Jun 2026 22:59:07 +0200 Subject: [PATCH 08/11] Convert legacy plugin ids (-) during deserialization --- .../plugin/metadata/Constants.java | 6 +-- .../plugin/metadata/PluginMetadata.java | 2 +- .../metadata/builtin/MetadataParser.java | 23 ++++++++++ ...dardPluginMetadataBuilderDeserializer.java | 3 +- .../model/PluginDependencyAdapter.java | 3 +- .../builtin/adapter/util/GsonUtils.java | 3 ++ .../builtin/adapter/util/LegacyIds.java | 42 +++++++++++++++++++ .../metadata/model/PluginDependency.java | 2 +- .../metadata/builtin/MetadataParserTest.java | 21 ++++++++++ src/test/resources/legacy/legacy_id.json | 24 +++++++++++ 10 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/LegacyIds.java create mode 100644 src/test/resources/legacy/legacy_id.json diff --git a/src/main/java/org/spongepowered/plugin/metadata/Constants.java b/src/main/java/org/spongepowered/plugin/metadata/Constants.java index 282d645..1334eb8 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/Constants.java +++ b/src/main/java/org/spongepowered/plugin/metadata/Constants.java @@ -35,13 +35,13 @@ public final class Constants { *

  • Must be between 2 and 64 characters in length
  • *
  • Must start with a lower case letter (a-z)
  • *
  • May only contain a mix of lower case letters (a-z), - * numbers (0-9), dashes (-), and underscores (_)
  • + * numbers (0-9) and underscores (_) * */ - public static final Pattern VALID_ID_PATTERN = Pattern.compile("^[a-z][a-z0-9-_]{1,63}$"); + public static final Pattern VALID_ID_PATTERN = Pattern.compile("^[a-z][a-z0-9_]{1,63}$"); public static final String INVALID_ID_REQUIREMENTS_MESSAGE = "IDs can be between 2 and 64 characters long and must start with a lower case" - + " letter, followed by any mix of lower case letters (a-z), numbers (0-9), dashes (-) and underscores (_)."; + + " letter, followed by any mix of lower case letters (a-z), numbers (0-9) and underscores (_)."; private Constants() { } diff --git a/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java index caf8c12..3a28297 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java @@ -48,7 +48,7 @@ public interface PluginMetadata extends Inheritable { *
  • Must be between 2 and 64 characters in length
  • *
  • Must start with a lower case letter (a-z)
  • *
  • May only contain a mix of lower case letters (a-z), - * numbers (0-9), dashes (-), and underscores (_)
  • + * numbers (0-9) and underscores (_) * * @return The id */ diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java index 9aed9bf..008d25f 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java @@ -45,7 +45,10 @@ import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.Objects; +import java.util.Set; public final class MetadataParser { private static final Gson GSON = new GsonBuilder() @@ -62,6 +65,8 @@ public final class MetadataParser { .registerTypeAdapter(StandardPluginMetadata.class, new StandardPluginMetadataSerializer()) .create(); + private static final Set warnings = new LinkedHashSet<>(); + private MetadataParser() { } @@ -69,6 +74,24 @@ public static Gson gson() { return MetadataParser.GSON; } + /** + * Returns all warnings generated by (de)serializers so that the platform may log them when appropriate. + * + * @return The warnings + */ + public static Set warnings() { + return Collections.unmodifiableSet(MetadataParser.warnings); + } + + /** + * Adds a warning generated by a (de)serializer so that the platform may log it later. + * + * @param warning The warning + */ + public static void addWarning(final String warning) { + MetadataParser.warnings.add(Objects.requireNonNull(warning)); + } + /** * Reads a {@link MetadataContainer container} from a given {@link Path path} using the default {@link Gson deserializer} * (retrieved from {@link #gson()}). diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java index 8f361a0..7998869 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java @@ -28,6 +28,7 @@ import org.spongepowered.plugin.metadata.builtin.StandardInheritable; import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; +import org.spongepowered.plugin.metadata.builtin.adapter.util.LegacyIds; import java.lang.reflect.Type; @@ -37,7 +38,7 @@ public final class StandardPluginMetadataBuilderDeserializer implements JsonDese public StandardPluginMetadata.Builder deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { final JsonObject obj = element.getAsJsonObject(); return StandardPluginMetadata.builder() - .id(GsonUtils.require(obj, "id").getAsString()) + .id(LegacyIds.fix(GsonUtils.require(obj, "id").getAsString())) .entrypoint(GsonUtils.require(obj, "entrypoint").getAsString()) .name(GsonUtils.optional(obj, "name").map(JsonElement::getAsString).orElse(null)) .description(GsonUtils.optional(obj, "description").map(JsonElement::getAsString).orElse(null)) diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java index efe340e..97917f5 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginDependencyAdapter.java @@ -26,6 +26,7 @@ import com.google.gson.*; import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.builtin.adapter.util.LegacyIds; import org.spongepowered.plugin.metadata.model.PluginDependency; import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; @@ -37,7 +38,7 @@ public final class PluginDependencyAdapter implements JsonSerializer context.deserialize(v, PluginDependency.LoadOrder.class)) diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java index 858a41a..dd08293 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/GsonUtils.java @@ -34,6 +34,9 @@ import java.util.function.Supplier; import java.util.stream.Stream; +/** + * This is not public API as this package is not exported by the module. + */ public final class GsonUtils { public static JsonElement require(final JsonObject obj, final String name) { diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/LegacyIds.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/LegacyIds.java new file mode 100644 index 0000000..9ba5b77 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/util/LegacyIds.java @@ -0,0 +1,42 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.util; + +import org.spongepowered.plugin.metadata.builtin.MetadataParser; + +/** + * This is not public API as this package is not exported by the module. + */ +public class LegacyIds { + + public static String fix(final String original) { + if (original.indexOf('-') < 0) { + return original; + } + final String fixed = original.replace('-', '_'); + MetadataParser.addWarning("Plugin id '" + original + "' is invalid and has been converted to '" + fixed + "'."); + return fixed; + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java index 745521c..5fde4f2 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java @@ -43,7 +43,7 @@ *
  • Must be between 2 and 64 characters in length
  • *
  • Must start with a lower case letter (a-z)
  • *
  • May only contain a mix of lower case letters (a-z), - * numbers (0-9), dashes (-), and underscores (_)
  • + * numbers (0-9) and underscores (_) * * * @param id The {@link String id} diff --git a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java index 4f91bb7..709718a 100644 --- a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java +++ b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java @@ -107,4 +107,25 @@ public void writeContainer1() throws IOException { final List result = MetadataParserTest.writeContainer(container1); Assertions.assertLinesMatch(expected, result); } + + @Test + public void readLegacyIdContainer() throws IOException { + final String pluginIdWarning = "Plugin id 'test-plugin' is invalid and has been converted to 'test_plugin'."; + final String dependencyIdWarning = "Plugin id 'test-dependency' is invalid and has been converted to 'test_dependency'."; + Assertions.assertFalse(MetadataParser.warnings().contains(pluginIdWarning)); + Assertions.assertFalse(MetadataParser.warnings().contains(dependencyIdWarning)); + + final MetadataContainer parsed = MetadataParserTest.readContainer("/legacy/legacy_id.json"); + + Assertions.assertEquals(1, parsed.metadata().size()); + final PluginMetadata plugin = parsed.metadata().iterator().next(); + Assertions.assertEquals("test_plugin", plugin.id()); + + Assertions.assertEquals(1, plugin.dependencies().size()); + final PluginDependency dependency = plugin.dependencies().iterator().next(); + Assertions.assertEquals("test_dependency", dependency.id()); + + Assertions.assertTrue(MetadataParser.warnings().contains(pluginIdWarning)); + Assertions.assertTrue(MetadataParser.warnings().contains(dependencyIdWarning)); + } } diff --git a/src/test/resources/legacy/legacy_id.json b/src/test/resources/legacy/legacy_id.json new file mode 100644 index 0000000..9a5f0a1 --- /dev/null +++ b/src/test/resources/legacy/legacy_id.json @@ -0,0 +1,24 @@ +{ + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "some_license", + "plugins": [ + { + "id": "test-plugin", + "name": "TestPlugin", + "description": "Some test plugin", + "entrypoint": "my.test.package.MyTestPlugin", + "version": "1.2.3", + "dependencies": [ + { + "id": "test-dependency", + "version": "4.5.6", + "load-order": "AFTER", + "optional": false + } + ] + } + ] +} From d1a0f6258c88c0bda2f51dfa65e26b3e28db369e Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 7 Jun 2026 21:10:07 +0200 Subject: [PATCH 09/11] Fix metadata order Map#copyOf does not preserve order. --- .../plugin/metadata/builtin/MetadataContainer.java | 3 ++- .../plugin/metadata/builtin/StandardInheritable.java | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index 90c822a..c7cfe95 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -32,6 +32,7 @@ import org.spongepowered.plugin.metadata.model.ContainerLoader; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -49,7 +50,7 @@ private MetadataContainer(final Builder builder) { this.loader = builder.loader; this.license = builder.license; this.globalMetadata = builder.globalMetadata; - this.metadata = Map.copyOf(builder.metadata); + this.metadata = Collections.unmodifiableMap(new LinkedHashMap<>(builder.metadata)); for (final StandardPluginMetadata element : this.metadata.values()) { element.setContainer(this); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java index eff1db1..18134f8 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java @@ -34,6 +34,7 @@ import org.spongepowered.plugin.metadata.model.PluginLinks; import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -57,8 +58,8 @@ protected StandardInheritable(final AbstractBuilder builder) { this.branding = builder.branding; this.links = builder.links; this.contributors = List.copyOf(builder.contributors); - this.dependencies = Map.copyOf(builder.dependencies); - this.properties = Map.copyOf(builder.properties); + this.dependencies = Collections.unmodifiableMap(new LinkedHashMap<>(builder.dependencies)); + this.properties = Collections.unmodifiableMap(new LinkedHashMap<>(builder.properties)); } @Override From d7b7f02b1f7d3513ca67cc72ea76358903849a1a Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sat, 20 Jun 2026 17:21:30 +0200 Subject: [PATCH 10/11] Remove Container and Inheritable from API (not from builtin impl) - Container has no use in plugin-spi. - Inheritable can only have optional values but PluginMetadata has required values. In consequence, we had some null-like workaround values such as NullVersion. - Third-parties are free to implement their own PluginMetadata, so forcing some values to be inheritable made no sense as it is a detail of the built-in impl. - Loader and license metadata can now be specified per plugin. - Global values and specific values used to be fully merged in StandardPluginMetadata with no ability to seperate them back during serialization. --- .../plugin/metadata/Container.java | 80 ----- .../plugin/metadata/Inheritable.java | 93 ----- .../plugin/metadata/PluginMetadata.java | 83 ++++- .../metadata/builtin/InheritableMetadata.java | 335 ++++++++++++++++++ .../metadata/builtin/MetadataContainer.java | 142 +------- .../metadata/builtin/MetadataParser.java | 6 +- .../plugin/metadata/builtin/NullVersion.java | 73 ---- .../metadata/builtin/StandardInheritable.java | 260 -------------- .../builtin/StandardPluginMetadata.java | 159 ++++++--- ...r.java => InheritableMetadataAdapter.java} | 34 +- .../adapter/MetadataContainerAdapter.java | 64 ++-- ...dardPluginMetadataBuilderDeserializer.java | 6 +- .../StandardPluginMetadataSerializer.java | 6 +- ... => PluginLoaderSpecificationAdapter.java} | 10 +- .../plugin/metadata/model/PluginBranding.java | 4 +- .../metadata/model/PluginContributor.java | 3 +- .../metadata/model/PluginDependency.java | 4 +- .../plugin/metadata/model/PluginLinks.java | 4 +- ...er.java => PluginLoaderSpecification.java} | 8 +- .../metadata/builtin/MetadataParserTest.java | 125 ++++--- .../{legacy_id.json => dash_in_id.json} | 0 src/test/resources/valid/full_global.json | 37 ++ src/test/resources/valid/full_override.json | 35 ++ src/test/resources/valid/mix.json | 37 ++ .../{container1.json => mix_in_root.json} | 4 +- 25 files changed, 802 insertions(+), 810 deletions(-) delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/Container.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/Inheritable.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java delete mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java rename src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/{StandardInheritableAdapter.java => InheritableMetadataAdapter.java} (69%) rename src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/{ContainerLoaderAdapter.java => PluginLoaderSpecificationAdapter.java} (79%) rename src/main/java/org/spongepowered/plugin/metadata/model/{ContainerLoader.java => PluginLoaderSpecification.java} (89%) rename src/test/resources/legacy/{legacy_id.json => dash_in_id.json} (100%) create mode 100644 src/test/resources/valid/full_global.json create mode 100644 src/test/resources/valid/full_override.json create mode 100644 src/test/resources/valid/mix.json rename src/test/resources/valid/{container1.json => mix_in_root.json} (100%) diff --git a/src/main/java/org/spongepowered/plugin/metadata/Container.java b/src/main/java/org/spongepowered/plugin/metadata/Container.java deleted file mode 100644 index a7b6445..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/Container.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata; - -import org.spongepowered.plugin.metadata.model.ContainerLoader; - -import java.util.Collection; -import java.util.Optional; - -/** - * A container joins together {@link Inheritable global metadata} with specific one or more - * {@link PluginMetadata plugin metadata}. - *

    - * How the vendor utilizes this concept is largely left up to their discretion. A - * typical use case would be to load a file as a container. - * - * @see org.spongepowered.plugin.metadata.builtin.MetadataContainer MetadataContainer, for a generic implementation - */ -public interface Container { - - /** - * @return The {@link ContainerLoader loader}. - */ - ContainerLoader loader(); - - /** - * Gets the {@link String license} of the data within this container. - *

    - * Consult the vendor for how this field is used. - *

    - * In the generic implementation, a license's name is expected. - *

    - * Notable examples include MIT or All Rights Reserved. - * - * @return The license - */ - String license(); - - /** - * @return The {@link Inheritable global metadata} or {@link Optional#empty()} otherwise - */ - Optional globalMetadata(); - - /** - * Gets a {@link PluginMetadata plugin metadata} by its {@link String id}. - *

    - * This maps to {@link PluginMetadata#id()}. - * - * @param id The id - * @return The plugin metadata or {@link Optional#empty()} otherwise - */ - Optional metadata(String id); - - /** - * @return All of the {@link PluginMetadata plugin metadata} as an unmodifiable {@link Collection}. - */ - Collection metadata(); -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java b/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java deleted file mode 100644 index 1de121e..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/Inheritable.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata; - -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.spongepowered.plugin.metadata.model.PluginBranding; -import org.spongepowered.plugin.metadata.model.PluginContributor; -import org.spongepowered.plugin.metadata.model.PluginDependency; -import org.spongepowered.plugin.metadata.model.PluginLinks; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** - * Represents metadata that is meant to be inherited/overwritten, per contract. - * - * @see org.spongepowered.plugin.metadata.builtin.StandardInheritable StandardInheritable, for a generic implementation - * @see PluginMetadata PluginMetadata, for specific "plugin" metadata - */ -public interface Inheritable { - - /** - * @return The {@link ArtifactVersion version}. - */ - ArtifactVersion version(); - - /** - * @return The {@link PluginBranding branding}. - */ - PluginBranding branding(); - - /** - * @return The {@link PluginLinks links} to various web resources. - */ - PluginLinks links(); - - /** - * @return The {@link PluginContributor contributors} as an unmodifiable {@link List}. - */ - List contributors(); - - /** - * Gets the {@link PluginDependency plugin dependency} by {@link String id}. - *

    - * This maps to {@link PluginDependency#id()}. - * @param id The id - * @return The dependency or {@link Optional#empty()} otherwise. - */ - Optional dependency(String id); - - /** - * @return The {@link PluginDependency dependencies} as an unmodifiable {@link Collection}. - */ - Collection dependencies(); - - /** - * Gets the {@link T property} by {@link String key}. - * - * @param key The key - * @param The type - * @return The property or {@link Optional#empty()} otherwise. - */ - Optional property(String key); - - /** - * @return The properties as an unmodifiable {@link Map}. - */ - Map properties(); -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java index 3a28297..35591e7 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java @@ -24,20 +24,24 @@ */ package org.spongepowered.plugin.metadata; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.Optional; /** - * Represents specific, unique metadata to a plugin. + * Represents the metadata of a plugin. * - * @see Inheritable Inheritable, for metadata that might be shared between multiple plugin metadata * @see org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata StandardPluginMetadata, for a generic implementation */ -public interface PluginMetadata extends Inheritable { - - /** - * @return The {@link Container container}. - */ - Container container(); +public interface PluginMetadata { /** * Gets the {@link String id}. @@ -64,6 +68,16 @@ public interface PluginMetadata extends Inheritable { */ String entrypoint(); + /** + * @return The {@link ArtifactVersion version}. + */ + ArtifactVersion version(); + + /** + * @return The {@link PluginLoaderSpecification loader}. + */ + PluginLoaderSpecification loader(); + /** * @return The {@link String name} or {@link Optional#empty()} otherwise. */ @@ -73,4 +87,57 @@ public interface PluginMetadata extends Inheritable { * @return The {@link String description} or {@link Optional#empty()} otherwise. */ Optional description(); + + /** + * Gets the {@link String license} of the data within this container. + *

    + * Notable examples include MIT or All Rights Reserved. + * + * @return The license + */ + Optional license(); + + /** + * @return The {@link PluginBranding branding}. + */ + PluginBranding branding(); + + /** + * @return The {@link PluginLinks links} to various web resources. + */ + PluginLinks links(); + + /** + * @return The {@link PluginContributor contributors} as an unmodifiable {@link List}. + */ + List contributors(); + + /** + * Gets the {@link PluginDependency plugin dependency} by {@link String id}. + *

    + * This maps to {@link PluginDependency#id()}. + * @param id The id + * @return The dependency or {@link Optional#empty()} otherwise. + */ + Optional dependency(String id); + + /** + * @return The {@link PluginDependency dependencies} as an unmodifiable {@link Collection}. + */ + Collection dependencies(); + + /** + * Gets the {@link T property} by {@link String key}. + * + * @param key The key + * @param The type + * @return The property or {@link Optional#empty()} otherwise. + */ + Optional property(String key); + + /** + * @return The properties as an unmodifiable {@link Map}. + */ + Map properties(); + } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java new file mode 100644 index 0000000..385f0bd --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java @@ -0,0 +1,335 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.StringJoiner; + +public final class InheritableMetadata { + private static final InheritableMetadata NONE = new InheritableMetadata(); + + private final @Nullable ArtifactVersion version; + private final @Nullable PluginLoaderSpecification loader; + private final @Nullable String name; + private final @Nullable String description; + private final @Nullable String license; + private final PluginBranding branding; + private final PluginLinks links; + private final List contributors; + private final Map dependencies; + private final Map properties; + + private InheritableMetadata() { + this.version = null; + this.loader = null; + this.name = null; + this.description = null; + this.license = null; + this.branding = PluginBranding.none(); + this.links = PluginLinks.none(); + this.contributors = List.of(); + this.dependencies = Map.of(); + this.properties = Map.of(); + } + + private InheritableMetadata(final Builder builder) { + this.version = builder.version; + this.loader = builder.loader; + this.name = builder.name; + this.description = builder.description; + this.license = builder.license; + this.branding = builder.branding; + this.links = builder.links; + this.contributors = List.copyOf(builder.contributors); + this.dependencies = Collections.unmodifiableMap(new LinkedHashMap<>(builder.dependencies)); + this.properties = Collections.unmodifiableMap(new LinkedHashMap<>(builder.properties)); + } + + public Optional version() { + return Optional.ofNullable(this.version); + } + + public Optional loader() { + return Optional.ofNullable(this.loader); + } + + public Optional name() { + return Optional.ofNullable(this.name); + } + + public Optional description() { + return Optional.ofNullable(this.description); + } + + public Optional license() { + return Optional.ofNullable(this.license); + } + + public PluginBranding branding() { + return this.branding; + } + + public PluginLinks links() { + return this.links; + } + + public List contributors() { + return this.contributors; + } + + public Map dependencies() { + return this.dependencies; + } + + public Map properties() { + return this.properties; + } + + public InheritableMetadata with(final InheritableMetadata override) { + return this.toBuilder().with(override).build(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof InheritableMetadata other)) { + return false; + } + + return Objects.equals(this.version, other.version) + && Objects.equals(this.loader, other.loader) + && Objects.equals(this.name, other.name) + && Objects.equals(this.description, other.description) + && Objects.equals(this.license, other.license) + && this.branding.equals(other.branding) + && this.links.equals(other.links) + && this.contributors.equals(other.contributors) + && this.dependencies.equals(other.dependencies) + && this.properties.equals(other.properties); + } + + @Override + public int hashCode() { + return Objects.hash(this.version, this.loader, this.name, this.description, this.license, + this.branding, this.links, this.contributors, this.dependencies, this.properties); + } + + @Override + public String toString() { + return new StringJoiner(", ", InheritableMetadata.class.getSimpleName() + "[", "]") + .add("version=" + this.version) + .add("loader=" + this.loader) + .add("license=" + this.license) + .add("branding=" + this.branding) + .add("links=" + this.links) + .add("contributors=" + this.contributors) + .add("dependencies=" + this.dependencies) + .add("properties=" + this.properties) + .toString(); + } + + public InheritableMetadata.Builder toBuilder() { + return new InheritableMetadata.Builder().from(this); + } + + public static InheritableMetadata.Builder builder() { + return new InheritableMetadata.Builder(); + } + + public static InheritableMetadata none() { + return InheritableMetadata.NONE; + } + + public static final class Builder { + private @Nullable ArtifactVersion version; + private @Nullable PluginLoaderSpecification loader; + private @Nullable String name; + private @Nullable String description; + private @Nullable String license; + private PluginBranding branding = PluginBranding.none(); + private PluginLinks links = PluginLinks.none(); + private final List contributors = new LinkedList<>(); + private final Map dependencies = new LinkedHashMap<>(); + private final Map properties = new LinkedHashMap<>(); + + private Builder() { + } + + public Builder version(final @Nullable ArtifactVersion version) { + this.version = version; + return this; + } + + public Builder loader(final @Nullable PluginLoaderSpecification loader) { + this.loader = loader; + return this; + } + + public Builder name(final @Nullable String name) { + this.name = name; + return this; + } + + public Builder description(final @Nullable String description) { + this.description = description; + return this; + } + + public Builder license(final @Nullable String license) { + this.license = license; + return this; + } + + public Builder branding(final PluginBranding branding) { + this.branding = Objects.requireNonNull(branding, "branding"); + return this; + } + + public Builder links(final PluginLinks links) { + this.links = Objects.requireNonNull(links, "links"); + return this; + } + + public Builder contributors(final Collection contributors) { + Objects.requireNonNull(contributors, "contributors"); + this.contributors.clear(); + this.contributors.addAll(contributors); + return this; + } + + public Builder addContributors(final Collection contributors) { + this.contributors.addAll(Objects.requireNonNull(contributors, "contributors")); + return this; + } + + public Builder addContributor(final PluginContributor contributor) { + this.contributors.add(Objects.requireNonNull(contributor, "contributor")); + return this; + } + + public Builder dependencies(final Collection dependencies) { + Objects.requireNonNull(dependencies, "dependencies"); + this.dependencies.clear(); + return this.addDependencies(dependencies); + } + + public Builder addDependencies(final Collection dependencies) { + for (final PluginDependency element : Objects.requireNonNull(dependencies, "dependencies")) { + this.dependencies.put(Objects.requireNonNull(element, "element").id(), element); + } + return this; + } + + public Builder addDependency(final PluginDependency dependency) { + this.dependencies.put(Objects.requireNonNull(dependency, "dependency").id(), dependency); + return this; + } + + public Builder properties(final Map properties) { + Objects.requireNonNull(properties, "properties"); + this.properties.clear(); + this.properties.putAll(properties); + return this; + } + + public Builder addProperties(final Map properties) { + this.properties.putAll(Objects.requireNonNull(properties, "properties")); + return this; + } + + public Builder addProperty(final String key, final Object value) { + this.properties.put(Objects.requireNonNull(key, "key"), Objects.requireNonNull(value, "value")); + return this; + } + + public Builder from(final InheritableMetadata value) { + Objects.requireNonNull(value, "value"); + this.version = value.version; + this.loader = value.loader; + this.name = value.name; + this.description = value.description; + this.license = value.license; + this.branding = value.branding; + this.links = value.links; + this.contributors.clear(); + this.contributors.addAll(value.contributors); + this.dependencies.clear(); + this.dependencies.putAll(value.dependencies); + this.properties.clear(); + this.properties.putAll(value.properties); + return this; + } + + public Builder with(final InheritableMetadata override) { + if (override.version != null) { + this.version = override.version; + } + if (override.loader != null) { + this.loader = override.loader; + } + if (override.name != null) { + this.name = override.name; + } + if (override.description != null) { + this.description = override.description; + } + if (override.license != null) { + this.license = override.license; + } + if (!override.branding.equals(PluginBranding.none())) { + this.branding = override.branding; + } + if (!override.links.equals(PluginLinks.none())) { + this.links = override.links; + } + this.contributors.addAll(override.contributors); + this.dependencies.putAll(override.dependencies); + this.properties.putAll(override.properties); + return this; + } + + public InheritableMetadata build() { + return new InheritableMetadata(this); + } + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java index c7cfe95..e71c3d8 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataContainer.java @@ -24,142 +24,20 @@ */ package org.spongepowered.plugin.metadata.builtin; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.metadata.Container; -import org.spongepowered.plugin.metadata.Inheritable; -import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.model.ContainerLoader; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.List; import java.util.Objects; -import java.util.Optional; -import java.util.StringJoiner; - -public final class MetadataContainer implements Container { - - private final String license; - private final ContainerLoader loader; - @Nullable private final Inheritable globalMetadata; - private final Map metadata; - - private MetadataContainer(final Builder builder) { - this.loader = builder.loader; - this.license = builder.license; - this.globalMetadata = builder.globalMetadata; - this.metadata = Collections.unmodifiableMap(new LinkedHashMap<>(builder.metadata)); - - for (final StandardPluginMetadata element : this.metadata.values()) { - element.setContainer(this); - } - } - - @Override - public ContainerLoader loader() { - return this.loader; - } - - @Override - public String license() { - return this.license; - } - - @Override - public Optional globalMetadata() { - return Optional.ofNullable(this.globalMetadata); - } - - @Override - public Optional metadata(final String id) { - return Optional.ofNullable(this.metadata.get(Objects.requireNonNull(id, "id"))); - } - - @Override - public Collection metadata() { - return this.metadata.values(); - } - - @Override - public String toString() { - return new StringJoiner(", ", MetadataContainer.class.getSimpleName() + "[", "]") - .add("loader=" + this.loader) - .add("license=" + this.license) - .add("globalMetadata=" + this.globalMetadata) - .toString(); - } - - public MetadataContainer.Builder toBuilder() { - return new MetadataContainer.Builder().from(this); - } - public static MetadataContainer.Builder builder() { - return new MetadataContainer.Builder(); - } - - public static final class Builder { - - private final Map metadata = new LinkedHashMap<>(); - private @MonotonicNonNull String license; - private @MonotonicNonNull ContainerLoader loader; - private @Nullable Inheritable globalMetadata; - - private Builder() {} - - public Builder loader(final ContainerLoader loader) { - this.loader = Objects.requireNonNull(loader, "loader"); - return this; - } +public record MetadataContainer(InheritableMetadata global, List plugins) { - public Builder license(final String license) { - this.license = Objects.requireNonNull(license, "license"); - return this; - } - - public Builder globalMetadata(final @Nullable Inheritable globalMetadata) { - this.globalMetadata = globalMetadata; - return this; - } - - public Builder metadata(final Collection metadata) { - Objects.requireNonNull(metadata, "metadata"); - this.metadata.clear(); - return this.addMetadata(metadata); - } - - public Builder addMetadata(final Collection metadata) { - for (final StandardPluginMetadata element : Objects.requireNonNull(metadata, "metadata")) { - this.metadata.put(Objects.requireNonNull(element, "element").id(), element); - } - return this; - } - - public Builder addMetadata(final StandardPluginMetadata metadata) { - this.metadata.put(Objects.requireNonNull(metadata, "metadata").id(), metadata); - return this; - } - - public Builder from(final MetadataContainer value) { - Objects.requireNonNull(value, "value"); - this.loader = value.loader; - this.license = value.license; - this.globalMetadata = value.globalMetadata; - this.metadata.clear(); - this.metadata.putAll(value.metadata); - return this; - } - - public MetadataContainer build() throws IllegalStateException { - Objects.requireNonNull(this.license, "license"); - Objects.requireNonNull(this.loader, "loader"); - - if (this.metadata.isEmpty()) { - throw new IllegalStateException("A MetadataHolder must hold at least 1 PluginMetadata!"); + public MetadataContainer { + Objects.requireNonNull(global, "global"); + Objects.requireNonNull(plugins, "plugins"); + for (final StandardPluginMetadata plugin : plugins) { + Objects.requireNonNull(plugin, "plugin"); + if (!plugin.global().equals(global)) { + throw new IllegalArgumentException("global metadata mismatch"); } - - return new MetadataContainer(this); } + plugins = List.copyOf(plugins); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java index 008d25f..584d60a 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java @@ -30,8 +30,8 @@ import com.google.gson.stream.JsonWriter; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.VersionRange; +import org.spongepowered.plugin.metadata.builtin.adapter.InheritableMetadataAdapter; import org.spongepowered.plugin.metadata.builtin.adapter.MetadataContainerAdapter; -import org.spongepowered.plugin.metadata.builtin.adapter.StandardInheritableAdapter; import org.spongepowered.plugin.metadata.builtin.adapter.StandardPluginMetadataBuilderDeserializer; import org.spongepowered.plugin.metadata.builtin.adapter.StandardPluginMetadataSerializer; import org.spongepowered.plugin.metadata.builtin.adapter.model.*; @@ -52,15 +52,15 @@ public final class MetadataParser { private static final Gson GSON = new GsonBuilder() - .registerTypeAdapter(ContainerLoader.class, new ContainerLoaderAdapter()) + .registerTypeAdapter(PluginLoaderSpecification.class, new PluginLoaderSpecificationAdapter()) .registerTypeAdapter(PluginBranding.class, new PluginBrandingAdapter()) .registerTypeAdapter(PluginContributor.class, new PluginContributorAdapter()) .registerTypeAdapter(PluginDependency.class, new PluginDependencyAdapter()) .registerTypeAdapter(PluginLinks.class, new PluginLinksAdapter()) .registerTypeAdapter(ArtifactVersion.class, new ArtifactVersionAdapter()) .registerTypeAdapter(VersionRange.class, new VersionRangeAdapter()) + .registerTypeAdapter(InheritableMetadata.class, new InheritableMetadataAdapter()) .registerTypeAdapter(MetadataContainer.class, new MetadataContainerAdapter()) - .registerTypeAdapter(StandardInheritable.class, new StandardInheritableAdapter()) .registerTypeAdapter(StandardPluginMetadata.Builder.class, new StandardPluginMetadataBuilderDeserializer()) .registerTypeAdapter(StandardPluginMetadata.class, new StandardPluginMetadataSerializer()) .create(); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java deleted file mode 100644 index 1fdb841..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/NullVersion.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin; - -import org.apache.maven.artifact.versioning.ArtifactVersion; - -public final class NullVersion implements ArtifactVersion { - - private static final NullVersion INSTANCE = new NullVersion(); - - public static NullVersion instance() { - return NullVersion.INSTANCE; - } - - private NullVersion() {} - - @Override - public int getMajorVersion() { - throw new RuntimeException("This is a null version and should not be used!"); - } - - @Override - public int getMinorVersion() { - throw new RuntimeException("This is a null version and should not be used!"); - } - - @Override - public int getIncrementalVersion() { - throw new RuntimeException("This is a null version and should not be used!"); - } - - @Override - public int getBuildNumber() { - throw new RuntimeException("This is a null version and should not be used!"); - } - - @Override - public String getQualifier() { - throw new RuntimeException("This is a null version and should not be used!"); - } - - @Override - public void parseVersion(final String version) { - throw new RuntimeException("This is a null version and should not be used!"); - } - - @Override - public int compareTo(final ArtifactVersion o) { - throw new RuntimeException("This is a null version and should not be used!"); - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java deleted file mode 100644 index 18134f8..0000000 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardInheritable.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * This file is part of plugin-meta, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * 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 org.spongepowered.plugin.metadata.builtin; - -import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.apache.maven.artifact.versioning.DefaultArtifactVersion; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.metadata.Inheritable; -import org.spongepowered.plugin.metadata.model.PluginBranding; -import org.spongepowered.plugin.metadata.model.PluginContributor; -import org.spongepowered.plugin.metadata.model.PluginDependency; -import org.spongepowered.plugin.metadata.model.PluginLinks; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.StringJoiner; - -public class StandardInheritable implements Inheritable { - - protected final ArtifactVersion version; - protected final PluginBranding branding; - protected final PluginLinks links; - protected final List contributors; - protected final Map dependencies; - protected final Map properties; - - @SuppressWarnings({"rawtypes", "unchecked"}) - protected StandardInheritable(final AbstractBuilder builder) { - this.version = builder.version; - this.branding = builder.branding; - this.links = builder.links; - this.contributors = List.copyOf(builder.contributors); - this.dependencies = Collections.unmodifiableMap(new LinkedHashMap<>(builder.dependencies)); - this.properties = Collections.unmodifiableMap(new LinkedHashMap<>(builder.properties)); - } - - @Override - public ArtifactVersion version() { - return this.version; - } - - @Override - public PluginBranding branding() { - return this.branding; - } - - @Override - public PluginLinks links() { - return this.links; - } - - @Override - public List contributors() { - return this.contributors; - } - - @Override - public Optional dependency(final String id) { - return Optional.ofNullable(this.dependencies.get(Objects.requireNonNull(id, "id"))); - } - - @Override - public Collection dependencies() { - return this.dependencies.values(); - } - - @SuppressWarnings("unchecked") - @Override - public Optional property(final String key) { - return Optional.ofNullable((T) this.properties.get(Objects.requireNonNull(key, "key"))); - } - - @Override - public Map properties() { - return this.properties; - } - - @Override - public String toString() { - return this.stringJoiner().toString(); - } - - protected StringJoiner stringJoiner() { - return new StringJoiner(", ", StandardInheritable.class.getSimpleName() + "[", "]") - .add("version=" + this.version) - .add("branding=" + this.branding) - .add("links=" + this.links) - .add("contributors=" + this.contributors) - .add("dependencies=" + this.dependencies) - .add("properties=" + this.properties) - ; - } - - @SuppressWarnings("unchecked") - public abstract static class AbstractBuilder> { - - protected final List contributors = new LinkedList<>(); - protected final Map dependencies = new LinkedHashMap<>(); - protected final Map properties = new LinkedHashMap<>(); - protected ArtifactVersion version = NullVersion.instance(); - protected PluginBranding branding = PluginBranding.none(); - protected PluginLinks links = PluginLinks.none(); - - protected AbstractBuilder() { - } - - public B version(final @Nullable String version) { - this.version = version == null ? NullVersion.instance() : new DefaultArtifactVersion(version); - return (B) this; - } - - public B version(final @Nullable ArtifactVersion version) { - this.version = version == null ? NullVersion.instance() : version; - return (B) this; - } - - public B branding(final PluginBranding branding) { - this.branding = Objects.requireNonNull(branding, "branding"); - return (B) this; - } - - public B links(final PluginLinks links) { - this.links = Objects.requireNonNull(links, "links"); - return (B) this; - } - - public B contributors(final Collection contributors) { - Objects.requireNonNull(contributors, "contributors"); - this.contributors.clear(); - this.contributors.addAll(contributors); - return (B) this; - } - - public B addContributors(final Collection contributors) { - this.contributors.addAll(Objects.requireNonNull(contributors, "contributors")); - return (B) this; - } - - public B addContributor(final PluginContributor contributor) { - this.contributors.add(Objects.requireNonNull(contributor, "contributor")); - return (B) this; - } - - public B dependencies(final Collection dependencies) { - Objects.requireNonNull(dependencies, "dependencies"); - this.dependencies.clear(); - return this.addDependencies(dependencies); - } - - public B addDependencies(final Collection dependencies) { - for (final PluginDependency element : Objects.requireNonNull(dependencies, "dependencies")) { - this.dependencies.put(Objects.requireNonNull(element, "element").id(), element); - } - return (B) this; - } - - public B addDependency(final PluginDependency dependency) { - this.dependencies.put(Objects.requireNonNull(dependency, "dependency").id(), dependency); - return (B) this; - } - - public B properties(final Map properties) { - Objects.requireNonNull(properties, "properties"); - this.properties.clear(); - this.properties.putAll(properties); - return (B) this; - } - - public B addProperties(final Map properties) { - this.properties.putAll(Objects.requireNonNull(properties, "properties")); - return (B) this; - } - - public B addProperty(final String key, final Object value) { - this.properties.put(Objects.requireNonNull(key, "key"), Objects.requireNonNull(value, "value")); - return (B) this; - } - - public B merge(final StandardInheritable value) { - Objects.requireNonNull(value, "value"); - if (this.version == NullVersion.instance()) { - this.version = value.version; - } - if (this.branding == PluginBranding.none()) { - this.branding = value.branding; - } - if (this.links == PluginLinks.none()) { - this.links = value.links; - } - if (this.contributors.isEmpty()) { - this.contributors.addAll(value.contributors); - } - for (final Map.Entry entry : value.dependencies.entrySet()) { - this.dependencies.putIfAbsent(entry.getKey(), entry.getValue()); - } - for (final Map.Entry entry : value.properties.entrySet()) { - this.properties.putIfAbsent(entry.getKey(), entry.getValue()); - } - return (B) this; - } - - public B from(final T value) { - Objects.requireNonNull(value, "value"); - this.version = value.version; - this.branding = value.branding; - this.links = value.links; - this.contributors.clear(); - this.contributors.addAll(value.contributors); - this.dependencies.clear(); - this.dependencies.putAll(value.dependencies); - this.properties.clear(); - this.properties.putAll(value.properties); - return (B) this; - } - - public final T build() { - Objects.requireNonNull(this.version, "version"); - - return this.build0(); - } - - protected abstract T build0(); - } - - public static final class Builder extends AbstractBuilder { - - @Override - protected StandardInheritable build0() { - return new StandardInheritable(this); - } - } -} diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java index a4d7093..a028326 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java @@ -24,47 +24,84 @@ */ package org.spongepowered.plugin.metadata.builtin; +import org.apache.maven.artifact.versioning.ArtifactVersion; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.plugin.metadata.Constants; -import org.spongepowered.plugin.metadata.Container; import org.spongepowered.plugin.metadata.PluginMetadata; - +import org.spongepowered.plugin.metadata.model.PluginBranding; +import org.spongepowered.plugin.metadata.model.PluginContributor; +import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; -public final class StandardPluginMetadata extends StandardInheritable implements PluginMetadata { - +public final class StandardPluginMetadata implements PluginMetadata { private final String id, entrypoint; - @Nullable private final String name, description; - @Nullable private Container container; + private final InheritableMetadata global, override; + + private final ArtifactVersion version; + private final PluginLoaderSpecification loader; + private final @Nullable String name; + private final @Nullable String description; + private final @Nullable String license; + private final PluginBranding branding; + private final PluginLinks links; + private final List contributors; + private final Map dependencies; + private final Map properties; private StandardPluginMetadata(final Builder builder) { - super(builder); this.id = builder.id; this.entrypoint = builder.entrypoint; - this.name = builder.name; - this.description = builder.description; + this.global = builder.global; + this.override = builder.override; + InheritableMetadata metadata = this.global.with(this.override); + this.version = metadata.version().orElseThrow(() -> new NoSuchElementException("version")); + this.loader = metadata.loader().orElseThrow(() -> new NoSuchElementException("loader")); + this.name = metadata.name().orElse(null); + this.description = metadata.description().orElse(null); + this.license = metadata.license().orElse(null); + this.branding = metadata.branding(); + this.links = metadata.links(); + this.contributors = metadata.contributors(); + this.dependencies = metadata.dependencies(); + this.properties = metadata.properties(); } @Override - public Container container() { - return this.container; + public String id() { + return this.id; + } + + @Override + public String entrypoint() { + return this.entrypoint; + } + + public InheritableMetadata global() { + return this.global; } - void setContainer(final MetadataContainer container) { - this.container = container; + public InheritableMetadata override() { + return this.override; } @Override - public String id() { - return this.id; + public ArtifactVersion version() { + return this.version; } @Override - public String entrypoint() { - return this.entrypoint; + public PluginLoaderSpecification loader() { + return this.loader; } @Override @@ -77,9 +114,50 @@ public Optional description() { return Optional.ofNullable(this.description); } + @Override + public Optional license() { + return Optional.ofNullable(this.license); + } + + @Override + public PluginBranding branding() { + return this.branding; + } + + @Override + public PluginLinks links() { + return this.links; + } + + @Override + public List contributors() { + return this.contributors; + } + + @Override + public Optional dependency(String id) { + return Optional.ofNullable(this.dependencies.get(Objects.requireNonNull(id, "id"))); + } + + @Override + public Collection dependencies() { + return this.dependencies.values(); + } + + @SuppressWarnings("unchecked") + @Override + public Optional property(final String key) { + return Optional.ofNullable((T) this.properties.get(Objects.requireNonNull(key, "key"))); + } + + @Override + public Map properties() { + return this.properties; + } + @Override public int hashCode() { - return Objects.hashCode(this.id); + return Objects.hash(this.id, this.entrypoint, this.global, this.override); } @Override @@ -92,18 +170,24 @@ public boolean equals(final Object o) { return false; } - return this.id.equals(other.id); + return this.id.equals(other.id) && this.entrypoint.equals(other.entrypoint) + && this.global.equals(other.global) && this.override.equals(other.override); } @Override public String toString() { - final StringJoiner joiner = new StringJoiner(", ", StandardPluginMetadata.class.getSimpleName() + "[", "]") + return new StringJoiner(", ", StandardPluginMetadata.class.getSimpleName() + "[", "]") .add("id=" + this.id) - .add("name=" + this.name) .add("entrypoint=" + this.entrypoint) - .add("description=" + this.description); - joiner.merge(this.stringJoiner()); - return joiner.toString(); + .add("version=" + this.version) + .add("loader=" + this.loader) + .add("license=" + this.license) + .add("branding=" + this.branding) + .add("links=" + this.links) + .add("contributors=" + this.contributors) + .add("dependencies=" + this.dependencies) + .add("properties=" + this.properties) + .toString(); } public StandardPluginMetadata.Builder toBuilder() { @@ -114,10 +198,10 @@ public static StandardPluginMetadata.Builder builder() { return new StandardPluginMetadata.Builder(); } - public static final class Builder extends StandardInheritable.AbstractBuilder { + public static final class Builder { private @MonotonicNonNull String id, entrypoint; - private @Nullable String name, description; + private InheritableMetadata global = InheritableMetadata.none(), override = InheritableMetadata.none(); private Builder() {} @@ -131,38 +215,31 @@ public Builder entrypoint(final String entrypoint) { return this; } - public Builder name(final @Nullable String name) { - this.name = name; + public Builder global(final InheritableMetadata global) { + this.global = Objects.requireNonNull(global, "global"); return this; } - public Builder description(final @Nullable String description) { - this.description = description; + public Builder override(final InheritableMetadata override) { + this.override = Objects.requireNonNull(override, "override"); return this; } - @Override public Builder from(final StandardPluginMetadata value) { this.id = value.id; this.entrypoint = value.entrypoint; - this.name = value.name; - this.description = value.description; - return super.from(value); + this.global = value.global; + this.override = value.override; + return this; } - @Override - protected StandardPluginMetadata build0() { + public StandardPluginMetadata build() { if (!Constants.VALID_ID_PATTERN.matcher(Objects.requireNonNull(this.id, "id")).matches()) { throw new IllegalStateException(String.format("PluginMetadata with supplied ID '{%s}' is invalid. %s", this.id, Constants.INVALID_ID_REQUIREMENTS_MESSAGE)); } - if (this.version == NullVersion.instance()) { - throw new IllegalStateException(String.format("PluginMetadata with supplied ID '{%s}' has no version specified.", this.id)); - } Objects.requireNonNull(this.entrypoint, "entrypoint"); - return new StandardPluginMetadata(this); } } - } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java similarity index 69% rename from src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java rename to src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java index b66775b..ddd4c9e 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardInheritableAdapter.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java @@ -26,24 +26,28 @@ import com.google.gson.*; import org.apache.maven.artifact.versioning.ArtifactVersion; -import org.spongepowered.plugin.metadata.builtin.NullVersion; -import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; import org.spongepowered.plugin.metadata.model.PluginBranding; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; import org.spongepowered.plugin.metadata.model.PluginLinks; -import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; import java.lang.reflect.Type; import java.util.LinkedHashMap; -public final class StandardInheritableAdapter implements JsonSerializer, JsonDeserializer { +public final class InheritableMetadataAdapter implements JsonSerializer, JsonDeserializer { @Override - public StandardInheritable deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + public InheritableMetadata deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { final JsonObject obj = element.getAsJsonObject(); - return new StandardInheritable.Builder() - .version(GsonUtils.optional(obj, "version").map(JsonElement::getAsString).orElse(null)) + return InheritableMetadata.builder() + .version(GsonUtils.optional(obj, "version").map(v -> context.deserialize(v, ArtifactVersion.class)).orElse(null)) + .loader(GsonUtils.optional(obj, "loader").map(v -> context.deserialize(v, PluginLoaderSpecification.class)).orElse(null)) + .name(GsonUtils.optional(obj, "name").map(JsonElement::getAsString).orElse(null)) + .description(GsonUtils.optional(obj, "description").map(JsonElement::getAsString).orElse(null)) + .license(GsonUtils.optional(obj, "license").map(JsonElement::getAsString).orElse(null)) .branding(GsonUtils.optional(obj, "branding").map(v -> context.deserialize(v, PluginBranding.class)).orElseGet(PluginBranding::none)) .links(GsonUtils.optional(obj, "links").map(v -> context.deserialize(v, PluginLinks.class)).orElseGet(PluginLinks::none)) .contributors(GsonUtils.stream(obj, "contributors").map(v -> context.deserialize(v, PluginContributor.class)).toList()) @@ -53,22 +57,24 @@ public StandardInheritable deserialize(final JsonElement element, final Type typ } @Override - public JsonElement serialize(final StandardInheritable value, final Type type, final JsonSerializationContext context) { + public JsonElement serialize(final InheritableMetadata value, final Type type, final JsonSerializationContext context) { final JsonObject obj = new JsonObject(); - if (value.version() != NullVersion.instance()) { - obj.add("version", context.serialize(value.version(), ArtifactVersion.class)); - } - if (value.branding() != PluginBranding.none()) { + value.version().ifPresent(v -> obj.add("version", context.serialize(v, ArtifactVersion.class))); + value.loader().ifPresent(v -> obj.add("loader", context.serialize(v, PluginLoaderSpecification.class))); + value.name().ifPresent(v -> obj.addProperty("name", v)); + value.description().ifPresent(v -> obj.addProperty("description", v)); + value.license().ifPresent(v -> obj.addProperty("license", v)); + if (!value.branding().equals(PluginBranding.none())) { obj.add("branding", context.serialize(value.branding(), PluginBranding.class)); } - if (value.links() != PluginLinks.none()) { + if (!value.links().equals(PluginLinks.none())) { obj.add("links", context.serialize(value.links(), PluginLinks.class)); } if (!value.contributors().isEmpty()) { obj.add("contributors", GsonUtils.toArray(value.contributors().stream().map(v -> context.serialize(v, PluginContributor.class)))); } if (!value.dependencies().isEmpty()) { - obj.add("dependencies", GsonUtils.toArray(value.dependencies().stream().map(v -> context.serialize(v, PluginDependency.class)))); + obj.add("dependencies", GsonUtils.toArray(value.dependencies().values().stream().map(v -> context.serialize(v, PluginDependency.class)))); } if (!value.properties().isEmpty()) { obj.add("properties", GsonUtils.serializeMap(value.properties(), v -> new JsonPrimitive(v.toString()))); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java index cbbfceb..db284ab 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/MetadataContainerAdapter.java @@ -24,13 +24,18 @@ */ package org.spongepowered.plugin.metadata.builtin.adapter; -import com.google.gson.*; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.metadata.PluginMetadata; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import org.spongepowered.plugin.metadata.builtin.MetadataContainer; -import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; -import org.spongepowered.plugin.metadata.model.ContainerLoader; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; import java.lang.reflect.Type; @@ -40,56 +45,43 @@ public final class MetadataContainerAdapter implements JsonSerializer, JsonDeserializer { @Override - public MetadataContainer deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) - throws JsonParseException { + public MetadataContainer deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { final JsonObject obj = element.getAsJsonObject(); - final MetadataContainer.Builder builder = MetadataContainer.builder() - .loader(context.deserialize(GsonUtils.require(obj, "loader"), ContainerLoader.class)) - .license(GsonUtils.require(obj, "license").getAsString()); + // Read some global data in the root element for retro-compatibility + InheritableMetadata global = InheritableMetadata.builder() + .loader(GsonUtils.optional(obj, "loader").map(v -> context.deserialize(v, PluginLoaderSpecification.class)).orElse(null)) + .license(GsonUtils.optional(obj, "license").map(JsonElement::getAsString).orElse(null)) + .build(); final JsonElement globalElement = obj.get("global"); - @Nullable StandardInheritable inheritable = null; if (globalElement instanceof JsonObject) { - inheritable = context.deserialize(globalElement, StandardInheritable.class); - builder.globalMetadata(inheritable); + global = global.with(context.deserialize(globalElement, InheritableMetadata.class)); } - final JsonElement pluginsElement = GsonUtils.require(obj, "plugins"); - final List pluginObjects = new LinkedList<>(); - - if (pluginsElement.isJsonArray()) { - for (final JsonElement pluginElement : ((JsonArray) pluginsElement)) { + final List plugins = new LinkedList<>(); + if (GsonUtils.require(obj, "plugins") instanceof JsonArray pluginsArray) { + for (final JsonElement pluginElement : pluginsArray) { if (pluginElement.isJsonObject()) { - pluginObjects.add((JsonObject) pluginElement); + plugins.add(context.deserialize(pluginElement, StandardPluginMetadata.Builder.class) + .global(global).build()); } } } - if (pluginObjects.isEmpty()) { - throw new JsonParseException("No plugin metadata has been specified for the 'plugins' tag!"); - } - - for (final JsonObject pluginObject : pluginObjects) { - final StandardPluginMetadata.Builder pluginBuilder = context.deserialize(pluginObject, StandardPluginMetadata.Builder.class); - if (inheritable != null) { - pluginBuilder.merge(inheritable); - } - builder.addMetadata(pluginBuilder.build()); - } - - return builder.build(); + return new MetadataContainer(global, plugins); } @Override public JsonElement serialize(final MetadataContainer value, final Type type, final JsonSerializationContext context) { final JsonObject obj = new JsonObject(); - obj.add("loader", context.serialize(value.loader(), ContainerLoader.class)); - obj.addProperty("license", value.license()); + if (!value.global().equals(InheritableMetadata.none())) { + obj.add("global", context.serialize(value.global(), InheritableMetadata.class)); + } final JsonArray plugins = new JsonArray(); - for (final PluginMetadata metadata : value.metadata()) { - plugins.add(context.serialize(metadata, StandardPluginMetadata.class)); + for (final StandardPluginMetadata plugin : value.plugins()) { + plugins.add(context.serialize(plugin, StandardPluginMetadata.class)); } obj.add("plugins", plugins); diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java index 7998869..eadb629 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java @@ -25,7 +25,7 @@ package org.spongepowered.plugin.metadata.builtin.adapter; import com.google.gson.*; -import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; import org.spongepowered.plugin.metadata.builtin.adapter.util.LegacyIds; @@ -40,8 +40,6 @@ public StandardPluginMetadata.Builder deserialize(final JsonElement element, fin return StandardPluginMetadata.builder() .id(LegacyIds.fix(GsonUtils.require(obj, "id").getAsString())) .entrypoint(GsonUtils.require(obj, "entrypoint").getAsString()) - .name(GsonUtils.optional(obj, "name").map(JsonElement::getAsString).orElse(null)) - .description(GsonUtils.optional(obj, "description").map(JsonElement::getAsString).orElse(null)) - .merge(context.deserialize(element, StandardInheritable.class)); + .override(context.deserialize(element, InheritableMetadata.class)); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java index eadd9ab..6c49ee2 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java @@ -28,7 +28,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import org.spongepowered.plugin.metadata.builtin.StandardInheritable; +import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; import java.lang.reflect.Type; @@ -39,10 +39,8 @@ public final class StandardPluginMetadataSerializer implements JsonSerializer obj.addProperty("name", v)); - value.description().ifPresent(v -> obj.addProperty("description", v)); obj.addProperty("entrypoint", value.entrypoint()); - obj.asMap().putAll(((JsonObject) context.serialize(value, StandardInheritable.class)).asMap()); + obj.asMap().putAll(((JsonObject) context.serialize(value.override(), InheritableMetadata.class)).asMap()); return obj; } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLoaderSpecificationAdapter.java similarity index 79% rename from src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java rename to src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLoaderSpecificationAdapter.java index bf2d451..2a1f752 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/ContainerLoaderAdapter.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginLoaderSpecificationAdapter.java @@ -32,24 +32,24 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import org.apache.maven.artifact.versioning.VersionRange; -import org.spongepowered.plugin.metadata.model.ContainerLoader; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; import java.lang.reflect.Type; -public final class ContainerLoaderAdapter implements JsonSerializer, JsonDeserializer { +public final class PluginLoaderSpecificationAdapter implements JsonSerializer, JsonDeserializer { @Override - public ContainerLoader deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + public PluginLoaderSpecification deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { final JsonObject obj = element.getAsJsonObject(); - return new ContainerLoader( + return new PluginLoaderSpecification( GsonUtils.require(obj, "name").getAsString(), context.deserialize(GsonUtils.require(obj, "version"), VersionRange.class) ); } @Override - public JsonElement serialize(final ContainerLoader value, final Type type, final JsonSerializationContext context) { + public JsonElement serialize(final PluginLoaderSpecification value, final Type type, final JsonSerializationContext context) { final JsonObject obj = new JsonObject(); obj.addProperty("name", value.name()); obj.add("version", context.serialize(value.version(), VersionRange.class)); diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java index 44fc592..9b1a37a 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginBranding.java @@ -25,15 +25,13 @@ package org.spongepowered.plugin.metadata.model; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; import java.util.Objects; import java.util.Optional; /** - * Specification for an entity representing the branding of an {@link Inheritable inheritable} - * or {@link PluginMetadata plugin metadata}. + * Specification for an entity representing the branding of a {@link PluginMetadata plugin metadata}. *

    * Consult the vendor for further information on how this is used. * diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java index 9ad109f..4113cb0 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginContributor.java @@ -25,14 +25,13 @@ package org.spongepowered.plugin.metadata.model; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; import java.util.Objects; import java.util.Optional; /** - * Specification for an entity considered to be a "contributor" to an {@link Inheritable inheritable} + * Specification for an entity considered to be a "contributor" to a {@link PluginMetadata metadata} * or {@link PluginMetadata plugin metadata}. *

    * Consult the vendor for further information on how this is used. diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java index 5fde4f2..2fbcb4b 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java @@ -26,14 +26,12 @@ import org.apache.maven.artifact.versioning.VersionRange; import org.spongepowered.plugin.metadata.Constants; -import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; import java.util.Objects; /** - * Specification for an entity considered to be a "dependency" for an {@link Inheritable inheritable} - * or {@link PluginMetadata plugin metadata}. + * Specification for an entity considered to be a "dependency" for a {@link PluginMetadata plugin metadata}. *

    * Consult the vendor for further information on how this is used. * diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java index 0ccd846..42f1947 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLinks.java @@ -25,7 +25,6 @@ package org.spongepowered.plugin.metadata.model; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.plugin.metadata.Inheritable; import org.spongepowered.plugin.metadata.PluginMetadata; import java.net.URI; @@ -33,8 +32,7 @@ import java.util.Optional; /** - * Specification for an entity representing the links to "web resources" of an {@link Inheritable inheritable} - * or {@link PluginMetadata plugin metadata}. + * Specification for an entity representing the links to "web resources" of a {@link PluginMetadata plugin metadata}. *

    * Consult the vendor for further information on how this is used. * diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLoaderSpecification.java similarity index 89% rename from src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java rename to src/main/java/org/spongepowered/plugin/metadata/model/PluginLoaderSpecification.java index fa82377..4f235a2 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/model/ContainerLoader.java +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginLoaderSpecification.java @@ -25,21 +25,21 @@ package org.spongepowered.plugin.metadata.model; import org.apache.maven.artifact.versioning.VersionRange; -import org.spongepowered.plugin.metadata.Container; +import org.spongepowered.plugin.metadata.PluginMetadata; import java.util.Objects; /** - * Specification for an entity representing the "loader" of a {@link Container container}. + * Specification for an entity representing the "loader" of a {@link PluginMetadata plugin metadata}. *

    * Consult the vendor for further information on how this is used. * * @param name The {@link String name} * @param version The {@link VersionRange version}, as a maven range. */ -public record ContainerLoader(String name, VersionRange version) { +public record PluginLoaderSpecification(String name, VersionRange version) { - public ContainerLoader { + public PluginLoaderSpecification { Objects.requireNonNull(name, "name"); Objects.requireNonNull(version, "version"); } diff --git a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java index 709718a..a10a107 100644 --- a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java +++ b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java @@ -24,14 +24,15 @@ */ package org.spongepowered.plugin.metadata.builtin; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; import org.apache.maven.artifact.versioning.VersionRange; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.spongepowered.plugin.metadata.PluginMetadata; -import org.spongepowered.plugin.metadata.model.ContainerLoader; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; import org.spongepowered.plugin.metadata.model.PluginLinks; +import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; import java.io.*; import java.net.URI; @@ -62,63 +63,107 @@ private static List writeContainer(final MetadataContainer container) th return List.of(writer.toString().split("\n")); } - private static final MetadataContainer container1 = MetadataContainer.builder() - .loader(new ContainerLoader("java_plain", VersionRange.createFromVersion("1.0"))) + private static final InheritableMetadata global = InheritableMetadata.builder() + .loader(new PluginLoaderSpecification("java_plain", VersionRange.createFromVersion("1.0"))) .license("some_license") - .addMetadata(StandardPluginMetadata.builder() + .build(); + + private static final InheritableMetadata override = InheritableMetadata.builder() + .name("TestPlugin") + .version(new DefaultArtifactVersion("1.2.3")) + .description("Some test plugin") + .links(new PluginLinks( + URI.create("https://spongepowered.org/"), + URI.create("https://github.com/SpongePowered/Sponge"), + URI.create("https://github.com/SpongePowered/Sponge/issues") + )) + .addContributor(new PluginContributor("Spongie", "Mascot")) + .addDependency(new PluginDependency("spongeapi", VersionRange.createFromVersion("17.0.0"), PluginDependency.LoadOrder.AFTER, false)) + .build(); + + private static final InheritableMetadata full = global.with(override); + + private static final MetadataContainer mixContainer = new MetadataContainer(global, List.of( + StandardPluginMetadata.builder() .id("test_plugin") - .name("TestPlugin") - .version("1.2.3") .entrypoint("my.test.package.MyTestPlugin") - .description("Some test plugin") - .links(new PluginLinks( - URI.create("https://spongepowered.org/"), - URI.create("https://github.com/SpongePowered/Sponge"), - URI.create("https://github.com/SpongePowered/Sponge/issues") - )) - .addContributor(new PluginContributor("Spongie", "Mascot")) - .addDependency(new PluginDependency("spongeapi", VersionRange.createFromVersion("17.0.0"), PluginDependency.LoadOrder.AFTER, false)) - .build()) - .build(); + .global(global) + .override(override) + .build() + )); + + private static final MetadataContainer fullGlobalContainer = new MetadataContainer(full, List.of( + StandardPluginMetadata.builder() + .id("test_plugin") + .entrypoint("my.test.package.MyTestPlugin") + .global(full) + .build() + )); + + private static final MetadataContainer fullOverrideContainer = new MetadataContainer(InheritableMetadata.none(), List.of( + StandardPluginMetadata.builder() + .id("test_plugin") + .entrypoint("my.test.package.MyTestPlugin") + .override(full) + .build() + )); + + @Test + public void readMix() throws IOException { + final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/mix.json"); + Assertions.assertEquals(mixContainer, parsed); + } @Test - public void readValidContainer1() throws IOException { - // TODO MetadataContainer#equals ? - final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/container1.json"); - Assertions.assertEquals(container1.loader(), parsed.loader()); - Assertions.assertEquals(container1.license(), parsed.license()); - Assertions.assertEquals(1, parsed.metadata().size()); - - final PluginMetadata plugin1 = container1.metadata().iterator().next(); - final PluginMetadata parsedPlugin = parsed.metadata().iterator().next(); - Assertions.assertEquals(plugin1.id(), parsedPlugin.id()); - Assertions.assertEquals(plugin1.name(), parsedPlugin.name()); - Assertions.assertEquals(plugin1.version(), parsedPlugin.version()); - Assertions.assertEquals(plugin1.entrypoint(), parsedPlugin.entrypoint()); - Assertions.assertEquals(plugin1.description(), parsedPlugin.description()); - Assertions.assertEquals(plugin1.links(), parsedPlugin.links()); - Assertions.assertIterableEquals(plugin1.contributors(), parsedPlugin.contributors()); - Assertions.assertIterableEquals(plugin1.dependencies(), parsedPlugin.dependencies()); + public void readMixInRoot() throws IOException { + final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/mix_in_root.json"); + Assertions.assertEquals(mixContainer, parsed); + } + + @Test + public void readFullGlobal() throws IOException { + final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/full_global.json"); + Assertions.assertEquals(fullGlobalContainer, parsed); + } + + @Test + public void readFullOverride() throws IOException { + final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/full_override.json"); + Assertions.assertEquals(fullOverrideContainer, parsed); + } + + @Test + public void writeMix() throws IOException { + final List expected = MetadataParserTest.readLines("/valid/mix.json"); + final List result = MetadataParserTest.writeContainer(mixContainer); + Assertions.assertLinesMatch(expected, result); + } + + @Test + public void writeFullGlobal() throws IOException { + final List expected = MetadataParserTest.readLines("/valid/full_global.json"); + final List result = MetadataParserTest.writeContainer(fullGlobalContainer); + Assertions.assertLinesMatch(expected, result); } @Test - public void writeContainer1() throws IOException { - final List expected = MetadataParserTest.readLines("/valid/container1.json"); - final List result = MetadataParserTest.writeContainer(container1); + public void writeFullOverride() throws IOException { + final List expected = MetadataParserTest.readLines("/valid/full_override.json"); + final List result = MetadataParserTest.writeContainer(fullOverrideContainer); Assertions.assertLinesMatch(expected, result); } @Test - public void readLegacyIdContainer() throws IOException { + public void readLegacyDashInId() throws IOException { final String pluginIdWarning = "Plugin id 'test-plugin' is invalid and has been converted to 'test_plugin'."; final String dependencyIdWarning = "Plugin id 'test-dependency' is invalid and has been converted to 'test_dependency'."; Assertions.assertFalse(MetadataParser.warnings().contains(pluginIdWarning)); Assertions.assertFalse(MetadataParser.warnings().contains(dependencyIdWarning)); - final MetadataContainer parsed = MetadataParserTest.readContainer("/legacy/legacy_id.json"); + final MetadataContainer parsed = MetadataParserTest.readContainer("/legacy/dash_in_id.json"); - Assertions.assertEquals(1, parsed.metadata().size()); - final PluginMetadata plugin = parsed.metadata().iterator().next(); + Assertions.assertEquals(1, parsed.plugins().size()); + final PluginMetadata plugin = parsed.plugins().getFirst(); Assertions.assertEquals("test_plugin", plugin.id()); Assertions.assertEquals(1, plugin.dependencies().size()); diff --git a/src/test/resources/legacy/legacy_id.json b/src/test/resources/legacy/dash_in_id.json similarity index 100% rename from src/test/resources/legacy/legacy_id.json rename to src/test/resources/legacy/dash_in_id.json diff --git a/src/test/resources/valid/full_global.json b/src/test/resources/valid/full_global.json new file mode 100644 index 0000000..209cd9b --- /dev/null +++ b/src/test/resources/valid/full_global.json @@ -0,0 +1,37 @@ +{ + "global": { + "version": "1.2.3", + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "name": "TestPlugin", + "description": "Some test plugin", + "license": "some_license", + "links": { + "homepage": "https://spongepowered.org/", + "source": "https://github.com/SpongePowered/Sponge", + "issues": "https://github.com/SpongePowered/Sponge/issues" + }, + "contributors": [ + { + "name": "Spongie", + "description": "Mascot" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "17.0.0", + "load-order": "AFTER", + "optional": false + } + ] + }, + "plugins": [ + { + "id": "test_plugin", + "entrypoint": "my.test.package.MyTestPlugin" + } + ] +} diff --git a/src/test/resources/valid/full_override.json b/src/test/resources/valid/full_override.json new file mode 100644 index 0000000..ba04291 --- /dev/null +++ b/src/test/resources/valid/full_override.json @@ -0,0 +1,35 @@ +{ + "plugins": [ + { + "id": "test_plugin", + "entrypoint": "my.test.package.MyTestPlugin", + "version": "1.2.3", + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "name": "TestPlugin", + "description": "Some test plugin", + "license": "some_license", + "links": { + "homepage": "https://spongepowered.org/", + "source": "https://github.com/SpongePowered/Sponge", + "issues": "https://github.com/SpongePowered/Sponge/issues" + }, + "contributors": [ + { + "name": "Spongie", + "description": "Mascot" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "17.0.0", + "load-order": "AFTER", + "optional": false + } + ] + } + ] +} diff --git a/src/test/resources/valid/mix.json b/src/test/resources/valid/mix.json new file mode 100644 index 0000000..4e6f8c8 --- /dev/null +++ b/src/test/resources/valid/mix.json @@ -0,0 +1,37 @@ +{ + "global": { + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "some_license" + }, + "plugins": [ + { + "id": "test_plugin", + "entrypoint": "my.test.package.MyTestPlugin", + "version": "1.2.3", + "name": "TestPlugin", + "description": "Some test plugin", + "links": { + "homepage": "https://spongepowered.org/", + "source": "https://github.com/SpongePowered/Sponge", + "issues": "https://github.com/SpongePowered/Sponge/issues" + }, + "contributors": [ + { + "name": "Spongie", + "description": "Mascot" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "17.0.0", + "load-order": "AFTER", + "optional": false + } + ] + } + ] +} diff --git a/src/test/resources/valid/container1.json b/src/test/resources/valid/mix_in_root.json similarity index 100% rename from src/test/resources/valid/container1.json rename to src/test/resources/valid/mix_in_root.json index 24547c2..2cd2314 100644 --- a/src/test/resources/valid/container1.json +++ b/src/test/resources/valid/mix_in_root.json @@ -7,10 +7,10 @@ "plugins": [ { "id": "test_plugin", - "name": "TestPlugin", - "description": "Some test plugin", "entrypoint": "my.test.package.MyTestPlugin", "version": "1.2.3", + "name": "TestPlugin", + "description": "Some test plugin", "links": { "homepage": "https://spongepowered.org/", "source": "https://github.com/SpongePowered/Sponge", From 3d034dbd3a21ffb5afb81d54f0386c410407874d Mon Sep 17 00:00:00 2001 From: Yeregorix Date: Sun, 28 Jun 2026 22:12:27 +0200 Subject: [PATCH 11/11] Add PluginEntrypoints --- .../plugin/metadata/PluginMetadata.java | 10 +-- .../builtin/StandardPluginMetadata.java | 26 ++++---- ...dardPluginMetadataBuilderDeserializer.java | 21 +++++- .../StandardPluginMetadataSerializer.java | 19 +++++- .../model/PluginEntrypointsAdapter.java | 64 +++++++++++++++++++ .../metadata/model/PluginEntrypoints.java | 58 +++++++++++++++++ .../metadata/builtin/MetadataParserTest.java | 43 ++++++++++++- src/test/resources/legacy/dash_in_id.json | 12 +++- src/test/resources/legacy/entrypoint.json | 37 +++++++++++ src/test/resources/valid/full_global.json | 12 +++- src/test/resources/valid/full_override.json | 12 +++- .../resources/valid/main_entrypoint_only.json | 39 +++++++++++ src/test/resources/valid/mix.json | 12 +++- src/test/resources/valid/mix_in_root.json | 12 +++- 14 files changed, 348 insertions(+), 29 deletions(-) create mode 100644 src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginEntrypointsAdapter.java create mode 100644 src/main/java/org/spongepowered/plugin/metadata/model/PluginEntrypoints.java create mode 100644 src/test/resources/legacy/entrypoint.json create mode 100644 src/test/resources/valid/main_entrypoint_only.json diff --git a/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java index 35591e7..ea6d4e0 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/PluginMetadata.java @@ -28,6 +28,7 @@ import org.spongepowered.plugin.metadata.model.PluginBranding; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; import org.spongepowered.plugin.metadata.model.PluginLinks; import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; @@ -59,14 +60,9 @@ public interface PluginMetadata { String id(); /** - * Gets the {@link String entrypoint}. - *

    - * Consult the vendor for how this field is used. As an example, this could be - * the name of a module or a fully realized path to a discrete class. - * - * @return The entrypoint + * @return The {@link PluginEntrypoints entrypoints} */ - String entrypoint(); + PluginEntrypoints entrypoints(); /** * @return The {@link ArtifactVersion version}. diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java index a028326..679f3dd 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java @@ -32,6 +32,7 @@ import org.spongepowered.plugin.metadata.model.PluginBranding; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; import org.spongepowered.plugin.metadata.model.PluginLinks; import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; @@ -44,7 +45,8 @@ import java.util.StringJoiner; public final class StandardPluginMetadata implements PluginMetadata { - private final String id, entrypoint; + private final String id; + private final PluginEntrypoints entrypoints; private final InheritableMetadata global, override; private final ArtifactVersion version; @@ -60,7 +62,7 @@ public final class StandardPluginMetadata implements PluginMetadata { private StandardPluginMetadata(final Builder builder) { this.id = builder.id; - this.entrypoint = builder.entrypoint; + this.entrypoints = builder.entrypoints; this.global = builder.global; this.override = builder.override; InheritableMetadata metadata = this.global.with(this.override); @@ -82,8 +84,8 @@ public String id() { } @Override - public String entrypoint() { - return this.entrypoint; + public PluginEntrypoints entrypoints() { + return this.entrypoints; } public InheritableMetadata global() { @@ -157,7 +159,7 @@ public Map properties() { @Override public int hashCode() { - return Objects.hash(this.id, this.entrypoint, this.global, this.override); + return Objects.hash(this.id, this.entrypoints, this.global, this.override); } @Override @@ -170,7 +172,7 @@ public boolean equals(final Object o) { return false; } - return this.id.equals(other.id) && this.entrypoint.equals(other.entrypoint) + return this.id.equals(other.id) && this.entrypoints.equals(other.entrypoints) && this.global.equals(other.global) && this.override.equals(other.override); } @@ -178,7 +180,7 @@ public boolean equals(final Object o) { public String toString() { return new StringJoiner(", ", StandardPluginMetadata.class.getSimpleName() + "[", "]") .add("id=" + this.id) - .add("entrypoint=" + this.entrypoint) + .add("entrypoints=" + this.entrypoints) .add("version=" + this.version) .add("loader=" + this.loader) .add("license=" + this.license) @@ -200,7 +202,8 @@ public static StandardPluginMetadata.Builder builder() { public static final class Builder { - private @MonotonicNonNull String id, entrypoint; + private @MonotonicNonNull String id; + private PluginEntrypoints entrypoints = PluginEntrypoints.none(); private InheritableMetadata global = InheritableMetadata.none(), override = InheritableMetadata.none(); private Builder() {} @@ -210,8 +213,8 @@ public Builder id(final String id) { return this; } - public Builder entrypoint(final String entrypoint) { - this.entrypoint = Objects.requireNonNull(entrypoint, "entrypoint"); + public Builder entrypoints(final PluginEntrypoints entrypoints) { + this.entrypoints = Objects.requireNonNull(entrypoints, "entrypoints"); return this; } @@ -227,7 +230,7 @@ public Builder override(final InheritableMetadata override) { public Builder from(final StandardPluginMetadata value) { this.id = value.id; - this.entrypoint = value.entrypoint; + this.entrypoints = value.entrypoints; this.global = value.global; this.override = value.override; return this; @@ -238,7 +241,6 @@ public StandardPluginMetadata build() { throw new IllegalStateException(String.format("PluginMetadata with supplied ID '{%s}' is invalid. %s", this.id, Constants.INVALID_ID_REQUIREMENTS_MESSAGE)); } - Objects.requireNonNull(this.entrypoint, "entrypoint"); return new StandardPluginMetadata(this); } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java index eadb629..fe2c132 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataBuilderDeserializer.java @@ -29,8 +29,10 @@ import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; import org.spongepowered.plugin.metadata.builtin.adapter.util.LegacyIds; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; import java.lang.reflect.Type; +import java.util.List; public final class StandardPluginMetadataBuilderDeserializer implements JsonDeserializer { @@ -39,7 +41,24 @@ public StandardPluginMetadata.Builder deserialize(final JsonElement element, fin final JsonObject obj = element.getAsJsonObject(); return StandardPluginMetadata.builder() .id(LegacyIds.fix(GsonUtils.require(obj, "id").getAsString())) - .entrypoint(GsonUtils.require(obj, "entrypoint").getAsString()) + .entrypoints( + GsonUtils.optional(obj, "entrypoints").map(v -> StandardPluginMetadataBuilderDeserializer.deserializeEntrypoints(v, context)) + .or(() -> GsonUtils.optional(obj, "entrypoint").map(v -> new PluginEntrypoints(List.of(v.getAsString())))) // legacy + .orElseGet(PluginEntrypoints::none) + ) .override(context.deserialize(element, InheritableMetadata.class)); } + + /** + * Should be in PluginEntrypointsAdapter but Gson refuses to pass a JsonArray to a JsonDeserializer. + */ + private static PluginEntrypoints deserializeEntrypoints(final JsonElement element, final JsonDeserializationContext context) { + if (element.isJsonNull()) { + return PluginEntrypoints.none(); + } + if (element instanceof JsonArray array) { + return new PluginEntrypoints(array.asList().stream().map(JsonElement::getAsString).toList()); + } + return context.deserialize(element, PluginEntrypoints.class); + } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java index 6c49ee2..5cc32ee 100644 --- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/StandardPluginMetadataSerializer.java @@ -26,12 +26,16 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import org.spongepowered.plugin.metadata.builtin.InheritableMetadata; import org.spongepowered.plugin.metadata.builtin.StandardPluginMetadata; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; import java.lang.reflect.Type; +import java.util.Optional; public final class StandardPluginMetadataSerializer implements JsonSerializer { @@ -39,8 +43,21 @@ public final class StandardPluginMetadataSerializer implements JsonSerializer obj.add("entrypoints", v)); obj.asMap().putAll(((JsonObject) context.serialize(value.override(), InheritableMetadata.class)).asMap()); return obj; } + + /** + * Should be in PluginEntrypointsAdapter but Gson refuses to pass a JsonArray to a JsonDeserializer. + */ + private static Optional serializeEntrypoints(final PluginEntrypoints value, final JsonSerializationContext context) { + if (value.server().isEmpty() && value.client().isEmpty()) { + if (value.main().isEmpty()) { + return Optional.empty(); + } + return Optional.of(GsonUtils.toArray(value.main().stream().map(JsonPrimitive::new))); + } + return Optional.of(context.serialize(value, PluginEntrypoints.class)); + } } diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginEntrypointsAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginEntrypointsAdapter.java new file mode 100644 index 0000000..b425efe --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginEntrypointsAdapter.java @@ -0,0 +1,64 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.builtin.adapter.model; + +import com.google.gson.*; +import org.spongepowered.plugin.metadata.builtin.adapter.util.GsonUtils; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; + +import java.lang.reflect.Type; +import java.util.List; + +public final class PluginEntrypointsAdapter implements JsonSerializer, JsonDeserializer { + + @Override + public PluginEntrypoints deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException { + final JsonObject obj = element.getAsJsonObject(); + return new PluginEntrypoints( + PluginEntrypointsAdapter.deserialize(obj, "main"), + PluginEntrypointsAdapter.deserialize(obj, "server"), + PluginEntrypointsAdapter.deserialize(obj, "client") + ); + } + + @Override + public JsonElement serialize(final PluginEntrypoints value, final Type type, final JsonSerializationContext context) { + final JsonObject obj = new JsonObject(); + PluginEntrypointsAdapter.serialize(obj, "main", value.main()); + PluginEntrypointsAdapter.serialize(obj, "server", value.server()); + PluginEntrypointsAdapter.serialize(obj, "client", value.client()); + return obj; + } + + private static List deserialize(final JsonObject obj, final String name) { + return GsonUtils.stream(obj, name).map(JsonElement::getAsString).toList(); + } + + private static void serialize(final JsonObject obj, final String name, final List list) { + if (!list.isEmpty()) { + obj.add(name, GsonUtils.toArray(list.stream().map(JsonPrimitive::new))); + } + } +} diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginEntrypoints.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginEntrypoints.java new file mode 100644 index 0000000..452e712 --- /dev/null +++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginEntrypoints.java @@ -0,0 +1,58 @@ +/* + * This file is part of plugin-meta, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * 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 org.spongepowered.plugin.metadata.model; + +import org.spongepowered.plugin.metadata.PluginMetadata; + +import java.util.List; +import java.util.Objects; + +/** + * Specification for an entity representing the entrypoints of a {@link PluginMetadata plugin metadata}. + *

    + * Consult the vendor for further information on how this is used. + * Usually these are the fully qualified name of some classes. + * + * @param main The main entrypoints + * @param server The server entrypoints + * @param client The client entrypoints + */ +public record PluginEntrypoints(List main, List server, List client) { + private static final PluginEntrypoints NONE = new PluginEntrypoints(List.of()); + + public PluginEntrypoints { + main = List.copyOf(Objects.requireNonNull(main, "main")); + server = List.copyOf(Objects.requireNonNull(server, "server")); + client = List.copyOf(Objects.requireNonNull(client, "client")); + } + + public PluginEntrypoints(List main) { + this(main, List.of(), List.of()); + } + + public static PluginEntrypoints none() { + return PluginEntrypoints.NONE; + } +} diff --git a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java index a10a107..ca8107c 100644 --- a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java +++ b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java @@ -31,6 +31,7 @@ import org.spongepowered.plugin.metadata.PluginMetadata; import org.spongepowered.plugin.metadata.model.PluginContributor; import org.spongepowered.plugin.metadata.model.PluginDependency; +import org.spongepowered.plugin.metadata.model.PluginEntrypoints; import org.spongepowered.plugin.metadata.model.PluginLinks; import org.spongepowered.plugin.metadata.model.PluginLoaderSpecification; @@ -83,10 +84,18 @@ private static List writeContainer(final MetadataContainer container) th private static final InheritableMetadata full = global.with(override); + private static final PluginEntrypoints entrypoints = new PluginEntrypoints( + List.of("my.test.package.MyTestPlugin_Main"), + List.of("my.test.package.MyTestPlugin_Server"), + List.of("my.test.package.MyTestPlugin_Client") + ); + + private static final PluginEntrypoints mainOnlyEntrypoints = new PluginEntrypoints(List.of("my.test.package.MyTestPlugin")); + private static final MetadataContainer mixContainer = new MetadataContainer(global, List.of( StandardPluginMetadata.builder() .id("test_plugin") - .entrypoint("my.test.package.MyTestPlugin") + .entrypoints(entrypoints) .global(global) .override(override) .build() @@ -95,7 +104,7 @@ private static List writeContainer(final MetadataContainer container) th private static final MetadataContainer fullGlobalContainer = new MetadataContainer(full, List.of( StandardPluginMetadata.builder() .id("test_plugin") - .entrypoint("my.test.package.MyTestPlugin") + .entrypoints(entrypoints) .global(full) .build() )); @@ -103,11 +112,20 @@ private static List writeContainer(final MetadataContainer container) th private static final MetadataContainer fullOverrideContainer = new MetadataContainer(InheritableMetadata.none(), List.of( StandardPluginMetadata.builder() .id("test_plugin") - .entrypoint("my.test.package.MyTestPlugin") + .entrypoints(entrypoints) .override(full) .build() )); + private static final MetadataContainer mainEntrypointOnlyContainer = new MetadataContainer(global, List.of( + StandardPluginMetadata.builder() + .id("test_plugin") + .entrypoints(mainOnlyEntrypoints) + .global(global) + .override(override) + .build() + )); + @Test public void readMix() throws IOException { final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/mix.json"); @@ -132,6 +150,12 @@ public void readFullOverride() throws IOException { Assertions.assertEquals(fullOverrideContainer, parsed); } + @Test + public void readMainEntrypointOnly() throws IOException { + final MetadataContainer parsed = MetadataParserTest.readContainer("/valid/main_entrypoint_only.json"); + Assertions.assertEquals(mainEntrypointOnlyContainer, parsed); + } + @Test public void writeMix() throws IOException { final List expected = MetadataParserTest.readLines("/valid/mix.json"); @@ -153,6 +177,13 @@ public void writeFullOverride() throws IOException { Assertions.assertLinesMatch(expected, result); } + @Test + public void writeMainEntrypointOnly() throws IOException { + final List expected = MetadataParserTest.readLines("/valid/main_entrypoint_only.json"); + final List result = MetadataParserTest.writeContainer(mainEntrypointOnlyContainer); + Assertions.assertLinesMatch(expected, result); + } + @Test public void readLegacyDashInId() throws IOException { final String pluginIdWarning = "Plugin id 'test-plugin' is invalid and has been converted to 'test_plugin'."; @@ -173,4 +204,10 @@ public void readLegacyDashInId() throws IOException { Assertions.assertTrue(MetadataParser.warnings().contains(pluginIdWarning)); Assertions.assertTrue(MetadataParser.warnings().contains(dependencyIdWarning)); } + + @Test + public void readLegacyEntrypoint() throws IOException { + final MetadataContainer parsed = MetadataParserTest.readContainer("/legacy/entrypoint.json"); + Assertions.assertEquals(mainEntrypointOnlyContainer, parsed); + } } diff --git a/src/test/resources/legacy/dash_in_id.json b/src/test/resources/legacy/dash_in_id.json index 9a5f0a1..e1c2f3b 100644 --- a/src/test/resources/legacy/dash_in_id.json +++ b/src/test/resources/legacy/dash_in_id.json @@ -9,7 +9,17 @@ "id": "test-plugin", "name": "TestPlugin", "description": "Some test plugin", - "entrypoint": "my.test.package.MyTestPlugin", + "entrypoints": { + "main": [ + "my.test.package.MyTestPlugin_Main" + ], + "server": [ + "my.test.package.MyTestPlugin_Server" + ], + "client": [ + "my.test.package.MyTestPlugin_Client" + ] + }, "version": "1.2.3", "dependencies": [ { diff --git a/src/test/resources/legacy/entrypoint.json b/src/test/resources/legacy/entrypoint.json new file mode 100644 index 0000000..4e6f8c8 --- /dev/null +++ b/src/test/resources/legacy/entrypoint.json @@ -0,0 +1,37 @@ +{ + "global": { + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "some_license" + }, + "plugins": [ + { + "id": "test_plugin", + "entrypoint": "my.test.package.MyTestPlugin", + "version": "1.2.3", + "name": "TestPlugin", + "description": "Some test plugin", + "links": { + "homepage": "https://spongepowered.org/", + "source": "https://github.com/SpongePowered/Sponge", + "issues": "https://github.com/SpongePowered/Sponge/issues" + }, + "contributors": [ + { + "name": "Spongie", + "description": "Mascot" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "17.0.0", + "load-order": "AFTER", + "optional": false + } + ] + } + ] +} diff --git a/src/test/resources/valid/full_global.json b/src/test/resources/valid/full_global.json index 209cd9b..3284035 100644 --- a/src/test/resources/valid/full_global.json +++ b/src/test/resources/valid/full_global.json @@ -31,7 +31,17 @@ "plugins": [ { "id": "test_plugin", - "entrypoint": "my.test.package.MyTestPlugin" + "entrypoints": { + "main": [ + "my.test.package.MyTestPlugin_Main" + ], + "server": [ + "my.test.package.MyTestPlugin_Server" + ], + "client": [ + "my.test.package.MyTestPlugin_Client" + ] + } } ] } diff --git a/src/test/resources/valid/full_override.json b/src/test/resources/valid/full_override.json index ba04291..57475db 100644 --- a/src/test/resources/valid/full_override.json +++ b/src/test/resources/valid/full_override.json @@ -2,7 +2,17 @@ "plugins": [ { "id": "test_plugin", - "entrypoint": "my.test.package.MyTestPlugin", + "entrypoints": { + "main": [ + "my.test.package.MyTestPlugin_Main" + ], + "server": [ + "my.test.package.MyTestPlugin_Server" + ], + "client": [ + "my.test.package.MyTestPlugin_Client" + ] + }, "version": "1.2.3", "loader": { "name": "java_plain", diff --git a/src/test/resources/valid/main_entrypoint_only.json b/src/test/resources/valid/main_entrypoint_only.json new file mode 100644 index 0000000..c9efc5a --- /dev/null +++ b/src/test/resources/valid/main_entrypoint_only.json @@ -0,0 +1,39 @@ +{ + "global": { + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "some_license" + }, + "plugins": [ + { + "id": "test_plugin", + "entrypoints": [ + "my.test.package.MyTestPlugin" + ], + "version": "1.2.3", + "name": "TestPlugin", + "description": "Some test plugin", + "links": { + "homepage": "https://spongepowered.org/", + "source": "https://github.com/SpongePowered/Sponge", + "issues": "https://github.com/SpongePowered/Sponge/issues" + }, + "contributors": [ + { + "name": "Spongie", + "description": "Mascot" + } + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "17.0.0", + "load-order": "AFTER", + "optional": false + } + ] + } + ] +} diff --git a/src/test/resources/valid/mix.json b/src/test/resources/valid/mix.json index 4e6f8c8..1401eb0 100644 --- a/src/test/resources/valid/mix.json +++ b/src/test/resources/valid/mix.json @@ -9,7 +9,17 @@ "plugins": [ { "id": "test_plugin", - "entrypoint": "my.test.package.MyTestPlugin", + "entrypoints": { + "main": [ + "my.test.package.MyTestPlugin_Main" + ], + "server": [ + "my.test.package.MyTestPlugin_Server" + ], + "client": [ + "my.test.package.MyTestPlugin_Client" + ] + }, "version": "1.2.3", "name": "TestPlugin", "description": "Some test plugin", diff --git a/src/test/resources/valid/mix_in_root.json b/src/test/resources/valid/mix_in_root.json index 2cd2314..b1efd2a 100644 --- a/src/test/resources/valid/mix_in_root.json +++ b/src/test/resources/valid/mix_in_root.json @@ -7,7 +7,17 @@ "plugins": [ { "id": "test_plugin", - "entrypoint": "my.test.package.MyTestPlugin", + "entrypoints": { + "main": [ + "my.test.package.MyTestPlugin_Main" + ], + "server": [ + "my.test.package.MyTestPlugin_Server" + ], + "client": [ + "my.test.package.MyTestPlugin_Client" + ] + }, "version": "1.2.3", "name": "TestPlugin", "description": "Some test plugin",