conflicts();
+
/**
* Gets the {@link PluginDependency plugin dependency} by {@link String id}.
*
diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java
index 385f0bd..c70ef47 100644
--- a/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java
+++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/InheritableMetadata.java
@@ -27,6 +27,7 @@
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.PluginConflict;
import org.spongepowered.plugin.metadata.model.PluginContributor;
import org.spongepowered.plugin.metadata.model.PluginDependency;
import org.spongepowered.plugin.metadata.model.PluginLinks;
@@ -53,6 +54,7 @@ public final class InheritableMetadata {
private final PluginBranding branding;
private final PluginLinks links;
private final List contributors;
+ private final List conflicts;
private final Map dependencies;
private final Map properties;
@@ -65,6 +67,7 @@ private InheritableMetadata() {
this.branding = PluginBranding.none();
this.links = PluginLinks.none();
this.contributors = List.of();
+ this.conflicts = List.of();
this.dependencies = Map.of();
this.properties = Map.of();
}
@@ -78,6 +81,7 @@ private InheritableMetadata(final Builder builder) {
this.branding = builder.branding;
this.links = builder.links;
this.contributors = List.copyOf(builder.contributors);
+ this.conflicts = List.copyOf(builder.conflicts);
this.dependencies = Collections.unmodifiableMap(new LinkedHashMap<>(builder.dependencies));
this.properties = Collections.unmodifiableMap(new LinkedHashMap<>(builder.properties));
}
@@ -114,6 +118,10 @@ public List contributors() {
return this.contributors;
}
+ public List conflicts() {
+ return this.conflicts;
+ }
+
public Map dependencies() {
return this.dependencies;
}
@@ -144,6 +152,7 @@ public boolean equals(Object o) {
&& this.branding.equals(other.branding)
&& this.links.equals(other.links)
&& this.contributors.equals(other.contributors)
+ && this.conflicts.equals(other.conflicts)
&& this.dependencies.equals(other.dependencies)
&& this.properties.equals(other.properties);
}
@@ -151,7 +160,7 @@ public boolean equals(Object o) {
@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);
+ this.branding, this.links, this.contributors, this.conflicts, this.dependencies, this.properties);
}
@Override
@@ -163,6 +172,7 @@ public String toString() {
.add("branding=" + this.branding)
.add("links=" + this.links)
.add("contributors=" + this.contributors)
+ .add("conflicts=" + this.conflicts)
.add("dependencies=" + this.dependencies)
.add("properties=" + this.properties)
.toString();
@@ -189,6 +199,7 @@ public static final class Builder {
private PluginBranding branding = PluginBranding.none();
private PluginLinks links = PluginLinks.none();
private final List contributors = new LinkedList<>();
+ private final List conflicts = new LinkedList<>();
private final Map dependencies = new LinkedHashMap<>();
private final Map properties = new LinkedHashMap<>();
@@ -247,6 +258,23 @@ public Builder addContributor(final PluginContributor contributor) {
return this;
}
+ public Builder conflicts(final Collection conflicts) {
+ Objects.requireNonNull(conflicts, "conflicts");
+ this.conflicts.clear();
+ this.conflicts.addAll(conflicts);
+ return this;
+ }
+
+ public Builder addConflicts(final Collection conflicts) {
+ this.conflicts.addAll(Objects.requireNonNull(conflicts, "conflicts"));
+ return this;
+ }
+
+ public Builder addConflict(final PluginConflict conflict) {
+ this.conflicts.add(Objects.requireNonNull(conflict, "conflict"));
+ return this;
+ }
+
public Builder dependencies(final Collection dependencies) {
Objects.requireNonNull(dependencies, "dependencies");
this.dependencies.clear();
@@ -293,6 +321,8 @@ public Builder from(final InheritableMetadata value) {
this.links = value.links;
this.contributors.clear();
this.contributors.addAll(value.contributors);
+ this.conflicts.clear();
+ this.conflicts.addAll(value.conflicts);
this.dependencies.clear();
this.dependencies.putAll(value.dependencies);
this.properties.clear();
@@ -323,6 +353,7 @@ public Builder with(final InheritableMetadata override) {
this.links = override.links;
}
this.contributors.addAll(override.contributors);
+ this.conflicts.addAll(override.conflicts);
this.dependencies.putAll(override.dependencies);
this.properties.putAll(override.properties);
return this;
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 e420740..97943e2 100644
--- a/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java
+++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/MetadataParser.java
@@ -54,6 +54,7 @@ public final class MetadataParser {
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(PluginLoaderSpecification.class, new PluginLoaderSpecificationAdapter())
.registerTypeAdapter(PluginBranding.class, new PluginBrandingAdapter())
+ .registerTypeAdapter(PluginConflict.class, new PluginConflictAdapter())
.registerTypeAdapter(PluginContributor.class, new PluginContributorAdapter())
.registerTypeAdapter(PluginDependency.class, new PluginDependencyAdapter())
.registerTypeAdapter(PluginEntrypoints.class, new PluginEntrypointsAdapter())
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 679f3dd..e7c7314 100644
--- a/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java
+++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/StandardPluginMetadata.java
@@ -30,6 +30,7 @@
import org.spongepowered.plugin.metadata.Constants;
import org.spongepowered.plugin.metadata.PluginMetadata;
import org.spongepowered.plugin.metadata.model.PluginBranding;
+import org.spongepowered.plugin.metadata.model.PluginConflict;
import org.spongepowered.plugin.metadata.model.PluginContributor;
import org.spongepowered.plugin.metadata.model.PluginDependency;
import org.spongepowered.plugin.metadata.model.PluginEntrypoints;
@@ -57,6 +58,7 @@ public final class StandardPluginMetadata implements PluginMetadata {
private final PluginBranding branding;
private final PluginLinks links;
private final List contributors;
+ private final List conflicts;
private final Map dependencies;
private final Map properties;
@@ -74,6 +76,7 @@ private StandardPluginMetadata(final Builder builder) {
this.branding = metadata.branding();
this.links = metadata.links();
this.contributors = metadata.contributors();
+ this.conflicts = metadata.conflicts();
this.dependencies = metadata.dependencies();
this.properties = metadata.properties();
}
@@ -136,6 +139,11 @@ public List contributors() {
return this.contributors;
}
+ @Override
+ public List conflicts() {
+ return this.conflicts;
+ }
+
@Override
public Optional dependency(String id) {
return Optional.ofNullable(this.dependencies.get(Objects.requireNonNull(id, "id")));
@@ -187,6 +195,7 @@ public String toString() {
.add("branding=" + this.branding)
.add("links=" + this.links)
.add("contributors=" + this.contributors)
+ .add("conflicts=" + this.conflicts)
.add("dependencies=" + this.dependencies)
.add("properties=" + this.properties)
.toString();
diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java
index ddd4c9e..1c5a325 100644
--- a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java
+++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/InheritableMetadataAdapter.java
@@ -29,6 +29,7 @@
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.PluginConflict;
import org.spongepowered.plugin.metadata.model.PluginContributor;
import org.spongepowered.plugin.metadata.model.PluginDependency;
import org.spongepowered.plugin.metadata.model.PluginLinks;
@@ -51,6 +52,7 @@ public InheritableMetadata deserialize(final JsonElement element, final Type typ
.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())
+ .conflicts(GsonUtils.stream(obj, "conflicts").map(v -> context.deserialize(v, PluginConflict.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();
@@ -73,6 +75,9 @@ public JsonElement serialize(final InheritableMetadata value, final Type type, f
if (!value.contributors().isEmpty()) {
obj.add("contributors", GsonUtils.toArray(value.contributors().stream().map(v -> context.serialize(v, PluginContributor.class))));
}
+ if (!value.conflicts().isEmpty()) {
+ obj.add("conflicts", GsonUtils.toArray(value.conflicts().stream().map(v -> context.serialize(v, PluginConflict.class))));
+ }
if (!value.dependencies().isEmpty()) {
obj.add("dependencies", GsonUtils.toArray(value.dependencies().values().stream().map(v -> context.serialize(v, PluginDependency.class))));
}
diff --git a/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginConflictAdapter.java b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginConflictAdapter.java
new file mode 100644
index 0000000..27069df
--- /dev/null
+++ b/src/main/java/org/spongepowered/plugin/metadata/builtin/adapter/model/PluginConflictAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.builtin.adapter.util.GsonUtils;
+import org.spongepowered.plugin.metadata.model.PluginConflict;
+
+import java.lang.reflect.Type;
+
+public final class PluginConflictAdapter implements JsonSerializer, JsonDeserializer {
+
+ @Override
+ public PluginConflict deserialize(final JsonElement element, final Type type, final JsonDeserializationContext context) throws JsonParseException {
+ final JsonObject obj = element.getAsJsonObject();
+ return new PluginConflict(
+ GsonUtils.require(obj, "id").getAsString(),
+ context.deserialize(GsonUtils.require(obj, "version"), VersionRange.class),
+ GsonUtils.optional(obj, "fatal").map(JsonElement::getAsBoolean).orElse(false),
+ GsonUtils.optional(obj, "reason").map(JsonElement::getAsString)
+ );
+ }
+
+ @Override
+ public JsonElement serialize(final PluginConflict 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.addProperty("fatal", value.fatal());
+ value.reason().ifPresent(v -> obj.addProperty("reason", v));
+ return obj;
+ }
+}
diff --git a/src/main/java/org/spongepowered/plugin/metadata/model/PluginConflict.java b/src/main/java/org/spongepowered/plugin/metadata/model/PluginConflict.java
new file mode 100644
index 0000000..587f455
--- /dev/null
+++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginConflict.java
@@ -0,0 +1,62 @@
+/*
+ * 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.apache.maven.artifact.versioning.VersionRange;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.spongepowered.plugin.metadata.Constants;
+import org.spongepowered.plugin.metadata.PluginMetadata;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Specification for an entity considered to be conflicting with a {@link PluginMetadata plugin metadata}.
+ *
+ * The vendor will either log a warning or refuse to load when this entity is present.
+ *
+ * @see Constants#VALID_ID_PATTERN
+ * @param id The {@link String id}
+ * @param version The {@link VersionRange version}, as a maven range.
+ * @param fatal Whether this conflict should prevent loading
+ * @param reason The {@link String reason}
+ */
+public record PluginConflict(String id, VersionRange version, boolean fatal, Optional reason) {
+
+ public PluginConflict {
+ Objects.requireNonNull(id, "id");
+ Objects.requireNonNull(version, "version");
+ Objects.requireNonNull(reason, "reason");
+
+ if (!Constants.VALID_ID_PATTERN.matcher(id).matches()) {
+ throw new IllegalStateException(String.format("Conflict with supplied ID '{%s}' is invalid. %s", id,
+ Constants.INVALID_ID_REQUIREMENTS_MESSAGE));
+ }
+ }
+
+ public PluginConflict(String id, VersionRange version, boolean fatal, @Nullable String reason) {
+ this(id, version, fatal, Optional.ofNullable(reason));
+ }
+}
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 2fbcb4b..0146b07 100644
--- a/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java
+++ b/src/main/java/org/spongepowered/plugin/metadata/model/PluginDependency.java
@@ -35,15 +35,7 @@
*
* Consult the vendor for further information on how this is used.
*
- *
Ids must conform to the following requirements:
- *
- *
- * - 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) and underscores (_)
- *
- *
+ * @see Constants#VALID_ID_PATTERN
* @param id The {@link String id}
* @param version The {@link VersionRange version}, as a maven range.
* @param loadOrder The {@link LoadOrder load order}
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 ca8107c..be12bee 100644
--- a/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java
+++ b/src/test/java/org/spongepowered/plugin/metadata/builtin/MetadataParserTest.java
@@ -29,6 +29,7 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.spongepowered.plugin.metadata.PluginMetadata;
+import org.spongepowered.plugin.metadata.model.PluginConflict;
import org.spongepowered.plugin.metadata.model.PluginContributor;
import org.spongepowered.plugin.metadata.model.PluginDependency;
import org.spongepowered.plugin.metadata.model.PluginEntrypoints;
@@ -79,6 +80,7 @@ private static List writeContainer(final MetadataContainer container) th
URI.create("https://github.com/SpongePowered/Sponge/issues")
))
.addContributor(new PluginContributor("Spongie", "Mascot"))
+ .addConflict(new PluginConflict("bad_plugin", VersionRange.createFromVersion("3.2.1"), true, "Incompatible for some reasons"))
.addDependency(new PluginDependency("spongeapi", VersionRange.createFromVersion("17.0.0"), PluginDependency.LoadOrder.AFTER, false))
.build();
diff --git a/src/test/resources/legacy/entrypoint.json b/src/test/resources/legacy/entrypoint.json
index 4e6f8c8..b073aaa 100644
--- a/src/test/resources/legacy/entrypoint.json
+++ b/src/test/resources/legacy/entrypoint.json
@@ -24,6 +24,14 @@
"description": "Mascot"
}
],
+ "conflicts": [
+ {
+ "id": "bad_plugin",
+ "version": "3.2.1",
+ "fatal": true,
+ "reason": "Incompatible for some reasons"
+ }
+ ],
"dependencies": [
{
"id": "spongeapi",
diff --git a/src/test/resources/valid/full_global.json b/src/test/resources/valid/full_global.json
index 3284035..117adc8 100644
--- a/src/test/resources/valid/full_global.json
+++ b/src/test/resources/valid/full_global.json
@@ -19,6 +19,14 @@
"description": "Mascot"
}
],
+ "conflicts": [
+ {
+ "id": "bad_plugin",
+ "version": "3.2.1",
+ "fatal": true,
+ "reason": "Incompatible for some reasons"
+ }
+ ],
"dependencies": [
{
"id": "spongeapi",
diff --git a/src/test/resources/valid/full_override.json b/src/test/resources/valid/full_override.json
index 57475db..28adb82 100644
--- a/src/test/resources/valid/full_override.json
+++ b/src/test/resources/valid/full_override.json
@@ -32,6 +32,14 @@
"description": "Mascot"
}
],
+ "conflicts": [
+ {
+ "id": "bad_plugin",
+ "version": "3.2.1",
+ "fatal": true,
+ "reason": "Incompatible for some reasons"
+ }
+ ],
"dependencies": [
{
"id": "spongeapi",
diff --git a/src/test/resources/valid/main_entrypoint_only.json b/src/test/resources/valid/main_entrypoint_only.json
index c9efc5a..5de12b2 100644
--- a/src/test/resources/valid/main_entrypoint_only.json
+++ b/src/test/resources/valid/main_entrypoint_only.json
@@ -26,6 +26,14 @@
"description": "Mascot"
}
],
+ "conflicts": [
+ {
+ "id": "bad_plugin",
+ "version": "3.2.1",
+ "fatal": true,
+ "reason": "Incompatible for some reasons"
+ }
+ ],
"dependencies": [
{
"id": "spongeapi",
diff --git a/src/test/resources/valid/mix.json b/src/test/resources/valid/mix.json
index 1401eb0..e66fe4b 100644
--- a/src/test/resources/valid/mix.json
+++ b/src/test/resources/valid/mix.json
@@ -34,6 +34,14 @@
"description": "Mascot"
}
],
+ "conflicts": [
+ {
+ "id": "bad_plugin",
+ "version": "3.2.1",
+ "fatal": true,
+ "reason": "Incompatible for some reasons"
+ }
+ ],
"dependencies": [
{
"id": "spongeapi",
diff --git a/src/test/resources/valid/mix_in_root.json b/src/test/resources/valid/mix_in_root.json
index b1efd2a..b1d03c0 100644
--- a/src/test/resources/valid/mix_in_root.json
+++ b/src/test/resources/valid/mix_in_root.json
@@ -32,6 +32,14 @@
"description": "Mascot"
}
],
+ "conflicts": [
+ {
+ "id": "bad_plugin",
+ "version": "3.2.1",
+ "fatal": true,
+ "reason": "Incompatible for some reasons"
+ }
+ ],
"dependencies": [
{
"id": "spongeapi",