Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public TypeBasedProperty(@NotNull String path, @NotNull PropertyType<T> type, @N

@Override
protected @Nullable T getFromReader(@NotNull PropertyReader reader, @NotNull ConvertErrorRecorder errorRecorder) {
return type.convert(reader.getObject(getPath()), errorRecorder);
return type.convert(reader.getValue(getPath()), errorRecorder);
}

@Override
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/ch/jalu/configme/resource/PathProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package ch.jalu.configme.resource;

import org.jetbrains.annotations.NotNull;

import java.util.Set;

/**
* Exposes the paths available in a configuration resource.
* <p>
* This interface is an optional extension for {@link PropertyReader} implementations that support path enumeration.
*/
public interface PathProvider {

/**
* Returns all paths available in the resource, including intermediate paths.
* <p>
* For a configuration like:
* <pre>
* header:
* title: Hello
* font:
* color: red
* size: 12
* </pre>
* this method returns {@code ["header", "header.title", "header.font", "header.font.color", "header.font.size"]}.
*
* @return all paths (in encounter order)
*/
@NotNull Set<String> getPaths();

/**
* Returns all leaf paths in the resource.
* <p>
* For the configuration example in {@link #getPaths()}, this method returns
* {@code ["header.title", "header.font.color", "header.font.size"]}.
*
* @return all leaf paths (in encounter order)
*/
@NotNull Set<String> getLeafPaths();

/**
* Returns the direct child paths of the given path. Returns an empty set
* if the path does not exist or has no children.
* <p>
* For the configuration example in {@link #getPaths()},
* {@code getChildPaths("header")} returns {@code ["header.title", "header.font"]}.
* <p>
* If {@code path} is empty, the top-level paths are returned.
*
* @param path the path whose direct child paths should be looked up
* @return all direct child paths (in encounter order, never null)
*/
@NotNull Set<String> getChildPaths(@NotNull String path);

}
75 changes: 54 additions & 21 deletions src/main/java/ch/jalu/configme/resource/PropertyReader.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package ch.jalu.configme.resource;

import ch.jalu.configme.properties.BooleanProperty;
import ch.jalu.configme.properties.DoubleProperty;
import ch.jalu.configme.properties.IntegerProperty;
import ch.jalu.configme.properties.StringProperty;
import ch.jalu.configme.properties.convertresult.PropertyValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Set;

/**
* A property reader provides values from a resource (e.g. a YAML file) based on whose data the values of properties
Expand All @@ -24,69 +29,97 @@ public interface PropertyReader {
boolean contains(@NotNull String path);

/**
* Returns the keys available in the file. Depending on the parameter either all keys are returned,
* or only the keys of the leaf nodes are considered.
*
* @param onlyLeafNodes true if only the paths of leaf nodes should be returned (no intermediate paths)
* @return set of all existing keys (ordered)
*/
@NotNull Set<String> getKeys(boolean onlyLeafNodes);

/**
* Returns the direct children of the given path which are available in the file. Returns an empty set
* if the path does not exist in the file (never null).
* Returns the object at the given path, or null if absent.
*
* @param path the path whose direct child paths should be looked up
* @return set of all direct children (ordered, never null)
* @param path the path to retrieve the value for
* @return the value, or null if there is none
*/
@NotNull Set<String> getChildKeys(@NotNull String path);
@Nullable Object getValue(@NotNull String path);

/**
* Returns the object at the given path, or null if absent.
*
* @param path the path to retrieve the value for
* @return the value, or null if there is none
* @deprecated Use {@link #getValue}
*/
@Nullable Object getObject(@NotNull String path);
@Deprecated
default @Nullable Object getObject(@NotNull String path) {
return getValue(path);
}

/**
* Returns the value of the given path as a String if available.
*
* @param path the path to retrieve a String for
* @return the value as a String, or null if not applicable or unavailable
* @deprecated read the value with a {@link StringProperty},
* or call {@link #getValue} and perform your own casts
*/
@Nullable String getString(@NotNull String path);
@Deprecated
default @Nullable String getString(@NotNull String path) {
StringProperty strProperty = new StringProperty(path, "");
PropertyValue<String> value = strProperty.determineValue(this);
return value.isValidInResource() ? value.getValue() : null;
}

/**
* Returns the value of the given path as an integer if available.
*
* @param path the path to retrieve an integer for
* @return the value as integer, or null if not applicable or unavailable
* @deprecated read the value with an {@link IntegerProperty},
* or call {@link #getValue} and perform your own casts
*/
@Nullable Integer getInt(@NotNull String path);
@Deprecated
default @Nullable Integer getInt(@NotNull String path) {
IntegerProperty intProperty = new IntegerProperty(path, 0);
PropertyValue<Integer> value = intProperty.determineValue(this);
return value.isValidInResource() ? value.getValue() : null;
}

/**
* Returns the value of the given path as a double if available.
*
* @param path the path to retrieve a double for
* @return the value as a double, or null if not applicable or unavailable
* @deprecated read the value with an {@link DoubleProperty},
* or call {@link #getValue} and perform your own casts
*/
@Nullable Double getDouble(@NotNull String path);
@Deprecated
default @Nullable Double getDouble(@NotNull String path) {
DoubleProperty doubleProperty = new DoubleProperty(path, 0);
PropertyValue<Double> value = doubleProperty.determineValue(this);
return value.isValidInResource() ? value.getValue() : null;
}

/**
* Returns the value of the given path as a boolean if available.
*
* @param path the path to retrieve a boolean for
* @return the value as a boolean, or null if not applicable or unavailable
* @deprecated read the value with a {@link BooleanProperty},
* or call {@link #getValue} and perform your own casts
*/
@Nullable Boolean getBoolean(@NotNull String path);
@Deprecated
default @Nullable Boolean getBoolean(@NotNull String path) {
BooleanProperty boolProperty = new BooleanProperty(path, true);
PropertyValue<Boolean> value = boolProperty.determineValue(this);
return value.isValidInResource() ? value.getValue() : null;
}

/**
* Returns the value of the given path as a list if available.
*
* @param path the path to retrieve a list for
* @return the value as a list, or null if not applicable or unavailable
* @deprecated read the value with a {@link ch.jalu.configme.properties.ListProperty ListProperty},
* or call {@link #getValue} and perform your own casts
*/
@Nullable List<?> getList(@NotNull String path);
@Deprecated
default @Nullable List<?> getList(@NotNull String path) {
Object value = getValue(path);
return value instanceof List<?> ? (List<?>) value : null;
}

}
94 changes: 28 additions & 66 deletions src/main/java/ch/jalu/configme/resource/YamlFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import ch.jalu.configme.exception.ConfigMeException;
import ch.jalu.configme.internal.PathUtils;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.Yaml;
Expand All @@ -17,15 +16,14 @@
import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
* YAML file reader.
*/
public class YamlFileReader implements PropertyReader {
public class YamlFileReader implements PropertyReader, PathProvider {

private final Path path;
private final Charset charset;
Expand Down Expand Up @@ -54,7 +52,7 @@ public YamlFileReader(@NotNull Path path, @NotNull Charset charset) {
}

@Override
public @Nullable Object getObject(@NotNull String path) {
public @Nullable Object getValue(@NotNull String path) {
if (path.isEmpty()) {
return root;
}
Expand All @@ -71,54 +69,23 @@ public YamlFileReader(@NotNull Path path, @NotNull Charset charset) {
}

@Override
public @Nullable String getString(@NotNull String path) {
return getTypedObject(path, String.class);
}

@Override
public @Nullable Integer getInt(@NotNull String path) {
Number n = getTypedObject(path, Number.class);
return (n == null)
? null
: n.intValue();
}

@Override
public @Nullable Double getDouble(@NotNull String path) {
Number n = getTypedObject(path, Number.class);
return (n == null)
? null
: n.doubleValue();
}

@Override
public @Nullable Boolean getBoolean(@NotNull String path) {
return getTypedObject(path, Boolean.class);
}

@Override
public @Nullable List<?> getList(@NotNull String path) {
return getTypedObject(path, List.class);
public boolean contains(@NotNull String path) {
return getValue(path) != null;
}

@Override
public boolean contains(@NotNull String path) {
return getObject(path) != null;
public @NotNull Set<String> getPaths() {
return collectPaths(false);
}

@Override
public @NotNull Set<String> getKeys(boolean onlyLeafNodes) {
if (root == null) {
return Collections.emptySet();
}
Set<String> allKeys = new LinkedHashSet<>();
collectKeysIntoSet("", root, allKeys, onlyLeafNodes);
return allKeys;
public @NotNull Set<String> getLeafPaths() {
return collectPaths(true);
}

@Override
public @NotNull Set<String> getChildKeys(@NotNull String path) {
Object object = getObject(path);
public @NotNull Set<String> getChildPaths(@NotNull String path) {
Object object = getValue(path);
if (object instanceof Map) {
String pathPrefix = path.isEmpty() ? "" : path + ".";
return ((Map<String, Object>) object).keySet().stream()
Expand All @@ -128,24 +95,33 @@ public boolean contains(@NotNull String path) {
return Collections.emptySet();
}

private @NotNull Set<String> collectPaths(boolean onlyLeafNodes) {
if (root == null) {
return Collections.emptySet();
}
Set<String> allPaths = new LinkedHashSet<>();
collectPathsIntoSet("", root, allPaths, onlyLeafNodes);
return allPaths;
}

/**
* Recursively collects keys from maps into the given set.
* Recursively collects keys from maps and adds them as paths to {@code result}.
*
* @param path the path of the given map
* @param path the path to the given map
* @param map the map to process recursively
* @param result set to save keys to
* @param result set to save paths to
* @param onlyLeafNodes whether only leaf nodes should be added to the result set
*/
private void collectKeysIntoSet(@NotNull String path, @NotNull Map<String, Object> map, @NotNull Set<String> result,
boolean onlyLeafNodes) {
private static void collectPathsIntoSet(@NotNull String path, @NotNull Map<String, Object> map,
@NotNull Set<String> result, boolean onlyLeafNodes) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String childPath = PathUtils.concat(path, entry.getKey());
if (!onlyLeafNodes || isLeafValue(entry.getValue())) {
result.add(childPath);
}

if (entry.getValue() instanceof Map) {
collectKeysIntoSet(childPath, (Map) entry.getValue(), result, onlyLeafNodes);
collectPathsIntoSet(childPath, (Map) entry.getValue(), result, onlyLeafNodes);
}
}
}
Expand Down Expand Up @@ -183,6 +159,9 @@ private static boolean isLeafValue(@Nullable Object o) {
return new MapNormalizer().normalizeMap(map);
}

/**
* @return the file this reader read from
*/
protected final @NotNull Path getPath() {
return path;
}
Expand All @@ -196,23 +175,6 @@ private static boolean isLeafValue(@Nullable Object o) {
return root;
}

/**
* Gets the object at the given path and safely casts it to the given class's type. Returns null
* if no value is available or if it cannot be cast.
*
* @param path the path to retrieve
* @param clazz the class to cast to
* @param <T> the class type
* @return cast value at the given path, null if not applicable
*/
protected <T> @Nullable T getTypedObject(@NotNull String path, @NotNull Class<T> clazz) {
Object value = getObject(path);
if (clazz.isInstance(value)) {
return clazz.cast(value);
}
return null;
}

private static @Nullable Object getEntryIfIsMap(@NotNull String key, @Nullable Object value) {
if (value instanceof Map<?, ?>) {
return ((Map<?, ?>) value).get(key);
Expand Down
Loading
Loading