http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java ---------------------------------------------------------------------- diff --git a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java index c538de7..5b61055 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValue.java @@ -19,48 +19,189 @@ package org.apache.tamaya.spi; import java.io.Serializable; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; /** * Class modelling the result of a request for a property value. A property value is basically identified by its key. * There might be reasons, where one want to further analyze, which PropertySources provided a value and which not, so - * it is possible to create a PropertyValue with a null value. Nevertheless in all cases the provider source (typically - * the name of the PropertySource) must be set. + * it is possible to create a PropertyValue with a null value. + * + * A PropertyValue represents an abstract data point in a configuration structure read. PropertyValues actually + * represent a tree, with additional functionality for representing data lists/arrays using indexed children + * names. This allows to support a full mapping of common document based configuration formats, such as JSON, YAML, + * XML and more. */ -public final class PropertyValue implements Serializable{ +public final class PropertyValue implements Serializable, Iterable<PropertyValue>{ + private static final long serialVersionUID = 1L; /** The requested key. */ private String key; /** The value. */ private String value; - /** The source of the value. */ - private String source; /** Additional metadata provided by the provider. */ - private Map<String,String> metaEntries = new HashMap<>(); + private final transient Map<String,Object> metaData = new HashMap<>(); + /** List of child properties. */ + private final List<PropertyValue> children = new ArrayList<>(); + /** The getParent getChild, null if it's a root getChild. */ + private PropertyValue parent; + /** The getChild's getIndex, if the getChild is participating in a list structure. */ + private int index = -1; + /** The value version, used for determining config changes. */ + private AtomicInteger version = new AtomicInteger(); + /** Helper structure used for indexing new list getChildren. */ + private Map<String, AtomicInteger> indices = new HashMap<>(); + /** Flag to mark a value as immutable. */ + private boolean immutable; + + + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param source the source, typically the name of the {@link PropertySource} + * providing the value, not {@code null}. + * @return a new builder instance. + * @deprecated Will be removed, use {@link PropertyValue} directly. + */ + @Deprecated + public static PropertyValueBuilder builder(String key, String source){ + Objects.requireNonNull(key, "Key must be given."); + Objects.requireNonNull(source, "Source must be given"); + + return new PropertyValueBuilder(key, source); + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param source the source. + * @return a new builder instance. + * @deprecated Use {@link #create(String)} + */ + @Deprecated + public static PropertyValue of(String key, String source){ + Objects.requireNonNull(key, "Key must be given."); + + return new PropertyValue(null, key).setMeta("source", source); + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param value the property value, not {@code null}. + * @param source the source, typically the name of the {@link PropertySource} + * providing the value, not {@code null}. + * @return a new builder instance. + */ + @Deprecated + public static PropertyValue of(String key, String value, String source) { + Objects.requireNonNull(key, "Key must be given."); + if(source!=null) { + return new PropertyValue(null, key).setValue(value).setMeta("source", source); + } + return new PropertyValue(null, key).setValue(value); + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param value the new value. + * @return a new builder instance. + */ + public static PropertyValue create(String key, String value){ + Objects.requireNonNull(key, "Key must be given."); + + return new PropertyValue(null, key).setValue(value); + } + + + /** + * Creates a new (invisible) root getChild, which is a getChild with an empty name. + * @return a new empty root getChild, never null. + */ + public static PropertyValue create(){ + return new PropertyValue(null, ""); + } + + /** + * Creates a new named root getChild. + * @param name the name, not null. + * @return a new named root getChild, never null. + */ + public static PropertyValue create(String name){ + return new PropertyValue(null, name); + } + + + + /** + * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}. + * @param config the String based map, not {@code null}. + * @param source the source name, not {@code null}. + * @return the corresponding value based map. + */ + public static Map<String, PropertyValue> map(Map<String, String> config, String source) { + Map<String, PropertyValue> result = new HashMap<>(config.size()); + for(Map.Entry<String,String> en:config.entrySet()){ + result.put(en.getKey(), PropertyValue.of(en.getKey(), en.getValue(), source)); + } + return result; + } + + /** + * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}. + * + * @param config the String based map, not {@code null}. + * @param source the source name, not {@code null}. + * @param metaData additional metadata, not {@code null}. + * @return the corresponding value based map. + */ + public static Map<String, PropertyValue> map(Map<String, String> config, String source, + Map<String,String> metaData) { + Objects.requireNonNull(config, "Config must be given."); + Objects.requireNonNull(metaData, "Meta data must be given."); + + Map<String, PropertyValue> result = new HashMap<>(config.size()); - PropertyValue(PropertyValueBuilder builder){ - this.key = Objects.requireNonNull(builder.key); - this.value = builder.value; - this.source = Objects.requireNonNull(builder.source); - if(builder.metaEntries !=null) { - this.metaEntries.putAll(builder.metaEntries); + for(Map.Entry<String,String> en:config.entrySet()){ + PropertyValue pv = PropertyValue.create(en.getKey(), en.getValue()) + .setMeta(metaData); + if(source!=null){ + pv.setMeta("source", source); + } + result.put(en.getKey(), pv); } + return result; } /** * Creates a new instance * @param key the key, not {@code null}. - * @param value the value, not {@code null}. - * @param source the source, typically the name of the {@link PropertySource} providing - * the value, not {@code null}. */ - private PropertyValue(String key, String value, String source){ + private PropertyValue(PropertyValue parent, String key){ + this.parent = parent; this.key = Objects.requireNonNull(key, "Key is required."); - this.value = Objects.requireNonNull(value, "Value is required."); - this.source = Objects.requireNonNull(source, "Source is required."); + } + + /** + * Checks if the instance is immutable. + * @return true, if the instance is immutable. + */ + public boolean isImmutable(){ + return immutable; + } + + /** + * Sets this instance and also all its direct an indirect children to immutable. Any further changes will throw + * an {@link IllegalStateException}. + * @return this instance for chaining. + */ + public PropertyValue setImmutable(){ + this.immutable = true; + children.forEach(PropertyValue::setImmutable); + return this; } /** @@ -72,15 +213,38 @@ public final class PropertyValue implements Serializable{ } /** - * The source. - * @return the source, which provided the value, not {@code null}. - * @see PropertySource#getName() . + * Get a qualified name of a getChild in property format using '.' as separator, e.g. + * {@code a.b.c} or {@code a.b.c[0]} for indexed entries. Entries hereby also can have multiple + * levels of indexing, e.g. {@code a[1].b.c[14].d} is a valid option. + * + * The qualified key is defined by {@link #getQualifiedKey()} of it's parent concatenated with the key + * of this node. If there is no parent, or the parent's qualified key is empty only {@link #getKey()} + * is returned. Additionally if the current values is an indeyed value the key is extended by the + * index in brackets, e.g. {@code [0], [1], ...}. All the subsequent keys are valid qualified keys: + * <pre> + * a + * a.b + * a[0].b + * [0] + * a.b[4].c.d[0].[1].any + * </pre> + * + * @return the qualified key, never null.. */ - public String getSource() { - return this.source; + public String getQualifiedKey(){ + if(parent==null){ + return key; + } + String parentName = parent.getQualifiedKey(); + if(!parentName.isEmpty()){ + parentName+="."; + } + if(isIndexed()){ + return parentName+key+"["+index+"]"; + } + return parentName+key; } - /** * The value. * @return the value, in case a value is null it is valid to return {#code null} as result for @@ -91,142 +255,539 @@ public final class PropertyValue implements Serializable{ } /** - * Creates a full configuration map for this key, value pair and all its meta context data. This map + * Get the getChild's getParent. + * @return the getParent, or null. + */ + public PropertyValue getParent(){ + return parent; + } + + /** + * Get the values version, the version is updated with each change written. + * @return the version. + */ + public int getVersion(){ + return version.get(); + } + + /** + * Get a getChild's getIndex. + * @return the getIndex, or -1, if the getChild does not participate in an array. + */ + public int getIndex(){ + return index; + } + + /** + * Get the source. + * @return the source, or null. + * @deprecated Use {@code getMeta("source")}. + */ + @Deprecated + public String getSource() { + return (String)this.metaData.get("source"); + } + + + /** + * Checks if the getChild is a root getChild. + * @return true, if the current getChild is a root getChild. + */ + public boolean isRoot() { + return parent == null; + } + + /** + * Checks if the getChild is a leaf getChild (has no getChildren). + * @return true, if the current getChild is a leaf getChild. + */ + public boolean isLeaf(){ + return children.isEmpty(); + } + + /** + * Allows to check if a getChild is indexed. + * @return true, if the getChild participates in an array. + */ + public boolean isIndexed(){ + if(parent==null){ + return false; + } + return index>=0; + } + + /** + * Creates a full configuration map for this key, value pair and all its getMeta context data. This map * is also used for subsequent processing, like value filtering. * @return the property value entry map. */ - public Map<String, String> getMetaEntries() { - return Collections.unmodifiableMap(metaEntries); + public Map<String, Object> getMeta() { + return Collections.unmodifiableMap(metaData); } /** - * Creates a new builder instance. + * Access the given key from this value. Valid keys are the key or any getMeta-context key. * @param key the key, not {@code null}. - * @param source the source, typically the name of the {@link PropertySource} - * providing the value, not {@code null}. - * @return a new builder instance. + * @return the value found, or {@code null}. + * @deprecated Use {@link #getMeta(String)} instead of. */ - public static PropertyValueBuilder builder(String key, String source){ - Objects.requireNonNull(key, "Key must be given."); - Objects.requireNonNull(source, "Source must be given"); - - return new PropertyValueBuilder(key, source); + @Deprecated + public String getMetaEntry(String key) { + return (String)this.metaData.get(Objects.requireNonNull(key)); } /** - * Creates a new builder instance. + * Access the given key from this value. Valid keys are the key or any getMeta-context key. * @param key the key, not {@code null}. - * @param value the property value, not {@code null}. - * @param source the source, typically the name of the {@link PropertySource} - * providing the value, not {@code null}. - * @return a new builder instance. + * @param <T> the target type. + * @return the value found, or {@code null}. */ - public static PropertyValueBuilder builder(String key, String value, String source) { - Objects.requireNonNull(key, "Key must be given."); - Objects.requireNonNull(value, "Value must be given"); - Objects.requireNonNull(source, "Source must be given."); + public <T> T getMeta(String key) { + return (T)this.metaData.get(Objects.requireNonNull(key)); + } + + /** + * Access the given metadata. + * @param type the type, not {@code null}. + * @param <T> the target type. + * @return the value found, or {@code null}. + */ + public <T> T getMeta(Class<T> type) { + return (T)this.metaData.get(type.getName()); + } - return new PropertyValueBuilder(key, value, source); + /** + * Get a single child getChild by name. + * @param name the child's name, not null. + * @return the child found, or null. + * @throws IllegalArgumentException if multiple getChildren with the given name are existing (ambigous). + */ + public PropertyValue getChild(String name){ + List<PropertyValue> nodes = this.getChildren(name); + if(nodes.isEmpty()){ + return null; + } + if(nodes.size()>1){ + throw new IllegalArgumentException("Multiple getChildren existing: " + name); + } + return nodes.get(0); } + /** + * Get a sub value. + * @param index the target getIndex. + * @return the value found. + * @throws java.util.NoSuchElementException if no such element is existing. + */ + public PropertyValue getChild(int index) { + return this.children.get(index); + } /** - * Creates a new PropertyValue without any metadata.. - * @param key the key, not {@code null}. - * @param value the value. - * @param source the source, typically the name of the {@link PropertySource} - * providing the value, not {@code null}. - * @return a new property value instance, or {@code null}, - * if the value passed is {@code null}.. + * Get a single child getChild with the given name, creates it if not existing. + * @param name the child's name, not null. + * @return the child found or created, never null. + * @throws IllegalArgumentException if multiple getChildren with the given name are existing (ambigous). + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() */ - public static PropertyValue of(String key, String value, String source) { - if (value==null) { + public PropertyValue getOrCreateChild(String name){ + List<PropertyValue> nodes = this.getChildren(name); + if(nodes.isEmpty()){ + checkImmutable(); + PropertyValue n = new PropertyValue(this, name); + this.children.add(n); + version.incrementAndGet(); + return n; + } + if(nodes.size()>1){ + throw new IllegalArgumentException("Multiple getChildren existing: " + name); + } + return nodes.get(0); + } + + /** + * Get's the n-th getChild of an indexed getChild setCurrent. + * @param name the child's name, not null. + * @param index the target getChild getIndex. + * @return the getChild found, or null. + */ + public PropertyValue getChildWithIndex(String name, int index){ + List<PropertyValue> nodes = this.getChildren(name); + if(nodes.isEmpty() || nodes.size()<=index){ return null; } - return new PropertyValue(key, value, source); + return nodes.get(index); } /** - * Access the given key from this value. Valid keys are the key or any meta-context key. - * @param key the key, not {@code null}. - * @return the value found, or {@code null}. + * Get all child getChildren with a given name. + * @param name the target name, not null. + * @return the list of matching getChildren, could be none, one or multiple in case of arrays. */ - public String getMetaEntry(String key) { - return this.metaEntries.get(Objects.requireNonNull(key)); + public List<PropertyValue> getChildren(String name){ + return children.stream().filter(n -> n.key.equals(name)).collect(Collectors.toList()); + } + + /** + * Get the value's number of elements. + * @return the getNumChilds of this multi value. + */ + public int getNumChilds() { + return this.children.size(); + } + + /** + * The value. + * @return the value, in case a value is null it is valid to return {#code null} as result for + * {@link PropertySource#get(String)}. + */ + public List<PropertyValue> getChildren() { + return Collections.unmodifiableList(this.children); + } + + @Override + public Iterator<PropertyValue> iterator() { + return Collections.unmodifiableList(this.children).iterator(); + } + + /** + * Adds a new non-indexed child getChild. + * @param name the child's name, not null. + * @return the new child, never null. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue createChild(String name){ + return createChild(name, false); + } + + /** + * Adds a new non-indexed child. + * @param name the child's name, not null. + * @param value the child's value, not null. + * @return the new child, not null. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue createChild(String name, String value){ + return createChild(name, false).setValue(value); + } + + /** + * Adds another existing node, hereby setting the corresponding parent node. + * @param value the value, not null + * @return this instance, for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue addChild(PropertyValue value) { + checkImmutable(); + value.parent = this; + this.children.add(value); + return this; + } + + /** + * Adds a new child getChild. + * @param name the child's name, not null. + * @param indexed if true, the getChild will be participate in an array of the given name. + * @return the new getChild, not null. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue createChild(String name, boolean indexed){ + checkImmutable(); + PropertyValue n = new PropertyValue(this, name); + this.children.add(n); + version.incrementAndGet(); + if(indexed) { + AtomicInteger index = indices.computeIfAbsent(name, s -> new AtomicInteger(0)); + n.index = index.getAndIncrement(); + }else{ + List<PropertyValue> nodes = this.getChildren(name); + if(nodes.size()>1){ + AtomicInteger index = indices.get(name); + if(index!=null){ + n.index = index.getAndIncrement(); + }else{ + index = new AtomicInteger(0); + indices.put(name, index); + for(PropertyValue node:nodes){ + node.index = index.getAndIncrement(); + } + } + } + } + return n; + } + + /** + * Adds a new child getChild, where the getChild is given in '.'-separated property notation, + * e.g. {@code a.b.c}. + * @param key the property key, e.g. {@code a.b.c} + * @param value the property value + * @return the new leaf-getChild created. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue addProperty(String key, String value) { + checkImmutable(); + PropertyValue node = this; + StringTokenizer tokenizer = new StringTokenizer(key, "\\.", false); + while(tokenizer.hasMoreTokens()){ + String token = tokenizer.nextToken().trim(); + node = node.getOrCreateChild(token); + if(!tokenizer.hasMoreTokens()){ + // Its the last or single token + node.setValue(value); + } + } + return node; + } + + /** + * Adds multiple child getChildren, where the getChildren are defined in '.'-separated property notation, + * * e.g. {@code a.b.c}. + * @param props the properties + * @return the collection of added leaf-child getChildren. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public Collection<PropertyValue> addProperties(Map<String,String> props) { + checkImmutable(); + List<PropertyValue> result = new ArrayList<>(); + props.entrySet().forEach(en -> result.add(addProperty(en.getKey(), en.getValue()))); + return result; + } + + /** + * Changes the entry's key, mapping also corresponding context entries. + * @param key the new key, not {@code null}. + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue setKey(String key) { + checkImmutable(); + if(!Objects.equals(this.key, key)) { + this.key = Objects.requireNonNull(key); + version.incrementAndGet(); + } + return this; + } + + /** + * Sets the value. + * @param value the value + * @return this getChild for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue setValue(String value) { + checkImmutable(); + if(!Objects.equals(this.value, value)) { + this.value = value; + version.incrementAndGet(); + } + return this; + } + + /** + * Replaces/sets the context data. + * @param metaEntries the context data to be applied, not {@code null}. + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue setMeta(Map<String, Object> metaEntries) { + checkImmutable(); + if(!Objects.equals(this.metaData, metaEntries)) { + this.metaData.clear(); + this.metaData.putAll(metaEntries); + version.incrementAndGet(); + } + return this; + } + + /** + * Add an additional context data information. + * @param key the context data key, not {@code null}. + * @param value the context value, not {@code null} (will be converted to String). + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue setMeta(String key, Object value) { + checkImmutable(); + Objects.requireNonNull(key, "Meta key must be given."); + Objects.requireNonNull(value, "Meta value must be given."); + if(!Objects.equals(this.metaData.get(key), value)) { + this.metaData.put(key, value); + version.incrementAndGet(); + } + return this; + } + + /** + * Add an additional context data information. + * @param type the context data type, used as key, not {@code null}. + * @param value the context value, not {@code null}. + * @param <T> the target type. + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public <T> PropertyValue setMeta(Class<T> type, T value) { + checkImmutable(); + Objects.requireNonNull(type, "Meta key must be given."); + Objects.requireNonNull(value, "Meta value must be given."); + if(!Objects.equals(this.metaData.get(type.toString()), value)) { + this.metaData.put(type.toString(), value); + version.incrementAndGet(); + } + return this; + } + + /** + * Add an additional context data information, using the data's class name as key. + * @param value the context value, not {@code null}. + * @param <T> the target type. + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public <T> PropertyValue setMeta(T value) { + checkImmutable(); + Objects.requireNonNull(value, "Meta value must be given."); + if(!Objects.equals(this.metaData.get(value.getClass().toString()), value)) { + this.metaData.put(value.getClass().toString(), value); + version.incrementAndGet(); + } + return this; + } + + /** + * Removes a getMeta entry. + * @param key the entry's key, not {@code null}. + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue removeMeta(String key) { + checkImmutable(); + Objects.requireNonNull(key, "Key must be given."); + if(this.metaData.containsKey(key)) { + this.metaData.remove(key); + version.incrementAndGet(); + } + return this; + } + + /** + * Removes a getMeta entry. + * @param type the entry's type, not {@code null}. + * @return the builder for chaining. + * @throws IllegalStateException if the instance is immutable. + * @see #isImmutable() + */ + public PropertyValue removeMeta(Class type) { + checkImmutable(); + Objects.requireNonNull(key, "Key must be given."); + if(this.metaData.containsKey(type.getName())) { + this.metaData.remove(type.getName()); + version.incrementAndGet(); + } + return this; + } + + /** + * Convert the getChild tree to a property map. + * @return the corresponding property map, not null. + */ + public Map<String,String> asMap(){ + Map<String, String> map = new TreeMap<>(); + if(isLeaf()){ + map.put(getQualifiedKey(), value); + } + for(PropertyValue n: children){ + map.putAll(n.asMap()); + } + return map; + } + + /** + * Create a String representation of the tree. + * @return the corresponding String representation, not null. + */ + public String asString() { + Map<String, String> map = asMap(); + StringBuilder b = new StringBuilder(); + map.entrySet().forEach(en -> b.append(en.getKey()).append(" = ").append(en.getValue()).append('\n')); + if(b.length()==0){ + return "<nodata>"; + } + return b.toString(); } /** * Creates a new builder instance based on this item. * @return a new builder, never null. + * @deprecated Use {@link PropertyValue} directly. */ + @Deprecated public PropertyValueBuilder toBuilder() { - return new PropertyValueBuilder(this.getKey(), this.getSource()) - .setValue(this.getValue()) - .setMetaEntries(this.metaEntries); + return new PropertyValueBuilder(this.getKey(), this.getValue()) + .setMeta(this.metaData); + } + + /** + * Clones this instance and all it's children, marking as mutable value. + * @return the new value clone. + */ + public PropertyValue mutable(){ + if(!immutable){ + return this; + } + PropertyValue newProp = new PropertyValue(this.parent, key); + newProp.setValue(this.value); + newProp.setMeta(metaData); + children.forEach(c -> newProp.children.add(c.mutable())); + newProp.version = new AtomicInteger(version.intValue()); + return newProp; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PropertyValue)) return false; - PropertyValue that = (PropertyValue) o; - return Objects.equals(getKey(), that.getKey()) && - Objects.equals(getValue(), that.getValue()) && - Objects.equals(getSource(), that.getSource()) && - Objects.equals(getMetaEntries(), that.getMetaEntries()); + PropertyValue dataNode = (PropertyValue) o; + return Objects.equals(parent, dataNode.parent) && + Objects.equals(key, dataNode.key) && + Objects.equals(value, dataNode.value) && + Objects.equals(metaData, dataNode.metaData); } @Override public int hashCode() { - return Objects.hash(getKey(), getValue(), getSource(), - getMetaEntries()); + return Objects.hash(parent, key, value, metaData); } + @Override public String toString() { return "PropertyValue{" + - "key='" + key + '\'' + - ", value='" + value + '\'' + - ", source='" + source + '\'' + - (metaEntries.isEmpty()?"":", metaEntries=" + metaEntries) + + '\'' +getQualifiedKey() + '\'' + + (value!=null?", value='" + value + '\'':"") + + ", children='" + children.size() + '\'' + + (metaData.isEmpty()?"":", metaData=" + metaData) + '}'; } - /** - * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}. - * @param config the String based map, not {@code null}. - * @param source the source name, not {@code null}. - * @return the corresponding value based map. - */ - public static Map<String,PropertyValue> map(Map<String, String> config, String source) { - Map<String,PropertyValue> result = new HashMap<>(config.size()); - for(Map.Entry<String,String> en:config.entrySet()){ - result.put(en.getKey(), PropertyValue.of(en.getKey(), en.getValue(), source)); + private void checkImmutable(){ + if(immutable){ + throw new IllegalStateException("Instance is immutable."); } - return result; } - /** - * Maps a map of {@code Map<String,String>} to a {@code Map<String,PropertyValue>}. - * - * @param config the String based map, not {@code null}. - * @param source the source name, not {@code null}. - * @param metaData additional metadata, not {@code null}. - * @return the corresponding value based map. - */ - public static Map<String,PropertyValue> map(Map<String, String> config, String source, - Map<String,String> metaData) { - Objects.requireNonNull(config, "Config must be given."); - Objects.requireNonNull(source, "Source must be given."); - Objects.requireNonNull(metaData, "Meta data must be given."); - - Map<String,PropertyValue> result = new HashMap<>(config.size()); - - for(Map.Entry<String,String> en:config.entrySet()){ - PropertyValue value = new PropertyValueBuilder(en.getKey(), source).setValue(en.getValue()) - .addMetaEntries(metaData).build(); - result.put(en.getKey(), value); - } - return result; - } }
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java ---------------------------------------------------------------------- diff --git a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java index af01987..20f49a0 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueBuilder.java @@ -18,55 +18,38 @@ */ package org.apache.tamaya.spi; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; /** - * Builder to create a {@link PropertyValue} instance. + * Builder to create a {@link org.apache.tamaya.spi.PropertyValue} instance. + * @deprecated Use {@link PropertyValue} directly. */ -public class PropertyValueBuilder { +@Deprecated +public final class PropertyValueBuilder { /** The key accessed. */ - String key; + protected String key; /** The property value. */ - String value; - /** The property vaoue source. */ - String source; + protected String value; + /** The property value. */ + protected String source; /** additional metadata entries (optional). */ - Map<String,String> metaEntries = new HashMap<>(); - - /** - * Create a new builder instance, for a given set of parameters. - * Before calling build at least a {@link #value} and its {@link #source} - * must be set. - */ - PropertyValueBuilder(String key){ - this.key = Objects.requireNonNull(key); - } - - /** - * Create a new builder instance, for a given set of parameters. - * @param key to access a property value, not {@code null}. - * @param source property source. - */ - PropertyValueBuilder(String key, String source) { - this.key = Objects.requireNonNull(key); - this.source = Objects.requireNonNull(source); - } + protected Map<String,Object> metaEntries = new HashMap<>(); + /** The getParent getChild, null if it's a root getChild. */ + protected PropertyValue parent; + /** The getChild's getIndex, if the getChild is participating in a list structure. */ + protected int index = -1; + /** Helper structure used for indexing new list getChildren. */ + protected Map<String, AtomicInteger> indices = new HashMap<>(); /** - * Create a new builder instance, for a given set of parameters. - * - * @param key to access a property value. - * @param value the value, not {@code null}. If a value is {@code null} - * {@link PropertySource#get(String)} should return {@code null}. - * @param source property source. + * Create a new builder instance, for a given setCurrent of parameters. + * Before calling build at least a {@link #value} + * must be setCurrent. */ - PropertyValueBuilder(String key, String value, String source) { + PropertyValueBuilder(String key, String value){ this.key = Objects.requireNonNull(key); this.value = value; - this.source = Objects.requireNonNull(source); } /** @@ -74,7 +57,7 @@ public class PropertyValueBuilder { * @param metaEntries the context data to be applied, not {@code null}. * @return the builder for chaining. */ - public PropertyValueBuilder setMetaEntries(Map<String, String> metaEntries) { + public PropertyValueBuilder setMeta(Map<String, Object> metaEntries) { this.metaEntries.clear(); this.metaEntries.putAll(metaEntries); return this; @@ -90,7 +73,35 @@ public class PropertyValueBuilder { Objects.requireNonNull(key, "Meta key must be given."); Objects.requireNonNull(value, "Meta value must be given."); - this.metaEntries.put(key, String.valueOf(value)); + this.metaEntries.put(key, value); + return this; + } + + /** + * Add an additional context data information. + * @param type the context data type, used as key, not {@code null}. + * @param value the context value, not {@code null}. + * @param <T> the type of the class modeled by the type parameter + * @return the builder for chaining. + */ + public <T> PropertyValueBuilder addMetaEntry(Class<T> type, T value) { + Objects.requireNonNull(type, "Meta key must be given."); + Objects.requireNonNull(value, "Meta value must be given."); + + this.metaEntries.put(type.toString(), value); + return this; + } + + /** + * Add an additional context data information, using the data's class name as key. + * @param value the context value, not {@code null}. + * @param <T> the type of the class modeled by the type parameter + * @return the builder for chaining. + */ + public <T> PropertyValueBuilder addMetaEntry(T value) { + Objects.requireNonNull(value, "Meta value must be given."); + + this.metaEntries.put(value.getClass().toString(), value); return this; } @@ -99,17 +110,17 @@ public class PropertyValueBuilder { * @param metaEntries the context data to be applied, not {@code null}. * @return the builder for chaining. */ - public PropertyValueBuilder addMetaEntries(Map<String, String> metaEntries) { + public PropertyValueBuilder addMetaEntries(Map<String, Object> metaEntries) { this.metaEntries.putAll(metaEntries); return this; } /** - * Removes a meta entry. + * Removes a getMeta entry. * @param key the entry's key, not {@code null}. * @return the builder for chaining. */ - public PropertyValueBuilder removeMetaEntry(String key) { + public PropertyValueBuilder removeMeta(String key) { Objects.requireNonNull(key, "Key must be given."); this.metaEntries.remove(key); @@ -120,19 +131,38 @@ public class PropertyValueBuilder { * Get the value's context data. * @return the context data, not {@code null}. */ - public Map<String,String> getMetaEntries() { + public Map<String,Object> getMetaEntries() { return Collections.unmodifiableMap(this.metaEntries); } /** + * Get the value's context data. + * @param <T> the type of the class modeled by the type parameter + * @return the context data, not {@code null}. + */ + public <T> T getMeta(String key) { + return (T)this.metaEntries.get(key); + } + + /** + * Get the value's context data. + * @param <T> the type of the class modeled by the type parameter + * @param type the target type, not null. + * @return the context data, not {@code null}. + */ + public <T> T getMeta(Class<T> type) { + return (T)this.metaEntries.get(type.toString()); + } + + /** * Changes the entry's key, mapping also corresponding context entries. * @param key the new key, not {@code null}. * @return the builder for chaining. */ public PropertyValueBuilder mapKey(String key) { // todo obf if (1==1) throw new RuntimeException("No tests written."); - Map<String,String> newContext = new HashMap<>(); - for(Map.Entry<String,String> en:this.metaEntries.entrySet()){ + Map<String,Object> newContext = new HashMap<>(); + for(Map.Entry<String,Object> en:this.metaEntries.entrySet()){ if(en.getKey().startsWith("_"+this.key)){ newContext.put("_"+key+'.'+ en.getKey().substring(this.key.length()+1), en.getValue()); }else{ @@ -169,26 +199,30 @@ public class PropertyValueBuilder { * Sets a new source. * @param source the new source, not {@code null}. * @return the builder for chaining. + * @deprecated Use {@link #addMetaEntry(String, Object)} (String, Object)} */ + @Deprecated public PropertyValueBuilder setSource(String source) { - // todo obf if (1==1) throw new RuntimeException("No tests written."); - this.source = Objects.requireNonNull(source); + if(source!=null) { + this.source = source; + } return this; } /** - * Creates a new immutable {@link PropertyValue}. - * @return a new immutable {@link PropertyValue}, never {@code null}. + * Creates a new immutable {@link org.apache.tamaya.spi.PropertyValue}. + * @return a new immutable {@link org.apache.tamaya.spi.PropertyValue}, never {@code null}. */ public PropertyValue build(){ - return new PropertyValue(this); + return PropertyValue.of(key, value, source).setMeta(metaEntries); } @Override public String toString() { return "PropertyValueBuilder{" + "key='" + key + '\'' + - "value='" + value + '\'' + + ", value='" + value + '\'' + + ", source='" + source + '\'' + ", metaEntries=" + metaEntries + '}'; } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java ---------------------------------------------------------------------- diff --git a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java index 14640e6..10a3200 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/PropertyValueCombinationPolicy.java @@ -19,18 +19,21 @@ package org.apache.tamaya.spi; + /** * Policy that determines how the final value of a configuration entry is evaluated. An instances of this - * interface can be registered to get control how multiple PropertySources are combined. This is useful in cases + * interface can be registered to current control how multiple PropertySources are combined. This is useful in cases * where the default overriding policy as implemented in {@link #DEFAULT_OVERRIDING_POLICY} is not matching * the need of the current application, e.g. then entries containing multiple values should be combined to new * values instead of overridden. + * @deprecated Will be implemented through implementation specific mechanisms. */ +@Deprecated public interface PropertyValueCombinationPolicy { /** * Default overriding collector, where each existing entry ({@code current} is overridden by a subsequent non-null - * entry evaluated by {@code propertySource.get(key)}. + * entry evaluated by {@code propertySource.current(key)}. */ PropertyValueCombinationPolicy DEFAULT_OVERRIDING_POLICY = new PropertyValueCombinationPolicy(){ @@ -48,7 +51,7 @@ public interface PropertyValueCombinationPolicy { PropertyValueCombinationPolicy DEFAULT_OVERRIDING_COLLECTOR = DEFAULT_OVERRIDING_POLICY; - /** + /** * Method that is called for each value evaluated by a PropertySource for the given key. This method is called * either when a single key is accessed, e.g. by calling {@code org.apache.tamaya.Configuration.getXXX}, but also * when the full configuration property map is accessed by calling @@ -61,11 +64,11 @@ public interface PropertyValueCombinationPolicy { * result to be used as new {@code currentValue}. * @param key The current key to be evaluated. * @param propertySource The PropertySource that may return an value for the given key. The PropertySource given - * may be evaluated for additional meta-data, how the given values are to be combined. + * may be evaluated for additional getMeta-data, how the given values are to be combined. * Note that the value returned by a PropertySource can be null. In that case * {@code currentValue} should be returned in almost all cases. * @return the value to be used for future evaluation, including metadata entries. */ - PropertyValue collect(PropertyValue currentValue, String key, PropertySource propertySource); + PropertyValue collect(PropertyValue currentValue, String key, PropertySource propertySource); } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java ---------------------------------------------------------------------- diff --git a/code/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java b/code/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java index 9eb18e8..c9a521f 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/ServiceContext.java @@ -18,22 +18,44 @@ */ package org.apache.tamaya.spi; +import org.apache.tamaya.ConfigException; + +import javax.annotation.Priority; import java.io.IOException; import java.net.URL; -import java.util.Enumeration; -import java.util.List; +import java.text.MessageFormat; +import java.util.*; +import java.util.logging.Level; /** * This class models the component that is managing the lifecycle current the * services used by the Configuration API. */ -public interface ServiceContext { +public interface ServiceContext extends ClassloaderAware{ /** + * Get the ordinal of the ServiceContext. * @return ordinal of the ServiceContext. The one with the highest ordinal will be taken. */ - int ordinal(); + default int ordinal(){ + return getPriority(this); + } + + /** + * Checks the given instance for a @Priority annotation. If present the annotation's value is evaluated. If no such + * annotation is present, a default priority of {@code 1} is returned. + * @param o the instance, not {@code null}. + * @return a priority, by default 1. + */ + static int getPriority(Object o){ + int prio = 1; //X TODO discuss default priority + Priority priority = o.getClass().getAnnotation(Priority.class); + if (priority != null) { + prio = priority.value(); + } + return prio; + } /** * Access a service singleton via its type. @@ -45,7 +67,9 @@ public interface ServiceContext { * @return The instance to be used, or {@code null} * @throws org.apache.tamaya.ConfigException if there are multiple service implementations with the maximum priority. */ - <T> T getService(Class<T> serviceType); + default <T> T getService(Class<T> serviceType){ + return create(serviceType); + } /** * Factory method to create a type, hereby a new instance is created on each access. @@ -58,7 +82,17 @@ public interface ServiceContext { * @return The new instance to be used, or {@code null} * @throws org.apache.tamaya.ConfigException if there are multiple service implementations with the maximum priority. */ - <T> T create(Class<T> serviceType); + default <T> T create(Class<T> serviceType){ + @SuppressWarnings("unchecked") + Class<? extends T> implType = null; + Collection<T> services = getServices(serviceType); + if (services.isEmpty()) { + return null; + } else { + return ((List<T>) services).get(0); + } + } + /** * Access a list current services, given its type. The bootstrap mechanism should @@ -71,23 +105,25 @@ public interface ServiceContext { * @return The instance to be used, never {@code null} */ <T> List<T> getServices(Class<T> serviceType); - /** * Loads resources from the current runtime context. This method allows to use runtime * specific code to load resources, e.g. within OSGI environments. * @param resource the resource, not {@code null}. - * @param cl the desired classloader context, if null, the current thread context classloader is used. * @return the resources found * @throws IOException if load fails. */ - Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException; + default Enumeration<URL> getResources(String resource) throws IOException{ + return getClassLoader().getResources(resource); + } /** * Loads a resource from the current runtime context. This method allows to use runtime * specific code to load a resource, e.g. within OSGI environments. * @param resource the resource, not {@code null}. - * @param cl the desired classloader context, if null, the current thread context classloader is used. * @return the resource found, or {@code null}. */ - URL getResource(String resource, ClassLoader cl); + default URL getResource(String resource){ + return getClassLoader().getResource(resource); + } + } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java ---------------------------------------------------------------------- diff --git a/code/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java b/code/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java index be287db..3c008d1 100644 --- a/code/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java +++ b/code/api/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java @@ -18,8 +18,10 @@ */ package org.apache.tamaya.spi; +import java.util.Map; import java.util.Objects; import java.util.ServiceLoader; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,7 +41,7 @@ public final class ServiceContextManager { /** * The ServiceProvider used. */ - private static volatile ServiceContext serviceContextProviderDelegate; + private static volatile Map<ClassLoader, ServiceContext> serviceContexts = new ConcurrentHashMap<>(); /** * Private singletons constructor. @@ -52,11 +54,11 @@ public final class ServiceContextManager { * * @return {@link ServiceContext} to be used for loading the services. */ - private static ServiceContext loadDefaultServiceProvider() { + private static ServiceContext loadDefaultServiceProvider(ClassLoader classLoader) { ServiceContext highestServiceContext = null; try { int highestOrdinal = 0; - for (ServiceContext serviceContext : ServiceLoader.load(ServiceContext.class)) { + for (ServiceContext serviceContext : ServiceLoader.load(ServiceContext.class, classLoader)) { if (highestServiceContext == null || serviceContext.ordinal() > highestOrdinal) { highestServiceContext = serviceContext; @@ -69,6 +71,7 @@ public final class ServiceContextManager { if (highestServiceContext==null){ throw new ConfigException("No ServiceContext found"); } + highestServiceContext.init(classLoader); LOG.info("Using Service Context of type: " + highestServiceContext.getClass().getName()); return highestServiceContext; } @@ -76,26 +79,37 @@ public final class ServiceContextManager { /** * Replace the current {@link ServiceContext} in use. * - * @param serviceContextProvider the new {@link ServiceContext}, not {@code null}. + * @param serviceContext the new {@link ServiceContext}, not {@code null}. * @return the currently used context after setting it. */ - public static ServiceContext set(ServiceContext serviceContextProvider) { - Objects.requireNonNull(serviceContextProvider); - ServiceContext currentContext = ServiceContextManager.serviceContextProviderDelegate; + public static ServiceContext set(ServiceContext serviceContext) { + Objects.requireNonNull(serviceContext); + ServiceContext previousContext; synchronized (ServiceContextManager.class) { - if (ServiceContextManager.serviceContextProviderDelegate == null) { - ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider; - LOG.log(Level.INFO, "Using ServiceProvider: " + serviceContextProvider.getClass().getName()); - } else { - LOG.log(Level.WARNING, "Replacing ServiceProvider " + - ServiceContextManager.serviceContextProviderDelegate.getClass().getName() + - " with: " + serviceContextProvider.getClass().getName()); - ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider; - } + previousContext = ServiceContextManager.serviceContexts + .put(serviceContext.getClassLoader(), serviceContext); + } + if(previousContext!=null) { + LOG.log(Level.WARNING, "Replaced ServiceProvider " + + previousContext.getClass().getName() + + " with: " + serviceContext.getClass().getName() + " for classloader: " + + serviceContext.getClassLoader()); + }else{ + LOG.log(Level.INFO, "Using ServiceProvider: " + serviceContext.getClass().getName() + + " for classloader: " + serviceContext.getClassLoader()); } + return serviceContext; + } - return currentContext; + /** + * Ge {@link ServiceContext}. If necessary the {@link ServiceContext} will be laziliy loaded. + * + * @param classLoader the classloader to be used, not null. + * @return the {@link ServiceContext} used. + */ + public static ServiceContext getServiceContext(ClassLoader classLoader) { + return serviceContexts.computeIfAbsent(classLoader, ServiceContextManager::loadDefaultServiceProvider); } /** @@ -104,14 +118,22 @@ public final class ServiceContextManager { * @return the {@link ServiceContext} used. */ public static ServiceContext getServiceContext() { - if (serviceContextProviderDelegate == null) { - synchronized (ServiceContextManager.class) { - if (serviceContextProviderDelegate == null) { - serviceContextProviderDelegate = loadDefaultServiceProvider(); - } - } + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if(cl==null){ + cl = ServiceContextManager.class.getClassLoader(); } - return serviceContextProviderDelegate; + return getServiceContext(cl); } + /** + * Evaluate the default classloader: 1. the thread context classloader, 2. This class's classloader. + * @return the classloder, not null. + */ + public static ClassLoader getDefaultClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if(cl==null){ + cl = ServiceContextManager.class.getClassLoader(); + } + return cl; + } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/ConfigurationProviderTest.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/ConfigurationProviderTest.java b/code/api/src/test/java/org/apache/tamaya/ConfigurationProviderTest.java index c65ae61..521c7cd 100644 --- a/code/api/src/test/java/org/apache/tamaya/ConfigurationProviderTest.java +++ b/code/api/src/test/java/org/apache/tamaya/ConfigurationProviderTest.java @@ -39,7 +39,7 @@ public class ConfigurationProviderTest { */ @Test public void testCreateConfiguration() { - Configuration result = ConfigurationProvider.createConfiguration(ConfigurationProvider.getConfiguration().getContext()); + Configuration result = ConfigurationProvider.createConfiguration(Configuration.current().getContext()); assertThat(result).isNotNull(); } @@ -66,20 +66,20 @@ public class ConfigurationProviderTest { */ @Test public void testGetSetConfiguration() { - Configuration currentConfig = ConfigurationProvider.getConfiguration(); + Configuration currentConfig = Configuration.current(); assertThat(currentConfig instanceof Configuration).isTrue(); Configuration newConfig = Mockito.mock(Configuration.class); try{ ConfigurationProvider.setConfiguration(newConfig); - assertThat(ConfigurationProvider.getConfiguration()).isEqualTo(newConfig); + assertThat(Configuration.current()).isEqualTo(newConfig); }finally{ ConfigurationProvider.setConfiguration(currentConfig); } - assertThat(ConfigurationProvider.getConfiguration()).isEqualTo(currentConfig); + assertThat(Configuration.current()).isEqualTo(currentConfig); } /** - * Test of getConfigurationBuilder method, of class ConfigurationProvider. + * Test of createConfigurationBuilder method, of class ConfigurationProvider. */ @Test public void testGetConfigurationBuilder() { http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java b/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java index ad13158..58811b4 100644 --- a/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java +++ b/code/api/src/test/java/org/apache/tamaya/ConfigurationTest.java @@ -18,7 +18,13 @@ */ package org.apache.tamaya; +import org.apache.tamaya.spi.ConfigurationBuilder; import org.junit.Test; +import org.mockito.Mockito; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + import static org.assertj.core.api.Assertions.*; /** @@ -31,58 +37,100 @@ public class ConfigurationTest { @Test public void testget() throws Exception { - assertThat(Boolean.TRUE).isEqualTo(ConfigurationProvider.getConfiguration().get("booleanTrue", Boolean.class)); - assertThat(Boolean.FALSE).isEqualTo(ConfigurationProvider.getConfiguration().get("booleanFalse", Boolean.class)); - assertThat((int) Byte.MAX_VALUE).isEqualTo((int) ConfigurationProvider.getConfiguration().get("byte", Byte.class)); - assertThat(Integer.MAX_VALUE).isEqualTo((int) ConfigurationProvider.getConfiguration().get("int", Integer.class)); - assertThat(Long.MAX_VALUE).isEqualTo((long) ConfigurationProvider.getConfiguration().get("long", Long.class)); - assertThat(Float.MAX_VALUE).isCloseTo((float) ConfigurationProvider.getConfiguration().get("float", Float.class), within(0.001f)); - assertThat(Double.MAX_VALUE).isEqualTo(ConfigurationProvider.getConfiguration().get("double", Double.class)); - assertThat("aStringValue").isEqualTo(ConfigurationProvider.getConfiguration().get("String")); + assertThat(Boolean.TRUE).isEqualTo(Configuration.current().get("booleanTrue", Boolean.class)); + assertThat(Boolean.FALSE).isEqualTo(Configuration.current().get("booleanFalse", Boolean.class)); + assertThat((int) Byte.MAX_VALUE).isEqualTo((int) Configuration.current().get("byte", Byte.class)); + assertThat(Integer.MAX_VALUE).isEqualTo((int) Configuration.current().get("int", Integer.class)); + assertThat(Long.MAX_VALUE).isEqualTo((long) Configuration.current().get("long", Long.class)); + assertThat(Float.MAX_VALUE).isCloseTo((float) Configuration.current().get("float", Float.class), within(0.001f)); + assertThat(Double.MAX_VALUE).isEqualTo(Configuration.current().get("double", Double.class)); + assertThat("aStringValue").isEqualTo(Configuration.current().get("String")); } @Test public void testGetBoolean() throws Exception { - assertThat(ConfigurationProvider.getConfiguration().get("booleanTrue", Boolean.class)).isTrue(); - assertThat(ConfigurationProvider.getConfiguration().get("booleanFalse", Boolean.class)).isFalse(); - assertThat(ConfigurationProvider.getConfiguration().get("foorBar", Boolean.class)).isFalse(); + assertThat(Configuration.current().get("booleanTrue", Boolean.class)).isTrue(); + assertThat(Configuration.current().get("booleanFalse", Boolean.class)).isFalse(); + assertThat(Configuration.current().get("foorBar", Boolean.class)).isFalse(); } @Test public void testGetInteger() throws Exception { - assertThat(Integer.MAX_VALUE).isEqualTo((int) ConfigurationProvider.getConfiguration().get("int", Integer.class)); + assertThat(Integer.MAX_VALUE).isEqualTo((int) Configuration.current().get("int", Integer.class)); } @Test public void testGetLong() throws Exception { - assertThat(Long.MAX_VALUE).isEqualTo((long) ConfigurationProvider.getConfiguration().get("long", Long.class)); + assertThat(Long.MAX_VALUE).isEqualTo((long) Configuration.current().get("long", Long.class)); } @Test public void testGetDouble() throws Exception { - assertThat(Double.MAX_VALUE).isEqualTo(ConfigurationProvider.getConfiguration().get("double", Double.class)); + assertThat(Double.MAX_VALUE).isEqualTo(Configuration.current().get("double", Double.class)); } @Test public void testGetOrDefault() throws Exception { - assertThat("StringIfThereWasNotAValueThere").isEqualTo(ConfigurationProvider.getConfiguration().getOrDefault("nonexistant", "StringIfThereWasNotAValueThere")); - assertThat("StringIfThereWasNotAValueThere").isEqualTo(ConfigurationProvider.getConfiguration().getOrDefault("nonexistant", String.class, "StringIfThereWasNotAValueThere")); + assertThat("StringIfThereWasNotAValueThere").isEqualTo(Configuration.current().getOrDefault("nonexistant", "StringIfThereWasNotAValueThere")); + assertThat("StringIfThereWasNotAValueThere").isEqualTo(Configuration.current().getOrDefault("nonexistant", String.class, "StringIfThereWasNotAValueThere")); } @Test public void testToBuilder() throws Exception { - assertThat(ConfigurationProvider.getConfiguration().toBuilder()).isNotNull(); + assertThat(Configuration.current().toBuilder()).isNotNull(); } @Test + @Deprecated public void testWith() throws Exception { ConfigOperator noop = (Configuration config) -> config; - assertThat(ConfigurationProvider.getConfiguration().with(noop)).isNotNull(); + assertThat(Configuration.current().with(noop)).isNotNull(); } @Test + @Deprecated public void testQuery() throws Exception { ConfigQuery<String> stringQuery = (ConfigQuery) (Configuration config) -> config.get("String"); - assertThat(ConfigurationProvider.getConfiguration().query(stringQuery)).isEqualTo("aStringValue"); + assertThat(Configuration.current().query(stringQuery)).isEqualTo("aStringValue"); + } + + @Test + public void testMap() throws Exception { + UnaryOperator<Configuration> noop = (Configuration config) -> config; + assertThat(Configuration.current().map(noop)).isNotNull(); + assertThat(Configuration.current().map(noop)== Configuration.current()); + } + + @Test + public void testAdapt() throws Exception { + Function<Configuration, String> stringQuery = (Configuration config) -> config.get("String"); + assertThat(Configuration.current().adapt(stringQuery)).isEqualTo("aStringValue"); + } + + + /** + * Test of getConfiguration method, of class ConfigurationProvider. + */ + @Test + public void testGetSetConfiguration() { + Configuration currentConfig = Configuration.current(); + assertThat(currentConfig instanceof Configuration).isTrue(); + Configuration newConfig = Mockito.mock(Configuration.class); + try{ + Configuration.setCurrent(newConfig); + assertThat(Configuration.current()).isEqualTo(newConfig); + }finally{ + Configuration.setCurrent(currentConfig); + } + assertThat(Configuration.current()).isEqualTo(currentConfig); + } + + /** + * Test of createConfigurationBuilder method, of class ConfigurationProvider. + */ + @Test + public void testGetConfigurationBuilder() { + ConfigurationBuilder result = Configuration.createConfigurationBuilder(); + assertThat(result instanceof ConfigurationBuilder).isTrue(); } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java b/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java index dd85a8d..94923ff 100644 --- a/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java +++ b/code/api/src/test/java/org/apache/tamaya/TestConfiguration.java @@ -18,12 +18,17 @@ */ package org.apache.tamaya; +import org.apache.tamaya.spi.ConfigurationBuilder; import org.apache.tamaya.spi.ConfigurationContext; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; + +import org.apache.tamaya.spi.ConfigurationProviderSpi; +import org.apache.tamaya.spi.ServiceContext; import org.mockito.Mockito; +import org.mockito.internal.matchers.Any; /** * Test Configuration class, that is used to testdata the default methods @@ -82,12 +87,20 @@ public class TestConfiguration implements Configuration { @Override public ConfigurationContext getContext() { - return Mockito.mock(ConfigurationContext.class); + ConfigurationContext ctx = Mockito.mock(ConfigurationContext.class); + ServiceContext serviceContext = Mockito.mock(ServiceContext.class); + ConfigurationProviderSpi spi = Mockito.mock(ConfigurationProviderSpi.class); + ConfigurationBuilder builder = Mockito.mock(ConfigurationBuilder.class); + Mockito.when(builder.setConfiguration(this)).thenReturn(builder); + Mockito.when(spi.getConfigurationBuilder()).thenReturn(builder); + Mockito.when(serviceContext.getService(ConfigurationProviderSpi.class)).thenReturn(spi); + Mockito.when(ctx.getServiceContext()).thenReturn(serviceContext); + return ctx; } @Override public Map<String, String> getProperties() { - // run toString on each value of the (key, value) set in VALUES + // run toString on each value of the (key, value) setCurrent in VALUES return VALUES.entrySet().stream().collect( Collectors.toMap( Map.Entry::getKey, http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java b/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java index 2136129..b4660f8 100644 --- a/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java +++ b/code/api/src/test/java/org/apache/tamaya/TestConfigurationProvider.java @@ -37,7 +37,7 @@ public class TestConfigurationProvider implements ConfigurationProviderSpi { private ConfigurationContext context = Mockito.mock(ConfigurationContext.class); @Override - public Configuration getConfiguration() { + public Configuration getConfiguration(ClassLoader classLoader) { return config; } @@ -72,7 +72,7 @@ public class TestConfigurationProvider implements ConfigurationProviderSpi { } @Override - public void setConfiguration(Configuration config) { + public void setConfiguration(Configuration config, ClassLoader classLoader) { this.config = config; } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/spi/ConfigurationProviderSpiTest.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/spi/ConfigurationProviderSpiTest.java b/code/api/src/test/java/org/apache/tamaya/spi/ConfigurationProviderSpiTest.java index 4fe631f..fb8e7b3 100644 --- a/code/api/src/test/java/org/apache/tamaya/spi/ConfigurationProviderSpiTest.java +++ b/code/api/src/test/java/org/apache/tamaya/spi/ConfigurationProviderSpiTest.java @@ -29,7 +29,7 @@ TestConfigurationProvider configProvider = new TestConfigurationProvider(); @Test public void testIsConfigurationSettableByDefault(){ - assertThat(configProvider.isConfigurationSettable()).isTrue(); + assertThat(configProvider.isConfigurationSettable(Thread.currentThread().getContextClassLoader())).isTrue(); } @Test @@ -48,7 +48,7 @@ TestConfigurationProvider configProvider = new TestConfigurationProvider(); ConfigurationContext newContext = Mockito.mock(ConfigurationContext.class); try{ configProvider.setConfigurationContext(newContext); - //The mocked TestConfigurationProvider doesn't set the context on the + //The mocked TestConfigurationProvider doesn't setCurrent the context on the // inner Configuration object, as that's deprecated. assertThat(configProvider.getConfigurationContext()).isEqualTo(newContext); }finally{ http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java b/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java index f52d368..ad0ff95 100644 --- a/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java +++ b/code/api/src/test/java/org/apache/tamaya/spi/ConversionContextTest.java @@ -24,6 +24,7 @@ import org.apache.tamaya.ConfigOperator; import org.apache.tamaya.ConfigQuery; import org.apache.tamaya.Configuration; import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.spi.*; import org.junit.Test; import java.net.InetAddress; @@ -64,7 +65,7 @@ public class ConversionContextTest { @Test public void testConfiguration() throws Exception { - Configuration config = new MyConfiguration(); + Configuration config = Configuration.EMPTY; ConversionContext ctx = new ConversionContext.Builder("testConfiguration", TypeLiteral.of(List.class)) .setConfiguration(config).build(); assertThat(ctx.getConfiguration()).isEqualTo(config); @@ -100,13 +101,6 @@ public class ConversionContextTest { assertThat(ctx.toString()).isEqualTo("ConversionContext{configuration=null, key='toString', targetType=TypeLiteral{type=interface java.util.List}, annotatedElement=null, supportedFormats=[0.0.0.0/nnn (MyConverter), x.x.x.x/yyy (MyConverter)]}"); } - @Test - public void testGetConfigurationContext() throws Exception { - ConfigurationContext context = new MyConfigurationContext(); - ConversionContext ctx = new ConversionContext.Builder("getConfigurationContext", TypeLiteral.of(List.class)) - .setConfigurationContext(context).build(); - assertThat(ctx.getConfigurationContext()).isEqualTo(context); - } private static final AnnotatedElement MyAnnotatedElement = new AnnotatedElement() { @Override @@ -128,110 +122,10 @@ public class ConversionContextTest { private static final class MyConverter implements PropertyConverter<InetAddress> { @Override - public InetAddress convert(String value, ConversionContext context) { + public InetAddress convert(String value) { return null; } - } - - private static final class MyConfigurationContext implements ConfigurationContext { - - @Override - public void addPropertySources(PropertySource... propertySources) { - - } - - @Override - public List<PropertySource> getPropertySources() { - return null; - } - - @Override - public PropertySource getPropertySource(String name) { - return null; - } - - @Override - public <T> void addPropertyConverter(TypeLiteral<T> typeToConvert, PropertyConverter<T> propertyConverter) { - - } - - @Override - public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() { - return null; - } - - @Override - public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> type) { - return null; - } - - @Override - public List<PropertyFilter> getPropertyFilters() { - return null; - } - - @Override - public PropertyValueCombinationPolicy getPropertyValueCombinationPolicy() { - return null; - } - - @Override - public ConfigurationContextBuilder toBuilder() { - return null; - } - } - private static final class MyConfiguration implements Configuration { - - @Override - public String get(String key) { - return null; - } - - @Override - public String getOrDefault(String key, String defaultValue) { - return null; - } - - @Override - public <T> T getOrDefault(String key, Class<T> type, T defaultValue) { - return null; - } - - @Override - public <T> T get(String key, Class<T> type) { - return null; - } - - @Override - public <T> T get(String key, TypeLiteral<T> type) { - return null; - } - - @Override - public <T> T getOrDefault(String key, TypeLiteral<T> type, T defaultValue) { - return null; - } - - @Override - public Map<String, String> getProperties() { - return null; - } - - @Override - public Configuration with(ConfigOperator operator) { - return null; - } - - @Override - public <T> T query(ConfigQuery<T> query) { - return null; - } - - @Override - public ConfigurationContext getContext() { - return null; - } } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/e45effd2/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java ---------------------------------------------------------------------- diff --git a/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java b/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java index 00567c1..35842ca 100644 --- a/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java +++ b/code/api/src/test/java/org/apache/tamaya/spi/FilterContextTest.java @@ -19,6 +19,7 @@ package org.apache.tamaya.spi; import org.apache.tamaya.TypeLiteral; +import org.apache.tamaya.spi.*; import org.junit.Test; import java.util.Collections; @@ -26,27 +27,60 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static junit.framework.TestCase.assertNull; import static org.assertj.core.api.Assertions.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; /** * Tests for {@link FilterContext}. */ public class FilterContextTest { + @Test + public void setNullContext() { + FilterContext.set(null); + } + + @Test + public void setGetContext() { + PropertyValue val = PropertyValue.of("getKey", "v", ""); + FilterContext ctx = new FilterContext(val, + new HashMap<String,PropertyValue>(), ConfigurationContext.EMPTY); + FilterContext.set(ctx); + assertEquals(ctx, FilterContext.get()); + } + + @Test + public void resetContext() { + PropertyValue val = PropertyValue.of("getKey", "v", ""); + FilterContext ctx = new FilterContext(val, + new HashMap<String,PropertyValue>(), ConfigurationContext.EMPTY); + FilterContext.set(ctx); + assertNotNull(FilterContext.get()); + FilterContext.reset(); + assertNull(FilterContext.get()); + } + @Test(expected = NullPointerException.class) - public void constructorRequiresNonNullPropertyValueTwoParameterVariant() { - new FilterContext(null, new TestConfigContext()); + public void constructorRequiresNonNullPropertyValueTwoParameterVariant1() { + new FilterContext((PropertyValue)null, ConfigurationContext.EMPTY); + } + + @Test(expected = NullPointerException.class) + public void constructorRequiresNonNullPropertyValueTwoParameterVariant2() { + new FilterContext((List<PropertyValue>)null, ConfigurationContext.EMPTY); } @Test(expected = NullPointerException.class) public void constructorRequiresNonNullConfigurationContextTwoParameterVariant() { - new FilterContext(PropertyValue.of("a", "b", "s"), null); + new FilterContext(Collections.singletonList(PropertyValue.of("a", "b", "s")), null); } @SuppressWarnings("unchecked") @Test(expected = NullPointerException.class) public void constructorRequiresNonNullPropertyValueThreeParameterVariant() { - new FilterContext(null, Collections.EMPTY_MAP, new TestConfigContext()); + new FilterContext(null, Collections.EMPTY_MAP, ConfigurationContext.EMPTY); } @SuppressWarnings("unchecked") @@ -57,23 +91,23 @@ public class FilterContextTest { @Test(expected = NullPointerException.class) public void constructorRequiresNonNullMapForConfigEntriesThreeParameterVariant() { - new FilterContext(PropertyValue.of("a", "b", "s"), null, new TestConfigContext()); + new FilterContext(PropertyValue.of("a", "b", "s"), null, ConfigurationContext.EMPTY); } @Test public void getKey() throws Exception { PropertyValue val = PropertyValue.of("getKey", "v", ""); FilterContext ctx = new FilterContext(val, - new HashMap<String,PropertyValue>(), new TestConfigContext()); + new HashMap<String,PropertyValue>(), ConfigurationContext.EMPTY); assertThat(ctx.getProperty()).isEqualTo(val); } @Test public void isSinglePropertyScoped() throws Exception { PropertyValue val = PropertyValue.of("isSinglePropertyScoped", "v", ""); - FilterContext ctx = new FilterContext(val, new HashMap<String,PropertyValue>(), new TestConfigContext()); + FilterContext ctx = new FilterContext(val, new HashMap<String,PropertyValue>(), ConfigurationContext.EMPTY); assertThat(ctx.isSinglePropertyScoped()).isEqualTo(false); - ctx = new FilterContext(val, new TestConfigContext()); + ctx = new FilterContext(Collections.singletonList(val), ConfigurationContext.EMPTY); assertThat(ctx.isSinglePropertyScoped()).isEqualTo(true); } @@ -84,7 +118,7 @@ public class FilterContextTest { config.put("key-"+i, PropertyValue.of("key-"+i, "value-"+i, "test")); } PropertyValue val = PropertyValue.of("getConfigEntries", "v", ""); - FilterContext ctx = new FilterContext(val, config, new TestConfigContext()); + FilterContext ctx = new FilterContext(val, config, ConfigurationContext.EMPTY); assertThat(ctx.getConfigEntries()).isEqualTo(config); assertThat(config != ctx.getConfigEntries()).isTrue(); } @@ -96,63 +130,16 @@ public class FilterContextTest { config.put("key-"+i, PropertyValue.of("key-"+i, "value-"+i, "test")); } PropertyValue val = PropertyValue.of("testToString", "val", "mySource"); - FilterContext ctx = new FilterContext(val, config, new TestConfigContext()); + FilterContext ctx = new FilterContext(val, config, ConfigurationContext.EMPTY); String toString = ctx.toString(); assertThat(toString).isNotNull(); - assertThat(toString.contains("FilterContext{value='PropertyValue{key='testToString', value='val', " + - "source='mySource'}', configEntries=[")).isTrue(); + System.out.println(toString); + assertThat(toString.contains("FilterContext{value='[PropertyValue{'testToString', value='val', children='0', " + + "metaData={source=mySource}}]', configEntries=[")).isTrue(); assertThat(toString.contains("key-0")).isTrue(); assertThat(toString.contains("key-1")).isTrue(); assertThat(toString.endsWith("}")).isTrue(); } - private static class TestConfigContext implements ConfigurationContext{ - - @Override - public void addPropertySources(PropertySource... propertySources) { - - } - - @Override - public List<PropertySource> getPropertySources() { - return null; - } - - @Override - public PropertySource getPropertySource(String name) { - return null; - } - - @Override - public <T> void addPropertyConverter(TypeLiteral<T> type, PropertyConverter<T> propertyConverter) { - - } - - @Override - public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() { - return null; - } - - @Override - public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> type) { - return null; - } - - @Override - public List<PropertyFilter> getPropertyFilters() { - return null; - } - - @Override - public PropertyValueCombinationPolicy getPropertyValueCombinationPolicy() { - return null; - } - - @Override - public ConfigurationContextBuilder toBuilder() { - return null; - } - } - } \ No newline at end of file
