/*
 * Smart GWT (GWT for SmartClient)
 * Copyright 2008 and beyond, Isomorphic Software, Inc.
 *
 * Smart GWT is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * is published by the Free Software Foundation.  Smart GWT is also
 * available under typical commercial license terms - see
 * http://smartclient.com/license
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */

package com.smartgwt.client.bean;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;

import com.smartgwt.client.bean.BeanProperty;

import com.smartgwt.client.widgets.BaseWidget;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.util.SC;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import java.lang.annotation.ElementType;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// ==================
// Technical Overview
// ==================
//
// Here is a road-map to the mechanics of the whole bean-factory mechanism.
//
// The BeanFactory class is the main class. There are subclasses for several of
// the core SmartGWT classes, starting with BeanFactoryForBaseWidget and
// BeanFactoryForDataClass. There is then one subclass, and one instance, for
// each BeanClass (which, for now, must be a subclass of BaseWidget or
// DataClass). So, each subclass for a BeanClass is a singleton.
//
// The main interface is meant to be the static methods on BeanFactory, but
// you can cache a BeanFactory instance and call the instance methods as well
// (for a slight efficiency gain).
//
// You trigger the generation of the subclasses through mechanisms discussed
// below.
//
// Each generated BeanFactory creates a corresponding SmartClient SGWTFactory
// and stores it in the isc[] space. This allows the SmartClient side to deal
// with SmartGWT classes through many usual SmartClient idioms. 
//
// Each BeanFactory keeps a hash of BeanProperty objects, where each
// BeanProperty represents one property. A property has a name, and may have
// multiple BeanMethod objects (representing getters and setters). Multiple
// setters would be overloaded methods, whereas multiple getters are such
// things as getWidthAsString().
//
// There are two big technical problems that need to be overcome to make this
// all work (and lots of little ones). The first is that you can't normally
// call a GWT function based on a run-time string -- you have to specify
// functions at compile time. So how does BeanMethod actually call the
// function? 
//
// There are several approaches possible. You could iterate through
// possible comparisons, by generating code like this:
//
// if (functionName == "getWidth") {
//    return bean.getWidth();
// } else if (functionName == "getHeight") {
//    return bean.getHeight();
// } ...
//
// However, that would be very inefficient, especially since there are so many
// possible properties.
//
// A second way would be to generate an anonymous subclass for every method type,
// something like this:
//
// new BeanMethod ("width") {
//    public Object getPropertyFromBean (BeanClass bean) {
//       return bean.getWidth();
//    }
// }
//
// This works, but it has a fair bit of overhead in the generated code size,
// because it is literally generating at least one (anonymous) class for every
// property, and each class has some boilerplate overhead. The next approach
// reduces the generated code size by about one-third (in my testing).
//
// The approach actually used is to create a JSNI function for each getter and
// setter. Conceptually, the generated code does something like this:
//
// public native Object callGetter (Object bean, String functionName) /*-{
//    var functions = {
//      width: function (b) {return b.@com.smartgwt.widgets.Canvas::getWidth()();},
//      height: function (b) {return b.@com.smartgwt.widgets.Canvas::getHeight()();}
//      ...
//    };
//    return functions[functionName](bean);
// }-*/;
//
// Though it's not implemented quite like that, because we would want to cache
// the hash of the functions, and there are sometimes multiple methods per
// function name. But you get the general idea -- experimentally, there is
// about one-third less overhead than through the anonymous subclass approach.
//
// The other big technical problem is what to do about the argument types ...
// that is, the types which setters expect, and the types which getters
// provide. Thinking about it from a purely SmartGWT point of view, if the
// property name is now dynamic and can be provided at run-time, it doesn't
// make much sense to require that the caller know exactly what type is needed.
// Of course, the SmartClient XML system can provide typed arguments for
// properties, but even there we would always need to convert between int and
// Integer and other numeric types. Also, where there are multiple overloaded
// setters, we need to use some intelligence in picking the setter, depending
// on what type of argument we've been provided. There are also various
// complexities in transitioning data from SmartClient Javascript to SmartGWT
// Java that need to be ironed out.
//
// That is what the BeanValueType class deals with. Essentially, it is meant to
// be able to do any of the conversions we might need. Some of them are fairly
// obvious, such as the conversion amongst numeric types and to and from Strings.
// But it also handles conversion among array types, which is trickier (and
// necessary, since all arrays provided from JavaScript are untyped as far as
// GWT is concerned). It also deals with SmartGWT-specific conversions, like
// the various ValueEnum types and conversions which are possible when we
// know what the desired type is (for instance, converting a JavaScriptObject
// which we know is a SmartClient Canvas to a SmartGWT Canvas).
//
// The general interface is in com.smartgwt.client.bean.BeanValueType, and the
// various files in com.smartgwt.client.bean.types.* represent subclasses.
// Some subclasses deal with a single type. Others are superclasses where we
// need to generate a subclass for each type. Others can be used to deal with
// more than one type.
//
// So, for setting a property, the basic steps are:
//
// 1. The BeanFactory static method finds the right BeanFactory for the object.
// 2. The BeanFactory instance picks out the correct BeanProperty.
// 3. The BeanProperty looks at its BeanMethods, and if more than one, uses
//    BeanValueType to determine which BeanMethod is best for the value supplied.
// 4. The BeanProperty uses BeanValueType to convert the value to the type
//    that the selected BeanMethod needs.
// 5. The BeanMethod uses JSNI to call the actual setter.

/**
 * Utility class for creating objects and setting properties at run-time via
 * reflection-like mechanisms.
 *
 * <p>Subclasses must be generated for each {@link Class} you want to use. To trigger
 * the generation of subclasses, see the documentation on
 * {@link com.smartgwt.client.docs.Reflection registering classes for reflection}.
 *
 * <p>Once the appropriate subclass has been created, you can use the class via
 * the static methods.
 *
 * <p>For the moment, this class only works with subclasses of 
 * {@link com.smartgwt.client.widgets.BaseWidget} or 
 * {@link com.smartgwt.client.widgets.form.fields.FormItem}.
 */
public abstract class BeanFactory<BeanClass> {
    // --------------------
    // Generator Interfaces
    // --------------------

    /**
    * An interface which you can extend in order to register classes
    * with the <code>BeanFactory</code> reflection mechanism.
    *
    * <p><b>Note</b>: While this mechanism continues to work, there is
    * now an easier way to register classes for reflection, by annotating
    * them with the {@link BeanFactory.Generate} annotation.
    *
    * <p>In order to use a {@link BeanFactory} for a class, you need to
    * register it by generating a <code>BeanFactory</code> subclass for the
    * class. You can use {@link BeanFactory.CanvasMetaFactory} to scan the
    * class path and register every {@link Canvas} subclass (including your
    * custom subclasses), or use {@link BeanFactory.FormItemMetaFactory} to
    * regiser every {@link com.smartgwt.client.widgets.form.fields.FormItem} 
    * subclass. However, if you know that you only need to register some
    * classes for reflection, then you can use
    * <code>BeanFactory.MetaFactory</code> instead (or, even more conveniently,
    * the {@link BeanFactory.Generate} annotation).
    *
    * <p>Usage of <code>BeanFactory.MetaFactory</code> is most easily explained
    * with an example. First, you define an interface. (Note that it can be an
    * inner interface.)
    *
    * <blockquote><pre>
    * public interface MyMetaFactory extends BeanFactory.MetaFactory {
    *     BeanFactory&lt;ListGrid&gt; getListGridFactory();
    *     BeanFactory&lt;TileGrid&gt; getTileGridBeanFactory();
    * }</pre></blockquote>
    *
    * ... and then you trigger the generation process:
    *
    * <blockquote><pre>
    * GWT.create(MyMetaFactory.class);</pre></blockquote>
    *
    * <p>Each function in the interface you define will result in the creation of
    * one <code>BeanFactory</code> ... so, in this case, we would end up with
    * bean factories for <code>ListGrid</code> and <code>TileGrid</code>. The rules
    * are as follows:
    *
    * <ol>
    * <li>The interface must extend <code>BeanFactory.MetaFactory</code></li>
    * <li>The return type for each function must be <code>BeanFactory</code>,
    * with a generic type that is the class you want the factory for.</li>
    * <li>The function must take no arguments.</li>
    * <li>The name of the function doesn't matter.</li>
    * </ol>
    *
    * <p>If you want, you can keep a reference to the results of the <code>GWT.create()</code>,
    * and call the functions:
    * 
    * <blockquote><pre>
    * MyMetaFactory metaFactory = GWT.create(MyMetaFactory.class);
    * BeanFactory&lt;TileGrid&gt; myTileGridFactory = myMetaFactory.getTileGridBeanFactory();</pre></blockquote>
    *
    * <p>However, you don't have to do that ... you can ignore the results of <code>GWT.create()</code>
    * and just use the <code>BeanFactory</code> static API:
    *
    * <blockquote><pre>
    * GWT.create(MyMetaFactory.class);
    * Object myGrid = BeanFactory.newInstance("TileGrid");
    * BeanFactory.setProperty(myGrid, "width", 207);</pre></blockquote>
    *
    * ... except that "TileGrid" would probably be a variable!
    *
    * <p>You can also use the generated classes in {@link com.smartgwt.client.docs.ComponentXML}, 
    * by specifying the fully-qualified class name as the constructor for objects.
    *
    * <p>Note that the call to <code>GWT.create()</code> must occur at run-time before
    * the factories are used. However, you can modularize by creating some factories
    * first and other factories later, as long as each factory is created before
    * being used.
    *
    * @see BeanFactory.Generate
    */
    public static interface MetaFactory {
        // One understands that a factory to generate factories is in danger of
        // being mocked. However, it really is the right way to do this in
        // GWT.
        //
        // The problem with using GWT.create to create the factory directly (e.g.
        // GWT.create(ListGrid.class)) is that then you have to register a
        // generator for ListGrid.class (or a superclass), and you can only have
        // one generator registered for that class. So, you can't generate anything
        // else for a ListGrid via that mechanism -- it's one shot.
        //
        // Also, the semantics are wrong. We're not generating the ListGrid class
        // -- we're generating a factory. Of course, it's legal to respond to
        // GWT.create(ListGrid.class) by generating something other than a
        // ListGrid class, but it is a bit odd.
        //
        // However, it would be tedious to have to define a separate interface for
        // each factory, so we instead define an interface for a second-level
        // factory -- a factory that can create factories.
        //
        // So, MetaFactory is just a marker -- by defining an interface
        // which extends it, you tell GWT to generate factories.
        //
        // Note that for most uses, BeanFactory.MetaFactory has now been superseded
        // by the more convenient BeanFactory.Generate annotation, but the older
        // method still works.
        
    }

    /**
    * Interface used to trigger the generation and registration of reflection
    * metadata for {@link com.smartgwt.client.widgets.Canvas} and all 
    * its subclasses found in the classpath (including your custom subclasses).
    *
    * <p>Usage is to simply call <code>GWT.create(BeanFactory.CanvasMetaFactory.class);</code>
    * Note that the return value is not useful -- you would then simply use
    * the {@link BeanFactory} API, like so:
    *
    * <blockquote><pre>
    * GWT.create(BeanFactory.CanvasMetaFactory.class);
    *
    * String className = "com.smartgwt.client.widgets.Button"; 
    * Canvas canvas = (Canvas) BeanFactory.newInstance(className);
    *
    * BeanFactory.setProperty(canvas, "title", "Hello World");
    * BeanFactory.setProperty(canvas, "tooltip", "My tooltip");
    * canvas.draw();</pre></blockquote>
    *
    * <p>Furthermore, the className can also be used as a Constructor in
    * {@link com.smartgwt.client.docs.ComponentXML Component XML}. 
    *
    * <p>Alternatively if only specific <code>Canvas</code> types need to be
    * instantiated and configured dynamically, you can generate specific
    * factories by annotating classes with the {@link BeanFactory.Generate}
    * annotation instead.
    * 
    * <p>If there are only a limited number of types which require dynamic
    * configuration, it will save code size to use the
    * <code>BeanFactory.Generate</code> annotation for those types. Once the
    * metadata is generated, GWT's opportunities to prune dead code are more
    * limited for those classes, since it cannot know what properties will be
    * set or retrieved at run-time.
    *
    * @see BeanFactory.Generate
    */
    public static interface CanvasMetaFactory {

    }

    /**
    * Interface used to trigger the generation and registration of reflection
    * metadata for {@link com.smartgwt.client.widgets.form.fields.FormItem} and
    * all its subclasses found in the classpath (including your custom subclasses).
    *
    * <p>Usage is to simply call <code>GWT.create(BeanFactory.FormItemMetaFactory.class);</code>
    * Note that the return value is not useful -- you would then simply use
    * the {@link BeanFactory} API, like so:
    *
    * <blockquote><pre>
    * GWT.create(BeanFactory.FormItemMetaFactory.class);
    *
    * String className = "com.smartgwt.client.widgets.form.fields.TextItem"; 
    * Object canvas = BeanFactory.newInstance(className);
    *
    * BeanFactory.setProperty(canvas, "title", "Hello World");
    * BeanFactory.setProperty(canvas, "tooltip", "My tooltip");
    * </pre></blockquote>
    *
    * <p>Furthermore, the className can also be used as a Constructor in
    * {@link com.smartgwt.client.docs.ComponentXML Component XML}. 
    *
    * <p>Alternatively if only specific <code>FormItem</code> types need to be
    * instantiated and configured dynamically, you can generate specific
    * factories by annotating classes with the {@link BeanFactory.Generate}
    * annotation instead.
    * 
    * <p>If there are only a limited number of types which require dynamic
    * configuration, it will save code size to use the
    * <code>BeanFactory.Generate</code> interface for those types. Once the
    * metadata is generated, GWT's opportunities to prune dead code are more
    * limited for those classes, since it cannot know what properties will be
    * set or retrieved at run-time.
    *
    * @see BeanFactory.Generate
    */
    public static interface FormItemMetaFactory {

    }

    // This is a marker interface used internally to trigger processing of the
    // BeanFactory.Generate annotations, via a call to GWT.create in
    // com.smartgwt.client.SmartGwtEntryPoint.
    public static interface AnnotationMetaFactory {

    }

    // -----------
    // Annotations
    // -----------
    
    // We use annotations to indicate some metadata that the generator needs
    // when generating a BeanFactory.

    // An annotation used to indicate that a SmartGWT class is part of the
    // the framework. The generator picks this up when generating the
    // BeanFactory.
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface FrameworkClass {}

    // An annotation used to indicate the default scClassName that a class
    // will use. The generator picks this up when generating the BeanFactory.
    // Of course, the class will also need to implement getScClassName (if
    // it has a different scClassName than its superclass). But the generator
    // can't pick up the results of getScClassName at compile-time. So, we
    // need to indicate the default in the metadata.
    @Inherited
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface ScClassName {
        String value();
    }

    /**
     * Annotation which will trigger the generation of BeanFactories.
     *
     * <p>If no value is supplied, a BeanFactory will be generated for the class
     * that the annotation is applied to. So, if you have a custom class for which you would like to generate
     * a BeanFactory, you can just annotate it like this:
     * <blockquote><pre>
     * {@literal @}BeanFactory.Generate
     * public MyCanvas extends Canvas {
     *    ...
     * }</pre></blockquote>
     * <p>
     * If you want to generate BeanFactories for framework classes, you can
     * supply a value for the annotation, where the value is an array
     * of class literals. For instance:
     * <blockquote><pre>
     * {@literal @}BeanFactory.Generate({ListGrid.class, TreeGrid.class})
     * public interface EmptyInterface {
     * }</pre></blockquote>
     * <p>
     * Note that when supplying values for the annotation, the class you
     * annotate (here <code>EmptyInterface</code>) will <b>not</b> itself have
     * a BeanFactory generated for it. Thus, you can use an empty inner interface
     * for this purpose.
     * <p>
     * If you want to generate BeanFactories for all {@link Canvas}
     * subclasses or all {@link com.smartgwt.client.widgets.form.fields.FormItem} 
     * subclasses, you can use {@link BeanFactory.CanvasMetaFactory} or 
     * {@link BeanFactory.FormItemMetaFactory} instead. However, that is less
     * efficient if there are only a limited number of classes which need
     * BeanFactories.
     *
     * @see BeanFactory.CanvasMetaFactory
     * @see BeanFactory.FormItemMetaFactory
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Generate {
        Class[] value() default {};
    }

    // ------------------------------------------
    // Static housekeeping properties and methods
    // ------------------------------------------
   
    // The sgwtModule is a JavaScriptOject which we create and register with
    // SmartClient to provide functions that need to be handled on a per-module
    // basis. For each relevant SmartClient object that we tag with a __ref
    // property -- holding the GWT wrapper -- we also provide a __module
    // property that indicates which set of module functions can handle that
    // __ref.
    private static JavaScriptObject sgwtModule = exportSGWTModule();

    private static native JavaScriptObject exportSGWTModule () /*-{
        var module = {
            newInstance : $entry(@com.smartgwt.client.bean.BeanFactory::newInstance(Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)),

            setProperty : $entry(function (obj, propName, value) {
                // We need to pre-convert the value to a Java object, so that
                // it can be a java.lang.Object when it reaches the JSNI glue code
                var javaValue = this.convertToJava(value);

                // Then set the property
                @com.smartgwt.client.bean.BeanFactory::setProperty(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)(obj, propName, javaValue);
            }),
            
            getProperty : $debox($entry(function (obj, propName) {
                // Get the Java property
                var javaValue = @com.smartgwt.client.bean.BeanFactory::getProperty(Ljava/lang/Object;Ljava/lang/String;)(obj, propName);

                // Convert it to a JavaScript value
                var jsArray = @com.smartgwt.client.bean.BeanValueType::wrapInJavascriptArray(Ljava/lang/Object;)(javaValue);

                // Dereference the wrapped array
                return jsArray[0];
            })),

            getPropertyAsString : $debox($entry(@com.smartgwt.client.bean.BeanFactory::getPropertyAsString(Ljava/lang/Object;Ljava/lang/String;))),

            getAttributes : $entry(function (className) {
                // Get the Java attributes, which will be a String[]
                var javaValue = @com.smartgwt.client.bean.BeanFactory::getAttributes(Ljava/lang/String;)(className);

                // Convert it to a JavaScript value
                var jsArray = @com.smartgwt.client.bean.BeanValueType::wrapInJavascriptArray(Ljava/lang/Object;)(javaValue);

                // Dereference the wrapped array
                return jsArray[0];
            }),
            
            getOrCreateJsObj : $entry(@com.smartgwt.client.bean.BeanFactory::getOrCreateJsObj(Ljava/lang/Object;)),

            setJsObj : $entry(@com.smartgwt.client.bean.BeanFactory::setJsObj(Ljava/lang/Object;Lcom/google/gwt/core/client/JavaScriptObject;)),

            getSGWTFactory : $entry(function (object) {
                var beanFactory = @com.smartgwt.client.bean.BeanFactory::getFactory(Ljava/lang/Object;)(object);
                if (beanFactory) {
                    return beanFactory.@com.smartgwt.client.bean.BeanFactory::sgwtFactory;
                } else {
                    return null;
                }
            }),

            getSGWTSuperClass : $entry(function (sgwtFactory) {
                var sc = sgwtFactory.beanFactory.@com.smartgwt.client.bean.BeanFactory::superclassFactory;
                if (sc) {
                    return sc.@com.smartgwt.client.bean.BeanFactory::sgwtFactory;
                } else {
                    return null;
                }
            }),

            getDefaultScClassName : $debox($entry(function (sgwtFactory) {
                return sgwtFactory.beanFactory.@com.smartgwt.client.bean.BeanFactory::getDefaultScClassName()();
            })),

            isSGWTFrameworkClass : $debox($entry(function (sgwtFactory) {
                return sgwtFactory.beanFactory.@com.smartgwt.client.bean.BeanFactory::isFrameworkClass()();
            })),

            // Performs basic conversion from a JavaScript type to an
            // equivalent Java value, without taking into account any
            // particularly desired Java types. This method may return a
            // JavaScriptObject -- either the value itself, or possibly a new
            // JavaScriptObject constructed from a config block.  
            //
            // JSOHelper.convertToJava has some relevant code, but it
            // doesn't quite do what we want in some respects. In particular,
            // it converts Canvas instances to strings, which is not what we
            // want -- we leave any SmartClient instances as JavaScriptObjects
            // and wrap them in a BeanValueType later (if possible). Also,
            // if we get a config block with a _constructor, we construct the
            // object and make it a JavaScriptObject (rather than turning it
            // into a Map).
            //
            // Note that we need to define this function on the Javascript side,
            // because in development mode it's necessary to do the conversion
            // before we hit GWT's JSNI <-> Java glue code. Otherwise, we have no
            // way of properly declaring parameter types on the Java side. If
            // we use JavaScriptObject as the parameter type, the glue code 
            // auto-converts strings to java.lang.String, which isn't castable to
            // JavaScriptObject. If we use Object, then the glue code doesn't
            // auto-convert primitive numbers, so we would need to take care of that
            // to avoid errors. None of that would be a problem if we had multiple
            // signatures for each parameter type ... but to have just one signature
            // we need to convert to Java objects before we hit the glue code. 

            convertToJava : function (obj) {
                if (obj == null) return null;
                
                // If we're passed a real Java Object, just return it.
                if ($wnd.SmartGWT.isNativeJavaObject(obj)) return obj;

                // Use typeof to check for primitive types. This is similar to
                // what JSOHelper.convertToJavaType does, but just picks out
                // the primitive types and uses isA to discriminate among
                // others.
                switch (typeof obj) { 
                    case 'undefined':
                        return null;
                        break;

                    case 'string':
                        return obj;
                        break;
                    
                    case 'number':
                        if (obj.toString().indexOf('.') == -1) {
                            if (obj <= @java.lang.Integer::MAX_VALUE && obj >= @java.lang.Integer::MIN_VALUE) {
                                return @com.smartgwt.client.util.JSOHelper::toInteger(I)(obj);
                            } else {
                                return @com.smartgwt.client.util.JSOHelper::toLong(D)(obj);
                            }
                        } else {
                            // Convert all non-integral numbers to Double,
                            // rather than Float, in order to avoid loss of
                            // precision in development mode.
                            return @com.smartgwt.client.util.JSOHelper::toDouble(D)(obj);
                        }
                        break;

                    case 'boolean':
                        return @com.smartgwt.client.util.JSOHelper::toBoolean(Z)(obj);
                        break;

                    default:
                        // If it's not a primitive type, then check various isA
                        // possibilities
                        if ($wnd.isc.isA.Date(obj)) {
                            return @com.smartgwt.client.util.JSOHelper::toDate(D)(obj.getTime());
                        }
                        
                        if ($wnd.isc.isAn.Array(obj)) {
                            var convertedArray = $wnd.Array.create();
                            
                            // First, convert each member of the array, by calling
                            // ourselves recursively
                            for (var i = 0; i < obj.length; i++) {
                                convertedArray[i] = this.convertToJava(obj[i]);
                            }

                            // Now we've converted all our members and we need to
                            // return a Java array
                            return @com.smartgwt.client.util.JSOHelper::convertToJavaObjectArray(Lcom/google/gwt/core/client/JavaScriptObject;)(convertedArray);
                        } 
                        
                        // If it's a SmartClient instance, then check for the __ref,
                        // and return the SmartGWT object if it exists. Otherwise, just
                        // return the SmartClient instance itself -- the specific
                        // BeanValueType may be able to do further conversion.
                        if ($wnd.isc.isAn.Instance(obj)) {
                            if (obj[$wnd.isc.gwtRef]) { 
                                return obj[$wnd.isc.gwtRef];
                            } else {
                                return obj;
                            }
                        }

                        // Check if it is a Class object, for which
                        // isc.isAn.Instance() returns false. We can't do any
                        // meaningful conversion of Class objects, so we just
                        // pass them through. Note that if a particular method
                        // wants a String, BeanValueType will convert it later
                        // to the class name.
                        if ($wnd.isc.isA.ClassObject(obj)) return obj;
                        
                        if ($wnd.isc.isAn.Object(obj)) {
                            // If it's a String object, use the primitive instead
                            if (obj instanceof $wnd.String || obj instanceof String) return obj.toString();

                            // And check for Number or Boolean and call recursively
                            if (obj instanceof $wnd.Number || obj instanceof Number || 
                                obj instanceof $wnd.Boolean || obj instanceof Boolean) {
                                return this.convertToJava(obj.valueOf());
                            }
                        
                            // If it has a __ref, then just return that.
                            if (obj[$wnd.isc.gwtRef]) { 
                                return obj[$wnd.isc.gwtRef];
                            }

                            if (obj._constructor == 'DateRange') {
                                return @com.smartgwt.client.widgets.form.fields.DateRangeItem::convertToDateRange(Lcom/google/gwt/core/client/JavaScriptObject;)(obj);
                            }
                            
                            // If it's a config block with a _constructor, then
                            // construct the SmartClient object and return it as a
                            // JavaScriptObject. This is adapted from
                            // Canvas.createCanvas (which isn't a class method, so we
                            // can't call it from here). 
                            //
                            // Note that we only are supporting a config block here --
                            // we're not dealing with autochildren or ID's specified as
                            // strings (since, at this level, we don't know that that
                            // is what is meant -- we would convert JavaScript strings
                            // to Java strings). In principle, we could convert from
                            // strings to Canvas via autochildren or ID's later on in
                            // BeanValueTypes that take Canvii).
                            //
                            // An alternative would be to modify various methods (like
                            // Layout.setMembers) so that they will take config blocks
                            // (as the SmartClient equivalents do). However, this is
                            // simpler for now. 
                            if (obj._constructor && $wnd.isc[obj._constructor]) {
                                var cons = obj._constructor;
                                delete obj._constructor;
                                return $wnd.isc.ClassFactory.newInstance(cons, obj);
                            }

                            // If nothing else, convert the POJO to a Map
                            var javaMap = @java.util.LinkedHashMap::new()();

                            // If it's a tree node, clean it up before converting
                            // otherwise we may end up serializing out all parents and
                            // children!
                            if ($wnd.isc.Tree != null && obj._isc_tree != null || obj.$42c != null) {
                                obj = $wnd.isc.Tree.getCleanNodeData(obj);
                            }
                            
                            for (var fieldName in obj) {
                                // This shouldn't really happen, but ...
                                if (!$wnd.isc.isA.String(fieldName)) continue;

                                // Don't convert the __module created by BeanFactory
                                if (fieldName == $wnd.isc.gwtModule) continue;

                                // If the field name is '__ref', the the value
                                // is already a GWT java object reference.
                                // Otherwise, we convert it by calling
                                // ourselves recursively.
                                var convertedVal = fieldName == $wnd.isc.gwtRef ? obj[fieldName] : this.convertToJava(obj[fieldName]);
                                
                                // And add it to the map
                                @com.smartgwt.client.util.JSOHelper::doAddToMap(Ljava/util/Map;Ljava/lang/String;Ljava/lang/Object;)(javaMap, fieldName, convertedVal);
                            }

                            return javaMap;
                        }
                }
                
                // If we haven't returned anything yet, then we really don't know how
                // to convert the JavaScriptObject ... we may as well just return it.
                return obj;
            }
        };

        return module;
    }-*/;

    public static JavaScriptObject getSGWTModule () {
        return sgwtModule;
    }

    // When creating a new instance via the BeanFactory, we may store some
    // properties here to be applied to the new instance. These properties are
    // picked up by the no-arg constructor, and then immediately cleared here
    // (so that they don't get picked up by any nested constructor calls).
    //
    // We do this instead of calling a constructor which takes properties,
    // because we don't want to force developers to implement such a
    // constructor in order to use the reflection mechanism.
    //
    // We can't simply apply the properties after the instance is constructed,
    // because the constructor may trigger getOrCreateJsObj(), and the
    // constructor on the SmartClient side may need the properties. However, we
    // do wait until the instance is fully constructed unless
    // getOrCreateJsObj() is called first.
    private static Map<String, Object> factoryProperties;

    public static Map<String, Object> getFactoryProperties () {
        return factoryProperties;
    }
    
    public static void clearFactoryProperties () {
        factoryProperties = null;
        factoryPropertiesClass = null;
    }

    // The class for which the factoryProperties are intended. Stashing this
    // helps us avoid the complication of triggering a static initializer,
    // which might itself create an instance before the instance for which the
    // properties are intended. By keeping track of the class for which the
    // properties are intended, we can limit the problem to static initializers
    // which create an instance of that very class.
    private static Class<?> factoryPropertiesClass;

    public static Class<?> getFactoryPropertiesClass () {
        return factoryPropertiesClass;
    }

    // The factories, hashed by the fully-qualified class name of the base class.
    private static Map<String, BeanFactory<?>> factoriesByName = new HashMap<String, BeanFactory<?>>();
 
    // The factories, hashed by the base class literal.
    private static Map<Class<?>, BeanFactory<?>> factoriesByClass = new HashMap<Class<?>, BeanFactory<?>>();
    // Note that we need to look up factories by class name and by class
    // literal to deal with two different scenarios. When we are creating a new
    // instance, and all we have is a class name, then we have to be able to
    // look up the class name.  When we have an existing object and we need to
    // manipulate it, then we have to look up the class literal. The reason is
    // that there could be duplicate class names in separately loaded modules.
    // Thus, even if the class name matches, it may be that we should not
    // "claim" the object -- it may have been created in another module, which
    // was loaded separately. Even if the class in that model is compiled from
    // the same source as the class in this module, we may not be able to
    // manipulate it from this module, because it may have obfuscated
    // differently.
    //
    // Note also that the map we maintain here is only for this module -- this
    // class itself may be compiled into other modules loaded separately. The 
    // only panoptic view of the factories is actually in the SmartClient
    // SGWTFactory class.

    // Note that this will only return factories defined in this GWT module ...
    // it is unaware of any other modules.
    public static BeanFactory<?> getFactory (String className) {
        return factoriesByName.get(className);
    }

    // Note that this will only return factories defined in this GWT module ...
    // it is unaware of any other modules.
    public static BeanFactory<?> getFactory (Class<?> klass) {
        return factoriesByClass.get(klass);
    }

    public static BeanFactory<?> getFactory (Object object) {
        // Overloading is based on compile-time types, so do some dynamic dispatch
        if (object instanceof Class) return getFactory((Class) object);
        if (object instanceof String) return getFactory((String) object);

        return getFactory(object.getClass());
    }

    // Registers a factory for a base class.
    protected static void registerFactory (BeanFactory<?> factory) {
        // Bail out if a factory has already been registered for the class name
        final String beanClassName = factory.getBeanClassName();
        if (factoriesByName.containsKey(beanClassName)) {
            SC.logWarn("BeanFactory for class has already been registered: " + beanClassName);    
            return;
        }

        factoriesByName.put(beanClassName, factory);
        factoriesByClass.put(factory.getBeanClass(), factory);
    }
    
    // -----------------
    // Static Public API
    // -----------------

    // Note that all of these functions only deal with factories defined in a
    // single GWT module at a time. The only way to access factories in other
    // modules is through the SmartClient SGWTFactory side. But even if one did
    // so, all one could get would be opaque references that could only be
    // manipulated through the SGWTFactory, since code in this module would
    // have no idea how code in the other module had been obfuscated. If
    // desirable, it would be possible write a public API here to manipulate
    // such references through SGWTFactory.

    // Convenience functions for exceptions
    protected static IllegalStateException noFactoryException (String name) {
        return new IllegalStateException("No factory has been generated for class: " + name);
    }

    protected static IllegalStateException noFactoryException (Class<?> klass) {
        return noFactoryException(klass.getName());
    }

    /**
     * Create an instance based on the provided class name.
     *
     * @param className the class name
     * @return a new instance of the class
     * @throws IllegalStateException If no factory has been generated for the className
     */
    public static Object newInstance (String className) {
        // Note that we can't use the parameterized BeanClass type in the static method
        BeanFactory<?> factory = BeanFactory.getFactory(className);
        if (factory == null) {
            throw noFactoryException(className);
        } else {
            return factory.newInstance();
        }
    }

    /**
     * Create an instance based on the provided class object.
     *
     * @param klass the class object
     * @return a new instance of the class
     * @throws IllegalStateException If no factory has been generated for the klass
     */
    public static Object newInstance (Class<?> klass) {
        // Note that we can't use the parameterized BeanClass type in the static method
        BeanFactory<?> factory = BeanFactory.getFactory(klass);
        if (factory == null) {
            throw noFactoryException(klass);
        } else {
            return factory.newInstance();
        }
    }
 
    /**
     * Create an instance based on the provided class name, and apply the
     * provided properties to it.
     *
     * The properties are applied after the instance is constructed with
     * its no-arg constructor, unless the constructor triggers a call to
     * getOrCreateJsObj(). In that case, the properties are applied before
     * calling getOrCreateJsObj(), to ensure that the jsObj gets all the
     * intended properties when it is constructed.
     *
     * @param className the class name
     * @param properties a JavaScriptObject whose key/value pairs represent
     *                   property names and values
     * @return a new instance of the class, with the properties applied
     * @throws IllegalStateException If no factory has been generated for the className
     */
    public static Object newInstance (String className, JavaScriptObject properties) {
        BeanFactory<?> factory = BeanFactory.getFactory(className);
        if (factory == null) {
            throw noFactoryException(className);
        } else {
            return factory.newInstance(properties);
        }
    }

    /**
     * Create an instance based on the provided class object, and apply the
     * provided properties to it.
     *
     * The properties are applied after the instance is constructed with
     * its no-arg constructor, unless the constructor triggers a call to
     * getOrCreateJsObj(). In that case, the properties are applied before
     * calling getOrCreateJsObj(), to ensure that the jsObj gets all the
     * intended properties when it is constructed.
     *
     * @param klass the class object
     * @param properties a JavaScriptObject whose key/value pairs represent
     *                   property names and values
     * @return a new instance of the class, with the properties applied
     * @throws IllegalStateException If no factory has been generated for the class
     */
    public static Object newInstance (Class<?> klass, JavaScriptObject properties) {
        BeanFactory<?> factory = BeanFactory.getFactory(klass);
        if (factory == null) {
            throw noFactoryException(klass);
        } else {
            return factory.newInstance(properties);
        }
    }

    /**
     * Create an instance based on the provided class name, and apply the
     * provided properties to it.
     *
     * The properties are applied after the instance is constructed with
     * its no-arg constructor, unless the constructor triggers a call to
     * getOrCreateJsObj(). In that case, the properties are applied before
     * calling getOrCreateJsObj(), to ensure that the jsObj gets all the
     * intended properties when it is constructed.
     *
     * @param className the class name
     * @param properties a Map whose key/value pairs represent
     *                   property names and values
     * @return a new instance of the class, with the properties applied
     * @throws IllegalStateException If no factory has been generated for the className
     */
    public static Object newInstance (String className, Map<String, Object> properties) {
        BeanFactory<?> factory = BeanFactory.getFactory(className);
        if (factory == null) {
            throw noFactoryException(className);
        } else {
            return factory.newInstance(properties);
        }
    }

    /**
     * Create an instance based on the provided class object, and apply the
     * provided properties to it.
     *
     * The properties are applied after the instance is constructed with
     * its no-arg constructor, unless the constructor triggers a call to
     * getOrCreateJsObj(). In that case, the properties are applied before
     * calling getOrCreateJsObj(), to ensure that the jsObj gets all the
     * intended properties when it is constructed.
     *
     * @param klass the class object
     * @param properties a Map whose key/value pairs represent
     *                   property names and values
     * @return a new instance of the class, with the properties applied
     * @throws IllegalStateException If no factory has been generated for the class
     */
    public static Object newInstance (Class<?> klass, Map<String, Object> properties) {
        BeanFactory<?> factory = BeanFactory.getFactory(klass);
        if (factory == null) {
            throw noFactoryException(klass);
        } else {
            return factory.newInstance(properties);
        }
    }

    /**
     * Sets a property of a bean to a value.
     *
     * @param bean The object whose property is to be set
     * @param property The name of the property
     * @param value The value to set
     * @throws IllegalStateException If no factory has been generated for the bean's class
     * @throws IllegalArgumentException If there is no appropriate setter for the value
     */
    public static void setProperty (Object bean, String property, Object value) {
        // Note that we can't use the parameterized BeanClass type in the
        // static method.
        //
        // Also, we used to have a separate implementation for
        // JavaScriptObject values, but it turns out that doesn't work well in
        // hosted mode, since in hosted mode some JavaScript objects get
        // auto-converted to Java objects (e.g. strings) by the glue code, and
        // are thus not castable to JavaScriptObject. So, we now pre-convert
        // the value to a Java object on the JavaScript side, before we hit
        // the glue code.
        if (bean != null) {
            BeanFactory<?> factory = BeanFactory.getFactory(bean.getClass());
            if (factory == null) {
                throw noFactoryException(bean.getClass());
            } else {
                // Need a different name for the instance method, since the signature
                // is the same.
                factory.doSetProperty(bean, property, value);
            }
        }
    }
   
    /**
     * Applies a Map of property names and values to a bean.
     *
     * @param bean The object whose properties are to be set
     * @param properties A Map whose key/value pairs represent property names and values
     * @throws IllegalStateException If no factory has been generated for the bean's class
     * @throws IllegalArgumentException If there is no appropriate setter for a value
     */
    public static void setProperties (Object bean, Map<String, Object> properties) {
        if (bean != null) {
            BeanFactory<?> factory = BeanFactory.getFactory(bean.getClass());
            if (factory == null) {
                throw noFactoryException(bean.getClass());
            } else {
                // Need a different name for the instance method, since the signature
                // is the same.
                factory.doSetProperties(bean, properties);
            }
        }
    }
   
    /**
     * Gets a property of a bean.
     *
     * @param bean The object whose property to get
     * @param property The name of the property
     * @throws IllegalStateException If no factory has been generated for the bean's class
     */
    public static Object getProperty (Object bean, String property) {
        // Note that we can't use the parameterized BeanClass type in the static method
        if (bean == null) return null;

        BeanFactory<?> factory = BeanFactory.getFactory(bean.getClass());
        if (factory == null) {
            throw noFactoryException(bean.getClass());
        } else {
            // Need a different name for the instance method, since the signature
            // is the same.
            return factory.doGetProperty(bean, property);
        }
    }

    /**
     * Gets a property of a bean as a String.
     *
     * In simple cases, this will be equivalent to getProperty().toString().
     * However, if there are multiple getters available, it will prefer getters
     * that actually return strings (e.g. getWidthAsString() would be preferred
     * to getWidth().toString()).
     *
     * @param bean The object whose property to get
     * @param property The name of the property
     * @throws IllegalStateException If no factory has been generated for the bean's class
     */
    public static String getPropertyAsString (Object bean, String property) {
        // Note that we can't use the parameterized BeanClass type in the static method
        if (bean == null)  return null;

        BeanFactory<?> factory = BeanFactory.getFactory(bean.getClass());
        if (factory == null) {
            throw noFactoryException(bean.getClass());
        } else {
            // Need a different name for the instance method, since the signature
            // is the same.
            return factory.doGetPropertyAsString(bean, property);
        }
    }

    /**
     * Gets an array of the names of the properties of a class.
     *
     * @param beanClassName The name of the class
     * @throws IllegalStateException If no factory has been generated for the class
     */
    public static String[] getAttributes (String beanClassName) {
        BeanFactory<?> factory = BeanFactory.getFactory(beanClassName);
        if (factory == null) {
            throw noFactoryException(beanClassName);
        } else {
            return factory.getAttributes();
        }
    }

    /**
     * Gets an array of the names of the properties of a class.
     *
     * @param beanClass The klass object
     * @throws IllegalStateException If no factory has been generated for the class
     */
    public static String[] getAttributes (Class<?> beanClass) {
        BeanFactory<?> factory = BeanFactory.getFactory(beanClass);
        if (factory == null) {
            throw noFactoryException(beanClass);
        } else {
            return factory.getAttributes();
        }
    }

    public static JavaScriptObject getOrCreateJsObj (Object bean) {
        // Note that we can't use the parameterized BeanClass type in the static method
        if (bean == null) return null;
            
        BeanFactory<?> factory = BeanFactory.getFactory(bean.getClass());
        if (factory == null) {
            throw noFactoryException(bean.getClass());
        } else {
            // Need a different name for the instance method, since the signature
            // is the same.
            return factory.doGetOrCreateJsObj(bean);
        }
    }

    public static void setJsObj (Object bean, JavaScriptObject jsObj) {
        // Note that we can't use the parameterized BeanClass type in the static method
        if (bean == null) return;
            
        BeanFactory<?> factory = BeanFactory.getFactory(bean.getClass());
        if (factory == null) {
            throw noFactoryException(bean.getClass());
        } else {
            // Need a different name for the instance method, since the signature
            // is the same.
            factory.doSetJsObj(bean, jsObj);
        }
    }

    /**
     * Indicates whether the class is defined by the SmartGWT framework.
     *
     * @param beanClass The Class object
     * @throws IllegalStateException If no factory has been generated for the class
     */
    public static boolean isFrameworkClass (Class<?> beanClass) {
        BeanFactory<?> factory = BeanFactory.getFactory(beanClass);
        if (factory == null) {
            throw noFactoryException(beanClass);
        } else {
            return factory.isFrameworkClass();
        }
    }

    /**
     * Gets the default scClassName for the class.
     *
     * @param beanClass The Class object
     * @throws IllegalStateException If no factory has been generated for the class
     */
    public static String getDefaultScClassName (Class<?> beanClass) {
        BeanFactory<?> factory = BeanFactory.getFactory(beanClass);
        if (factory == null) {
            throw noFactoryException(beanClass);
        } else {
            return factory.getDefaultScClassName();
        }
    }

    // ------------------------------
    // Instance variables and methods
    // ------------------------------

    // We use the superclassFactory to find properties in the superclasses
    // (rather than recording them redundantly).
    protected BeanFactory superclassFactory;

    // The generated subclass must populate the properties.
    protected Map<String, BeanProperty<BeanClass>> properties = new HashMap<String, BeanProperty<BeanClass>>();

    // Our SGWTFactory (the SmartClient interface to the factory)
    protected JavaScriptObject sgwtFactory;

    // A cache for our attribute names, including those defined in superclasses
    private String[] attributeNames;

    /**
     * The constructor is protected because you should not create BeanFactory
     * instances directly. Instead, use {@link BeanFactory.MetaFactory},
     * {@link BeanFactory.CanvasMetaFactory} or {@link BeanFactory.FormItemMetaFactory}
     * to create bean factories, and then use the static methods on
     * BeanFactory.
     *
     * @see BeanFactory.MetaFactory
     * @see BeanFactory.CanvasMetaFactory
     */
    protected BeanFactory () { 
        // Ensure that all the value types we use have been registered.
        registerValueTypes();
        
        // Initialize the JSNI function array for the setters and getters,
        // and then initialilze the properties. 
        for (BeanProperty<BeanClass> property : getProperties(getMethods())) {
            properties.put(property.getName(), property);    
        }

        // Create the SmartClient SGWTFactory equivalent. Note that we need
        // this whether or not we're registering our class name with
        // SGWTFactory, since we need it to deal with any SGWT objects we
        // create.
        sgwtFactory = createSGWTFactory();
        
        // This registers us locally, on the SmartGWT side, for this module
        BeanFactory.registerFactory(this);

        // See if we can get the superclass factory ... we don't want to
        // construct it if we don't have to ...
        Class superclass = getSuperclass();
        if (superclass != null) {
            superclassFactory = getFactory(superclass);
            if (superclassFactory == null) {
                // Note that creating it will also register it, so next time
                // getFactory will find it.
                superclassFactory = createSuperclassFactory();
            }
        }
    }

    // To be implemented by generated subclasses if it is appropriate to check
    // the superclass for properties. Can leave as null if generating for a base
    // class for which no further superclasses are relevant.
    protected Class getSuperclass () {
        return null;
    }

    // To be implemented by subclasses to actually create the superclass
    // factory. This will only be called if the factory is not already cached
    // (for instance, if GWT.create was called to create it, or it has already
    // been created by a different subclass).
    protected BeanFactory<?> createSuperclassFactory () {
        return null;
    }

    // To be implemented by subclasses to register any ValueTypes which they use
    protected abstract void registerValueTypes ();

    // This will be implemented by generated subclasses to set up all the
    // getters and setters for the BeanFactory, as a Javascript array of JSNI
    // functions, The reason we use JSNI functions is that we can then pass the
    // function around as a function pointer -- for instance, we can associate
    // it with a property as data, and call it. 
    //
    // The "standard" GWT way to do that would be an anonymous subclass with a
    // callback function. That works, but it has a lot of overhead, since it
    // creates a class for every method. The size of the generated code ends up
    // approximately one-third larger that way.
    //
    // Ideally, we could simply get the function pointer for the *real* setter
    // or getter itself (rather than constructing a function that calls it).
    // However, that's hard to generate, because GWT will only generate the
    // obfuscated function name in JSNI in the context of calling the function.
    protected abstract JsArray<JavaScriptObject> getMethods ();

    // To be implemented by generated subclasses to return an array of properties.
    // Each property will use one or more methods from the JsArray generated by
    // getMethods().
    protected abstract BeanProperty<BeanClass>[] getProperties (JsArray<JavaScriptObject> methods);

    // Creates the SmartClient SGWTFactory. Most of the callback functions are
    // actually the same for every SGWTFactory, and defined in the SGWTModule,
    // so we pass the reference to the module.
    private native JavaScriptObject createSGWTFactory () /*-{
        return $wnd.isc.SGWTFactory.create({
            beanClassName: this.@com.smartgwt.client.bean.BeanFactory::getBeanClassName()(),
            beanFactory: this,
            sgwtModule: @com.smartgwt.client.bean.BeanFactory::getSGWTModule()()
        });
    }-*/;

    // Calls the SGWTFactory function which registers the class name in the
    // isc[] space, so that idioms like isc[className].create will work. The
    // generated code will call this if the factory has been explicitly created
    // (i.e. not just as a superclass of something which was explicitly
    // created.)
    public native void registerClassNameWithSGWTFactory () /*-{
        this.@com.smartgwt.client.bean.BeanFactory::sgwtFactory.registerClassName();
    }-*/;

    // Gets the base class for which we are the factory. Subclasses must
    // implement.
    public abstract Class<BeanClass> getBeanClass();

    // Gets the name of the base class for which we are the factory.
    public String getBeanClassName () {
        return getBeanClass().getName();
    }

    // Create an new instance of the factory's underlying class.
    // Must be generated for all subclasses.
    protected abstract BeanClass doNewInstance ();
    
    public BeanClass newInstance () {
        return newInstance((Map<String, Object>) null);
    }

    @SuppressWarnings("unchecked")
    public BeanClass newInstance (JavaScriptObject properties) {
        if (properties == null) return newInstance();

        Object javaProperties = BeanValueType.convertToJava(properties);
        if (javaProperties instanceof Map) {
            return newInstance ((Map<String, Object>) javaProperties);
        } else {
            throw new IllegalArgumentException("properties were not a plain JavaScript object");
        }
    }

    // All calls to the various overloaded newInstance methods will end up here
    public BeanClass newInstance (Map<String, Object> properties) {
        // Always provide the factoryCreated property, so that we can test
        // whether the properties were correctly applied. There is one scenario
        // in which the properties will be applied to the wrong object: where
        // (a) the BeanClass has a static initializer; (b) the static initializer
        // has not previously run; and (c) the static initializer creates an 
        // instance of the BeanClass. We can't check for that scenario in 
        // advance, but we can check afterwards and try to recover.
        if (properties == null) properties = new HashMap<String, Object>();
        properties.put("factoryCreated", true);

        // Trigger any static initializers before stashing the properties.
        // This is a no-op unless the BeanClass defines a public static no-arg
        // method named beanFactoryInit(). Doing so avoids the problem
        // mentioned above re: static initializers.
        triggerStaticInitializers();

        // Stash the provided properties. See comments above on factoryProperties
        factoryProperties = properties;
        factoryPropertiesClass = getBeanClass();

        // Construct the new instance. Note that the BeanClass will apply the
        // properties if getOrCreateJsObj() is called before the constructor returns
        BeanClass instance = doNewInstance();

        // Apply the properties supplied by BeanFactory, if they haven't
        // already been applied via getOrCreateJsObj().
        applyFactoryProperties(instance);

        // If the BeanClass is abstract, doNewInstance will return null and we
        // don't need any further checking
        if (instance == null) return instance;

        // Check whether the properties were applied, by checking for the
        // factoryCreated property we always supply
        if (isFactoryCreated(instance)) {
            // It worked!
            return instance;
        } else {
            // In the static initializer scenario discussed above, we should be
            // able to make it work just by trying again ... since we've now
            // triggered the static initializer, the problem won't occur a
            // second time. Note that we can't just apply the properties now
            // to the already-created instance, because it may have already
            // called getOrCreateJsObj(), and may have needed the properties
            // then. We could possibly check whether the jsObj has been created ...
            // if not, we could just apply the properties now rather than 
            // recreating the object.
            factoryProperties = properties;
            factoryPropertiesClass = getBeanClass();
            instance = doNewInstance();
            applyFactoryProperties(instance);

            if (isFactoryCreated(instance)) {
                // It worked the second time. Now, it's still a problem,
                // because the instance created by the static initializer will
                // have received some unwanted properties, and we don't know
                // what the effects of that are. Also, we've created two new instances
                // instead of one (since we had to retry). So, we still log an error
                // message.
                String beanClassName = getBeanClassName();
                SC.logWarn(
                    "The BeanFactory for " + beanClassName + " failed to apply properties " +
                    "to a new instance on the first attempt, but succeeded on the second " +
                    "attempt. A known cause for this is where " + beanClassName + " has a " +
                    "static initializer, the static initializer has not run yet, and the " +
                    "static initializer itself creates an instance of " + beanClassName + ". " +
                    "To work around this problem, define a static no-arg public method within " +
                    beanClassName + " named beanFactoryInit(). BeanFactory will call that function " +
                    "in order to trigger static initialization before trying to create " +
                    "a new instance. Or, you can do something else to ensure that the " +
                    "static initializer is called before creating instances of " +
                    beanClassName + " via BeanFactory."
                );
                return instance;
            } else {
                // It failed again, so we better throw an exception
                throw new IllegalStateException("BeanFactory for " + getBeanClassName() + " failed to apply properties to a new instance.");
            }
        }
    }

    protected void triggerStaticInitializers () {
        // This is a no-op, unless a specific BeanClass defines a public static
        // no-arg method names beanFactoryInit()
    }

    // Sets a property of the bean to a value
    @SuppressWarnings("unchecked")
    public void doSetProperty (Object bean, String propertyName, Object value) {
        // Note that we don't want to use the parameterized BeanClass type
        // here, because we'd like to be able to call this from the static
        // method.
        
        BeanProperty<BeanClass> property = properties.get(propertyName);
        if (property == null) {
            // If we don't have it, check the superclass
            if (superclassFactory == null) {
                // If there is no superclassFactory, then set the property on
                // the underlying Javascript object directly.
                setJavascriptProperty((BeanClass) bean, propertyName, value);
            } else {
                // If we have a superclass, let it try
                superclassFactory.doSetProperty(bean, propertyName, value);
            }
        } else {
            // The type-cast should be safe, because we only get here once we've
            // picked the correct factory for the bean
            try {
                property.setProperty((BeanClass) bean, value);
            }
            catch (BeanProperty.NoSetterException e) {
                // There are some properties which have no setter on the SmartGWT
                // side (because they aren't meant to be set from user code), but
                // which do need to be set from framework code. So, if we have no
                // setter, we fall back on setting the property via Javascript.
                setJavascriptProperty((BeanClass) bean, propertyName, value);
            }
        }
    }

    public void doSetProperties (Object bean, Map<String, Object> properties) {
        for (Map.Entry<String, Object> entry : properties.entrySet()) {
            Object key = entry.getKey();
            Object value = entry.getValue();

            if (key instanceof String) {
                doSetProperty(bean, (String) key, value);
            } else {
                throw new IllegalArgumentException("a property name was not a String");
            }
        }
    }

    public String[] getAttributes () {
        // We lazily create a cache for the return value. Note that this
        // assumes that the method won't be called too "early" -- that is,
        // before the BeanFactory is finished registering its properties and
        // superclass factories. That happens in the constructor, so it should
        // be a safe assumption.
        if (attributeNames == null) {
            // Construct a new set of strings out of our properties keySet, since
            // the keySet should not be mutated here.
            Set<String> attributes = new HashSet<String>(properties.keySet());
            
            // Add property names from superclass factories
            BeanFactory<?> superclassIterator = superclassFactory;
            while (superclassIterator != null) {
                attributes.addAll(superclassIterator.properties.keySet());
                superclassIterator = superclassIterator.superclassFactory;
            }

            // Supply the parameter to provide the array type
            attributeNames = attributes.toArray(new String[attributes.size()]);
        }

        return attributeNames;
    }

    @SuppressWarnings("unchecked")
    public Object doGetProperty (Object bean, String propertyName) {
        // Note that we don't want to use the parameterized BeanClass type
        // here, because we'd like to be able to call this from the static
        // method.
        BeanProperty<BeanClass> property = properties.get(propertyName);
        if (property == null) {
            // If we don't have the property, try a superclassFactory.
            if (superclassFactory == null) {
                // If there is no superclassFactory, then try to get the
                // property from the Javascript side.
                return getJavascriptProperty((BeanClass) bean, propertyName);
            } else {
                // Otherwise, let the superclass try
                return superclassFactory.doGetProperty(bean, propertyName);
            }
        } else {
            // The type-cast should be safe, since we only get here once we've
            // picked the correct factory.
            return property.getProperty((BeanClass) bean);
        }
    }

    @SuppressWarnings("unchecked")
    public String doGetPropertyAsString (Object bean, String propertyName) {
        // Note that we don't want to use the parameterized BeanClass type
        // here, because we'd like to be able to call this from the static
        // method.
        BeanProperty<BeanClass> property = properties.get(propertyName);
        if (property == null) {
            // If we don't have the property, try a superclassFactory.
            if (superclassFactory == null) {
                // If there is no superclassFactory, then try to get the
                // property from the Javascript side.
                Object jsProperty = getJavascriptProperty((BeanClass) bean, propertyName);
                return jsProperty == null ? null : jsProperty.toString();
            } else {
                // Otherwise, let the superclass try
                return superclassFactory.doGetPropertyAsString(bean, propertyName);
            }
        } else {
            // The type-cast should be safe, since we only get here once we've
            // picked the correct factory.
            return property.getPropertyAsString((BeanClass) bean);
        }
    }

    // These are abstract because their implementation differs depending on the
    // BeanClass.
    
    // Sets a property on the underlying Javascript property directly. 
    protected abstract void setJavascriptProperty (BeanClass bean, String propertyName, Object value);

    // Gets a property from the underlying JavaScript object
    protected abstract Object getJavascriptProperty (BeanClass bean, String propertyName);
    
    // Gets the uncerlying JavaScriptObject.
    public abstract JavaScriptObject doGetOrCreateJsObj (Object bean);
    
    // Sets the uncerlying JavaScriptObject.
    public abstract void doSetJsObj (Object bean, JavaScriptObject jsObj);

    // Whether the bean was created by a BeanFactory
    public abstract boolean isFactoryCreated (BeanClass bean);

    protected abstract void applyFactoryProperties (BeanClass bean);

    public boolean isFrameworkClass () {
        // default to false, so we only need to generate a method when it is true
        return false;
    }

    public String getDefaultScClassName () {
        // default to null
        return null;
    }
}
