Author: pderop
Date: Sun Nov  6 23:32:02 2016
New Revision: 1768405

URL: http://svn.apache.org/viewvc?rev=1768405&view=rev
Log:
FELIX-5355: Allow to use properties having dots with configuration proxies.

Modified:
    
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
    
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/ConfigurationDependencyBuilder.java
    
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
    
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java

Modified: 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java?rev=1768405&r1=1768404&r2=1768405&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
 (original)
+++ 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.annotation/src/org/apache/felix/dm/annotation/api/ConfigurationDependency.java
 Sun Nov  6 23:32:02 2016
@@ -59,10 +59,10 @@ import java.util.Map;
  * @OCD(description = "Declare here the Printer Configuration.")
  * public interface PrinterConfiguration {
  *     @AD(description = "Enter the printer ip address")
- *     String ipAddress();
+ *     String getAddress();
  *
  *     @AD(description = "Enter the printer address port number.")
- *     default int portNumber() { return 8080; }
+ *     default int getPort() { return 8080; }
  * }
  * </pre>
  * </blockquote>
@@ -78,8 +78,8 @@ import java.util.Map;
  * public class Printer {
  *     &#64;ConfigurationDependency // Will use the fqdn of the  
PrinterConfiguration interface as the pid.
  *     void updated(PrinterConfiguration cnf) {
- *         String ip = cnf.ipAddress();
- *         int port = cnf.portNumber();
+ *         String ip = cnf.getAddress();
+ *         int port = cnf.getPort();
  *         ...
  *     }
  * }
@@ -101,8 +101,18 @@ import java.util.Map;
  * "mangled" to the following form: <tt>[lower case letter] [any valid 
character]*</tt>. Method names starting with
  * <tt>get</tt> or <tt>is</tt> (JavaBean convention) are stripped from these 
prefixes. For example: given a dictionary
  * with the key <tt>"foo"</tt> can be accessed from a configuration-type using 
the following method names:
- * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.
- * </p>
+ * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.<p>
+ * If the property contains a dot (which is invalid in java method names), 
then dots (".") can be converted using the following conventions: 
+ * <ul>
+ * 
+ * <li> if the method name follows the javabean convention and/or kamel casing 
convention, then each capital letter is assumed to map to a "dot", 
+ * followed by the same letter in lower case. This means only lower case 
properties are 
+ * supported in this case. Example: getFooBar() or fooBar() will map to 
"foo.bar" property.
+ * 
+ * <li> else, if the method name follows the standard OSGi metatype 
specification, then dots  
+ * are encoded as "_"; and "_" is encoded as "__". (see OSGi r6 compendium, 
chapter 105.9.2).
+ * Example: "foo_BAR()" is mapped to "foo.BAR" property; "foo__BAR_zoo()" is 
mapped to "foo_BAR.zoo" property.
+ * </ul>
  * <p>
  * The return values supported are: primitive types (or their object 
wrappers), strings, enums, arrays of
  * primitives/strings, {@link Collection} types, {@link Map} types, {@link 
Class}es and interfaces. When an interface is

Modified: 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/ConfigurationDependencyBuilder.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/ConfigurationDependencyBuilder.java?rev=1768405&r1=1768404&r2=1768405&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/ConfigurationDependencyBuilder.java
 (original)
+++ 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager.lambda/src/org/apache/felix/dm/lambda/ConfigurationDependencyBuilder.java
 Sun Nov  6 23:32:02 2016
@@ -62,8 +62,18 @@ import org.osgi.annotation.versioning.Pr
  * "mangled" to the following form: <tt>[lower case letter] [any valid 
character]*</tt>. Method names starting with
  * <tt>get</tt> or <tt>is</tt> (JavaBean convention) are stripped from these 
prefixes. For example: given a dictionary
  * with the key <tt>"foo"</tt> can be accessed from a configuration-type using 
the following method names:
- * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.
- * </p>
+ * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.<p>
+ * If the property contains a dot (which is invalid in java method names), 
then dots (".") can be converted using the following conventions: 
+ * <ul>
+ * 
+ * <li> if the method name follows the javabean convention and/or kamel casing 
convention, then each capital letter is assumed to map to a "dot", 
+ * followed by the same letter in lower case. This means only lower case 
properties are 
+ * supported in this case. Example: getFooBar() or fooBar() will map to 
"foo.bar" property.
+ * 
+ * <li> else, if the method name follows the standard OSGi metatype 
specification, then dots  
+ * are encoded as "_"; and "_" is encoded as "__". (see OSGi r6 compendium, 
chapter 105.9.2).
+ * Example: "foo_BAR()" is mapped to "foo.BAR" property; "foo__BAR_zoo()" is 
mapped to "foo_BAR.zoo" property.
+ * </ul>
  * <p>
  * The return values supported are: primitive types (or their object 
wrappers), strings, enums, arrays of
  * primitives/strings, {@link Collection} types, {@link Map} types, {@link 
Class}es and interfaces. When an interface is

Modified: 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java?rev=1768405&r1=1768404&r2=1768405&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
 (original)
+++ 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/ConfigurationDependency.java
 Sun Nov  6 23:32:02 2016
@@ -69,8 +69,19 @@ import aQute.bnd.annotation.ProviderType
  * "mangled" to the following form: <tt>[lower case letter] [any valid 
character]*</tt>. Method names starting with
  * <tt>get</tt> or <tt>is</tt> (JavaBean convention) are stripped from these 
prefixes. For example: given a dictionary
  * with the key <tt>"foo"</tt> can be accessed from a configuration-type using 
the following method names:
- * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.
- * </p>
+ * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.<p> 
+ * 
+ * If the property contains a dot (which is invalid in java method names), 
then dots (".") can be converted using the following conventions: 
+ * <ul>
+ * 
+ * <li> if the method name follows the javabean convention and/or kamel casing 
convention, then each capital letter is assumed to map to a "dot", 
+ * followed by the same letter in lower case. This means only lower case 
properties are 
+ * supported in this case. Example: getFooBar() or fooBar() will map to 
"foo.bar" property.
+ * 
+ * <li> else, if the method name follows the standard OSGi metatype 
specification, then dots  
+ * are encoded as "_"; and "_" is encoded as "__". (see OSGi r6 compendium, 
chapter 105.9.2).
+ * Example: "foo_BAR()" is mapped to "foo.BAR" property; "foo__BAR_zoo()" is 
mapped to "foo_BAR.zoo" property.
+ * </ul>
  * <p>
  * The return values supported are: primitive types (or their object 
wrappers), strings, enums, arrays of
  * primitives/strings, {@link Collection} types, {@link Map} types, {@link 
Class}es and interfaces. When an interface is

Modified: 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java?rev=1768405&r1=1768404&r2=1768405&view=diff
==============================================================================
--- 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java
 (original)
+++ 
felix/trunk/dependencymanager/org.apache.felix.dependencymanager/src/org/apache/felix/dm/impl/Configurable.java
 Sun Nov  6 23:32:02 2016
@@ -59,6 +59,15 @@ import java.util.TreeSet;
  * <tt>get</tt> or <tt>is</tt> (JavaBean convention) are stripped from these 
prefixes. For example: given a dictionary
  * with the key <tt>"foo"</tt> can be accessed from a configuration-type using 
the following method names:
  * <tt>foo()</tt>, <tt>getFoo()</tt> and <tt>isFoo()</tt>.
+ *  
+ * If the property name contains some dots, the the following conventions are 
used: 
+ * <ul> 
+ * <li>camel casing: if a property contains multiple words separated by dots, 
then you can indicate words boundaries using medial capitalization.
+ * For example, the property "foo.bar" could be accessed with a method name 
like "fooBar()" or "getFooBar()".  
+ * <li> use underscore to wrap dots: underscore ("_") found in method names 
are converted to ".", unless they are followed by another underscore.
+ * (in this case, the double "__" is then converted to single underscore ("_").
+ * For Example: foo_bar() will be mapped to "foo.bar" property, and foo__bar() 
will be mapped to "foo_bar" property.
+ * </ul>
  * </p>
  * <p>
  * The return values supported are: primitive types (or their object 
wrappers), strings, enums, arrays of
@@ -437,21 +446,119 @@ public final class Configurable {
 
             return result;
         }
-
-        private String getPropertyName(String id) {
-            StringBuilder sb = new StringBuilder(id);
-            if (id.startsWith("get")) {
+        
+        private String getPropertyName(String methodName) {
+            // First, derive the property name from the method name, using 
simple javabean convention.
+            // i.e: fooBar() or getFooBar() will map to "fooBar" property.
+            
+            String javaBeanMethodName = 
derivePropertyNameUsingJavaBeanConvention(methodName);
+            if (hasValueFor(javaBeanMethodName)) {
+                // there is a value in the actual configuration for the 
derived property name.
+                return javaBeanMethodName;
+            }
+            
+            // Derive the property name from the method name, using javabeans 
and/or camel casing convention,
+            // where each capital letter is assumed to map a "dot".
+            // i.e: fooBar() or getFooBar() will map to "foo.bar" property.
+
+            String camelCasePropertyName = 
derivePropertyNameUsingCamelCaseConvention(javaBeanMethodName);
+            if (hasValueFor(camelCasePropertyName)) {
+                // there is a value in the actual configuration for the 
derived property name.
+                return camelCasePropertyName;
+            }
+            
+            // Derive the property name from the method name, using OSGi 
metatype convention,
+            // where a "_" is mapped to a dot, except if the understcore is 
followed by another undescore
+            // (in this case, the double "__" is replaced by "_").
+            // i.e: foo_bar() will map to "foo.bar" property and foo__bar() 
will map to "foo_bar" property.
+            
+            String metaTypePropertyName = 
derivePropertyNameUsingMetaTypeConvention(methodName);
+            if (hasValueFor(metaTypePropertyName)) {
+                // there is a value in the actual configuration for the 
derived property name.
+                return metaTypePropertyName;
+            }
+            
+            // No value could be found, return by default a property name 
derived from javabean convention.
+            return javaBeanMethodName;
+        }
+        
+        private String derivePropertyNameUsingJavaBeanConvention(String 
methodName) {
+            StringBuilder sb = new StringBuilder(methodName);
+            
+            if (methodName.startsWith("get")) {
                 sb.delete(0, 3);
-            }
-            else if (id.startsWith("is")) {
+            } else if (methodName.startsWith("is")) {
                 sb.delete(0, 2);
             }
+            
             char c = sb.charAt(0);
             if (Character.isUpperCase(c)) {
                 sb.setCharAt(0, Character.toLowerCase(c));
             }
+            
+            return (sb.toString());
+        }
+
+        private String derivePropertyNameUsingCamelCaseConvention(String 
methodName) {
+            StringBuilder sb = new StringBuilder(methodName);
+            for (int i = 0; i < sb.length(); i++) {
+                char c = sb.charAt(i);
+                if (Character.isUpperCase(c)) {
+                    // camel casing: replace fooBar -> foo.bar
+                    sb.setCharAt(i, Character.toLowerCase(c));
+                    sb.insert(i, ".");
+                }
+            }
+            return sb.toString();            
+        }
+        
+        // see metatype spec, chapter 105.9.2 in osgi r6 cmpn.
+        private String derivePropertyNameUsingMetaTypeConvention(String 
methodName) {
+            StringBuilder sb = new StringBuilder(methodName);
+            // replace "__" by "_" or "_" by ".": foo_bar -> foo.bar; 
foo__BAR_zoo -> foo_BAR.zoo
+            for (int i = 0; i < sb.length(); i ++) {
+                if (sb.charAt(i) == '_') {
+                    if (i < (sb.length() - 1) && sb.charAt(i+1) == '_') {
+                        // replace foo__bar -> foo_bar
+                        sb.replace(i, i+2, "_");
+                    } else {
+                        // replace foo_bar -> foo.bar
+                        sb.replace(i, i+1, ".");
+                    }
+                } else if (sb.charAt(i) == '$') {
+                    if (i < (sb.length() - 1) && sb.charAt(i+1) == '$') {
+                        // replace foo__bar -> foo_bar
+                        sb.replace(i, i+2, "$");
+                    } else {
+                        // remove single dollar character.
+                        sb.delete(i, i+1);
+                    }
+                }
+            }
             return sb.toString();
         }
+        
+        /**
+         * Checks if a property name has a given value. This method takes care 
about special array values (arr.0, arr.1,...) 
+         * and about map values (map.key1, map.key2, ...).
+         * 
+         * @param property name
+         * @return true if the given property has a value in the actual 
configuration, false if not.
+         */
+        private boolean hasValueFor(String property)
+        {
+            if (m_config.containsKey(property)) {
+                return true;
+            }
+            String needle = property.concat(".");       
+            for (Map.Entry<?, ?> entry : m_config.entrySet()) {
+                String key = entry.getKey().toString();
+                if (key.startsWith(needle)) {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 
     private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;


Reply via email to