Author: pete.muir(a)jboss.org
Date: 2008-10-04 11:23:25 -0400 (Sat, 04 Oct 2008)
New Revision: 9191
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Xml.xml
trunk/src/main/org/jboss/seam/Component.java
trunk/src/main/org/jboss/seam/components-2.1.xsd
trunk/src/main/org/jboss/seam/init/Initialization.java
trunk/src/main/org/jboss/seam/util/Conversions.java
Log:
JBSEAM-3460
Modified: trunk/doc/Seam_Reference_Guide/en-US/Xml.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Xml.xml 2008-10-04 14:46:32 UTC (rev 9190)
+++ trunk/doc/Seam_Reference_Guide/en-US/Xml.xml 2008-10-04 15:23:25 UTC (rev 9191)
@@ -333,6 +333,24 @@
</component>]]></programlisting>
<para>
+ When configuring multi-valued properties, by default, Seam will preserve
the order in which you place the attributes
+ in <literal>components.xml</literal> (unless you use a
<literal>SortedSet</literal>/<literal>SortedMap</literal>
+ then Seam will use
<literal>TreeMap</literal>/<literal>TreeSet</literal>). If the
property has a concrete type (for
+ example <literal>LinkedList</literal> Seam will use that
type.
+ </para>
+ <para>
+ You can also override the type by specifying a fully qualified class
name:
+ </para>
+
+ <programlisting role="XML"><![CDATA[<component
name="issueEditor">
+ <property name="issueStatusOptions"
type="java.util.LinkedHashMap">
+ <key>open</key> <value>open issue</value>
+ <key>resolved</key> <value>issue resolved by
developer</value>
+ <key>closed</key> <value>resolution accepted by
user</value>
+ </property>
+</component>]]></programlisting>
+
+ <para>
Finally, you may wire together components using a value-binding
expression. Note that this is quite
different to injection using <literal>@In</literal>, since it
happens at component instantiation time
instead of invocation time. It is therefore much more similar to the
dependency injection facilities
Modified: trunk/src/main/org/jboss/seam/Component.java
===================================================================
--- trunk/src/main/org/jboss/seam/Component.java 2008-10-04 14:46:32 UTC (rev 9190)
+++ trunk/src/main/org/jboss/seam/Component.java 2008-10-04 15:23:25 UTC (rev 9191)
@@ -41,9 +41,15 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
@@ -2450,7 +2456,7 @@
}
else if (converter!=null && value instanceof String[])
{
- return converter.toObject( new Conversions.MultiPropertyValue( (String[])
value ), parameterType );
+ return converter.toObject( new Conversions.MultiPropertyValue( (String[])
value, null), parameterType );
}
else
{
@@ -2480,12 +2486,21 @@
{
private InitialValue[] initialValues;
private Class elementType;
-
+ private Class collectionClass;
+
public SetInitialValue(PropertyValue propertyValue, Class collectionClass, Type
collectionType)
{
String[] expressions = propertyValue.getMultiValues();
initialValues = new InitialValue[expressions.length];
elementType = Reflections.getCollectionElementType(collectionType);
+ if (propertyValue.getType() != null)
+ {
+ this.collectionClass = propertyValue.getType();
+ }
+ else
+ {
+ this.collectionClass = collectionClass;
+ }
for ( int i=0; i<expressions.length; i++ )
{
PropertyValue elementValue = new Conversions.FlatPropertyValue(
expressions[i] );
@@ -2495,10 +2510,43 @@
public Object getValue(Class type)
{
- Set set = new HashSet(initialValues.length);
- for (InitialValue iv: initialValues) {
- set.add( iv.getValue(elementType) );
+ Set set;
+ //if no configuration has been specified then we first see if
+ //the property is an abstract type, if so we create it using newInstance
+ if (Modifier.isAbstract(collectionClass.getModifiers()) ||
Modifier.isInterface(collectionClass.getModifiers()))
+ {
+ if(collectionClass == SortedSet.class)
+ {
+ set = new TreeSet();
+ }
+ else
+ {
+ set = new LinkedHashSet(initialValues.length);
+ }
}
+ else
+ {
+ try
+ {
+ set = (Set) collectionClass.newInstance();
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate a set of type
" + collectionClass + "; try specifying type type in components.xml");
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException("Cannot cast " +
collectionClass + " to java.util.Set");
+ }
+ catch (java.lang.InstantiationException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate a set of type
" + collectionClass + "; try specifying type type in components.xml");
+ }
+ }
+ for (InitialValue iv: initialValues)
+ {
+ set.add( iv.getValue(elementType) );
+ }
return set;
}
@@ -2514,6 +2562,7 @@
private InitialValue[] initialValues;
private Class elementType;
private boolean isArray;
+ private Class collectionClass;
public ListInitialValue(PropertyValue propertyValue, Class collectionClass, Type
collectionType)
{
@@ -2523,6 +2572,14 @@
elementType = isArray ?
collectionClass.getComponentType() :
Reflections.getCollectionElementType(collectionType);
+ if (propertyValue.getType() != null)
+ {
+ this.collectionClass = propertyValue.getType();
+ }
+ else
+ {
+ this.collectionClass = collectionClass;
+ }
for ( int i=0; i<expressions.length; i++ )
{
PropertyValue elementValue = new Conversions.FlatPropertyValue(
expressions[i] );
@@ -2543,7 +2600,32 @@
}
else
{
- List list = new ArrayList(initialValues.length);
+ List list;
+ //if no configuration has been specified then we first see if
+ //the property is an abstract type, if so we create it using newInstance
+ if (Modifier.isAbstract(collectionClass.getModifiers()) ||
Modifier.isInterface(collectionClass.getModifiers()))
+ {
+ list = new ArrayList(initialValues.length);
+ }
+ else
+ {
+ try
+ {
+ list = (List) collectionClass.newInstance();
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate a list of
type " + collectionClass + "; try specifying type type in
components.xml");
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException("Cannot cast " +
collectionClass + " to java.util.List");
+ }
+ catch (java.lang.InstantiationException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate a list of
type " + collectionClass + "; try specifying type type in
components.xml");
+ }
+ }
for (InitialValue iv: initialValues)
{
list.add( iv.getValue(elementType) );
@@ -2565,13 +2647,22 @@
private Map<InitialValue, InitialValue> initialValues;
private Class elementType;
private Class keyType;
-
+ private Class collectionClass;
+
public MapInitialValue(PropertyValue propertyValue, Class collectionClass, Type
collectionType)
{
Map<String, String> expressions = propertyValue.getKeyedValues();
- initialValues = new HashMap<InitialValue,
InitialValue>(expressions.size());
+ initialValues = new LinkedHashMap<InitialValue,
InitialValue>(expressions.size());
elementType = Reflections.getCollectionElementType(collectionType);
keyType = Reflections.getMapKeyType(collectionType);
+ if (propertyValue.getType() != null )
+ {
+ this.collectionClass = propertyValue.getType();
+ }
+ else
+ {
+ this.collectionClass = collectionClass;
+ }
for ( Map.Entry<String, String> me: expressions.entrySet() )
{
PropertyValue keyValue = new Conversions.FlatPropertyValue( me.getKey() );
@@ -2582,7 +2673,38 @@
public Object getValue(Class type)
{
- Map result = new HashMap(initialValues.size());
+ Map result;
+ if (Modifier.isAbstract(collectionClass.getModifiers()) ||
Modifier.isInterface(collectionClass.getModifiers()))
+ {
+ if (collectionClass == SortedMap.class)
+ {
+ result = new TreeMap();
+ }
+ else
+ {
+ result = new LinkedHashMap(initialValues.size());
+ }
+ }
+ else
+ {
+ try
+ {
+ result = (Map) collectionClass.newInstance();
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate a map of type
" + collectionClass + "; try specifying type type in components.xml");
+ }
+ catch (ClassCastException e)
+ {
+ throw new IllegalArgumentException("Cannot cast " +
collectionClass + " to java.util.Map");
+ }
+ catch (java.lang.InstantiationException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate a map of type
" + collectionClass + "; try specifying type type in components.xml");
+ }
+ }
+
for ( Map.Entry<InitialValue, InitialValue> me : initialValues.entrySet()
)
{
result.put( me.getKey().getValue(keyType),
me.getValue().getValue(elementType) );
Modified: trunk/src/main/org/jboss/seam/components-2.1.xsd
===================================================================
--- trunk/src/main/org/jboss/seam/components-2.1.xsd 2008-10-04 14:46:32 UTC (rev 9190)
+++ trunk/src/main/org/jboss/seam/components-2.1.xsd 2008-10-04 15:23:25 UTC (rev 9191)
@@ -219,6 +219,11 @@
<xs:documentation>The property name</xs:documentation>
</xs:annotation>
</xs:attribute>
+ <xs:attribute name="type" type="components:string" >
+ <xs:annotation>
+ <xs:documentation>Concrete type to use if the property is
multi-valued</xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
</xs:attributeGroup>
@@ -344,7 +349,16 @@
<xs:enumeration value="APPLICATION"/>
</xs:restriction>
</xs:simpleType>
-
+ <xs:simpleType name="implementationType">
+ <xs:restriction base="xs:token">
+ <xs:enumeration value="hash"/>
+ <xs:enumeration value="linked"/>
+ <xs:enumeration value="tree"/>
+ <xs:enumeration value="HASH"/>
+ <xs:enumeration value="LINKED"/>
+ <xs:enumeration value="TREE"/>
+ </xs:restriction>
+ </xs:simpleType>
<xs:simpleType name="precedenceType">
<xs:restriction base="xs:int">
<xs:enumeration value="0"/>
Modified: trunk/src/main/org/jboss/seam/init/Initialization.java
===================================================================
--- trunk/src/main/org/jboss/seam/init/Initialization.java 2008-10-04 14:46:32 UTC (rev
9190)
+++ trunk/src/main/org/jboss/seam/init/Initialization.java 2008-10-04 15:23:25 UTC (rev
9191)
@@ -15,6 +15,7 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -594,6 +595,21 @@
private Conversions.PropertyValue getPropertyValue(Element prop, String propName,
Properties replacements)
{
+ String typeName = prop.attributeValue("type");
+ Class type = null;
+ try
+ {
+ if(typeName != null )
+ {
+ type = Class.forName(typeName);
+ }
+ }
+ catch(ClassNotFoundException e)
+ {
+ throw new RuntimeException("Cannot find class " + typeName + "
when setting up property " + propName);
+ }
+
+
List<Element> keyElements = prop.elements("key");
List<Element> valueElements = prop.elements("value");
@@ -611,7 +627,7 @@
{
values[i] = trimmedText(valueElements.get(i), propName, replacements);
}
- return new Conversions.MultiPropertyValue(values);
+ return new Conversions.MultiPropertyValue(values, type);
}
else
{
@@ -621,14 +637,14 @@
throw new IllegalArgumentException("value elements must match key
elements: "
+ propName);
}
- Map<String, String> keyedValues = new HashMap<String, String>();
+ Map<String, String> keyedValues = new LinkedHashMap<String,
String>();
for (int i = 0; i < keyElements.size(); i++)
{
String key = trimmedText(keyElements.get(i), propName, replacements);
String value = trimmedText(valueElements.get(i), propName, replacements);
keyedValues.put(key, value);
}
- return new Conversions.AssociativePropertyValue(keyedValues);
+ return new Conversions.AssociativePropertyValue(keyedValues, type);
}
}
Modified: trunk/src/main/org/jboss/seam/util/Conversions.java
===================================================================
--- trunk/src/main/org/jboss/seam/util/Conversions.java 2008-10-04 14:46:32 UTC (rev
9190)
+++ trunk/src/main/org/jboss/seam/util/Conversions.java 2008-10-04 15:23:25 UTC (rev
9191)
@@ -277,6 +277,7 @@
boolean isExpression();
boolean isMultiValued();
boolean isAssociativeValued();
+ Class getType();
}
public static class FlatPropertyValue implements PropertyValue
@@ -342,16 +343,24 @@
return string;
}
+ public Class getType()
+ {
+ return null;
+ }
+
}
public static class MultiPropertyValue implements PropertyValue
{
private String[] strings;
- public MultiPropertyValue(String[] strings)
+ private Class type;
+
+ public MultiPropertyValue(String[] strings, Class type)
{
if (strings==null) throw new IllegalArgumentException();
this.strings = strings;
+ this.type = type;
}
public String[] getMultiValues()
@@ -390,16 +399,23 @@
return Strings.toString( ", ", (Object[]) strings );
}
+ public Class getType()
+ {
+ return type;
+ }
}
public static class AssociativePropertyValue implements PropertyValue
{
private Map<String, String> keyedValues;
- public AssociativePropertyValue(Map<String, String> keyedValues)
+ private Class type;
+
+ public AssociativePropertyValue(Map<String, String> keyedValues, Class type)
{
if (keyedValues==null) throw new IllegalArgumentException();
this.keyedValues = keyedValues;
+ this.type = type;
}
public String[] getMultiValues()
@@ -438,6 +454,12 @@
return keyedValues.toString();
}
+
+ public Class getType()
+ {
+ return type;
+ }
+
}
}