[seam-commits] Seam SVN: r11897 - in modules/remoting/trunk/src/main/java/org/jboss/seam/remoting: wrapper and 1 other directory.
seam-commits at lists.jboss.org
seam-commits at lists.jboss.org
Sat Jan 2 06:04:40 EST 2010
Author: shane.bryzak at jboss.com
Date: 2010-01-02 06:04:40 -0500 (Sat, 02 Jan 2010)
New Revision: 11897
Modified:
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/model/ModelHandler.java
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BagWrapper.java
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BeanWrapper.java
Log:
support for updating bag and map model properties
Modified: modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/model/ModelHandler.java
===================================================================
--- modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/model/ModelHandler.java 2010-01-01 04:42:31 UTC (rev 11896)
+++ modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/model/ModelHandler.java 2010-01-02 11:04:40 UTC (rev 11897)
@@ -4,7 +4,12 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import javax.enterprise.context.Conversation;
import javax.enterprise.inject.spi.BeanManager;
@@ -22,6 +27,7 @@
import org.jboss.seam.remoting.RequestHandler;
import org.jboss.seam.remoting.wrapper.BagWrapper;
import org.jboss.seam.remoting.wrapper.BeanWrapper;
+import org.jboss.seam.remoting.wrapper.MapWrapper;
import org.jboss.seam.remoting.wrapper.Wrapper;
import org.jboss.weld.Container;
import org.jboss.weld.context.ContextLifecycle;
@@ -32,7 +38,7 @@
import org.slf4j.LoggerFactory;
/**
- * Handles incoming model fetch/applyUpdate requests
+ * Handles incoming model fetch/apply requests
*
* @author Shane Bryzak
*/
@@ -45,7 +51,6 @@
@Inject ModelRegistry registry;
@Inject Conversation conversation;
- @SuppressWarnings("unchecked")
public void handle(HttpServletRequest request, HttpServletResponse response)
throws Exception
{
@@ -154,10 +159,10 @@
}
// TODO Unmarshal expressions - don't support this until security implications investigated
- for (Element exprElement : (List<Element>) modelElement.elements("expression"))
- {
+ //for (Element exprElement : (List<Element>) modelElement.elements("expression"))
+ //{
- }
+ //}
if (model.getAction() != null)
{
@@ -228,38 +233,79 @@
if (changeset.elements("member").size() > 0)
{
+ Wrapper target = model.getCallContext().getOutRefs().get(refId);
+ if (!(target instanceof BeanWrapper))
+ {
+ throw new IllegalStateException("Changeset for refId [" +
+ refId + "] does not reference a valid bean object");
+ }
+
for (Element member : (List<Element>) changeset.elements("member"))
{
String name = member.attributeValue("name");
- Wrapper w = model.getCallContext().createWrapperFromElement(
+ Wrapper source = model.getCallContext().createWrapperFromElement(
(Element) member.elementIterator().next());
-
- if (w instanceof BagWrapper)
- {
- // TODO process collection updates
+
+ if (source instanceof BagWrapper)
+ {
+ Object targetBag = ((BeanWrapper) target).getBeanProperty(name);
+ if (targetBag == null)
+ {
+ ((BeanWrapper) target).setBeanProperty(name, source);
+ }
+ else
+ {
+ Type t = ((BeanWrapper) target).getBeanPropertyType(name);
+ if (!cloneBagContents(source.convert(t), targetBag))
+ {
+ ((BeanWrapper) target).setBeanProperty(name, source);
+ }
+ }
}
- else
+ else if (source instanceof MapWrapper)
{
- Wrapper ref = model.getCallContext().getOutRefs().get(refId);
- if (ref instanceof BeanWrapper)
+ Object targetMap = ((BeanWrapper) target).getBeanProperty(name);
+ if (!Map.class.isAssignableFrom(targetMap.getClass()))
{
- ((BeanWrapper) ref).setBeanProperty(name, w);
+ throw new IllegalStateException("Cannot assign Map value " +
+ "to non Map property [" + target.getClass().getName() +
+ "." + name + "]");
}
- else
+
+ if (targetMap == null)
{
- throw new IllegalStateException("Changeset for refId [" +
- refId + "] does not reference a valid bean object");
+ ((BeanWrapper) target).setBeanProperty(name, source);
}
+ else
+ {
+ Type t = ((BeanWrapper) target).getBeanPropertyType(name);
+ cloneMapContents((Map) source.convert(t), (Map) targetMap);
+ }
+ }
+ else
+ {
+ ((BeanWrapper) target).setBeanProperty(name, source);
}
-
}
}
if (changeset.elements("bag").size() > 0)
{
- // TODO process root node collection updates
+ Wrapper target = model.getCallContext().getOutRefs().get(refId);
+ Wrapper source = model.getCallContext().createWrapperFromElement(
+ (Element) changeset.element("bag"));
+ cloneBagContents(source.convert(target.getValue().getClass()),
+ target.getValue());
}
+ else if (changeset.elements("map").size() > 0)
+ {
+ Wrapper target = model.getCallContext().getOutRefs().get(refId);
+ Wrapper source = model.getCallContext().createWrapperFromElement(
+ (Element) changeset.element("map"));
+ cloneMapContents((Map) source.convert(target.getValue().getClass()),
+ (Map) target.getValue());
+ }
}
}
@@ -276,6 +322,98 @@
return model;
}
+ /**
+ * Clones the contents of the specified source bag into the specified target
+ * bag. If the contents can be cloned, this method returns true, otherwise it
+ * returns false.
+ *
+ * @param sourceBag
+ * @param targetBag
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private boolean cloneBagContents(Object sourceBag, Object targetBag)
+ {
+ Class<?> cls = sourceBag.getClass();
+ if (cls.isArray())
+ {
+ int sourceLen = Array.getLength(sourceBag);
+ int targetLen = Array.getLength(targetBag);
+ if (targetLen != sourceLen) return false;
+ for (int i = 0; i < sourceLen; i++)
+ {
+ Array.set(targetBag, i, Array.get(sourceBag, i));
+ }
+ return true;
+ }
+ else if (List.class.isAssignableFrom(cls))
+ {
+ List sourceList = (List) sourceBag;
+ List targetList = (List) targetBag;
+
+ while (targetList.size() > sourceList.size())
+ {
+ targetList.remove(targetList.size() - 1);
+ }
+
+ for (int i = 0; i < sourceList.size(); i++)
+ {
+ targetList.set(i, sourceList.get(i));
+ }
+ return true;
+ }
+ else if (Set.class.isAssignableFrom(cls))
+ {
+ Set sourceSet = (Set) sourceBag;
+ Set targetSet = (Set) targetBag;
+
+ for (Object e : sourceSet)
+ {
+ if (!targetSet.contains(e))
+ {
+ targetSet.add(e);
+ }
+ }
+
+ for (Object e : targetSet)
+ {
+ if (!sourceSet.contains(e))
+ {
+ targetSet.remove(e);
+ }
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Clones the contents of one Map into another
+ *
+ * @param sourceMap
+ * @param targetMap
+ */
+ @SuppressWarnings("unchecked")
+ private void cloneMapContents(Map sourceMap, Map targetMap)
+ {
+ for (Object key : sourceMap.keySet())
+ {
+ if (!targetMap.containsKey(key))
+ {
+ targetMap.put(key, sourceMap.get(key));
+ }
+ }
+
+ for (Object key : targetMap.keySet())
+ {
+ if (!sourceMap.containsKey(key))
+ {
+ targetMap.remove(key);
+ }
+ }
+ }
+
private void marshalResponse(Model model, RequestContext ctx,
OutputStream out) throws IOException
{
Modified: modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BagWrapper.java
===================================================================
--- modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BagWrapper.java 2010-01-01 04:42:31 UTC (rev 11896)
+++ modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BagWrapper.java 2010-01-02 11:04:40 UTC (rev 11897)
@@ -58,12 +58,19 @@
{
vals = new ArrayList<Object>();
for (int i = 0; i < Array.getLength(value); i++)
+ {
vals.add(Array.get(value, i));
- } else if (Collection.class.isAssignableFrom(value.getClass()))
+ }
+ }
+ else if (Collection.class.isAssignableFrom(value.getClass()))
+ {
vals = (Collection) value;
+ }
else
+ {
throw new RuntimeException(String.format(
"Can not marshal object as bag: [%s]", value));
+ }
for (Object val : vals)
{
@@ -82,32 +89,39 @@
List<Wrapper> vals = new ArrayList<Wrapper>();
for (Element e : (List<Element>) element.elements("element"))
- vals.add(context.createWrapperFromElement((Element) e.elements()
- .get(0)));
+ {
+ vals.add(context.createWrapperFromElement((Element) e.elements().get(0)));
+ }
if (type instanceof Class && ((Class) type).isArray())
{
Class arrayType = ((Class) type).getComponentType();
value = Array.newInstance(arrayType, vals.size()); // Fix this
for (int i = 0; i < vals.size(); i++)
+ {
Array.set(value, i, vals.get(i).convert(arrayType));
- } else if (type instanceof Class
- && Collection.class.isAssignableFrom((Class) type))
+ }
+ }
+ else if (type instanceof Class &&
+ Collection.class.isAssignableFrom((Class) type))
{
try
{
value = getConcreteClass((Class) type).newInstance();
- } catch (Exception ex)
+ }
+ catch (Exception ex)
{
throw new ConversionException(String.format(
"Could not create instance of target type [%s].", type));
}
+
for (Wrapper w : vals)
+ {
((Collection) value).add(w.convert(Object.class));
- } else if (type instanceof ParameterizedType
- && Collection.class
- .isAssignableFrom((Class) ((ParameterizedType) type)
- .getRawType()))
+ }
+ }
+ else if (type instanceof ParameterizedType &&
+ Collection.class.isAssignableFrom((Class) ((ParameterizedType) type).getRawType()))
{
Class rawType = (Class) ((ParameterizedType) type).getRawType();
Type genType = Object.class;
@@ -121,14 +135,17 @@
try
{
value = getConcreteClass(rawType).newInstance();
- } catch (Exception ex)
+ }
+ catch (Exception ex)
{
throw new ConversionException(String.format(
"Could not create instance of target type [%s].", rawType));
}
for (Wrapper w : vals)
+ {
((Collection) value).add(w.convert(genType));
+ }
}
return value;
@@ -140,13 +157,22 @@
{
// Support Set, Queue and (by default, and as a last resort) List
if (Set.class.isAssignableFrom(c))
+ {
return HashSet.class;
+ }
else if (Queue.class.isAssignableFrom(c))
+ {
return LinkedList.class;
+ }
else
+ {
return ArrayList.class;
- } else
+ }
+ }
+ else
+ {
return c;
+ }
}
/**
@@ -158,18 +184,14 @@
public ConversionScore conversionScore(Class<?> cls)
{
// There's no such thing as an exact match for a bag, so we'll just look
- // for
- // a compatible match
-
- if (cls.isArray())
+ // for a compatible match
+ if (cls.isArray() || cls.equals(Object.class) || Collection.class.isAssignableFrom(cls))
+ {
return ConversionScore.compatible;
-
- if (cls.equals(Object.class))
- return ConversionScore.compatible;
-
- if (Collection.class.isAssignableFrom(cls))
- return ConversionScore.compatible;
-
- return ConversionScore.nomatch;
+ }
+ else
+ {
+ return ConversionScore.nomatch;
+ }
}
}
Modified: modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BeanWrapper.java
===================================================================
--- modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BeanWrapper.java 2010-01-01 04:42:31 UTC (rev 11896)
+++ modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BeanWrapper.java 2010-01-02 11:04:40 UTC (rev 11897)
@@ -50,20 +50,19 @@
}
return metadataCache;
}
-
+
+ @Override
@SuppressWarnings("unchecked")
- @Override
public void setElement(Element element)
{
super.setElement(element);
String beanType = element.attributeValue("type");
- // TODO do it this way, it might not be a managed bean...
- Bean bean = beanManager.getBeans(beanType).iterator().next();
-
- if (bean != null)
+ Set<Bean<?>> beans = beanManager.getBeans(beanType);
+ if (beans.size() > 0)
{
+ Bean bean = beans.iterator().next();
value = bean.create(beanManager.createCreationalContext(bean));
}
else
@@ -80,12 +79,113 @@
}
}
+ public Type getBeanPropertyType(String propertyName)
+ {
+ Class<?> cls = value.getClass();
+
+ String getter = "get" + Character.toUpperCase(propertyName.charAt(0)) +
+ propertyName.substring(1);
+
+ for (Method m : cls.getMethods())
+ {
+ if (getter.equals(m.getName())) return m.getGenericReturnType();
+ }
+
+ Field field = null;
+ while (field == null && !cls.equals(Object.class))
+ {
+ try
+ {
+ field = cls.getDeclaredField(propertyName);
+ }
+ catch (NoSuchFieldException e)
+ {
+ cls = cls.getSuperclass();
+ }
+ }
+
+ if (field == null)
+ {
+ throw new IllegalArgumentException("Invalid property name [" + propertyName +
+ "] specified for target class [" + value.getClass() + "]");
+ }
+
+ return field.getGenericType();
+ }
+
+ public Wrapper getBeanProperty(String propertyName)
+ {
+ Class<?> cls = value.getClass();
+
+ Field f = null;
+ try
+ {
+ f = cls.getField(propertyName);
+ }
+ catch (NoSuchFieldException ex) { }
+
+ boolean accessible = false;
+ try
+ {
+ // Temporarily set the field's accessibility so we can read it
+ if (f != null)
+ {
+ accessible = f.isAccessible();
+ f.setAccessible(true);
+ return context.createWrapperFromObject(f.get(value), null);
+ }
+ else
+ {
+ Method accessor = null;
+ try
+ {
+ accessor = cls.getMethod(String.format("get%s%s",
+ Character.toUpperCase(propertyName.charAt(0)),
+ propertyName.substring(1)));
+ }
+ catch (NoSuchMethodException ex)
+ {
+ try
+ {
+ accessor = cls.getMethod(String.format("is%s%s",
+ Character.toUpperCase(propertyName.charAt(0)),
+ propertyName.substring(1)));
+ }
+ catch (NoSuchMethodException ex2)
+ {
+ // uh oh... continue with the next one
+ return null;
+ }
+ }
+
+ try
+ {
+ return context.createWrapperFromObject(accessor.invoke(value), null);
+ }
+ catch (InvocationTargetException ex)
+ {
+ throw new RuntimeException(String.format(
+ "Failed to read property [%s] for object [%s]",
+ propertyName, value));
+ }
+ }
+ }
+ catch (IllegalAccessException ex)
+ {
+ throw new RuntimeException("Error reading value from field.");
+ }
+ finally
+ {
+ if (f != null)
+ f.setAccessible(accessible);
+ }
+ }
+
public void setBeanProperty(String propertyName, Wrapper valueWrapper)
{
Class<?> cls = value.getClass();
// We're going to try a combination of ways to set the property value
- // here
Method method = null;
Field field = null;
@@ -164,8 +264,7 @@
boolean accessible = field.isAccessible();
try
{
- if (!accessible)
- field.setAccessible(true);
+ if (!accessible) field.setAccessible(true);
field.set(value, fieldValue);
}
catch (Exception ex)
@@ -210,14 +309,15 @@
}
}
+ /**
+ * Writes the object reference ID to the specified OutputStream
+ */
public void marshal(OutputStream out) throws IOException
{
context.addOutRef(this);
out.write(REF_START_TAG_OPEN);
- out
- .write(Integer.toString(context.getOutRefs().indexOf(this))
- .getBytes());
+ out.write(Integer.toString(context.getOutRefs().indexOf(this)).getBytes());
out.write(REF_START_TAG_END);
}
@@ -227,6 +327,14 @@
serialize(out, null);
}
+ /**
+ * Writes a serialized representation of the object's properties to the specified
+ * OutputStream.
+ *
+ * @param out
+ * @param constraints
+ * @throws IOException
+ */
public void serialize(OutputStream out, List<String> constraints)
throws IOException
{
@@ -275,81 +383,20 @@
componentName != null ? componentName : cls.getName(),
propertyName);
- if (constraints == null
- || (!constraints.contains(fieldPath) && !constraints
- .contains(wildCard)))
+ if (constraints == null || (!constraints.contains(fieldPath) &&
+ !constraints.contains(wildCard)))
{
out.write(MEMBER_START_TAG_OPEN);
out.write(propertyName.getBytes());
out.write(MEMBER_START_TAG_CLOSE);
- Field f = null;
- try
+ Wrapper w = getBeanProperty(propertyName);
+ if (w != null)
{
- f = cls.getField(propertyName);
+ w.setPath(fieldPath);
+ w.marshal(out);
}
- catch (NoSuchFieldException ex)
- {
- }
- boolean accessible = false;
- try
- {
- // Temporarily set the field's accessibility so we can read it
- if (f != null)
- {
- accessible = f.isAccessible();
- f.setAccessible(true);
- context.createWrapperFromObject(f.get(value), fieldPath)
- .marshal(out);
- }
- else
- {
- Method accessor = null;
- try
- {
- accessor = cls.getMethod(String.format("get%s%s",
- Character.toUpperCase(propertyName.charAt(0)),
- propertyName.substring(1)));
- }
- catch (NoSuchMethodException ex)
- {
- try
- {
- accessor = cls.getMethod(String.format("is%s%s",
- Character.toUpperCase(propertyName.charAt(0)),
- propertyName.substring(1)));
- }
- catch (NoSuchMethodException ex2)
- {
- // uh oh... continue with the next one
- continue;
- }
- }
-
- try
- {
- context.createWrapperFromObject(accessor.invoke(value),
- fieldPath).marshal(out);
- }
- catch (InvocationTargetException ex)
- {
- throw new RuntimeException(String.format(
- "Failed to read property [%s] for object [%s]",
- propertyName, value));
- }
- }
- }
- catch (IllegalAccessException ex)
- {
- throw new RuntimeException("Error reading value from field.");
- }
- finally
- {
- if (f != null)
- f.setAccessible(accessible);
- }
-
out.write(MEMBER_CLOSE_TAG);
}
}
More information about the seam-commits
mailing list