[seam-commits] Seam SVN: r11889 - in modules/remoting/trunk/src/main: java/org/jboss/seam/remoting/wrapper and 1 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Mon Dec 28 23:20:43 EST 2009


Author: shane.bryzak at 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
+ */
+ at 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



More information about the seam-commits mailing list