diff --git a/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java b/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java index 807561c4c..05eb2e564 100644 --- a/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java +++ b/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java @@ -637,7 +637,7 @@ public N setHint(final RepresentationHint hint, final @Nullable V value) return (V) value; } final @Nullable A parent = this.parent; - if (parent != null) { + if (parent != null && hint.isInheritable()) { return parent.getHint(hint); } else { return hint.getDefaultValue(); diff --git a/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java b/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java index 789cd8c0e..c19778d30 100644 --- a/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java +++ b/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java @@ -32,22 +32,44 @@ @AutoValue public abstract class RepresentationHint { - public static final RepresentationHint INDENT = of("indent", Integer.class); - + /** + * Create a new basic representation hint. + * + *

The created hint will be inheritable and have no default + * value set.

+ * + * @param identifier hint identifier + * @param valueType type of value the hint will hold + * @param value type + * @return a new hint + */ public static RepresentationHint of(final String identifier, final Class valueType) { - return new AutoValue_RepresentationHint<>(identifier, TypeToken.get(valueType), null); + return RepresentationHint.builder().setIdentifier(identifier).setValueType(valueType).build(); } + /** + * Create a new basic representation hint. + * + *

The created hint will be inheritable and have no default + * value set.

+ * + * @param identifier hint identifier + * @param valueType type of value the hint will hold + * @param value type + * @return a new hint + */ public static RepresentationHint of(final String identifier, final TypeToken valueType) { - return new AutoValue_RepresentationHint<>(identifier, valueType, null); - } - - public static RepresentationHint of(final String identifier, final Class valueType, final V defaultValue) { - return new AutoValue_RepresentationHint<>(identifier, TypeToken.get(valueType), defaultValue); + return RepresentationHint.builder().setIdentifier(identifier).setValueType(valueType).build(); } - public static RepresentationHint of(final String identifier, final TypeToken valueType, final V defaultValue) { - return new AutoValue_RepresentationHint<>(identifier, valueType, defaultValue); + /** + * Create a builder for a new hint. + * + * @param value type + * @return a new builder + */ + public static Builder builder() { + return new AutoValue_RepresentationHint.Builder<>(); } RepresentationHint() { } @@ -74,4 +96,86 @@ public static RepresentationHint of(final String identifier, final TypeTo */ public abstract @Nullable V getDefaultValue(); + /** + * Get whether or not this hint can draw its value from parent nodes. + * + * @return if inheritable + */ + public abstract boolean isInheritable(); + + /** + * A builder for {@link RepresentationHint}s. + * + * @param value type + */ + @AutoValue.Builder + public abstract static class Builder { + + Builder() { + this.setInheritable(true); + } + + /** + * Set the identifier to refer to this hint. + * + * @param identifier hint identifier + * @return this builder + */ + public abstract Builder setIdentifier(String identifier); + + /** + * Set the type used for this node's value. + * + *

Raw types are forbidden.

+ * + * @param valueType the value type + * @return this builder + */ + public final Builder setValueType(final Class valueType) { + return setValueType(TypeToken.get(valueType)); + } + + /** + * Set the type used for this node's value. + * + *

Raw types are forbidden.

+ * + * @param valueType the value type + * @return this builder + */ + public abstract Builder setValueType(TypeToken valueType); + + /** + * Set the default value when this hint is not present in the hierarchy. + * + *

This defaults to {@code null}.

+ * + * @param defaultValue Default value + * @return this builder + */ + public abstract Builder setDefaultValue(@Nullable V defaultValue); + + /** + * Set whether or not the hint can be inherited. + * + *

Defaults to {@code true}.

+ * + * @param inheritable if inheritable + * @return this builder + * @see #isInheritable() + */ + public abstract Builder setInheritable(boolean inheritable); + + /** + * Create a new hint from the provided options. + * + *

The {@code identifier} and {@code valueType} must have been set to + * build a complete hint.

+ * + * @return a new representation hint + */ + public abstract RepresentationHint build(); + + } + } diff --git a/core/src/test/java/org/spongepowered/configurate/AbstractConfigurationNodeTest.java b/core/src/test/java/org/spongepowered/configurate/AbstractConfigurationNodeTest.java index c94ba2e90..3ee3a4cb6 100644 --- a/core/src/test/java/org/spongepowered/configurate/AbstractConfigurationNodeTest.java +++ b/core/src/test/java/org/spongepowered/configurate/AbstractConfigurationNodeTest.java @@ -323,6 +323,18 @@ void testNullOutListValue() { */ private static final RepresentationHint IS_EVIL = RepresentationHint.of("evil", Boolean.class); + /** + * A representation hint for indentation + */ + public static final RepresentationHint INDENT = RepresentationHint.of("indent", Integer.class); + + public static final RepresentationHint NAME = + RepresentationHint.builder() + .setIdentifier("name") + .setValueType(String.class) + .setInheritable(false) + .build(); + @Test void testHintsReadWrite() { final ConfigurationNode node = BasicConfigurationNode.root(); @@ -355,6 +367,15 @@ void testGetHintInherited() { assertEquals(false, root.getHint(IS_EVIL)); } + @Test + void testNonInheritableHints() { + final ConfigurationNode root = BasicConfigurationNode.root(); + root.setHint(NAME, "secondary"); + + final ConfigurationNode child = root.getNode("other"); + assertNull(child.getHint(NAME)); + } + @Test void testHintsCopied() { final ConfigurationNode original = BasicConfigurationNode.root(); @@ -375,16 +396,16 @@ void testHintsMerged() { .setHint(IS_EVIL, true); final ConfigurationNode mergeTarget = BasicConfigurationNode.root() .setValue('o') - .setHint(RepresentationHint.INDENT, 34); + .setHint(INDENT, 34); mergeTarget.mergeValuesFrom(hintHolder); - assertEquals(34, mergeTarget.getHint(RepresentationHint.INDENT)); + assertEquals(34, mergeTarget.getHint(INDENT)); assertEquals(true, mergeTarget.getHint(IS_EVIL)); } @Test - void testCollectToMap() throws ObjectMappingException { + void testCollectToMap() { final ConfigurationNode target = ImmutableMap.of("one", 3, "two", 28, "test", 14).entrySet().stream()