Fix the issue Felix-629
Allows an instance configuration to contain complex types.
Now an instance configuration can contain properties like :
<instance component="org.apache.felix.ipojo.test.scenarios.component.ComplexConfiguration" name="complex">
<property name="array" type="array"> <!-- creates an array -->
<property value="a"/>
<property value="b"/>
</property>
<property name="list" type="list"> <!-- creates a list -->
<property value="a"/>
<property value="b"/>
</property>
<property name="dict" type="dictionary"> <!-- creates a dictionary (default case) -->
<property name="a" value="a"/>
<property name="b" value="b"/>
</property>
<property name="map" type="map"> <!-- creates a map -->
<property name="a" value="a"/>
<property name="b" value="b"/>
</property>
<property name="complex-array" type="array"> <!-- creates an array containing lists -->
<property type="list">
<property value="a"/>
<property value="b"/>
</property>
<property type="list">
<property value="c"/>
<property value="d"/>
</property>
</property>
<property name="complex-list" type="list"> <!-- creates a list containing lists -->
<property type="list">
<property value="a"/>
<property value="b"/>
</property>
<property type="list">
<property value="c"/>
<property value="d"/>
</property>
</property>
<property name="complex-map" type="map"> <!-- creates a map containing lists -->
<property name="a" type="list">
<property value="a"/>
<property value="b"/>
</property>
<property name="b" type="list">
<property value="c"/>
<property value="d"/>
</property>
</property>
</instance>
As illustrated, any complex type can contain any complex type (and obviously itself). When no sub-property is declared, an empty structure is returned.
The patch contains also code allowing to use [...] to describe an array instead of {...}. So it is possible to write something like : <property name="foo" type="String[]" value="[a, b, c]"/>. This is equivalent to the existing syntax : <property name="foo" type="String[]" value="{a, b, c}"/>
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@675569 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
index e98fff2..81633de 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ManifestMetadataParser.java
@@ -20,7 +20,9 @@
import java.util.ArrayList;
import java.util.Dictionary;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Properties;
import org.apache.felix.ipojo.metadata.Attribute;
@@ -111,27 +113,192 @@
String name = prop.getAttribute("name");
String value = prop.getAttribute("value");
if (name == null) {
- throw new ParseException("A property does not have the 'name' attribute");
+ throw new ParseException("A property does not have the 'name' attribute: " + prop);
}
- //case : the property element has a 'value' attribute
+ //case : the property element has no 'value' attribute
if (value == null) {
// Recursive case
- // Check if there is 'property' element
- Element[] subProps = prop.getElements("property");
- if (subProps != null) {
- Dictionary dict2 = new Properties();
- for (int i = 0; i < subProps.length; i++) {
- parseProperty(subProps[i], dict2);
- dict.put(name, dict2);
+ // Get the type of the structure to create
+ String type = prop.getAttribute("type");
+ if (type == null || type.equalsIgnoreCase("dictionary")) {
+ dict.put(name, parseDictionary(prop));
+ } else if (type.equalsIgnoreCase("map")) {
+ dict.put(name, parseMap(prop));
+ } else if (type.equalsIgnoreCase("list")) {
+ dict.put(name, parseList(prop));
+ } else if (type.equalsIgnoreCase("array")) {
+ List list = parseList(prop);
+ boolean isString = true;
+ for (int i = 0; isString && i < list.size(); i++) {
+ isString = list.get(i) instanceof String;
}
- } else {
- // If the no sub-properties, inject an empty dictionary.
- dict.put(name, new Properties());
+ Object[] obj = null;
+ if (isString) {
+ obj = new String[list.size()];
+ } else {
+ obj = new Object[list.size()];
+ }
+ dict.put(name, list.toArray(obj)); // Transform the list to array
}
} else {
dict.put(prop.getAttribute("name"), prop.getAttribute("value"));
}
}
+
+ private Dictionary parseDictionary(Element prop) throws ParseException {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Dictionary dict2 = new Properties();
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], dict2);
+ }
+ return dict2;
+ } else {
+ // If the no sub-properties, inject an empty dictionary.
+ return new Properties();
+ }
+ }
+
+ private Map parseMap(Element prop) throws ParseException {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Map map = new HashMap(); // Create an hashmap to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], map);
+ }
+ return map;
+ } else { // if not inject an empty map
+ return new HashMap(0);
+ }
+ }
+
+ private List parseList(Element prop) throws ParseException {
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ List list = new ArrayList(subProps.length); // Create a list to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseAnonymousProperty(subProps[i], list); // Anonymous properties.
+ }
+ return list;
+ } else {
+ // If no sub-properties, inject an empty list.
+ return new ArrayList(0);
+ }
+ }
+
+ /**
+ * Parse a property.
+ * @param prop : the current element to parse
+ * @param map : the map to populate
+ * @throws ParseException : occurs if the property cannot be parsed correctly
+ */
+ private void parseProperty(Element prop, Map map) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name == null) {
+ throw new ParseException("A property does not have the 'name' attribute");
+ }
+ //case : the property element has no 'value' attribute
+ if (value == null) {
+ // Recursive case
+ // Get the type of the structure to create
+ String type = prop.getAttribute("type");
+ if (type == null || type.equalsIgnoreCase("dictionary")) {
+ map.put(name, parseDictionary(prop));
+ } else if (type.equalsIgnoreCase("map")) {
+ map.put(name, parseMap(prop));
+ } else if (type.equalsIgnoreCase("list")) {
+ map.put(name, parseList(prop));
+ } else if (type.equalsIgnoreCase("array")) {
+ List list = parseList(prop);
+ boolean isString = true;
+ for (int i = 0; isString && i < list.size(); i++) {
+ isString = list.get(i) instanceof String;
+ }
+ Object[] obj = null;
+ if (isString) {
+ obj = new String[list.size()];
+ } else {
+ obj = new Object[list.size()];
+ }
+ map.put(name, list.toArray(obj)); // Transform the list to array
+ }
+ } else {
+ map.put(prop.getAttribute("name"), prop.getAttribute("value"));
+ }
+ }
+
+ private void parseAnonymousProperty(Element prop, List list) throws ParseException {
+ // Check that the property has a name
+ String name = prop.getAttribute("name");
+ String value = prop.getAttribute("value");
+ if (name != null) {
+ throw new ParseException("Anonymous property expected in a list or in an array");
+ }
+ //case : the property element has no 'value' attribute
+ if (value == null) {
+ // Recursive case
+
+ // Get the type of the structure to create
+ String type = prop.getAttribute("type");
+ if (type == null || type.equalsIgnoreCase("dictionary")) {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Dictionary dict2 = new Properties();
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], dict2);
+ }
+ list.add(dict2);
+ } else {
+ // If the no sub-properties, inject an empty dictionary.
+ list.add(new Properties());
+ }
+ } else if (type.equalsIgnoreCase("map")) {
+ // Check if there is 'property' elements
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ Map map2 = new HashMap(); // Create an hashmap to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseProperty(subProps[i], map2);
+ }
+ list.add(map2);
+ } else { // if not inject an empty map
+ list.add(new HashMap(0));
+ }
+ } else if (type.equalsIgnoreCase("list")) {
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ List list2 = new ArrayList(subProps.length); // Create a list to store elements.
+ for (int i = 0; i < subProps.length; i++) {
+ parseAnonymousProperty(subProps[i], list2); // Anonymous properties.
+ }
+ list.add(list2);
+ } else {
+ // If no sub-properties, inject an empty list.
+ list.add(new ArrayList(0));
+ }
+ } else if (type.equalsIgnoreCase("array")) {
+ // Check sub-props.
+ Element[] subProps = prop.getElements("property");
+ if (subProps != null) {
+ List list2 = new ArrayList(subProps.length); // Use list as pivot type
+ for (int i = 0; i < subProps.length; i++) {
+ parseAnonymousProperty(subProps[i], list2);
+ }
+ list.add(list.toArray(new Object[list.size()])); // Transform the list to array
+ } else {
+ list.add(new Element[0]); // Insert an empty Element array.
+ }
+ }
+ } else {
+ list.add(prop.getAttribute("value"));
+ }
+
+ }
/**
* Add an element to the list.
diff --git a/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java b/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
index e8d5bba..2405edc 100644
--- a/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
+++ b/ipojo/core/src/main/java/org/apache/felix/ipojo/parser/ParseUtils.java
@@ -40,8 +40,9 @@
return new String[0];
}
- // Remove { and }
- if (str.charAt(0) == '{' && str.charAt(str.length() - 1) == '}') {
+ // Remove { and } or [ and ]
+ if ((str.charAt(0) == '{' && str.charAt(str.length() - 1) == '}') ||
+ (str.charAt(0) == '[' && str.charAt(str.length() - 1) == ']') ) {
String internal = (str.substring(1, str.length() - 1)).trim();
// Check empty array
if (internal.length() == 0) {