Author: sseifert
Date: Thu Dec  1 18:05:41 2016
New Revision: 1772244

URL: http://svn.apache.org/viewvc?rev=1772244&view=rev
Log:
SLING-6338 enhance nested configuration handling
refactor SPI metadata classes to make them fluent

Added:
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
   (with props)
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
   (with props)
Modified:
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/ConfigurationManager.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImpl.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImpl.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/ConfigurationResourceResolverImpl.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/impl/metadata/ConfigurationMetadataProviderMultiplexerTest.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImplTest.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplNoDefaultTest.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplTest.java
    
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/console/CAConfigInventoryPrinterTest.java
    
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/ConfigurationPersistenceStrategy.java
    
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/AbstractMetadata.java
    
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadata.java
    
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadata.java
    
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadataTest.java
    
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadataTest.java

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/ConfigurationBuilderImpl.java
 Thu Dec  1 18:05:41 2016
@@ -27,7 +27,6 @@ import java.util.Iterator;
 
 import org.apache.commons.collections.IteratorUtils;
 import org.apache.commons.collections.Transformer;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
@@ -37,6 +36,7 @@ import org.apache.sling.caconfig.Configu
 import org.apache.sling.caconfig.impl.ConfigurationProxy.ChildResolver;
 import org.apache.sling.caconfig.impl.metadata.AnnotationClassParser;
 import org.apache.sling.caconfig.impl.override.ConfigurationOverrideManager;
+import org.apache.sling.caconfig.resource.impl.util.ConfigNameUtil;
 import 
org.apache.sling.caconfig.resource.spi.ConfigurationResourceResolvingStrategy;
 import org.apache.sling.caconfig.spi.ConfigurationInheritanceStrategy;
 import org.apache.sling.caconfig.spi.ConfigurationPersistenceStrategy;
@@ -68,26 +68,12 @@ class ConfigurationBuilderImpl implement
 
     @Override
     public ConfigurationBuilder name(String configName) {
-        if (!isNameValid(configName)) {
-            throw new IllegalArgumentException("Invalid configuration name: " 
+ configName);
-        }
+        ConfigNameUtil.ensureValidConfigName(configName);
         this.configName = configName;
         return this;
     }
 
     /**
-     * Check the name.
-     * A name must not be null and relative.
-     * @param name The name
-     * @return {@code true} if it is valid
-     */
-    private boolean isNameValid(final String name) {
-        return !StringUtils.isBlank(name)
-                && !StringUtils.startsWith(name, "/")
-                && !StringUtils.contains(name, "../");
-    }
-
-    /**
      * Validate the configuration name.
      * @param name Configuration name or relative path
      */

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/impl/metadata/AnnotationClassParser.java
 Thu Dec  1 18:05:41 2016
@@ -19,8 +19,11 @@
 package org.apache.sling.caconfig.impl.metadata;
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -119,16 +122,14 @@ public final class AnnotationClassParser
             throw new IllegalArgumentException("Class has not @Configuration 
annotation: " + clazz.getName());
         }
         
-        // configuration metadata
+        // configuration metadata and property metadata
         String configName = getConfigurationName(clazz, configAnnotation);
-        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(configName);
-        configMetadata.setLabel(emptyToNull(configAnnotation.label()));
-        
configMetadata.setDescription(emptyToNull(configAnnotation.description()));
-        
configMetadata.setProperties(propsArrayToMap(configAnnotation.property()));
-        configMetadata.setCollection(configAnnotation.collection());
-
-        // property metadata
-        
configMetadata.setPropertyMetadata(buildConfigurationMetadata_PropertyMetadata(clazz));
+        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(configName,
+                buildConfigurationMetadata_PropertyMetadata(clazz),
+                configAnnotation.collection())
+                .label(emptyToNull(configAnnotation.label()))
+                .description(emptyToNull(configAnnotation.description()))
+                .properties(propsArrayToMap(configAnnotation.property()));
         
         return configMetadata;
     }
@@ -138,21 +139,18 @@ public final class AnnotationClassParser
      * @param clazz Configuration annotation class
      * @return Configuration metadata
      */
-    private static ConfigurationMetadata 
buildConfigurationMetadata_Nested(Class<?> clazz, String configName) {
-        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(configName);
-
-        // property metadata
-        
configMetadata.setPropertyMetadata(buildConfigurationMetadata_PropertyMetadata(clazz));
-        
-        return configMetadata;
+    private static ConfigurationMetadata 
buildConfigurationMetadata_Nested(Class<?> clazz, String configName, boolean 
collection) {
+        return new ConfigurationMetadata(configName,
+                buildConfigurationMetadata_PropertyMetadata(clazz),
+                collection);
     }
     
-    private static Map<String,PropertyMetadata<?>> 
buildConfigurationMetadata_PropertyMetadata(Class<?> clazz) {
-        Map<String,PropertyMetadata<?>> propertyMetadataList = new HashMap<>();
+    private static Collection<PropertyMetadata<?>> 
buildConfigurationMetadata_PropertyMetadata(Class<?> clazz) {
+        List<PropertyMetadata<?>> propertyMetadataList = new ArrayList<>();
         Method[] propertyMethods = clazz.getDeclaredMethods();
         for (Method propertyMethod : propertyMethods) {
             PropertyMetadata<?> propertyMetadata = 
buildPropertyMetadata(propertyMethod, propertyMethod.getReturnType());
-            propertyMetadataList.put(propertyMetadata.getName(), 
propertyMetadata);
+            propertyMetadataList.add(propertyMetadata);
         }
         return propertyMetadataList;
     }
@@ -161,34 +159,34 @@ public final class AnnotationClassParser
     private static <T> PropertyMetadata<T> buildPropertyMetadata(Method 
propertyMethod, Class<T> type) {
         String propertyName = getPropertyName(propertyMethod.getName());
         
-        PropertyMetadata propertyMetadata;
+        PropertyMetadata<?> propertyMetadata;
         if (type.isArray() && type.getComponentType().isAnnotation()) {
-            ConfigurationMetadata nestedConfigMetadata = 
buildConfigurationMetadata_Nested(type.getComponentType(), propertyName);
-            propertyMetadata = new PropertyMetadata<>(propertyName, 
ConfigurationMetadata[].class);
-            propertyMetadata.setConfigurationMetadata(nestedConfigMetadata);
+            ConfigurationMetadata nestedConfigMetadata = 
buildConfigurationMetadata_Nested(type.getComponentType(), propertyName, true);
+            propertyMetadata = new PropertyMetadata<>(propertyName, 
ConfigurationMetadata[].class)
+                    .configurationMetadata(nestedConfigMetadata);
         }
         else if (type.isAnnotation()) {
-            ConfigurationMetadata nestedConfigMetadata = 
buildConfigurationMetadata_Nested(type, propertyName);
-            propertyMetadata = new PropertyMetadata<>(propertyName, 
ConfigurationMetadata.class);
-            propertyMetadata.setConfigurationMetadata(nestedConfigMetadata);
+            ConfigurationMetadata nestedConfigMetadata = 
buildConfigurationMetadata_Nested(type, propertyName, false);
+            propertyMetadata = new PropertyMetadata<>(propertyName, 
ConfigurationMetadata.class)
+                    .configurationMetadata(nestedConfigMetadata);
         }
         else {
-            propertyMetadata = new PropertyMetadata<>(propertyName, type);     
       
-            
propertyMetadata.setDefaultValue((T)propertyMethod.getDefaultValue());
+            propertyMetadata = new PropertyMetadata<>(propertyName, type)      
      
+                    .defaultValue((T)propertyMethod.getDefaultValue());
         }
         
         Property propertyAnnotation = 
propertyMethod.getAnnotation(Property.class);
         if (propertyAnnotation != null) {            
-            propertyMetadata.setLabel(emptyToNull(propertyAnnotation.label()));
-            
propertyMetadata.setDescription(emptyToNull(propertyAnnotation.description()));
-            
propertyMetadata.setProperties(propsArrayToMap(propertyAnnotation.property()));
+            propertyMetadata.label(emptyToNull(propertyAnnotation.label()))
+                .description(emptyToNull(propertyAnnotation.description()))
+                .properties(propsArrayToMap(propertyAnnotation.property()));
         }
         else {
             Map<String,String> emptyMap = Collections.emptyMap();
-            propertyMetadata.setProperties(emptyMap);
+            propertyMetadata.properties(emptyMap);
         }
         
-        return propertyMetadata;
+        return (PropertyMetadata)propertyMetadata;
     }
     
     private static String emptyToNull(String value) {

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/ConfigurationManager.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/ConfigurationManager.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/ConfigurationManager.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/ConfigurationManager.java
 Thu Dec  1 18:05:41 2016
@@ -92,6 +92,13 @@ public interface ConfigurationManager {
      * @param configName Configuration name
      * @return Configuration metadata or null if none exists for the given 
name.
      */
-    @CheckForNull ConfigurationMetadata getConfigurationMetadata(String 
configName);
+    @CheckForNull ConfigurationMetadata getConfigurationMetadata(@Nonnull 
String configName);
+    
+    /**
+     * Rewrite given resource path or configuration name according to current 
persistence strategies.
+     * @param configResourcePath Resource path or config name
+     * @return Rewritten resoure path or config name
+     */
+    @CheckForNull String getPersistenceResourcePath(@Nonnull String 
configResourcePath);
     
 }

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImpl.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImpl.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImpl.java
 Thu Dec  1 18:05:41 2016
@@ -43,7 +43,7 @@ import org.apache.sling.caconfig.spi.met
 
 final class ConfigurationDataImpl implements ConfigurationData {
     
-    private ConfigurationMetadata configMetadata;
+    private final ConfigurationMetadata configMetadata;
     private final Resource resolvedConfigurationResource;
     private final Resource writebackConfigurationResource;
     private final List<Resource> configurationResourceInheritanceChain;
@@ -96,10 +96,6 @@ final class ConfigurationDataImpl implem
                 configResourceCollection, null);
     }
     
-    void setConfigMetadata(ConfigurationMetadata configMetadata) {
-        this.configMetadata = configMetadata;
-    }
-    
     @Override
     public String getConfigName() {
         return configName;
@@ -186,8 +182,8 @@ final class ConfigurationDataImpl implem
             return;
         }
         for (PropertyMetadata<?> propertyMetadata : 
configMetadata.getPropertyMetadata().values()) {
-            ConfigurationMetadata nestedConfigMetadata = 
propertyMetadata.getConfigurationMetadata();
-            if (nestedConfigMetadata != null) {
+            if (propertyMetadata.isNestedConfiguration()) {
+                ConfigurationMetadata nestedConfigMetadata = 
propertyMetadata.getConfigurationMetadata();
                 String nestedConfigName;
                 if (configResourceCollection) {
                     nestedConfigName = 
configurationPersistenceStrategy.getResourcePath(configName + "/" + 
getCollectionItemName()) + "/" + nestedConfigMetadata.getName();
@@ -197,16 +193,10 @@ final class ConfigurationDataImpl implem
                 }
                 if 
(propertyMetadata.getType().equals(ConfigurationMetadata.class)) {
                     ConfigurationData configData = 
configurationManager.getConfiguration(contextResource, nestedConfigName);
-                    if (configData != null) {
-                        
((ConfigurationDataImpl)configData).setConfigMetadata(nestedConfigMetadata);
-                    }
                     props.put(propertyMetadata.getName(), configData);
                 }
                 else if 
(propertyMetadata.getType().equals(ConfigurationMetadata[].class)) {
                     Collection<ConfigurationData> configDatas = 
configurationManager.getConfigurationCollection(contextResource, 
nestedConfigName).getItems();
-                    for (ConfigurationData configData : configDatas) {
-                        
((ConfigurationDataImpl)configData).setConfigMetadata(nestedConfigMetadata);
-                    }
                     props.put(propertyMetadata.getName(), 
configDatas.toArray(new ConfigurationData[configDatas.size()]));
                 }
             }
@@ -220,8 +210,15 @@ final class ConfigurationDataImpl implem
         Object value;
         Object effectiveValue;
         if (propertyMetadata != null) {
-            value = getValues().get(propertyName, propertyMetadata.getType());
-            effectiveValue = getEffectiveValues().get(propertyName, 
ClassUtils.primitiveToWrapper(propertyMetadata.getType()));
+            Class<?> type = 
ClassUtils.primitiveToWrapper(propertyMetadata.getType());
+            if (type == ConfigurationMetadata.class) {
+                type = ConfigurationData.class;
+            }
+            else if (type == ConfigurationMetadata[].class) {
+                type = ConfigurationData[].class;
+            }
+            value = getValues().get(propertyName, type);
+            effectiveValue = getEffectiveValues().get(propertyName, type);
         }
         else {
             value = getValues().get(propertyName);
@@ -244,5 +241,5 @@ final class ConfigurationDataImpl implem
             return configMetadata.getPropertyMetadata().get(propertyName);
         }
     }
-
+    
 }

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImpl.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImpl.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImpl.java
 Thu Dec  1 18:05:41 2016
@@ -31,6 +31,7 @@ import org.apache.commons.collections.It
 import org.apache.commons.collections.ResettableIterator;
 import org.apache.commons.collections.Transformer;
 import org.apache.commons.collections.iterators.ListIteratorWrapper;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.resource.Resource;
 import 
org.apache.sling.caconfig.impl.ConfigurationInheritanceStrategyMultiplexer;
 import 
org.apache.sling.caconfig.impl.metadata.ConfigurationMetadataProviderMultiplexer;
@@ -39,10 +40,12 @@ import org.apache.sling.caconfig.managem
 import org.apache.sling.caconfig.management.ConfigurationData;
 import org.apache.sling.caconfig.management.ConfigurationManager;
 import 
org.apache.sling.caconfig.resource.impl.ConfigurationResourceResolvingStrategyMultiplexer;
+import org.apache.sling.caconfig.resource.impl.util.ConfigNameUtil;
 import org.apache.sling.caconfig.spi.ConfigurationCollectionPersistData;
 import org.apache.sling.caconfig.spi.ConfigurationPersistData;
 import org.apache.sling.caconfig.spi.ConfigurationPersistenceException;
 import org.apache.sling.caconfig.spi.metadata.ConfigurationMetadata;
+import org.apache.sling.caconfig.spi.metadata.PropertyMetadata;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
 
@@ -63,7 +66,8 @@ public class ConfigurationManagerImpl im
     @SuppressWarnings("unchecked")
     @Override
     public ConfigurationData getConfiguration(Resource resource, String 
configName) {
-        ConfigurationMetadata configMetadata = 
configurationMetadataProvider.getConfigurationMetadata(configName);
+        ConfigNameUtil.ensureValidConfigName(configName);
+        ConfigurationMetadata configMetadata = 
getConfigurationMetadata(configName);
         Resource configResource = null;
         Iterator<Resource> configResourceInheritanceChain = 
configurationResourceResolvingStrategy
                 .getResourceInheritanceChain(resource, CONFIGS_PARENT_NAME, 
configName);
@@ -97,7 +101,8 @@ public class ConfigurationManagerImpl im
     @SuppressWarnings("unchecked")
     @Override
     public ConfigurationCollectionData getConfigurationCollection(Resource 
resource, String configName) {
-        ConfigurationMetadata configMetadata = 
configurationMetadataProvider.getConfigurationMetadata(configName);
+        ConfigNameUtil.ensureValidConfigName(configName);
+        ConfigurationMetadata configMetadata = 
getConfigurationMetadata(configName);
         String writebackConfigResourceCollectionParentPath = 
configurationResourceResolvingStrategy.getResourceCollectionParentPath(resource,
 CONFIGS_PARENT_NAME, configName);
         List<ConfigurationData> configData = new ArrayList<>();
 
@@ -177,6 +182,7 @@ public class ConfigurationManagerImpl im
 
     @Override
     public void persistConfiguration(Resource resource, String configName, 
ConfigurationPersistData data) {
+        ConfigNameUtil.ensureValidConfigName(configName);
         String configResourcePath = 
configurationResourceResolvingStrategy.getResourcePath(resource, 
CONFIGS_PARENT_NAME, configName);
         if (configResourcePath == null) {
             throw new ConfigurationPersistenceException("Unable to persist 
configuration: Configuration resolving strategy returned no path.");
@@ -188,6 +194,7 @@ public class ConfigurationManagerImpl im
 
     @Override
     public void persistConfigurationCollection(Resource resource, String 
configName, ConfigurationCollectionPersistData data) {
+        ConfigNameUtil.ensureValidConfigName(configName);
         String configResourceParentPath = 
configurationResourceResolvingStrategy.getResourceCollectionParentPath(resource,
 CONFIGS_PARENT_NAME, configName);
         if (configResourceParentPath == null) {
             throw new ConfigurationPersistenceException("Unable to persist 
configuration collection: Configuration resolving strategy returned no parent 
path.");
@@ -199,7 +206,8 @@ public class ConfigurationManagerImpl im
 
     @Override
     public ConfigurationData newCollectionItem(Resource resource, String 
configName) {
-        ConfigurationMetadata configMetadata = 
configurationMetadataProvider.getConfigurationMetadata(configName);
+        ConfigNameUtil.ensureValidConfigName(configName);
+        ConfigurationMetadata configMetadata = 
getConfigurationMetadata(configName);
         if (configMetadata != null) {
             return new ConfigurationDataImpl(configMetadata,
                     resource, configName, this, configurationOverrideManager, 
configurationPersistenceStrategy, true);
@@ -214,7 +222,70 @@ public class ConfigurationManagerImpl im
 
     @Override
     public ConfigurationMetadata getConfigurationMetadata(String configName) {
-        return 
configurationMetadataProvider.getConfigurationMetadata(configName);
+        ConfigNameUtil.ensureValidConfigName(configName);
+        ConfigurationMetadata metadata = 
configurationMetadataProvider.getConfigurationMetadata(configName);
+        if (metadata != null) {
+            return metadata;
+        }
+        
+        // if no metadata found with direct match try to resolve nested 
configuration metadata references
+        for (String partialConfigName : 
ConfigNameUtil.getAllPartialConfigNameVariations(configName)) {
+            ConfigurationMetadata partialConfigMetadata = 
getConfigurationMetadata(partialConfigName);
+            if (partialConfigMetadata != null) {
+                ConfigurationMetadata nestedConfigMetadata = 
getNestedConfigurationMetadata(partialConfigMetadata, configName, 
partialConfigName);
+                if (nestedConfigMetadata != null) {
+                    return nestedConfigMetadata;
+                }
+            }
+        }
+        return null;
+    }
+    
+    private ConfigurationMetadata 
getNestedConfigurationMetadata(ConfigurationMetadata configMetadata, String 
configName, String partialConfigName) {
+        if (StringUtils.startsWith(configName, partialConfigName + "/")) {
+            String prefixToRemove;
+            if (configMetadata.isCollection()) {
+                String collectionItemName = 
StringUtils.substringBefore(StringUtils.substringAfter(configName, 
partialConfigName + "/"), "/");
+                prefixToRemove = 
configurationPersistenceStrategy.getResourcePath(partialConfigName + "/" + 
collectionItemName) + "/";
+            }
+            else {
+                prefixToRemove = 
configurationPersistenceStrategy.getResourcePath(partialConfigName) + "/";
+            }
+            String remainingConfigName = 
StringUtils.substringAfter(configName, prefixToRemove);
+            // try direct match
+            ConfigurationMetadata nestedConfigMetadata = 
getNestedConfigurationMetadataFromProperty(configMetadata, remainingConfigName);
+            if (nestedConfigMetadata != null) {
+                return nestedConfigMetadata;
+            }
+            // try to find partial match for deeper nestings
+            for (String partialRemainingConfigName : 
ConfigNameUtil.getAllPartialConfigNameVariations(remainingConfigName)) {
+                ConfigurationMetadata partialConfigMetadata = 
getNestedConfigurationMetadataFromProperty(configMetadata, 
partialRemainingConfigName);
+                if (partialConfigMetadata != null) {
+                    nestedConfigMetadata = 
getNestedConfigurationMetadata(partialConfigMetadata, remainingConfigName, 
partialRemainingConfigName);
+                    if (nestedConfigMetadata != null) {
+                        return nestedConfigMetadata;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+    
+    private ConfigurationMetadata 
getNestedConfigurationMetadataFromProperty(ConfigurationMetadata 
partialConfigMetadata, String configName) {
+        for (PropertyMetadata<?> propertyMetadata : 
partialConfigMetadata.getPropertyMetadata().values()) {
+            if (propertyMetadata.isNestedConfiguration()) {
+                ConfigurationMetadata nestedConfigMetadata = 
propertyMetadata.getConfigurationMetadata();
+                if (StringUtils.equals(configName, 
nestedConfigMetadata.getName())) {
+                    return nestedConfigMetadata;
+                }
+            }
+        }
+        return null;
     }
 
+    @Override
+    public String getPersistenceResourcePath(String configResourcePath) {
+        return 
configurationPersistenceStrategy.getResourcePath(configResourcePath);
+    }
+    
 }

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/ConfigurationResourceResolverImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/ConfigurationResourceResolverImpl.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/ConfigurationResourceResolverImpl.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/ConfigurationResourceResolverImpl.java
 Thu Dec  1 18:05:41 2016
@@ -25,6 +25,7 @@ import java.util.List;
 
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.caconfig.resource.ConfigurationResourceResolver;
+import org.apache.sling.caconfig.resource.impl.util.ConfigNameUtil;
 import org.apache.sling.caconfig.resource.spi.ContextResource;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.Reference;
@@ -39,11 +40,13 @@ public class ConfigurationResourceResolv
 
     @Override
     public Resource getResource(Resource resource, String bucketName, String 
configName) {
+        ConfigNameUtil.ensureValidConfigName(configName);
         return configurationResourceResolvingStrategy.getResource(resource, 
bucketName, configName);
     }
 
     @Override
     public Collection<Resource> getResourceCollection(Resource resource, 
String bucketName, String configName) {
+        ConfigNameUtil.ensureValidConfigName(configName);
         return 
configurationResourceResolvingStrategy.getResourceCollection(resource, 
bucketName, configName);
     }
 
@@ -67,5 +70,5 @@ public class ConfigurationResourceResolv
         }
         return contextPaths;
     }
-
+    
 }

Added: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java?rev=1772244&view=auto
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
 (added)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
 Thu Dec  1 18:05:41 2016
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.caconfig.resource.impl.util;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Helper methods for configuration names.
+ */
+public final class ConfigNameUtil {
+    
+    private ConfigNameUtil() {
+        // static methods only
+    }
+
+    /**
+     * Check if the config name is valid.
+     * @param configName The name
+     * @return {@code true} if it is valid
+     */
+    public static boolean isValid(final String configName) {
+        return !StringUtils.isBlank(configName)
+                && !StringUtils.startsWith(configName, "/")
+                && !StringUtils.contains(configName, "../");
+    }
+    
+    /**
+     * Ensure that the config name is valid.
+     * @param configName The name
+     * @throws IllegalArgumentException if the config name is not valid
+     */
+    public static void ensureValidConfigName(final String configName) {
+        if (!isValid(configName)) {
+            throw new IllegalArgumentException("Invalid configuration name: " 
+ configName);
+        }
+    }
+    
+    /**
+     * Returns all partial combinations like: a, a/b, a/b/c from config name 
a/b/c/d
+     * @param configName Config name
+     * @return All partial combinations
+     */
+    public static String[] getAllPartialConfigNameVariations(String 
configName) {
+        String[] configNameParts = 
StringUtils.splitPreserveAllTokens(configName, "/");
+        if (configNameParts.length < 2) {
+            return ArrayUtils.EMPTY_STRING_ARRAY;
+        }
+        String[] partialConfigNameVariations = new 
String[configNameParts.length - 1];
+        for (int i = 0; i < configNameParts.length - 1; i++) {
+            partialConfigNameVariations[i] = 
StringUtils.join(ArrayUtils.subarray(configNameParts, 0, i + 1), "/");
+        }
+        return partialConfigNameVariations;
+    }
+
+}

Propchange: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Thu Dec  1 18:05:41 2016
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author

Propchange: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/impl/metadata/ConfigurationMetadataProviderMultiplexerTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/impl/metadata/ConfigurationMetadataProviderMultiplexerTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/impl/metadata/ConfigurationMetadataProviderMultiplexerTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/impl/metadata/ConfigurationMetadataProviderMultiplexerTest.java
 Thu Dec  1 18:05:41 2016
@@ -29,11 +29,13 @@ import java.util.TreeSet;
 
 import org.apache.sling.caconfig.spi.ConfigurationMetadataProvider;
 import org.apache.sling.caconfig.spi.metadata.ConfigurationMetadata;
+import org.apache.sling.caconfig.spi.metadata.PropertyMetadata;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSortedSet;
 
 public class ConfigurationMetadataProviderMultiplexerTest {
@@ -89,7 +91,7 @@ public class ConfigurationMetadataProvid
     private void registerConfigurationMetadataProvider(String... names) {
         final Map<String,ConfigurationMetadata> metadata = new HashMap<>();
         for (String name : names) {
-            metadata.put(name, new ConfigurationMetadata(name));
+            metadata.put(name, new ConfigurationMetadata(name, 
ImmutableList.<PropertyMetadata<?>>of(), false));
         }
         context.registerService(ConfigurationMetadataProvider.class, new 
ConfigurationMetadataProvider() {
             @Override

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImplTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImplTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImplTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationDataImplTest.java
 Thu Dec  1 18:05:41 2016
@@ -40,7 +40,7 @@ import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
-import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -66,11 +66,11 @@ public class ConfigurationDataImplTest {
         configResource = context.create().resource("/conf/test",
                 "prop1", "value1",
                 "prop4", true);
-        configMetadata = new ConfigurationMetadata("testName");
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
+        configMetadata = new ConfigurationMetadata("testName", 
ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5)),
+                false);
     }
 
     @Test

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplNoDefaultTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplNoDefaultTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplNoDefaultTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplNoDefaultTest.java
 Thu Dec  1 18:05:41 2016
@@ -44,7 +44,6 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 
 /**
@@ -77,18 +76,18 @@ public class ConfigurationManagerImplNoD
         contextResourceNoConfig = 
context.create().resource("/content/testNoConfig",
                 PROPERTY_CONFIG_REF, "/conf/testNoConfig");
         
-        configMetadata = new ConfigurationMetadata(CONFIG_NAME);
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
+        configMetadata = new ConfigurationMetadata(CONFIG_NAME, 
ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5)),
+                false);
         
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_NAME)).thenReturn(configMetadata);
 
-        configMetadata = new ConfigurationMetadata(CONFIG_COL_NAME);
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
+        configMetadata = new ConfigurationMetadata(CONFIG_COL_NAME, 
ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5)),
+                true);
         
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_COL_NAME)).thenReturn(configMetadata);
     }
     

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/ConfigurationManagerImplTest.java
 Thu Dec  1 18:05:41 2016
@@ -123,61 +123,77 @@ public class ConfigurationManagerImplTes
                 PROPERTY_CONFIG_PROPERTY_INHERIT, true);
 
         // test fixture nested configuration
-        context.create().resource(getConfigPropsPath("/conf/test/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME),
+        context.create().resource(getConfigPropsPath("/conf/test/level2/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME),
                 "prop1", "value1",
                 "prop4", true);
-        
context.create().resource(getConfigPropsPath(getConfigPropsPath("/conf/global/" 
+ CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSub"),
+        
context.create().resource(getConfigPropsPath(getConfigPropsPath("/conf/test/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSub"),
                 "prop1", "propSubValue1",
                 "prop4", true);
-        
context.create().resource(getConfigPropsPath(getConfigPropsPath("/conf/test/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSubList/item1"),
+        
context.create().resource(getConfigPropsPath(getConfigPropsPath(getConfigPropsPath("/conf/test/"
 + CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSub") + 
"/propSubLevel2"),
+                "prop1", "propSubLevel2Value1",
+                "prop4", true);
+        
context.create().resource(getConfigPropsPath(getConfigPropsPath("/conf/test/level2/"
 + CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSubList/item1"),
                 "prop1", "propSubListValue1.1");
-        
context.create().resource(getConfigPropsPath(getConfigPropsPath("/conf/test/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSubList/item2"),
+        
context.create().resource(getConfigPropsPath(getConfigPropsPath("/conf/test/level2/"
 + CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSubList/item2"),
                 "prop1", "propSubListValue1.2");
-        
context.create().resource(getConfigPropsPath(getConfigPropsPath(getConfigPropsPath("/conf/global/"
 + CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSubList/item1") + 
"/propSub"),
+        
context.create().resource(getConfigPropsPath(getConfigPropsPath(getConfigPropsPath("/conf/test/"
 + CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME) + "/propSubList/item1") + 
"/propSub"),
                 "prop1", "propSubList1_proSubValue1",
                 "prop4", true);
         
         
         // config metadata singleton config
-        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(CONFIG_NAME);
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
+        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(CONFIG_NAME, ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5)),
+                false);
         
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_NAME)).thenReturn(configMetadata);
 
         // config metadata config collection
-        configMetadata = new ConfigurationMetadata(CONFIG_COL_NAME);
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
-        
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_COL_NAME)).thenReturn(configMetadata);
+        ConfigurationMetadata configColMetadata = new 
ConfigurationMetadata(CONFIG_COL_NAME, ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5)),
+                true);
+        
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_COL_NAME)).thenReturn(configColMetadata);
 
         // config metadata nested config
-        configMetadata = new ConfigurationMetadata(CONFIG_NESTED_NAME);
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "propSub", new PropertyMetadata<>("propSub", 
ConfigurationMetadata.class),
-                "propSubList", new PropertyMetadata<>("propSubList", 
ConfigurationMetadata[].class)));
-        ConfigurationMetadata propSubMetadata = new 
ConfigurationMetadata("propSub");
-        
propSubMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
-        ConfigurationMetadata propSubListMetadata = new 
ConfigurationMetadata("propSubList");
-        
propSubListMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", String.class),
-                "propSub", new PropertyMetadata<>("propSub", 
ConfigurationMetadata.class)));
-        
configMetadata.getPropertyMetadata().get("propSub").setConfigurationMetadata(propSubMetadata);
-        
configMetadata.getPropertyMetadata().get("propSubList").setConfigurationMetadata(propSubListMetadata);
-        
propSubListMetadata.getPropertyMetadata().get("propSub").setConfigurationMetadata(propSubMetadata);
+        /*
+         * testConfigNested
+         *  |
+         *  +- propSub
+         *  |   |
+         *  |   +- propSubLevel2
+         *  |
+         *  +- propSubList
+         *      |
+         *      +- <collection>
+         *          |
+         *          +- propSub
+         *              |
+         *              +- propSubLevel2
+         */
+        ConfigurationMetadata propSubLevel2Metadata = new 
ConfigurationMetadata("propSubLevel2", ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValueLevel2")),
+                false);
+        ConfigurationMetadata propSubMetadata = new 
ConfigurationMetadata("propSub", ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5),
+                new PropertyMetadata<>("propSubLevel2", 
ConfigurationMetadata.class).configurationMetadata(propSubLevel2Metadata)),
+                false);
+        ConfigurationMetadata propSubListMetadata = new 
ConfigurationMetadata("propSubList", ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValueSubList"),
+                new PropertyMetadata<>("propSub", 
ConfigurationMetadata.class).configurationMetadata(propSubMetadata)),
+                true);
+        ConfigurationMetadata configNestedMetadata = new 
ConfigurationMetadata(CONFIG_NESTED_NAME, ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("propSub", 
ConfigurationMetadata.class).configurationMetadata(propSubMetadata),
+                new PropertyMetadata<>("propSubList", 
ConfigurationMetadata[].class).configurationMetadata(propSubListMetadata)),
+                false);
+        
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_NESTED_NAME)).thenReturn(configNestedMetadata);
 
         
when(configurationMetadataProvider.getConfigurationNames()).thenReturn(ImmutableSortedSet.of(CONFIG_NAME,
 CONFIG_COL_NAME, CONFIG_NESTED_NAME));
-        
-        
-        
when(configurationMetadataProvider.getConfigurationMetadata(CONFIG_NESTED_NAME)).thenReturn(configMetadata);
-
     }
     
     protected String getConfigPropsPath(String path) {
@@ -541,9 +557,10 @@ public class ConfigurationManagerImplTes
 
     @Test
     public void testGetConfigurationNested() {
-        ConfigurationData configData = 
underTest.getConfiguration(contextResource, CONFIG_NESTED_NAME);
+        ConfigurationData configData = 
underTest.getConfiguration(contextResourceLevel2, CONFIG_NESTED_NAME);
         assertNotNull(configData);
 
+        // root level
         assertEquals(ImmutableSet.of("prop1", "propSub", "propSubList", 
"prop4"), configData.getPropertyNames());
         assertEquals("value1", configData.getValues().get("prop1", 
String.class));
         assertEquals("value1", configData.getEffectiveValues().get("prop1", 
String.class));
@@ -553,21 +570,45 @@ public class ConfigurationManagerImplTes
         assertEquals(ConfigurationMetadata.class, 
configData.getValueInfo("propSub").getPropertyMetadata().getType());
         assertEquals(ConfigurationMetadata[].class, 
configData.getValueInfo("propSubList").getPropertyMetadata().getType());
         
+        // propSub
         ConfigurationData subData = configData.getValues().get("propSub", 
ConfigurationData.class);
         ConfigurationData subDataEffective = 
configData.getEffectiveValues().get("propSub", ConfigurationData.class);
         assertNotNull(subData);
         assertNotNull(subDataEffective);
         
+        
assertTrue(ConfigurationData.class.isAssignableFrom(configData.getValueInfo("propSub").getValue().getClass()));
+        
assertTrue(ConfigurationData.class.isAssignableFrom(configData.getValueInfo("propSub").getEffectiveValue().getClass()));
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getValue().getClass()));
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getEffectiveValue().getClass()));
+
         assertNull(subData.getValues().get("prop1", String.class));
         assertEquals("propSubValue1", 
subData.getEffectiveValues().get("prop1", String.class));
         assertNull(subData.getValues().get("prop4", String.class));
         assertEquals(true, subData.getEffectiveValues().get("prop4", false));
         
+        // propSub/propSubLevel2
+        ConfigurationData subDataLevel2 = 
subData.getValues().get("propSubLevel2", ConfigurationData.class);
+        ConfigurationData subDataLevel2Effective = 
subData.getEffectiveValues().get("propSubLevel2", ConfigurationData.class);
+        assertNotNull(subDataLevel2);
+        assertNotNull(subDataLevel2Effective);
+        
+        
assertTrue(ConfigurationData.class.isAssignableFrom(subData.getValueInfo("propSubLevel2").getValue().getClass()));
+        
assertTrue(ConfigurationData.class.isAssignableFrom(subData.getValueInfo("propSubLevel2").getEffectiveValue().getClass()));
+
+        assertNull(subDataLevel2.getValues().get("prop1", String.class));
+        assertEquals("propSubLevel2Value1", 
subDataLevel2.getEffectiveValues().get("prop1", String.class));
+        assertNull(subDataLevel2.getValues().get("prop4", String.class));
+        assertEquals(true, subDataLevel2.getEffectiveValues().get("prop4", 
false));
+        
+        // propSubList
         ConfigurationData[] subListData = 
configData.getValues().get("propSubList", ConfigurationData[].class);
         ConfigurationData[] subListDataEffective = 
configData.getEffectiveValues().get("propSubList", ConfigurationData[].class);
         assertNotNull(subListData);
         assertNotNull(subListDataEffective);
         
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getValue().getClass()));
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getEffectiveValue().getClass()));
+        
         assertEquals(2, subListData.length);
         assertEquals("propSubListValue1.1", 
subListData[0].getValues().get("prop1", String.class));
         assertEquals("propSubListValue1.1", 
subListData[0].getEffectiveValues().get("prop1", String.class));
@@ -585,4 +626,122 @@ public class ConfigurationManagerImplTes
         assertEquals(true, 
subListDataItem1Sub.getEffectiveValues().get("prop4", false));
     }
 
+    @Test
+    public void testGetConfigurationNested_NoConfigResource() {
+        ConfigurationData configData = 
underTest.getConfiguration(contextResourceNoConfig, CONFIG_NESTED_NAME);
+        assertNotNull(configData);
+
+        assertEquals(ImmutableSet.of("prop1", "propSub", "propSubList"), 
configData.getPropertyNames());
+        assertNull(configData.getValues().get("prop1", String.class));
+        assertEquals("defValue", configData.getEffectiveValues().get("prop1", 
String.class));
+        
+        assertEquals(ConfigurationMetadata.class, 
configData.getValueInfo("propSub").getPropertyMetadata().getType());
+        assertEquals(ConfigurationMetadata[].class, 
configData.getValueInfo("propSubList").getPropertyMetadata().getType());
+        
+        ConfigurationData subData = configData.getValues().get("propSub", 
ConfigurationData.class);
+        ConfigurationData subDataEffective = 
configData.getEffectiveValues().get("propSub", ConfigurationData.class);
+        assertNotNull(subData);
+        assertNotNull(subDataEffective);
+        
+        
assertTrue(ConfigurationData.class.isAssignableFrom(configData.getValueInfo("propSub").getValue().getClass()));
+        
assertTrue(ConfigurationData.class.isAssignableFrom(configData.getValueInfo("propSub").getEffectiveValue().getClass()));
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getValue().getClass()));
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getEffectiveValue().getClass()));
+
+        assertNull(subData.getValues().get("prop1", String.class));
+        assertEquals("defValue", subData.getEffectiveValues().get("prop1", 
String.class));
+        
+        ConfigurationData[] subListData = 
configData.getValues().get("propSubList", ConfigurationData[].class);
+        ConfigurationData[] subListDataEffective = 
configData.getEffectiveValues().get("propSubList", ConfigurationData[].class);
+        assertNotNull(subListData);
+        assertNotNull(subListDataEffective);
+        
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getValue().getClass()));
+        
assertTrue(ConfigurationData[].class.isAssignableFrom(configData.getValueInfo("propSubList").getEffectiveValue().getClass()));
+        
+        assertEquals(0, subListData.length);
+    }
+
+    @Test
+    public void testGetConfigurationNames() {
+        assertEquals(ImmutableSortedSet.of(CONFIG_NAME, CONFIG_COL_NAME, 
CONFIG_NESTED_NAME), underTest.getConfigurationNames());
+    }
+
+    @Test
+    public void testGetConfigurationMetadata() {
+        ConfigurationMetadata configMetadata = 
underTest.getConfigurationMetadata(CONFIG_NAME);
+        assertNotNull(configMetadata);
+        assertEquals(CONFIG_NAME, configMetadata.getName());
+    }
+
+    @Test
+    public void testGetConfigurationMetadata_Nested() {
+        ConfigurationMetadata configMetadata = 
underTest.getConfigurationMetadata(CONFIG_NESTED_NAME);
+        assertNotNull(configMetadata);
+        assertEquals(CONFIG_NESTED_NAME, configMetadata.getName());
+    }
+    
+    @Test
+    public void testGetConfigurationMetadata_Nested_Sub() {
+        ConfigurationMetadata configMetadataSub = 
underTest.getConfigurationMetadata(getConfigPropsPath(CONFIG_NESTED_NAME) + 
"/propSub");
+        assertNotNull(configMetadataSub);
+        assertEquals("propSub", configMetadataSub.getName());
+    }
+    
+    @Test
+    public void testGetConfigurationMetadata_Nested_SubLevel2() {
+        ConfigurationMetadata configMetadataSubLevel2 = 
underTest.getConfigurationMetadata(getConfigPropsPath(getConfigPropsPath(CONFIG_NESTED_NAME)
+                + "/propSub") + "/propSubLevel2");
+        assertNotNull(configMetadataSubLevel2);
+        assertEquals("propSubLevel2", configMetadataSubLevel2.getName());
+    }
+    
+    @Test
+    public void testGetConfigurationMetadata_Nested_SubList() {
+        ConfigurationMetadata configMetadataSubList = 
underTest.getConfigurationMetadata(getConfigPropsPath(CONFIG_NESTED_NAME) + 
"/propSubList");
+        assertNotNull(configMetadataSubList);
+        assertEquals("propSubList", configMetadataSubList.getName());
+    }
+    
+    @Test
+    public void testGetConfigurationMetadata_Nested_SubList_Sub() throws 
Exception {
+        // delete resource already existing in test fixture to test with 
non-existing resource but existing collection item as parent
+        
context.resourceResolver().delete(context.resourceResolver().getResource(
+                
getConfigPropsPath(getConfigPropsPath(getConfigPropsPath("/conf/test/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME)
+                        + "/propSubList/item1") + "/propSub")));
+
+        ConfigurationMetadata subListDataItem1Sub = 
underTest.getConfigurationMetadata(getConfigPropsPath(getConfigPropsPath(CONFIG_NESTED_NAME)
+                + "/propSubList/item1") + "/propSub");
+        assertNotNull(subListDataItem1Sub);
+        assertEquals("propSub", subListDataItem1Sub.getName());
+    }
+    
+    @Test
+    public void testGetConfigurationMetadata_Nested_SubList_SubLevel2() throws 
Exception {
+        // delete resource already existing in test fixture to test with 
non-existing resource but existing collection item as parent
+        
context.resourceResolver().delete(context.resourceResolver().getResource(
+                
getConfigPropsPath(getConfigPropsPath(getConfigPropsPath("/conf/test/" + 
CONFIGS_PARENT_NAME + "/" + CONFIG_NESTED_NAME)
+                        + "/propSubList/item1") + "/propSub")));
+
+        ConfigurationMetadata subListDataItem1SubLevel2 = 
underTest.getConfigurationMetadata(getConfigPropsPath(getConfigPropsPath(getConfigPropsPath(CONFIG_NESTED_NAME)
+                + "/propSubList/item1") + "/propSub") + "/propSubLevel2");
+        assertNotNull(subListDataItem1SubLevel2);
+        assertEquals("propSubLevel2", subListDataItem1SubLevel2.getName());
+    }
+    
+    @Test
+    public void testNewCollectionItem_Nested_SubList() {
+        ConfigurationData configData = 
underTest.newCollectionItem(contextResource, 
getConfigPropsPath(CONFIG_NESTED_NAME) + "/propSubList");
+        assertEquals(getConfigPropsPath(CONFIG_NESTED_NAME) + "/propSubList", 
configData.getConfigName());
+        
+        assertNull(configData.getValues().get("prop1", String.class));
+        assertEquals("defValueSubList", 
configData.getEffectiveValues().get("prop1", String.class));
+    }
+
+    @Test
+    public void testGetPersistenceResourcePath() {
+        assertEquals(getConfigPropsPath("/a/b/c"), 
underTest.getPersistenceResourcePath("/a/b/c"));
+        assertEquals(getConfigPropsPath("a/b"), 
underTest.getPersistenceResourcePath("a/b"));
+    }
+        
 }

Modified: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/console/CAConfigInventoryPrinterTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/console/CAConfigInventoryPrinterTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/console/CAConfigInventoryPrinterTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/management/impl/console/CAConfigInventoryPrinterTest.java
 Thu Dec  1 18:05:41 2016
@@ -43,7 +43,6 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -70,11 +69,11 @@ public class CAConfigInventoryPrinterTes
         ConfigurationTestUtils.registerConfigurationResolver(context);
         underTest = context.registerInjectActivateService(new 
CAConfigInventoryPrinter());
     
-        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(SAMPLE_CONFIG_NAME);
-        
configMetadata.setPropertyMetadata(ImmutableMap.<String,PropertyMetadata<?>>of(
-                "prop1", new PropertyMetadata<>("prop1", "defValue"),
-                "prop2", new PropertyMetadata<>("prop2", String.class),
-                "prop3", new PropertyMetadata<>("prop3", 5)));
+        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata(SAMPLE_CONFIG_NAME, ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "defValue"),
+                new PropertyMetadata<>("prop2", String.class),
+                new PropertyMetadata<>("prop3", 5)),
+                false);
         
when(configurationMetadataProvider.getConfigurationMetadata(SAMPLE_CONFIG_NAME)).thenReturn(configMetadata);
         
when(configurationMetadataProvider.getConfigurationNames()).thenReturn(ImmutableSortedSet.of(SAMPLE_CONFIG_NAME));
         

Added: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java?rev=1772244&view=auto
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
 (added)
+++ 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
 Thu Dec  1 18:05:41 2016
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.caconfig.resource.impl.util;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ConfigNameUtilTest {
+
+    @Test
+    public void testIsValid() {
+        assertTrue(ConfigNameUtil.isValid("a"));
+        assertTrue(ConfigNameUtil.isValid("a/b"));
+        assertTrue(ConfigNameUtil.isValid("a/b/c"));
+        assertTrue(ConfigNameUtil.isValid("a/jcr:content/b/c"));
+
+        assertFalse(ConfigNameUtil.isValid(null));
+        assertFalse(ConfigNameUtil.isValid(""));
+        assertFalse(ConfigNameUtil.isValid("/a"));
+        assertFalse(ConfigNameUtil.isValid("/a/b/c"));
+        assertFalse(ConfigNameUtil.isValid("a/b/../c"));
+    }
+
+    @Test
+    public void testGetAllPartialConfigNameVariations() {
+        assertArrayEquals(new String[0], 
ConfigNameUtil.getAllPartialConfigNameVariations(""));
+        assertArrayEquals(new String[0], 
ConfigNameUtil.getAllPartialConfigNameVariations("a"));
+        assertArrayEquals(new String[] {"a"}, 
ConfigNameUtil.getAllPartialConfigNameVariations("a/b"));
+        assertArrayEquals(new String[] {"a","a/b"}, 
ConfigNameUtil.getAllPartialConfigNameVariations("a/b/c"));
+        assertArrayEquals(new String[] {"a","a/b","a/b/c"}, 
ConfigNameUtil.getAllPartialConfigNameVariations("a/b/c/d"));
+    }
+
+}

Propchange: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Thu Dec  1 18:05:41 2016
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author

Propchange: 
sling/trunk/contrib/extensions/contextaware-config/impl/src/test/java/org/apache/sling/caconfig/resource/impl/util/ConfigNameUtilTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/ConfigurationPersistenceStrategy.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/ConfigurationPersistenceStrategy.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/ConfigurationPersistenceStrategy.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/ConfigurationPersistenceStrategy.java
 Thu Dec  1 18:05:41 2016
@@ -43,7 +43,7 @@ public interface ConfigurationPersistenc
     /**
      * Allows the strategy to transform the given configuration resource path 
according to it's persistent strategies,
      * e.g. fetching the data from a child resource instead of the given 
resource. 
-     * @param resource Configuration resource path
+     * @param resource Configuration resource path or part of it (e.g. config 
name)
      * @return Transformed configuration resource path. If null is returned 
this strategy does not support the given configuration resource path.
      */
     @CheckForNull String getResourcePath(@Nonnull String resourcePath);

Modified: 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/AbstractMetadata.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/AbstractMetadata.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/AbstractMetadata.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/AbstractMetadata.java
 Thu Dec  1 18:05:41 2016
@@ -25,7 +25,7 @@ import javax.annotation.Nonnull;
 /**
  * Common properties for configuration and properties.
  */
-abstract class AbstractMetadata {
+abstract class AbstractMetadata<T> {
 
     private final String name;
     private String label;
@@ -55,9 +55,12 @@ abstract class AbstractMetadata {
 
     /**
      * @param label Label
+     * @return this;
      */
-    public void setLabel(String label) {
+    @SuppressWarnings("unchecked")
+    public T label(String label) {
         this.label = label;
+        return (T)this;
     }
 
     /**
@@ -69,9 +72,12 @@ abstract class AbstractMetadata {
 
     /**
      * @param description Description
+     * @return this;
      */
-    public void setDescription(String description) {
+    @SuppressWarnings("unchecked")
+    public T description(String description) {
         this.description = description;
+        return (T)this;
     }
 
     /**
@@ -83,9 +89,12 @@ abstract class AbstractMetadata {
     
     /**
      * @param properties Further properties for documentation and 
configuration of behavior in configuration editor.
+     * @return this;
      */
-    public void setProperties(Map<String,String> properties) {
+    @SuppressWarnings("unchecked")
+    public T properties(Map<String,String> properties) {
         this.properties = properties;
+        return (T)this;
     }
     
     @Override

Modified: 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadata.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadata.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadata.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadata.java
 Thu Dec  1 18:05:41 2016
@@ -18,6 +18,8 @@
  */
 package org.apache.sling.caconfig.spi.metadata;
 
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.Map;
 
 import javax.annotation.Nonnull;
@@ -28,16 +30,31 @@ import org.osgi.annotation.versioning.Pr
  * Defines a configuration.
  */
 @ProviderType
-public final class ConfigurationMetadata extends AbstractMetadata {
+public final class ConfigurationMetadata extends 
AbstractMetadata<ConfigurationMetadata> {
 
-    private boolean collection;
-    private Map<String,PropertyMetadata<?>> propertyMetadata;
+    private final Map<String,PropertyMetadata<?>> propertyMetadata;
+    private final boolean collection;
 
     /**
      * @param name Configuration name
      */
-    public ConfigurationMetadata(@Nonnull String name) {
+    public ConfigurationMetadata(@Nonnull String name,
+            Collection<PropertyMetadata<?>> propertyMetadata,
+            boolean collection) {
         super(name);
+        this.propertyMetadata = toMap(propertyMetadata);
+        this.collection = collection;
+    }
+    
+    private static Map<String,PropertyMetadata<?>> 
toMap(Collection<PropertyMetadata<?>> propertyMetadata) {
+        Map<String,PropertyMetadata<?>> map = new HashMap<>();
+        for (PropertyMetadata<?> item : propertyMetadata) {
+            if (map.containsKey(item.getName())) {
+                throw new IllegalArgumentException("Duplicate property name: " 
+ item.getName());
+            }
+            map.put(item.getName(), item);
+        }
+        return map;
     }
     
     /**
@@ -55,24 +72,10 @@ public final class ConfigurationMetadata
     }
 
     /**
-     * @param isList true if configuration is collection
-     */
-    public void setCollection(boolean value) {
-        this.collection = value;
-    }
-
-    /**
      * @return Configuration properties
      */
     public Map<String,PropertyMetadata<?>> getPropertyMetadata() {
         return this.propertyMetadata;
     }
 
-    /**
-     * @param propertyMetadata Configuration properties
-     */
-    public void setPropertyMetadata(Map<String,PropertyMetadata<?>> 
propertyMetadata) {
-        this.propertyMetadata = propertyMetadata;
-    }
-
 }

Modified: 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadata.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadata.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadata.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/spi/src/main/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadata.java
 Thu Dec  1 18:05:41 2016
@@ -33,7 +33,7 @@ import org.osgi.annotation.versioning.Pr
  * @param <T> Property value type
  */
 @ProviderType
-public final class PropertyMetadata<T> extends AbstractMetadata {
+public final class PropertyMetadata<T> extends 
AbstractMetadata<PropertyMetadata<T>> {
 
     // these are all types supported for fields of annotation classes (plus 
class which indicates nested configurations)
     private static final Class<?>[] SUPPORTED_TYPES_ARRAY = {
@@ -119,9 +119,11 @@ public final class PropertyMetadata<T> e
     
     /**
      * @param value Default value if parameter is not set for configuration
+     * @return this;
      */
-    public void setDefaultValue(T value) {
+    public PropertyMetadata<T> defaultValue(T value) {
         this.defaultValue = value;
+        return this;
     }
     
     /**
@@ -133,9 +135,20 @@ public final class PropertyMetadata<T> e
 
     /**
      * @param configurationMetadata Metadata for nested configuration
+     * @return this;
      */
-    public void setConfigurationMetadata(ConfigurationMetadata 
configurationMetadata) {
+    public PropertyMetadata<T> configurationMetadata(ConfigurationMetadata 
configurationMetadata) {
         this.configurationMetadata = configurationMetadata;
+        return this;
+    }
+    
+    /**
+     * @return true if this property describes a nested configuration.
+     *   In this case it is ensured configuration metadata is present, and the 
type is ConfigurationMetadata or ConfigurationMetadata[].
+     */
+    public boolean isNestedConfiguration() {
+        return configurationMetadata != null
+                && (this.type.equals(ConfigurationMetadata.class) || 
this.type.equals(ConfigurationMetadata[].class));
     }
 
     @Override

Modified: 
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadataTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadataTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadataTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/ConfigurationMetadataTest.java
 Thu Dec  1 18:05:41 2016
@@ -26,28 +26,51 @@ import java.util.Map;
 
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 public class ConfigurationMetadataTest {
 
     @Test
     public void testProps() {
-        ConfigurationMetadata underTest = new ConfigurationMetadata("name1");
+        ConfigurationMetadata underTest = new ConfigurationMetadata("name1", 
ImmutableList.<PropertyMetadata<?>>of(), false);
         assertEquals("name1", underTest.getName());
         assertTrue(underTest.isSingleton());
         assertFalse(underTest.isCollection());
         
-        underTest.setLabel("label1");
-        underTest.setDescription("desc1");
-        underTest.setCollection(true);
         Map<String,String> props = ImmutableMap.of("p1", "v1");
-        underTest.setProperties(props);
+        underTest.label("label1")
+            .description("desc1")
+            .properties(props);
         
         assertEquals("label1", underTest.getLabel());
         assertEquals("desc1", underTest.getDescription());
+        assertEquals(props, underTest.getProperties());
+    }
+
+    @Test
+    public void testCollectionProps() {
+        ConfigurationMetadata underTest = new ConfigurationMetadata("name1", 
ImmutableList.<PropertyMetadata<?>>of(), true);
+        assertEquals("name1", underTest.getName());
         assertFalse(underTest.isSingleton());
         assertTrue(underTest.isCollection());
-        assertEquals(props, underTest.getProperties());
+    }
+
+    @Test
+    public void testPropertyMap() {
+        ConfigurationMetadata underTest = new ConfigurationMetadata("name1", 
ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "devValue"),
+                new PropertyMetadata<>("prop2", 5)), false);
+        assertEquals(2, underTest.getPropertyMetadata().size());
+        assertTrue(underTest.getPropertyMetadata().containsKey("prop1"));
+        assertTrue(underTest.getPropertyMetadata().containsKey("prop2"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testDuplicateKey() {
+        new ConfigurationMetadata("name1", 
ImmutableList.<PropertyMetadata<?>>of(
+                new PropertyMetadata<>("prop1", "devValue"),
+                new PropertyMetadata<>("prop1", 5)), false);
     }
 
 }

Modified: 
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadataTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadataTest.java?rev=1772244&r1=1772243&r2=1772244&view=diff
==============================================================================
--- 
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadataTest.java
 (original)
+++ 
sling/trunk/contrib/extensions/contextaware-config/spi/src/test/java/org/apache/sling/caconfig/spi/metadata/PropertyMetadataTest.java
 Thu Dec  1 18:05:41 2016
@@ -25,6 +25,7 @@ import java.util.Map;
 
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 public class PropertyMetadataTest {
@@ -35,14 +36,13 @@ public class PropertyMetadataTest {
         assertEquals("name1", underTest.getName());
         assertEquals(String.class, underTest.getType());
         
-        underTest.setLabel("label1");
-        underTest.setDescription("desc1");
-        underTest.setDefaultValue("value1");
+        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata("test", ImmutableList.<PropertyMetadata<?>>of(), false);
         Map<String,String> props = ImmutableMap.of("p1", "v1");
-        underTest.setProperties(props);
-        
-        ConfigurationMetadata configMetadata = new 
ConfigurationMetadata("test");
-        underTest.setConfigurationMetadata(configMetadata);
+        underTest.label("label1")
+            .description("desc1")
+            .defaultValue("value1")
+            .properties(props)
+            .configurationMetadata(configMetadata);
         
         assertEquals("label1", underTest.getLabel());
         assertEquals("desc1", underTest.getDescription());


Reply via email to