diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e629fa011..593399c69 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -11,6 +11,8 @@ java { } dependencies { + implementation("com.google.auto.value:auto-value-annotations:1.7.2") + annotationProcessor("com.google.auto.value:auto-value:1.7.2") api("com.google.guava:guava:${Versions.GUAVA}") "guiceSupportImplementation"("com.google.inject:guice:4.2.3") api("org.checkerframework:checker-qual:3.3.0") diff --git a/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java b/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java index 4d1d8283f..86dacecf9 100644 --- a/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java +++ b/core/src/main/java/org/spongepowered/configurate/AbstractConfigurationNode.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Supplier; @@ -77,6 +78,11 @@ abstract class AbstractConfigurationNode, A @NonNull volatile ConfigValue value; + /** + * Storage for representation hints. + */ + private final Map, Object> hints = new ConcurrentHashMap<>(); + protected AbstractConfigurationNode(final @Nullable Object key, final @Nullable A parent, final @NonNull ConfigurationOptions options) { requireNonNull(options, "options"); this.key = key; @@ -652,6 +658,27 @@ private T visitInternal(final ConfigurationVisitor N setHint(final RepresentationHint hint, @Nullable final V value) { + this.hints.put(hint, value); + return self(); + } + + @SuppressWarnings("unchecked") + @Override + public @Nullable V getHint(final RepresentationHint hint) { + final Object value = this.hints.get(hint); + if (value != null) { + return (V) value; + } + final @Nullable A parent = this.parent; + if (parent != null) { + return parent.getHint(hint); + } else { + return hint.getDefaultValue(); + } + } + @Override public boolean equals(final Object o) { if (this == o) { diff --git a/core/src/main/java/org/spongepowered/configurate/ConfigurationNode.java b/core/src/main/java/org/spongepowered/configurate/ConfigurationNode.java index 27f2e00d0..a94b71948 100644 --- a/core/src/main/java/org/spongepowered/configurate/ConfigurationNode.java +++ b/core/src/main/java/org/spongepowered/configurate/ConfigurationNode.java @@ -676,4 +676,14 @@ default boolean getBoolean(boolean def) { @NonNull ConfigurationNode copy(); + ConfigurationNode setHint(RepresentationHint hint, @Nullable V value); + + /** + * Query a representation hint from this node. + * @param hint The hint to get + * @param value type + * @return value of the hint, or {@link RepresentationHint#getDefaultValue()} + */ + @Nullable V getHint(RepresentationHint hint); + } diff --git a/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java b/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java new file mode 100644 index 000000000..661d01b38 --- /dev/null +++ b/core/src/main/java/org/spongepowered/configurate/RepresentationHint.java @@ -0,0 +1,78 @@ +/* + * Configurate + * Copyright (C) zml and Configurate contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.spongepowered.configurate; + +import com.google.auto.value.AutoValue; +import com.google.common.reflect.TypeToken; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * A flag for configuration loaders describing how a node should be serialized. + * + *

A loader may not accept every representation hint available, but any + * understood hints should be exposed as constant fields on the loader class. + * Any unknown hints will be ignored. + * + * @param The value type + */ +@AutoValue +public abstract class RepresentationHint { + + public static final RepresentationHint INDENT = of("indent", Integer.class); + + public static RepresentationHint of(final String identifier, final Class valueType) { + return new AutoValue_RepresentationHint<>(identifier, TypeToken.of(valueType), null); + } + + 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.of(valueType), defaultValue); + } + + public static RepresentationHint of(final String identifier, final TypeToken valueType, final V defaultValue) { + return new AutoValue_RepresentationHint<>(identifier, valueType, defaultValue); + } + + RepresentationHint() { } + + /** + * An identifier used to represent this hint in error messages. + * + * @return the identifier + */ + public abstract String getIdentifier(); + + /** + * The type that values of this type have to have. + * + * @return value type + */ + public abstract TypeToken getValueType(); + + /** + * If a value for a representation hint cannot be found by quering a node + * or any of this parents, the default value will be returned. + * + * @return default type + */ + @AutoValue.CopyAnnotations + public abstract @Nullable V getDefaultValue(); + +} diff --git a/core/src/main/java/org/spongepowered/configurate/ScopedConfigurationNode.java b/core/src/main/java/org/spongepowered/configurate/ScopedConfigurationNode.java index ec5b96aa8..a5fc866f2 100644 --- a/core/src/main/java/org/spongepowered/configurate/ScopedConfigurationNode.java +++ b/core/src/main/java/org/spongepowered/configurate/ScopedConfigurationNode.java @@ -178,4 +178,7 @@ default T visit(ConfigurationVisitor.Safe visitor) { */ T visit(ConfigurationVisitor.Safe visitor, S state); + @Override + N setHint(RepresentationHint hint, @Nullable V value); + }