Author: shane.bryzak(a)jboss.com
Date: 2009-12-28 23:20:43 -0500 (Mon, 28 Dec 2009)
New Revision: 11889
Added:
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/BeanMetadata.java
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/MetadataCache.java
Modified:
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/InterfaceGenerator.java
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BeanWrapper.java
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/WrapperFactory.java
modules/remoting/trunk/src/main/resources/org/jboss/seam/remoting/remote.js
Log:
refactored InterfaceGenerator in preparation for dynamic type loading
Added: modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/BeanMetadata.java
===================================================================
--- modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/BeanMetadata.java
(rev 0)
+++
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/BeanMetadata.java 2009-12-29
04:20:43 UTC (rev 11889)
@@ -0,0 +1,66 @@
+package org.jboss.seam.remoting;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Contains the metadata (either the accessible properties or invokable methods)
+ * for a single action or state bean.
+ *
+ * @author Shane Bryzak
+ */
+public class BeanMetadata
+{
+ public enum BeanType {action, state}
+
+ private BeanType beanType;
+
+ private String name;
+ private Map<String,Integer> methods;
+ private Map<String,String> properties;
+
+ public BeanMetadata(BeanType beanType, String name)
+ {
+ this.beanType = beanType;
+ this.name = name;
+
+ if (beanType == BeanType.action)
+ {
+ methods = new HashMap<String,Integer>();
+ }
+ else
+ {
+ properties = new HashMap<String,String>();
+ }
+ }
+
+ public BeanType getBeanType()
+ {
+ return beanType;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void addMethod(String name, int paramCount)
+ {
+ methods.put(name, paramCount);
+ }
+
+ public void addProperty(String name, String remotingType)
+ {
+ properties.put(name, remotingType);
+ }
+
+ public Map<String,Integer> getMethods()
+ {
+ return methods;
+ }
+
+ public Map<String,String> getProperties()
+ {
+ return properties;
+ }
+}
Modified:
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/InterfaceGenerator.java
===================================================================
---
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/InterfaceGenerator.java 2009-12-28
22:39:01 UTC (rev 11888)
+++
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/InterfaceGenerator.java 2009-12-29
04:20:43 UTC (rev 11889)
@@ -3,16 +3,6 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
@@ -26,7 +16,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.jboss.seam.remoting.annotations.WebRemote;
+import org.jboss.seam.remoting.BeanMetadata.BeanType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,36 +30,26 @@
private static final Logger log = LoggerFactory.getLogger(InterfaceGenerator.class);
@Inject BeanManager beanManager;
+ @Inject MetadataCache metadataCache;
/**
- * Maintain a cache of the accessible fields
- */
- private static Map<Class<?>, Set<String>> accessibleProperties = new
HashMap<Class<?>, Set<String>>();
-
- /**
* A cache of component interfaces, keyed by name.
*/
private Map<Class<?>, byte[]> interfaceCache = new
HashMap<Class<?>, byte[]>();
/**
- *
- * @param request
- * HttpServletRequest
- * @param response
- * HttpServletResponse
- * @throws Exception
+ * Handles the request
*/
public void handle(final HttpServletRequest request,
final HttpServletResponse response) throws Exception
{
if (request.getQueryString() == null)
{
- throw new ServletException("Invalid request - no component
specified");
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST,
+ "Invalid request - no component specified");
}
Set<Class<?>> typesCached = new HashSet<Class<?>>();
- Set<Type> types = new HashSet<Type>();
-
response.setContentType("text/javascript");
Enumeration<?> e = request.getParameterNames();
@@ -94,187 +74,62 @@
{
log.error(String.format("Component not found: [%s]",
componentName));
- throw new ServletException(
- "Invalid request - component not found.");
+ response.sendError(HttpServletResponse.SC_NOT_FOUND,
+ String.format("Component not found: [%s]",
componentName));
}
}
typesCached.add(beanClass);
}
- generateBeanInterface(typesCached, response.getOutputStream(), types);
+ generateBeanInterface(typesCached, response.getOutputStream(), null);
}
/**
- * Generates the JavaScript code required to invoke the methods of a
- * component/s.
+ * Generates the JavaScript code required to invoke the methods of a bean.
*
- * @param components
- * Component[] The components to generate javascript for
- * @param out
- * OutputStream The OutputStream to write the generated javascript
- * to
- * @throws IOException
- * Thrown if there is an error writing to the OutputStream
+ * @param classes Set<Class<?>> The bean classes for which to generate
JavaScript stubs
+ * @param out OutputStream The OutputStream to write the generated JavaScript
+ * @param types Set<Type> Used to keep track of which bean classes have been
+ * generated, can be null
+ * @throws IOException Thrown if there is an error writing to the OutputStream
*/
public void generateBeanInterface(Set<Class<?>> classes, OutputStream
out,
- Set<Type> types) throws IOException
+ Set<BeanMetadata> types) throws IOException
{
- for (Class<?> cls : classes)
+ if (types == null)
{
- if (cls != null)
+ types = new HashSet<BeanMetadata>();
+ }
+
+ for (Class<?> beanClass : classes)
+ {
+ if (beanClass != null)
{
- if (!interfaceCache.containsKey(cls))
+ if (!interfaceCache.containsKey(beanClass))
{
synchronized (interfaceCache)
{
- if (!interfaceCache.containsKey(cls))
+ if (!interfaceCache.containsKey(beanClass))
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
- appendBeanSource(bOut, cls, types);
- interfaceCache.put(cls, bOut.toByteArray());
+ addDependency(beanClass, types);
+ appendBeanSource(bOut, beanClass, types);
+ interfaceCache.put(beanClass, bOut.toByteArray());
}
}
}
- out.write(interfaceCache.get(cls));
+ out.write(interfaceCache.get(beanClass));
}
}
}
/**
- * A helper method, used internally by InterfaceGenerator and also when
- * serializing responses. Returns a list of the property names for the
- * specified class which should be included in the generated interface for
- * the type.
- *
- * @param cls
- * Class
- * @return List
- */
- public static Set<String> getAccessibleProperties(Class<?> cls)
- {
- /**
- * @todo This is a hack to get the "real" class - find out if there is
an
- * API method in CGLIB that can be used instead
- */
- if (cls.getName().contains("EnhancerByCGLIB"))
- cls = cls.getSuperclass();
-
- if (!accessibleProperties.containsKey(cls))
- {
- synchronized (accessibleProperties)
- {
- if (!accessibleProperties.containsKey(cls))
- {
- Set<String> properties = new HashSet<String>();
-
- Class<?> c = cls;
- while (c != null && !c.equals(Object.class))
- {
- for (Field f : c.getDeclaredFields())
- {
- if (!Modifier.isTransient(f.getModifiers())
- && !Modifier.isStatic(f.getModifiers()))
- {
- String fieldName = f.getName().substring(0, 1)
- .toUpperCase()
- + f.getName().substring(1);
- String getterName = String.format("get%s", fieldName);
- String setterName = String.format("set%s", fieldName);
- Method getMethod = null;
- Method setMethod = null;
-
- try
- {
- getMethod = c.getMethod(getterName);
- }
- catch (SecurityException ex)
- {
- }
- catch (NoSuchMethodException ex)
- {
- // it might be an "is" method...
- getterName = String.format("is%s", fieldName);
- try
- {
- getMethod = c.getMethod(getterName);
- }
- catch (NoSuchMethodException ex2)
- { /* don't care */
- }
- }
-
- try
- {
- setMethod = c.getMethod(setterName, new Class[] { f
- .getType() });
- }
- catch (SecurityException ex)
- {
- }
- catch (NoSuchMethodException ex)
- { /* don't care */
- }
-
- if (Modifier.isPublic(f.getModifiers())
- || (getMethod != null
- && Modifier.isPublic(getMethod
- .getModifiers()) || (setMethod != null
&& Modifier
- .isPublic(setMethod.getModifiers()))))
- {
- properties.add(f.getName());
- }
- }
- }
-
- //
- for (Method m : c.getDeclaredMethods())
- {
- if (m.getName().startsWith("get")
- || m.getName().startsWith("is"))
- {
- int startIdx = m.getName().startsWith("get") ? 3 : 2;
-
- try
- {
- c.getMethod(String.format("set%s", m.getName()
- .substring(startIdx)), m.getReturnType());
- }
- catch (NoSuchMethodException ex)
- {
- continue;
- }
-
- String propertyName = String.format("%s%s", Character
- .toLowerCase(m.getName().charAt(startIdx)), m
- .getName().substring(startIdx + 1));
-
- if (!properties.contains(propertyName))
- {
- properties.add(propertyName);
- }
- }
- }
-
- c = c.getSuperclass();
- }
-
- accessibleProperties.put(cls, properties);
- }
- }
- }
-
- return accessibleProperties.get(cls);
- }
-
- /**
- * Appends component interface code to an outputstream for a specified
+ * Appends component interface code to an OutputStream for a specified
* component.
*
- * @param out
- * OutputStream The OutputStream to write to
- * @param component
- * Component The component to generate an interface for
+ * @param out OutputStream The OutputStream to write to
+ * @param beanClass Class<?> The bean class to generate an interface for
* @param types
* Set A list of types that have already been generated for this
* request. If this component has already been generated (i.e. it
@@ -282,366 +137,85 @@
* @throws IOException
* If there is an error writing to the OutputStream.
*/
- private void appendBeanSource(OutputStream out, Class<?> beanClass,
Set<Type> types)
+ private void addDependency(Class<?> beanClass, Set<BeanMetadata> types)
throws IOException
{
- Set<Class<?>> componentTypes = new HashSet<Class<?>>();
-
- // Check if any of the methods are annotated with @WebRemote, and if so
- // treat it as an "action" component instead of a type component
- for (Method m : beanClass.getDeclaredMethods())
+ types.add(metadataCache.getMetadata(beanClass));
+
+ for (Class<?> dependencyClass : metadataCache.getDependencies(beanClass))
{
- if (m.getAnnotation(WebRemote.class) != null)
+ if (!types.contains(dependencyClass))
{
- componentTypes.add(beanClass);
- break;
+ addDependency(dependencyClass, types);
}
- }
-
- if (componentTypes.isEmpty())
- {
- appendTypeSource(out, beanClass, types);
- return;
- }
-
- // If types already contains all the component types, then return
- boolean foundNew = false;
- for (Class<?> type : componentTypes)
- {
- if (!types.contains(type))
- {
- foundNew = true;
- break;
- }
- }
-
- if (!foundNew)
- {
- return;
- }
-
- String name = beanManager.getBeans(beanClass).iterator().next().getName();
- String beanName = name != null ? name : beanClass.getName();
-
- for (Class<?> type : componentTypes)
- {
- if (types.contains(type))
- {
- break;
- }
- else
- {
- types.add(type);
-
- // Build the bean stub
- StringBuilder componentSrc = new StringBuilder();
- componentSrc.append("Seam.registerBean(\"");
- componentSrc.append(beanName);
- componentSrc.append("\", null, {");
-
- boolean first = true;
- for (Method m : type.getDeclaredMethods())
- {
- if (m.getAnnotation(WebRemote.class) == null)
- continue;
-
- if (!first)
- {
- componentSrc.append(", ");
- }
- else
- {
- first = false;
- }
-
- // Append the return type to the source block
- appendTypeSource(out, m.getGenericReturnType(), types);
-
- componentSrc.append(m.getName());
- componentSrc.append(": ");
- componentSrc.append(m.getGenericParameterTypes().length);
-
- for (int i = 0; i < m.getGenericParameterTypes().length; i++)
- {
- appendTypeSource(out, m.getGenericParameterTypes()[i], types);
- }
- }
- componentSrc.append("});\n");
- out.write(componentSrc.toString().getBytes());
- }
- }
+ }
}
/**
- * Append Javascript interface code for a specified class to a block of code.
+ * Appends the interface code for a JavaBean (state holding) class to an
OutputStream.
*
- * @param source
- * StringBuilder The code block to append to
- * @param type
- * Class The type to generate a Javascript interface for
- * @param types
- * Set A list of the types already generated (only include each
- * type once).
- */
- private void appendTypeSource(OutputStream out, Type type, Set<Type> types)
- throws IOException
- {
- if (type instanceof Class<?>)
- {
- Class<?> classType = (Class<?>) type;
-
- if (classType.isArray())
- {
- appendTypeSource(out, classType.getComponentType(), types);
- return;
- }
-
- if (classType.getName().startsWith("java.") || types.contains(type)
- || classType.isPrimitive())
- {
- return;
- }
-
- // Keep track of which types we've already added
- types.add(type);
-
- appendClassSource(out, classType, types);
- }
- else if (type instanceof ParameterizedType)
- {
- for (Type t : ((ParameterizedType) type).getActualTypeArguments())
- {
- appendTypeSource(out, t, types);
- }
- }
- }
-
- /**
- * Appends the interface code for a non-component class to an OutputStream.
- *
- * @param out
- * OutputStream
- * @param classType
- * Class
- * @param types
- * Set
+ * @param out OutputStream
+ * @param classType Class<?>
+ * @param types Set<Type>
* @throws IOException
*/
- private void appendClassSource(OutputStream out, Class<?> classType,
- Set<Type> types) throws IOException
+ private void appendBeanSource(OutputStream out, Class<?> classType,
+ Set<BeanMetadata> types) throws IOException
{
- // Don't generate interfaces for enums
- if (classType.isEnum())
- {
- return;
- }
-
- Bean<?> bean = beanManager.getBeans(classType).iterator().next();
+ StringBuilder src = new StringBuilder();
- String componentName = bean.getName();
- if (componentName == null)
- componentName = classType.getName();
-
- Map<String, String> metadata = new HashMap<String, String>();
-
- String getMethodName = null;
- String setMethodName = null;
-
- for (String propertyName : getAccessibleProperties(classType))
- {
- Type propertyType = null;
-
- Field f = null;
- try
+ for (BeanMetadata meta : types)
+ {
+ if (meta.getBeanType() == BeanType.action)
{
- f = classType.getDeclaredField(propertyName);
- propertyType = f.getGenericType();
- }
- catch (NoSuchFieldException ex)
- {
- setMethodName = String.format("set%s%s", Character
- .toUpperCase(propertyName.charAt(0)), propertyName
- .substring(1));
-
- try
- {
- getMethodName = String.format("get%s%s", Character
- .toUpperCase(propertyName.charAt(0)), propertyName
- .substring(1));
- propertyType = classType.getMethod(getMethodName)
- .getGenericReturnType();
- }
- catch (NoSuchMethodException ex2)
- {
- try
+ src.append("Seam.registerBean(\"");
+ src.append(meta.getName());
+ src.append("\", null, {");
+
+ boolean first = true;
+ for (String methodName : meta.getMethods().keySet())
+ {
+ if (!first)
{
- getMethodName = String.format("is%s%s", Character
- .toUpperCase(propertyName.charAt(0)), propertyName
- .substring(1));
-
- propertyType = classType.getMethod(getMethodName)
- .getGenericReturnType();
+ src.append(", ");
}
- catch (NoSuchMethodException ex3)
+ else
{
- // ???
- continue;
+ first = false;
}
+
+ src.append(methodName);
+ src.append(": ");
+ src.append(meta.getMethods().get(methodName));
}
+ src.append("});\n");
}
-
- appendTypeSource(out, propertyType, types);
-
- // Include types referenced by generic declarations
- if (propertyType instanceof ParameterizedType)
+ else
{
- for (Type t : ((ParameterizedType) propertyType)
- .getActualTypeArguments())
+ src.append("Seam.registerBean(\"");
+ src.append(meta.getName());
+ src.append("\", {");
+
+ boolean first = true;
+ for (String propertyName : meta.getProperties().keySet())
{
- if (t instanceof Class<?>)
+ if (!first)
{
- appendTypeSource(out, t, types);
+ src.append(", ");
}
- }
- }
-
- if (f != null)
- {
- String fieldName = propertyName.substring(0, 1).toUpperCase()
- + propertyName.substring(1);
- String getterName = String.format("get%s", fieldName);
- String setterName = String.format("set%s", fieldName);
-
- try
- {
- classType.getMethod(getterName);
- getMethodName = getterName;
- }
- catch (SecurityException ex)
- {
- }
- catch (NoSuchMethodException ex)
- {
- getterName = String.format("is%s", fieldName);
- try
+ else
{
- if (Modifier.isPublic(classType.getMethod(getterName)
- .getModifiers()))
- getMethodName = getterName;
+ first = false;
}
- catch (NoSuchMethodException ex2)
- { /* don't care */
- }
+
+ src.append(propertyName);
+ src.append(": \"");
+ src.append(meta.getProperties().get(propertyName));
+ src.append("\"");
}
-
- try
- {
- if (Modifier.isPublic(classType.getMethod(setterName,
- f.getType()).getModifiers()))
- setMethodName = setterName;
- }
- catch (SecurityException ex)
- {
- }
- catch (NoSuchMethodException ex)
- { /* don't care */
- }
+ src.append("});\n");
}
-
- // Construct the list of fields.
- if (getMethodName != null || setMethodName != null)
- {
- metadata.put(propertyName, getFieldType(propertyType));
- }
- }
-
- StringBuilder typeSource = new StringBuilder();
- typeSource.append("Seam.registerBean(\"");
- typeSource.append(componentName);
- typeSource.append("\", {");
-
- boolean first = true;
- for (String key : metadata.keySet())
- {
- if (!first)
- typeSource.append(", ");
- typeSource.append(key);
- typeSource.append(": \"");
- typeSource.append(metadata.get(key));
- typeSource.append("\"");
- first = false;
}
-
- typeSource.append("});\n");
-
- out.write(typeSource.toString().getBytes());
+ out.write(src.toString().getBytes());
}
-
- /**
- * Returns the remoting "type" for a specified class.
- *
- * @param type
- * Class
- * @return String
- */
- protected String getFieldType(Type type)
- {
- if (type.equals(String.class)
- || (type instanceof Class<?> && ((Class<?>)
type).isEnum())
- || type.equals(BigInteger.class) || type.equals(BigDecimal.class))
- {
- return "str";
- }
- else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE))
- {
- return "bool";
- }
- else if (type.equals(Short.class) || type.equals(Short.TYPE)
- || type.equals(Integer.class) || type.equals(Integer.TYPE)
- || type.equals(Long.class) || type.equals(Long.TYPE)
- || type.equals(Float.class) || type.equals(Float.TYPE)
- || type.equals(Double.class) || type.equals(Double.TYPE)
- || type.equals(Byte.class) || type.equals(Byte.TYPE))
- {
- return "number";
- }
- else if (type instanceof Class<?>)
- {
- Class<?> cls = (Class<?>) type;
- if (Date.class.isAssignableFrom(cls)
- || Calendar.class.isAssignableFrom(cls))
- {
- return "date";
- }
- else if (cls.isArray())
- {
- return "bag";
- }
- else if (cls.isAssignableFrom(Map.class))
- {
- return "map";
- }
- else if (cls.isAssignableFrom(Collection.class))
- {
- return "bag";
- }
- }
- else if (type instanceof ParameterizedType)
- {
- ParameterizedType pt = (ParameterizedType) type;
-
- if (pt.getRawType() instanceof Class<?>
- && Map.class.isAssignableFrom((Class<?>) pt.getRawType()))
- {
- return "map";
- }
- else if (pt.getRawType() instanceof Class<?>
- && Collection.class.isAssignableFrom((Class<?>)
pt.getRawType()))
- {
- return "bag";
- }
- }
-
- return "bean";
- }
}
Added: modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/MetadataCache.java
===================================================================
--- modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/MetadataCache.java
(rev 0)
+++
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/MetadataCache.java 2009-12-29
04:20:43 UTC (rev 11889)
@@ -0,0 +1,392 @@
+package org.jboss.seam.remoting;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.inject.Inject;
+
+import org.jboss.seam.remoting.BeanMetadata.BeanType;
+import org.jboss.seam.remoting.annotations.WebRemote;
+
+/**
+ * Caches BeanMetadata instances
+ *
+ * @author Shane Bryzak
+ */
+@ApplicationScoped
+public class MetadataCache
+{
+ private Map<Class<?>,BeanMetadata> metadataCache;
+
+ private Map<Class<?>, Set<Class<?>>> beanDependencies;
+
+ /**
+ * A cache of the accessible properties for a bean class
+ */
+ private Map<Class<?>, Map<String,Type>> accessibleProperties = new
HashMap<Class<?>, Map<String,Type>>();
+
+ @Inject BeanManager beanManager;
+
+ public MetadataCache()
+ {
+ metadataCache = new HashMap<Class<?>, BeanMetadata>();
+ beanDependencies = new HashMap<Class<?>, Set<Class<?>>>();
+ }
+
+ public BeanMetadata getMetadata(Class<?> beanClass)
+ {
+ if (beanClass == null) throw new IllegalArgumentException("beanClass cannot be
null");
+
+ if (metadataCache.containsKey(beanClass))
+ {
+ return metadataCache.get(beanClass);
+ }
+ else
+ {
+ synchronized(metadataCache)
+ {
+ if (!metadataCache.containsKey(beanClass))
+ {
+ metadataCache.put(beanClass, generateBeanMetadata(beanClass));
+
+ Set<Class<?>> dependencies = beanDependencies.get(beanClass);
+ if (dependencies != null)
+ {
+ for (Class<?> dependency : dependencies)
+ {
+ getMetadata(dependency);
+ }
+ }
+ }
+ }
+ return metadataCache.get(beanClass);
+ }
+ }
+
+ private BeanMetadata generateBeanMetadata(Class<?> beanClass)
+ {
+ BeanType beanType = BeanType.state;
+ String name = beanClass.getName();
+
+ // If any of the methods are annotated with @WebRemote, it's an action bean
+ for (Method m : beanClass.getDeclaredMethods())
+ {
+ if (m.getAnnotation(WebRemote.class) != null)
+ {
+ beanType = BeanType.action;
+ String beanName =
beanManager.getBeans(beanClass).iterator().next().getName();
+ if (beanName != null)
+ {
+ name = beanName;
+ }
+ break;
+ }
+ }
+
+ BeanMetadata meta = new BeanMetadata(beanType, name);
+
+ if (beanType == BeanType.state)
+ {
+ populateMetadataProperties(beanClass, meta);
+ }
+ else
+ {
+ populateMetadataMethods(beanClass, meta);
+ }
+
+ return meta;
+ }
+
+ private void populateMetadataProperties(Class<?> beanClass, BeanMetadata meta)
+ {
+ Map<String,Type> props = getAccessibleProperties(beanClass);
+
+ for (String propertyName : props.keySet())
+ {
+ Type propertyType = props.get(propertyName);
+
+ meta.addProperty(propertyName, getFieldType(propertyType));
+ addTypeDependency(beanClass, propertyType);
+ }
+ }
+
+ private void populateMetadataMethods(Class<?> beanClass, BeanMetadata meta)
+ {
+ for (Method m : beanClass.getDeclaredMethods())
+ {
+ if (m.getAnnotation(WebRemote.class) == null) continue;
+ meta.addMethod(m.getName(), m.getParameterTypes().length);
+
+ addTypeDependency(beanClass, m.getGenericReturnType());
+ for (int i = 0; i < m.getGenericParameterTypes().length; i++)
+ {
+ addTypeDependency(beanClass, m.getGenericParameterTypes()[i]);
+ }
+ }
+ }
+
+ private void addTypeDependency(Class<?> beanType, Type dependency)
+ {
+ if (!beanDependencies.containsKey(beanType))
+ {
+ beanDependencies.put(beanType, new HashSet<Class<?>>());
+ }
+
+ Set<Class<?>> dependencies = beanDependencies.get(beanType);
+
+ if (dependencies.contains(dependency)) return;
+
+ if (dependency instanceof Class<?>)
+ {
+ Class<?> classType = (Class<?>) dependency;
+
+ if (classType.isArray())
+ {
+ addTypeDependency(beanType, classType.getComponentType());
+ return;
+ }
+
+ if (classType.getName().startsWith("java.") || classType.isPrimitive()
+ || classType.isEnum())
+ {
+ return;
+ }
+
+ dependencies.add(classType);
+ }
+ else if (dependency instanceof ParameterizedType)
+ {
+ for (Type t : ((ParameterizedType) dependency).getActualTypeArguments())
+ {
+ addTypeDependency(beanType, t);
+ }
+ }
+ }
+
+ /**
+ * Returns the metadata for the specified bean type and all of its reachable
+ * dependencies
+ *
+ * @param beanType
+ * @return
+ */
+ public Set<Class<?>> getDependencies(Class<?> beanType)
+ {
+ return beanDependencies.get(beanType);
+ }
+
+ /**
+ * Returns the remoting "type" for a specified class.
+ *
+ * @param type Type The type for which to return the remoting type.
+ * @return String The remoting type for the specified type.
+ */
+ protected String getFieldType(Type type)
+ {
+ if (type.equals(String.class)
+ || (type instanceof Class<?> && ((Class<?>)
type).isEnum())
+ || type.equals(BigInteger.class) || type.equals(BigDecimal.class))
+ {
+ return "str";
+ }
+ else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE))
+ {
+ return "bool";
+ }
+ else if (type.equals(Short.class) || type.equals(Short.TYPE)
+ || type.equals(Integer.class) || type.equals(Integer.TYPE)
+ || type.equals(Long.class) || type.equals(Long.TYPE)
+ || type.equals(Float.class) || type.equals(Float.TYPE)
+ || type.equals(Double.class) || type.equals(Double.TYPE)
+ || type.equals(Byte.class) || type.equals(Byte.TYPE))
+ {
+ return "number";
+ }
+ else if (type instanceof Class<?>)
+ {
+ Class<?> cls = (Class<?>) type;
+ if (Date.class.isAssignableFrom(cls)
+ || Calendar.class.isAssignableFrom(cls))
+ {
+ return "date";
+ }
+ else if (cls.isArray())
+ {
+ return "bag";
+ }
+ else if (cls.isAssignableFrom(Map.class))
+ {
+ return "map";
+ }
+ else if (cls.isAssignableFrom(Collection.class))
+ {
+ return "bag";
+ }
+ }
+ else if (type instanceof ParameterizedType)
+ {
+ ParameterizedType pt = (ParameterizedType) type;
+
+ if (pt.getRawType() instanceof Class<?>
+ && Map.class.isAssignableFrom((Class<?>) pt.getRawType()))
+ {
+ return "map";
+ }
+ else if (pt.getRawType() instanceof Class<?>
+ && Collection.class.isAssignableFrom((Class<?>)
pt.getRawType()))
+ {
+ return "bag";
+ }
+ }
+
+ return "bean";
+ }
+
+ /**
+ * A helper method, used internally by InterfaceGenerator and also when
+ * serializing responses. Returns a list of the property names for the
+ * specified class which should be included in the generated interface for
+ * the type.
+ *
+ * @param cls Class The class to scan for accessible properties
+ * @return Set<String> A Set containing the accessible property names
+ */
+ public Map<String,Type> getAccessibleProperties(Class<?> cls)
+ {
+ if (cls.getName().contains("EnhancerByCGLIB"))
+ cls = cls.getSuperclass();
+
+ if (!accessibleProperties.containsKey(cls))
+ {
+ synchronized (accessibleProperties)
+ {
+ if (!accessibleProperties.containsKey(cls))
+ {
+ Map<String, Type> properties = new HashMap<String, Type>();
+
+ Class<?> c = cls;
+ while (c != null && !c.equals(Object.class))
+ {
+ // Scan the declared fields
+ for (Field f : c.getDeclaredFields())
+ {
+ // If we already have this field, continue processing
+ if (properties.containsKey(f.getName()))
+ {
+ continue;
+ }
+
+ // Don't include transient or static fields
+ if (!Modifier.isTransient(f.getModifiers())
+ && !Modifier.isStatic(f.getModifiers()))
+ {
+ if (Modifier.isPublic(f.getModifiers()))
+ {
+ properties.put(f.getName(), f.getGenericType());
+ }
+ else
+ {
+ // Look for a public getter method
+ String fieldName = f.getName().substring(0, 1).toUpperCase()
+ + f.getName().substring(1);
+
+ String getterName = String.format("get%s",
fieldName);
+ Method getMethod = null;
+
+ try
+ {
+ getMethod = c.getMethod(getterName);
+ }
+ catch (SecurityException ex) { }
+ catch (NoSuchMethodException ex)
+ {
+ // it might be an "is" method...
+ getterName = String.format("is%s", fieldName);
+ try
+ {
+ getMethod = c.getMethod(getterName);
+ }
+ catch (NoSuchMethodException ex2) { }
+ }
+
+ if (getMethod != null &&
Modifier.isPublic(getMethod.getModifiers()))
+ {
+ properties.put(f.getName(),
getMethod.getGenericReturnType());
+ }
+ else
+ {
+ // Last resort, look for a public setter method
+ String setterName = String.format("set%s",
fieldName);
+ Method setMethod = null;
+ try
+ {
+ setMethod = c.getMethod(setterName, new Class[] {
f.getType() });
+ }
+ catch (SecurityException ex) { }
+ catch (NoSuchMethodException ex) { }
+
+ if (setMethod != null &&
Modifier.isPublic(setMethod.getModifiers()))
+ {
+ properties.put(f.getName(),
setMethod.getGenericParameterTypes()[0]);
+ }
+ }
+ }
+ }
+ }
+
+ // Scan the declared methods
+ for (Method m : c.getDeclaredMethods())
+ {
+ if (m.getName().startsWith("get") ||
m.getName().startsWith("is"))
+ {
+ int startIdx = m.getName().startsWith("get") ? 3 : 2;
+
+ try
+ {
+ c.getMethod(String.format("set%s", m.getName()
+ .substring(startIdx)), m.getReturnType());
+ }
+ catch (NoSuchMethodException ex)
+ {
+ // If there is no setter method, ignore and continue
processing
+ continue;
+ }
+
+ String propertyName = String.format("%s%s", Character
+ .toLowerCase(m.getName().charAt(startIdx)), m
+ .getName().substring(startIdx + 1));
+
+ if (!properties.containsKey(propertyName))
+ {
+ properties.put(propertyName, m.getGenericReturnType());
+ }
+ }
+ }
+
+ // Recursively scan the class hierarchy
+ c = c.getSuperclass();
+ }
+
+ accessibleProperties.put(cls, properties);
+ }
+ }
+ }
+
+ return accessibleProperties.get(cls);
+ }
+
+}
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 2009-12-28
22:39:01 UTC (rev 11888)
+++
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/BeanWrapper.java 2009-12-29
04:20:43 UTC (rev 11889)
@@ -12,7 +12,7 @@
import javax.enterprise.inject.spi.BeanManager;
import org.dom4j.Element;
-import org.jboss.seam.remoting.InterfaceGenerator;
+import org.jboss.seam.remoting.MetadataCache;
/**
* @author Shane Bryzak
@@ -35,7 +35,22 @@
{
super(beanManager);
}
+
+ private MetadataCache metadataCache;
+
+ @SuppressWarnings("unchecked")
+ private MetadataCache getMetadataCache()
+ {
+ if (metadataCache == null)
+ {
+ Bean<MetadataCache> bean = (Bean<MetadataCache>)
beanManager.getBeans(
+ MetadataCache.class).iterator().next();
+ metadataCache = bean.create(beanManager.createCreationalContext(bean));
+ }
+ return metadataCache;
+ }
+ @SuppressWarnings("unchecked")
@Override
public void setElement(Element element)
{
@@ -244,8 +259,7 @@
out.write(BEAN_START_TAG_CLOSE);
- for (String propertyName : InterfaceGenerator
- .getAccessibleProperties(cls))
+ for (String propertyName :
getMetadataCache().getAccessibleProperties(cls).keySet())
{
String fieldPath = path != null && path.length() > 0 ?
String.format(
"%s.%s", path, propertyName) : propertyName;
Modified:
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/WrapperFactory.java
===================================================================
---
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/WrapperFactory.java 2009-12-28
22:39:01 UTC (rev 11888)
+++
modules/remoting/trunk/src/main/java/org/jboss/seam/remoting/wrapper/WrapperFactory.java 2009-12-29
04:20:43 UTC (rev 11889)
@@ -25,8 +25,9 @@
/**
* A registry of wrapper types
*/
- private Map<String, Class> wrapperRegistry = new HashMap<String,
Class>();
+ private Map<String, Class<?>> wrapperRegistry = new HashMap<String,
Class<?>>();
+ @SuppressWarnings("unchecked")
private Map<Class, Class> classRegistry = new HashMap<Class, Class>();
/**
@@ -63,12 +64,12 @@
registerWrapperClass(Byte.class, NumberWrapper.class);
}
- public void registerWrapper(String type, Class wrapperClass)
+ public void registerWrapper(String type, Class<?> wrapperClass)
{
wrapperRegistry.put(type, wrapperClass);
}
- public void registerWrapperClass(Class cls, Class wrapperClass)
+ public void registerWrapperClass(Class<?> cls, Class<?> wrapperClass)
{
classRegistry.put(cls, wrapperClass);
}
@@ -78,6 +79,7 @@
return factory;
}
+ @SuppressWarnings("unchecked")
public Wrapper createWrapper(String type, BeanManager beanManager)
{
Class wrapperClass = wrapperRegistry.get(type);
@@ -99,6 +101,7 @@
"Failed to create wrapper for type: %s", type));
}
+ @SuppressWarnings("unchecked")
public Wrapper getWrapperForObject(Object obj, BeanManager beanManager)
{
if (obj == null)
Modified: modules/remoting/trunk/src/main/resources/org/jboss/seam/remoting/remote.js
===================================================================
--- modules/remoting/trunk/src/main/resources/org/jboss/seam/remoting/remote.js 2009-12-28
22:39:01 UTC (rev 11888)
+++ modules/remoting/trunk/src/main/resources/org/jboss/seam/remoting/remote.js 2009-12-29
04:20:43 UTC (rev 11889)
@@ -10,7 +10,7 @@
Seam.createBean = function(name) {
if (!Seam.beans[name]) return null;
- var b = new Seam.beans[name];
+ var b = new Seam.beans[name];
if (arguments.length > 1) {
b.__qualifiers = new Array();
for (var i=1; i<arguments.length; i++) {
@@ -34,8 +34,7 @@
Seam.registerBean = function(name, metadata, methods) {
var t = function() {};
- t.__name = name;
-
+ t.__name = name;
if (metadata) {
var m = new Array();
for (var f in metadata) {
@@ -45,8 +44,7 @@
m.push({field:f, type:metadata[f]});
}
t.__metadata = m;
- }
- else {
+ } else {
for (var m in methods) {
var pc = methods[m];
t.prototype[m] = function() {
@@ -58,9 +56,8 @@
var eh = (arguments.length > (pc + 1)) ? arguments[pc + 1] : undefined;
return Seam.execute(this, m, p, c, eh);
};
- }
+ }
}
-
Seam.beans[name] = t;
}
@@ -138,21 +135,17 @@
if (v1 == v2) return true;
if (v1 instanceof Date && v2 instanceof Date &&
v1.getTime() == v2.getTime()) return true;
-
- return false;
+ return false;
}
Seam.Map = function() {
this.elements = new Array();
-
Seam.Map.prototype.size = function() {
return this.elements.length;
}
-
Seam.Map.prototype.isEmpty = function() {
return this.elements.length == 0;
}
-
Seam.Map.prototype.keySet = function() {
var keySet = new Array();
for (var i=0; i<this.elements.length; i++) {
@@ -160,7 +153,6 @@
}
return keySet;
}
-
Seam.Map.prototype.values = function() {
var vals = new Array();
for (var i=0; i<this.elements.length; i++) {
@@ -168,7 +160,6 @@
}
return vals;
}
-
Seam.Map.prototype.get = function(key) {
for (var i=0; i<this.elements.length; i++) {
var e = this.elements[i];
@@ -176,7 +167,6 @@
}
return null;
}
-
Seam.Map.prototype.put = function(key, value) {
for (var i=0; i<this.elements.length; i++) {
if (Seam.equals(this.elements[i].key, key)) {
@@ -186,7 +176,6 @@
}
this.elements.push({key:key,value:value});
}
-
Seam.Map.prototype.remove = function(key) {
for (var i=0; i<this.elements.length; i++) {
if (Seam.equals(this.elements[i].key, key)) {
@@ -195,7 +184,6 @@
}
}
}
-
Seam.Map.prototype.contains = function(key) {
for (var i=0; i<this.elements.length; i++) {
if (Seam.equals(this.elements[i].key, key)) return true;
@@ -216,8 +204,7 @@
case "map": return Seam.serializeMap(v, refs);
default: return "<str>" + encodeURIComponent(v) +
"</str>";
}
- }
- else {
+ } else {
switch (typeof(v)) {
case "number":
return "<number>" + v + "</number>";
@@ -385,8 +372,7 @@
var c = Seam.createCall(component, methodName, params, callback, exceptionHandler);
if (Seam.inBatch) {
Seam.batchedCalls[Seam.batchedCalls.length] = c;
- }
- else {
+ } else {
var envelope = Seam.createEnvelope(Seam.createHeader(), c.data);
Seam.pendingCalls.put(c.id, c);
Seam.sendAjaxRequest(envelope, Seam.PATH_EXECUTE, Seam.processResponse, false);
@@ -401,8 +387,7 @@
if (window.XMLHttpRequest) {
r = new XMLHttpRequest();
if (r.overrideMimeType) r.overrideMimeType('text/xml');
- }
- else {
+ } else {
r = new ActiveXObject("Microsoft.XMLHTTP");
}
@@ -440,8 +425,7 @@
}
}
}
- }
- else {
+ } else {
Seam.displayError(r.status);
}
}
@@ -474,7 +458,7 @@
Seam.unmarshalContext(contextNode, ctx);
}
}
- Seam.context.setConversationId(ctx.getConversationId() ? ctx.getConversationId() :
null);
+ Seam.context.setConversationId(ctx.getConversationId() ? ctx.getConversationId() :
null);
if (bodyNode) {
var n = cn(bodyNode, "result");
if (n) {
@@ -489,8 +473,7 @@
var msgNode = cn(exceptionNode, "message");
var msg = Seam.unmarshalValue(msgNode.firstChild);
call.exceptionHandler(new Seam.Exception(msg));
- }
- else {
+ } else {
var refs = Seam.unmarshalRefs(refsNode);
var v = Seam.unmarshalValue(valueNode.firstChild, refs);
call.callback(v, ctx, callId);
@@ -581,7 +564,7 @@
Seam.cloneObject = function(obj, refMap) {
if (refMap && refMap.contains(obj)) return refMap.get(obj);
if (typeof obj == "object") {
- if (obj == null) return null;
+ if (obj == null) return null;
var m = (refMap == null) ? new Seam.Map() : refMap;
if (obj instanceof Array) {
var c = new Array();
@@ -589,16 +572,15 @@
for (var i=0; i<obj.length; i++) {
c[i] = Seam.cloneObject(obj[i], m);
}
- return c;
+ return c;
+ } else if (obj instanceof Date) {
+ return new Date(obj.getTime())
}
- else if (obj instanceof Date) {
- return new Date(obj.getTime())
- }
var t = Seam.getBeanType(obj);
var c = (t == undefined) ? new Object() : new t();
m.put(obj, c);
- for (var p in obj) c[p] = Seam.cloneObject(obj[p], m);
- return c;
+ for (var p in obj) c[p] = Seam.cloneObject(obj[p], m);
+ return c;
}
return obj;
}
@@ -635,8 +617,7 @@
document.body.appendChild(d);
var text = document.createTextNode(Seam.loadingMessage);
d.appendChild(text);
- }
- else {
+ } else {
Seam.loadingMsgDiv.innerHTML = Seam.loadingMessage;
Seam.loadingMsgDiv.style.visibility = 'visible';
}
@@ -652,27 +633,22 @@
this.method = null;
this.params = new Array();
this.expression = null;
-
Seam.Action.prototype.setBeanType = function(beanType) {
this.beanType = beanType;
return this;
}
-
Seam.Action.prototype.setQualifiers = function(qualifiers) {
this.qualifiers = qualifiers;
return this;
}
-
Seam.Action.prototype.setMethod = function(method) {
this.method = method;
return this;
}
-
Seam.Action.prototype.addParam = function(param) {
this.params.push(param);
return this;
}
-
Seam.Action.prototype.setExpression = function(expr) {
this.expression = expr;
return this;
@@ -683,13 +659,13 @@
this.propertyChange = new Seam.Map();
Seam.Changeset.prototype.addProperty = function(name, val) {
this.propertyChange.put(name, val);
- }
+ }
}
Seam.Delta = function(model) {
this.model = model;
this.refs = new Seam.Map();
-
+
Seam.Delta.prototype.testEqual = function(v1, v2) {
var eq = this.testEqual;
if (v1 == null) return v2 == null;
@@ -697,32 +673,29 @@
case "number":
return typeof(v2) == "number" && v1 == v2;
case "boolean":
- return typeof(v2) == "boolean" && v1 == v2;
+ return typeof(v2) == "boolean" && v1 == v2;
case "string":
- return typeof(v2) == "string" && v1 == v2;
+ return typeof(v2) == "string" && v1 == v2;
case "object":
if (v1 instanceof Date) {
return (v2 instanceof Date) && v1.getTime() == v2.getTime();
- }
- else if (Seam.getBeanType(v1)) {
+ } else if (Seam.getBeanType(v1)) {
return this.getSourceObject(v1) == v2;
- }
- else if (v1 instanceof Array) {
+ } else if (v1 instanceof Array) {
if (!(v2 instanceof Array)) return false;
if (v1.length != v2.length) return false;
for (var i=0; i<v1.length; i++) {
- if (!eq(v1[i], v2[i])) return false;
+ if (!eq(v1[i], v2[i])) return false;
}
return true;
- }
- else if (v1 instanceof Seam.Map) {
+ } else if (v1 instanceof Seam.Map) {
if (!(v2 instanceof Seam.Map)) return false;
if (v1.size() != v2.size()) return false;
for (var i=0; i<v1.size(); i++) {
var e = v1.elements[i];
if (Seam.getBeanType(e.key) && eq(e.value,
v2.get(this.getSourceObject(e.key)))) break;
if (eq(e.value, v2.get(e.key)) && (e.value != null ||
v2.contains(e.key))) break;
- return false;
+ return false;
}
return true;
}
@@ -738,58 +711,56 @@
}
cs.addProperty(prop, obj[prop]);
}
-
- Seam.Delta.prototype.scanForChanges = function(obj) {
+
+ Seam.Delta.prototype.scanForChanges = function(obj) {
if (obj == null || this.refs.contains(obj)) return;
this.refs.put(obj, null);
if (Seam.getBeanType(obj)) {
var src = this.getSourceObject(obj);
var m = Seam.getBeanMetadata(obj);
for (var i=0; i<m.length; i++) {
- var f=m[i].field;
- var t=m[i].type;
+ var f=m[i].field;
+ var t=m[i].type;
if (src && !this.testEqual(obj[f], src[f]))
this.registerPropertyChange(obj, f);
if (t == "bag" || t == "map" || t == "bean")
this.scanForChanges(obj[f]);
}
- }
- else if (obj instanceof Array) {
+ } else if (obj instanceof Array) {
var src = this.getSourceObject(obj);
if (!this.testEqual(obj, src)) this.refs.put(obj, true);
for (var i=0; i<obj.length; i++) {
if (Seam.getBeanType(obj[i]) || obj[i] instanceof Array || obj[i] instanceof
Seam.Map) {
- this.scanForChanges(obj[i]);
+ this.scanForChanges(obj[i]);
}
}
- }
- else if (obj instanceof Seam.Map) {
+ } else if (obj instanceof Seam.Map) {
var src = this.getSourceObject(obj);
if (!this.testEqual(obj, src)) this.refs.put(obj, true);
for (var i=0; i<obj.elements.length; i++) {
var k = obj.elements[i].key;
var v = obj.elements[i].value;
if (Seam.getBeanType(k) || k instanceof Array || k instanceof Seam.Map) {
- this.scanForChanges(k);
- }
+ this.scanForChanges(k);
+ }
if (Seam.getBeanType(v) || v instanceof Array || v instanceof Seam.Map) {
- this.scanForChanges(v);
+ this.scanForChanges(v);
}
- }
+ }
}
}
Seam.Delta.prototype.getSourceObject = function(obj) {
for (var i=0;i<this.model.workingRefs.length; i++) {
- if (obj == this.model.workingRefs[i]) return this.model.sourceRefs[i];
+ if (obj == this.model.workingRefs[i]) return this.model.sourceRefs[i];
}
return null;
}
-
- Seam.Delta.prototype.buildRefs = function() {
- var refs = new Array();
+
+ Seam.Delta.prototype.buildRefs = function() {
+ var refs = new Array();
for (var i=0; i<this.refs.elements.length; i++) {
refs.push(this.refs.elements[i]);
- }
- return refs;
+ }
+ return refs;
}
}
@@ -825,7 +796,7 @@
}
this.beans.push({alias: alias, bean: bean, qualifiers: q});
}
-
+
Seam.Model.prototype.addBeanProperty = function(alias, bean, property) {
var q = null;
if (arguments.length > 3) {
@@ -835,7 +806,7 @@
}
}
this.beans.push({alias: alias, bean: bean, property: property, qualifiers: q});
- }
+ }
Seam.Model.prototype.fetch = function(action, callback) {
this.callback = callback;
@@ -869,8 +840,7 @@
}
d += "</refs>";
}
- }
- else if (a.expression) {
+ } else if (a.expression) {
d += "<target>" + a.expression + "</target>";
}
d += "</action>";
@@ -908,7 +878,7 @@
this.workingRefs = Seam.cloneObject(this.sourceRefs);
for (var i=0; i<valueNodes.length; i++) {
var value = Seam.unmarshalValue(valueNodes[i].firstChild,this.workingRefs);
- this.values.push({alias:valueNodes[i].getAttribute("alias"),value:value,
refIndex:i});
+ this.values.push({alias:valueNodes[i].getAttribute("alias"),value:value,
refIndex:i});
}
if (this.callback) this.callback(this);
@@ -924,7 +894,7 @@
Seam.pendingCalls.put(r.id, r);
Seam.sendAjaxRequest(env, Seam.PATH_MODEL, Seam.processResponse, false);
}
-
+
Seam.Model.prototype.createApplyRequest = function(a, delta) {
var callId = "" + Seam.__callId++;
var d = "<model uid=\"" + this.id + "\"
operation=\"apply\" callId=\"" + callId + "\">";
@@ -942,8 +912,7 @@
}
d += "</params>";
}
- }
- else if (a.expression) {
+ } else if (a.expression) {
d += "<target>" + a.expression + "</target>";
}
d += "</action>";
@@ -960,17 +929,16 @@
d += "<member name=\"" + v.propertyChange.elements[j].key +
"\">";
d += Seam.serializeValue(v.propertyChange.elements[j].value, null, refs);
d += "</member>";
- }
+ }
+ } else {
+ d += Seam.serializeValue(k, null, refs);
}
- else {
- d += Seam.serializeValue(k, null, refs);
- }
d += "</changeset>";
}
}
d += "</delta>";
if (refs.length > 0) {
- d += "<refs>";
+ d += "<refs>";
var idx = this.workingRefs.length;
for (var i=0; i<refs.length; i++) {
if (this.getRefId(refs[i]) != -1) {
@@ -982,11 +950,11 @@
d += "</model>";
return {data:d, id:callId, model:this};
}
-
+
Seam.Model.prototype.getRefId = function(v) {
for (var i=0; i<this.workingRefs.length; i++) {
- if (this.workingRefs[i] == v) return i;
+ if (this.workingRefs[i] == v) return i;
}
return -1;
- }
+ }
}
\ No newline at end of file