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

Reply via email to