com.smartgwt.client.util.JSOHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.smartgwt.client.util.JSOHelper.java

Source

/*
 * SmartGWT (GWT for SmartClient)
 * Copyright 2008 and beyond, Isomorphic Software, Inc.
 *
 * SmartGWT is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.  SmartGWT 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.util;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsDate;
import com.google.gwt.user.client.Element;
import com.smartgwt.client.core.BaseClass;
import com.smartgwt.client.core.DataClass;
import com.smartgwt.client.core.Function;
import com.smartgwt.client.core.RefDataClass;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.data.RelativeDate;
import com.smartgwt.client.data.ResultSet;
import com.smartgwt.client.types.ValueEnum;
import com.smartgwt.client.widgets.BaseWidget;

/**
 * Utility class containing many useful static helper methods.
 * <p>
 **/
/*
 * NOTES - low-level detail of how the JS -> Java conversion works:
 * In SmartGwtEntryPoint, set up $wnd.SmartGWT.convertToJavaType():
 * - Converts simple and primitive JS types to Java equivalents:
 *   - string
 *     - the object itself is returned (presumably GWT can automap JS String to Java)
 *   - number
 *     - If the stringified number contains a ".", convert to Double
 *     - Otherwise, convert to Integer if the value is less than Integer.MAX_VALUE, otherwise Long
 *   - boolean -> Boolean
 *   - date -> java.util.Date (via JSOHelper.convertToJavaDate)
 *   - object where _constructor is "DateRange" -> com.smartgwt.client.data.DateRange
 *   - isJSO(object) == true -> return the object itself
 *   - array -> return the result of calling JSOHelper.convertToJavaObjectArray(object)
 *   - If none of the above, gives up and returns the object itself
 *
 * In SmartGwtEntryPoint, set up $wnd.SmartGWT.convertToJavaObject():
 * - Converts complex objects to Java equivalents:
 *   - null -> return null
 *   - isc.isA.Object() == false -> return the result of calling convertToJavaType(object) - see above
 *   - isc.isA.Date() == true -> return a java.util.Date (via JSOHelper.convertToJavaDate)
 *   - isc.isAn.Array:
 *     - Iterate over the array members and convert each by calling convertToJavaObject()
 *     - Add each converted member to a JS array
 *     - After the iteration, convert the JS array to either a Java array (via SOHelper.convertToJavaObjectArray)
 *       or an ArrayList, depending on the setting of the incoming "listAsArray" parameter (second param)
 *     - Return this array or ArrayList
 *   - If the incoming "forceMap" parameter (third param) is not true (using exact !== test)
 *     - isJSO(object) == true -> return the object itself
 *     - Object has a non-null "__ref" property -> return the value of the "__ref" property
 *     - isc.isA.Canvas() -> return the result of calling com.smartgwt.client.widgets.Canvas.getById()
 *       with the object's "ID" property
 *     - object has a "name" property of type String and a "form" property of type "DynamicForm"
 *       - Create a DynamicForm object by calling com.smartgwt.client.widgets.form.DynamicForm.getOrCreateRef()
 *         with the value of the object's "form" property
 *       - Return the result of calling getField() on that form, passing in the object's "name" property
 *     - object has a "_constructor" property of "RelativeDate" => return a new instance of 
 *       com.smartgwt.client.data.RelativeDate, passing the object to the constructor that accepts a JSO
 *     - isc.isAn.Instance() == true and the object has a getClassName() method -> return the result of
 *       calling com.smartgwt.client.util.ObjectFactory.createInstance(), passing the object to the 
 *       constructor that accepts a JSO
 *   - If the incoming "forceMap" param is true
 *     - The object has a "__ref" property, and it is a Java Map (checked with JSOHelper::isJavaMap()),
 *       return the value of the object's __ref property
 *   - If get to this point without having converted the object, we try to render it as a Map
 *     - If the object has a property named the same as isc.Tree.getPrototype().treeProperty, we assume
 *       it is a TreeNode and call isc.Tree.getCleanNodeData() on it
 *     - Create a new empty map to contain the converted properties
 *     - Iterate over the property names in object
 *       - If the property name is not a string, skip
 *       - If the property name is the same as isc.gwtModule, skip
 *       - If the property value is an object with a "__ref" property and that __ref property refers 
 *         to a native Java object (as defined by SmartGWT.isNativeJavaObject(), which ultimately calls
 *         on to isJSO()), add the __ref object to the converted map
 *       - Otherwise, convert the value by passing it to convertToJavaObject(), then add it to the 
 *         converted map
 *       - Return the converted map
 */
public class JSOHelper {

    private JSOHelper() {
    }

    /**
     * Returns the javascript class name.
     * @return
     */
    public static native String getClassName(JavaScriptObject javaScriptObject)/*-{
                                                                               return javaScriptObject.getClassName();
                                                                               }-*/;

    /**
     * Returns whether the supplied JavaScriptObject is a SmartClient class instance.
     * @param the object to evaluate
     * @return whether object is an instance
     */
    public static native boolean isScClassInstance(JavaScriptObject javaScriptObject)/*-{
                                                                                     return $wnd.isc.isAn.Instance(javaScriptObject);
                                                                                     }-*/;

    /**
     * Returns whether the supplied JavaScriptObject is a SmartClient class (a Class object
     * itself, rather than an instance of a class).
     * @param the object to evaluate
     * @return whether object is a Class object
     */
    public static native boolean isScClassObject(JavaScriptObject javaScriptObject)/*-{
                                                                                   return $wnd.isc.isA.ClassObject(javaScriptObject);
                                                                                   }-*/;

    /**
     * Evaluate the passed string as Javascript
     *
     * @param jsFrag the string to evaluate
     * @return the JavaScriptObject upon evaluation
     */
    public static native JavaScriptObject eval(String jsFrag) /*-{
                                                              if(!($wnd.isc.startsWith(jsFrag, '(') && $wnd.isc.endsWith(jsFrag, ')'))) {
                                                              jsFrag = '(' + jsFrag + ')';
                                                              }
                                                              return $wnd.isc.Class.evaluate(jsFrag);
                                                              }-*/;

    public static boolean isJSO(Object object) {
        return object instanceof JavaScriptObject;
    }

    public static double doubleValue(Number num) {
        if (num == null)
            return Double.NaN;
        if (num instanceof Float) {
            // When a `float' is cast to a `double', the significand is zero-extended. This can
            // lead to the unexpected scenario where Double.toString((double)f) != Float.toString(f).
            // For example, Double.toString(Float.valueOf(0.15f).doubleValue()) is the string
            // "0.15000000596046448". This is because the significand of (double)f is
            // 0b0011001100110011001101000000000000000000000000000000, but the significand of
            // Double.parseDouble("0.15") is:
            // 0b0011001100110011001100110011001100110011001100110011
            return Double.parseDouble(num.toString());
        } else if (num instanceof Long) {
            long l = num.longValue();
            final double d = (double) l;
            if (Math.abs(l) > 9007199254740992L) {
                SC.logWarn("The long value " + Long.toString(l)
                        + " cannot be exactly represented in JavaScript. It will be truncated to: "
                        + Long.toString((long) d));
            }
            return d;
        } else {
            return num.doubleValue();
        }
    }

    public static native String getAttribute(JavaScriptObject elem, String attr) /*-{
                                                                                 var ret = elem[attr];
                                                                                 return (ret == null ? null : String(ret));
                                                                                 }-*/;

    public static native void setAttribute(JavaScriptObject elem, String attr, String value) /*-{
                                                                                             // When setting a string attribute, make sure to convert the value to a normal, non-wrapped
                                                                                             // string as opposed to a String object (i.e. typeof is "string", not "object").
                                                                                             // This is needed because an equality comparison of two String objects can fail even
                                                                                             // though they represent the same string. For example, `new String("test") == new String("test")'
                                                                                             // is `false'.
                                                                                             //
                                                                                             // Also, both $wnd.String() and String() called as functions on a value return the same thing,
                                                                                             // a native browser string that is the result of calling the internal ToString() conversion function
                                                                                             // (Sections 15.5.1.1 and 9.8, ECMA-262). Use String() because it is the fastest:
                                                                                             // http://jsperf.com/cross-frame-tostring/2
                                                                                             elem[attr] = (value == null ? null : String(value));
                                                                                             }-*/;

    public static native JavaScriptObject getAttributeAsJavaScriptObject(JavaScriptObject elem, String attr) /*-{
                                                                                                             var ret = elem[attr];
                                                                                                             return (ret == null ? null : ret);
                                                                                                             }-*/;

    public static native JavaScriptObject[] getAttributeAsJavaScriptObjectArray(JavaScriptObject elem,
            String attr) /*-{
                         var arrayJS = elem[attr];
                         return (arrayJS === undefined) ? null : @com.smartgwt.client.util.JSOHelper::toArray(Lcom/google/gwt/core/client/JavaScriptObject;)(arrayJS);
                         }-*/;

    public static JavaScriptObject[] toArray(JavaScriptObject array) {
        //handle case where a ResultSet is passed
        if (com.smartgwt.client.data.ResultSet.isResultSet(array)) {
            array = JSOHelper.resultSetToArray(array);
        }
        final int length = getJavaScriptObjectArraySize(array);
        JavaScriptObject[] recs = new JavaScriptObject[length];
        for (int i = 0; i < length; i++) {
            recs[i] = getValueFromJavaScriptObjectArray(array, i);
        }
        return recs;
    }

    private static native JavaScriptObject resultSetToArray(JavaScriptObject rs) /*-{
                                                                                 if (!rs.lengthIsKnown() || !rs.allMatchingRowsCached()) return $wnd.Array.create();
                                                                                 return rs.getRange(0, rs.getLength());
                                                                                     
                                                                                 }-*/;

    public static native boolean isArray(JavaScriptObject jsObj)/*-{
                                                                return $wnd.isc.isA.Array(jsObj);
                                                                }-*/;

    public static Element[] toElementArray(JavaScriptObject array) {
        int length = getJavaScriptObjectArraySize(array);
        Element[] recs = new Element[length];
        for (int i = 0; i < length; i++) {
            recs[i] = getElementValueFromJavaScriptObjectArray(array, i);
        }
        return recs;
    }

    public static native void setAttribute(JavaScriptObject elem, String attr, JavaScriptObject[] value) /*-{
                                                                                                         elem[attr] = value;
                                                                                                         }-*/;

    public static void setAttribute(JavaScriptObject elem, String attr, int[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, float[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, double[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, String[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Integer[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Float[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Boolean[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Double[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Long[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Date[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    public static void setAttribute(JavaScriptObject elem, String attr, ValueEnum[] values) {
        setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(values));
    }

    /**
     * Set the value of attribute "attr" in object "elem" to the Object "value"
     * <P> 
     * This method converts the value passed in to an equivalent 
     * object in JavaScript before storing on the underlying data object - see 
     * {@link com.smartgwt.client.docs.JavaToJavaScriptConversion} for details of the conversions
     * that take place.<br>
     * Developers can use {@link #setObjectAttribute(JavaScriptObject, String, Object)} 
     * to store Java objects without converting to JavaScript.
     *
     * @param elem the JavaScriptObject on which to set the attribute
     * @param attr the attribute name
     * @param value the attribute value.
     */
    public static void setAttribute(JavaScriptObject elem, String attr, Object value) {
        if (value == null) {
            setNullAttribute(elem, attr);
        } else if (value instanceof String || value instanceof Character) {
            setAttribute(elem, attr, value.toString());
        } else if (value instanceof Number) {
            setAttribute(elem, attr, (Number) value);
        } else if (value instanceof Boolean) {
            setAttribute(elem, attr, ((Boolean) value).booleanValue());
        } else if (value instanceof Date) {
            setAttribute(elem, attr, (Date) value);
        } else if (value instanceof RelativeDate) {
            setAttribute(elem, attr, ((RelativeDate) value).getValue());
        } else if (value instanceof ValueEnum) {
            setAttribute(elem, attr, ((ValueEnum) value).getValue());
        } else if (value.getClass().isArray()) {
            if (value instanceof Object[]) {
                // The Object[] passed in may contain unconvertible elements (POJOs)
                // In this case we'll end up with a JavaScript array which contains some
                // unconverted sub objects
                // We used to pass the extra "strict" parameter in to avoid this and
                // instead throw an exception, but that's more intrusive than silently 
                // storing some unconverted member.
                setAttribute(elem, attr, convertToJavaScriptArray((Object[]) value));
            } else if (value instanceof int[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((int[]) value));
            } else if (value instanceof double[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((double[]) value));
            } else if (value instanceof float[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((float[]) value));
            } else if (value instanceof boolean[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((boolean[]) value));
            } else if (value instanceof char[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((char[]) value));
            } else if (value instanceof byte[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((byte[]) value));
            } else if (value instanceof short[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((short[]) value));
            } else if (value instanceof long[]) {
                setAttribute(elem, attr, convertToJavaScriptArray((long[]) value));
            } else {
                assert false : value.getClass() + " should not be an array class.";
                setObjectAttribute(elem, attr, value);
            }

            // As with Object[]s, Java collections may contain unconvertible elements (POJOs)
            // In this case we'll end up with a JavaScript array which contains some
            // unconverted sub objects
        } else if (value instanceof List) {
            setAttribute(elem, attr, convertToJavaScriptArray(((List<?>) value).toArray()));
        } else if (value instanceof Iterator) {
            List listVal = new ArrayList();
            while (((Iterator) value).hasNext())
                listVal.add(((Iterator) value).next());
            setAttribute(elem, attr, convertToJavaScriptArray(((List<?>) listVal).toArray()));
        } else if (value instanceof Set) {
            setAttribute(elem, attr, JSOHelper.convertToJavaScriptArray(((Set<?>) value).toArray()));
        } else if (value instanceof Map) {
            setAttribute(elem, attr, convertMapToJavascriptObject((Map<?, ?>) value));
        } else {
            setObjectAttribute(elem, attr, value);
        }
    }

    public static native void setAttribute(JavaScriptObject elem, String attr, JavaScriptObject value) /*-{
                                                                                                       elem[attr] = value;
                                                                                                       }-*/;

    public static native void setAttribute(JavaScriptObject elem, String attr, int value) /*-{
                                                                                          elem[attr] = value;
                                                                                          }-*/;

    public static void setAttribute(JavaScriptObject elem, String attr, Number value) {
        if (value == null) {
            setNullAttribute(elem, attr);
        } else {
            setAttribute(elem, attr, doubleValue(value));
        }
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Long value) {
        if (value == null) {
            setNullAttribute(elem, attr);
        } else {
            final long l = value.longValue();
            final double d = (double) l;
            if (Math.abs(l) > 9007199254740992L) {
                SC.logWarn("The long value " + Long.toString(l)
                        + " cannot be exactly represented in JavaScript. It will be truncated to: "
                        + Long.toString((long) d));
            }
            setAttribute(elem, attr, d);
        }
    }

    public static void setAttribute(JavaScriptObject elem, String attr, Boolean value) {
        if (value == null) {
            setNullAttribute(elem, attr);
        } else {
            setAttribute(elem, attr, value.booleanValue());
        }
    }

    public static native void setNullAttribute(JavaScriptObject elem, String attr) /*-{
                                                                                   elem[attr] = null;
                                                                                   }-*/;

    public static native void deleteAttribute(JavaScriptObject elem, String attr) /*-{
                                                                                  delete elem[attr];
                                                                                  }-*/;

    public static native void deleteAttributeIfExists(JavaScriptObject elem, String attr) /*-{
                                                                                          var undef;
                                                                                          if (elem[attr] !== undef) delete elem[attr];
                                                                                          }-*/;

    public static native void setAttribute(JavaScriptObject elem, String attr, boolean value) /*-{
                                                                                              elem[attr] = value;
                                                                                              }-*/;

    public static native void setAttribute(JavaScriptObject elem, String attr, double value) /*-{
                                                                                             elem[attr] = value;
                                                                                             }-*/;

    public static native void setAttribute(JavaScriptObject elem, String attr, Function handler) /*-{
                                                                                                 elem[attr] = $entry(function() {
                                                                                                 handler.@com.smartgwt.client.core.Function::execute()();
                                                                                                 });
                                                                                                 }-*/;

    public static void setAttribute(JavaScriptObject elem, String attr, Date value) {
        setAttribute(elem, attr, convertToJavaScriptDate(value));
    }

    public static native void setObjectAttribute(JavaScriptObject elem, String attr, Object object) /*-{
                                                                                                    elem[attr] = object;
                                                                                                    }-*/;

    public static native Element getAttributeAsElement(JavaScriptObject elem, String attr) /*-{
                                                                                           var ret = elem[attr];
                                                                                           return (ret === undefined) ? null : ret;
                                                                                           }-*/;

    public static native Integer getAttributeAsInt(JavaScriptObject elem, String attr) /*-{
                                                                                       var ret = elem[attr];
                                                                                       return (ret == null ? null : @com.smartgwt.client.util.JSOHelper::toInteger(I)(ret));
                                                                                       }-*/;

    public static native Double getAttributeAsDouble(JavaScriptObject elem, String attr) /*-{
                                                                                         var ret = elem[attr];
                                                                                         if (ret === undefined || ret == null) {
                                                                                         return null;
                                                                                         } else {
                                                                                         if(typeof ret == "object") {
                                                                                         return ret;
                                                                                         } else {
                                                                                         return  @com.smartgwt.client.util.JSOHelper::toDouble(D)(ret);
                                                                                         }
                                                                                         }
                                                                                         }-*/;

    public static native Date getAttributeAsDate(JavaScriptObject elem, String attr) /*-{
                                                                                     var val = elem[attr];
                                                                                     return @com.smartgwt.client.util.JSOHelper::convertToJavaDate(Lcom/google/gwt/core/client/JavaScriptObject;)(val);
                                                                                     }-*/;

    public static native Float getAttributeAsFloat(JavaScriptObject elem, String attr) /*-{
                                                                                       var ret = elem[attr];
                                                                                       return (ret == null ? null : @com.smartgwt.client.util.JSOHelper::toFloat(F)(ret));
                                                                                       }-*/;

    public static int[] getAttributeAsIntArray(JavaScriptObject elem, String attr) {
        int[] rtn = null;
        JavaScriptObject hold = getAttributeAsJavaScriptObject(elem, attr);

        if (hold != null) {
            rtn = new int[getJavaScriptObjectArraySize(hold)];

            for (int i = 0; i < rtn.length; i++) {
                rtn[i] = getIntValueFromJavaScriptObjectArray(hold, i);
            }
        }
        return rtn;
    }

    public static double[] getAttributeAsDoubleArray(JavaScriptObject elem, String attr) {
        double[] rtn = null;
        JavaScriptObject hold = getAttributeAsJavaScriptObject(elem, attr);

        if (hold != null) {
            rtn = new double[getJavaScriptObjectArraySize(hold)];

            for (int i = 0; i < rtn.length; i++) {
                rtn[i] = getDoubleValueFromJavaScriptObjectArray(hold, i);
            }
        }
        return rtn;
    }

    public static String[] getAttributeAsStringArray(JavaScriptObject elem, String attr) {
        final JavaScriptObject hold = getAttributeAsJavaScriptObject(elem, attr);

        final String[] rtn;
        if (hold == null)
            rtn = null;
        else {
            rtn = new String[getJavaScriptObjectArraySize(hold)];

            for (int i = 0; i < rtn.length; i++) {
                rtn[i] = getStringValueFromJavaScriptObjectArray(hold, i);
            }
        }
        return rtn;
    }

    public static native int getJavaScriptObjectArraySize(JavaScriptObject elem) /*-{
                                                                                 var length;
                                                                                 if (elem) length = elem.length;
                                                                                 if (length == null) length = 0;
                                                                                 return length;
                                                                                 }-*/;

    public static native int getIntValueFromJavaScriptObjectArray(JavaScriptObject elem, int i) /*-{
                                                                                                return elem[i];
                                                                                                }-*/;

    public static native double getDoubleValueFromJavaScriptObjectArray(JavaScriptObject elem, int i) /*-{
                                                                                                      return elem[i];
                                                                                                      }-*/;

    public static native String getStringValueFromJavaScriptObjectArray(JavaScriptObject elem, int i) /*-{
                                                                                                      return elem[i];
                                                                                                      }-*/;

    public static native JavaScriptObject getValueFromJavaScriptObjectArray(JavaScriptObject elem, int i) /*-{
                                                                                                          return elem[i];
                                                                                                          }-*/;

    // Lists of cells are stored as arrays of 2 element arrays in JS.
    // Helpers to convert between this and the equivalent format in Java (int[][])
    public static int[][] getCellArray(JavaScriptObject jsCells) {
        if (jsCells == null)
            return null;

        int length = JSOHelper.getArrayLength(jsCells);
        int[][] cells = new int[length][];

        for (int i = 0; i < length; i++) {
            JavaScriptObject jsCell = JSOHelper.getValueFromJavaScriptObjectArray(jsCells, i);
            cells[i] = new int[2];
            cells[i][0] = JSOHelper.getIntArrayValue(jsCell, 0);
            cells[i][1] = JSOHelper.getIntArrayValue(jsCell, 1);
        }
        return cells;
    }

    public static JavaScriptObject convertToCellArray(int[][] cells) {
        if (cells == null)
            return null;
        JavaScriptObject jsCells = JSOHelper.createJavaScriptArray();
        for (int i = 0; i < cells.length; i++) {
            int[] cell = cells[i];
            JavaScriptObject jsCell = JSOHelper.convertToJavaScriptArray(cell);
            JSOHelper.setArrayValue(jsCells, i, jsCell);
        }
        return jsCells;
    }

    /**
     * Returns attribute value set as a Boolean.  For convenience in checking boolean
     * properties, <code>getAttributeAsBoolean</code> will return Boolean <code>false</code>
     * if the attribute value is <code>null</code> or not a Boolean.  Use the three parameter
     * variant of this API {@link #getAttributeAsBoolean(JavaScriptObject, String, boolean)}
     * if you want <code>null</code> returned for <code>null</code> attribute values.
     * @param elem the JavaScriptObject containing the property
     * @param attr the property name
     * @return the property value
     */
    public static boolean getAttributeAsBoolean(JavaScriptObject elem, String attr) {
        return getAttributeAsBoolean(elem, attr, false);
    }

    /**
     * Returns attribute value set as a Boolean.  If the attribute value is <code>null</code>
     * or not a Boolean, the return value depends upon <code>allowNull</code>.  If 
     * <code>allowNull</code> is true, <code>null</code> will be returned; otherwise Boolean
     * <code>false</code> will be returned.  For a simpler approach that never returns 
     * <codE>null</code>, use the two parameter variant of this API 
     * {@link #getAttributeAsBoolean(JavaScriptObject, String)}.
     * @param elem the JavaScriptObject containing the property
     * @param attr the property name
     * @param allowNull whether to permit a <code>null</code> return value
     * @return the property value
     */
    public static native Boolean getAttributeAsBoolean(JavaScriptObject elem, String attr, boolean allowNull) /*-{
                                                                                                              var ret = elem[attr];
                                                                                                              if ($wnd.isc.isA.Boolean(ret)) ret = ret.valueOf();
                                                                                                              else if (!allowNull)         ret = false;
                                                                                                              else                         return null;
                                                                                                              return @com.smartgwt.client.util.JSOHelper::toBoolean(Z)(ret);
                                                                                                              }-*/;

    public static native Object getAttributeAsObject(JavaScriptObject elem, String attr) /*-{
                                                                                         var ret = elem[attr];
                                                                                         if (ret === undefined) return null;
                                                                                         if ($wnd.isc.isA.Number(ret) || $wnd.isc.isA.Boolean(ret)) {
                                                                                         ret = $wnd.SmartGWT.convertToJavaType(ret);
                                                                                         }
                                                                                         return ret;
                                                                                         }-*/;

    /**
     * Returns attribute "attr" of JavaScriptObject "elem", converted to a Map following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}
     *
     * @param elem JavaScriptObject containing the attribute
     * @param attr The attribute name
     * @return The attribute converted to a Java Map
     */
    public static Map getAttributeAsMap(JavaScriptObject elem, String attr) {
        JavaScriptObject value = getAttributeAsJavaScriptObject(elem, attr);
        if (value == null)
            return null;
        return convertToMap(value);
    }

    public static JavaScriptObject[] listToArray(List list) {
        JavaScriptObject[] array = new JavaScriptObject[list.size()];

        for (int i = 0; i < array.length; i++) {
            array[i] = (JavaScriptObject) list.get(i);
        }
        return array;
    }

    public static JavaScriptObject arrayConvert(Object[] array) {
        if (array == null)
            return null;
        JavaScriptObject result = newJSArray(array.length);

        for (int i = 0; i < array.length; i++) {
            arraySet(result, i, array[i]);
        }
        return result;
    }

    public static JavaScriptObject arrayConvert(JavaScriptObject[] array) {
        if (array == null)
            return null;
        JavaScriptObject result = newJSArray(array.length);

        for (int i = 0; i < array.length; i++) {
            arraySet(result, i, array[i]);
        }
        return result;
    }

    private static native JavaScriptObject newJSArray(int length) /*-{
                                                                  if (length < 0) {
                                                                  return $wnd.Array.create();
                                                                  } else {
                                                                  var arr = $wnd.Array.create();
                                                                  arr.setLength(length);
                                                                  return arr;
                                                                  }
                                                                  }-*/;

    public static native int arrayLength(JavaScriptObject array) /*-{
                                                                 return array.length;
                                                                 }-*/;

    public static native Object arrayGetObject(JavaScriptObject array, int index) /*-{
                                                                                  return array[index];
                                                                                  }-*/;

    public static native void arraySet(JavaScriptObject array, int index, Object value) /*-{
                                                                                        array[index] = value;
                                                                                        }-*/;

    public static native void arraySet(JavaScriptObject array, int index, JavaScriptObject value) /*-{
                                                                                                  array[index] = value;
                                                                                                  }-*/;

    /**
     * This is used to access Element array as JavaScriptObject
     */
    public static native Element getElementValueFromJavaScriptObjectArray(JavaScriptObject elem, int i) /*-{
                                                                                                        return elem[i];
                                                                                                        }-*/;

    public static native JavaScriptObject createObject() /*-{
                                                         return new Object;
                                                         }-*/;

    public static JavaScriptObject convertToJavaScriptArray(boolean[] array) {
        if (array == null)
            return null;
        final JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; ++i) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(char[] array) {
        if (array == null)
            return null;
        final JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; ++i) {
            JSOHelper.setArrayValue(jsArray, i, Character.toString(array[i]));
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(byte[] array) {
        if (array == null)
            return null;
        final JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; ++i) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(short[] array) {
        if (array == null)
            return null;
        final JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; ++i) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(int[] array) {
        if (array == null)
            return null;
        JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; i++) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(float[] array) {
        if (array == null)
            return null;
        JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; i++) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(double[] array) {
        if (array == null)
            return null;
        JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; i++) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    public static JavaScriptObject convertToJavaScriptArray(long[] array) {
        if (array == null)
            return null;
        JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; i++) {
            JSOHelper.setArrayValue(jsArray, i, array[i]);
        }
        return jsArray;
    }

    private static void doAddToMap(Map map, String key, Object value) {
        map.put(key, value);
    }

    private native static Object convertToJava(JavaScriptObject object, boolean listAsArray,
            boolean forceMap) /*-{
                              return $wnd.SmartGWT.convertToJavaObject(object, listAsArray, forceMap);
                              }-*/;

    /**
     * Convert a JavaScriptObject to the appropriate type of Java Object, following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}
     *
     * @param object JavaScriptObject to convert
     * @param listAsArray Should arrays be converted to Object[] or List
     * @return converted Java object. May be a Map, a List, an Object[], a JavaScriptObject, 
     *         a Canvas or a FormItem, depending on the underlying JavaScript type
     */
    public static Object convertToJava(JavaScriptObject object, boolean listAsArray) {
        return convertToJava(object, listAsArray, false);
    }

    /**
     * Convert a JavaScriptObject to the appropriate type of Java Object, following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}.
     * Any arrays encountered during conversion will be converted to Java <code>ArrayList</code>s
     *
     * @param object JavaScriptObject to convert
     * @return converted Java object. May be a Map, a List, an Object[], a JavaScriptObject, 
     *         a Canvas or a FormItem, depending on the underlying JavaScript type
     */
    public static Object convertToJava(JavaScriptObject object) {
        return convertToJava(object, false);
    }

    /**
     * Convert a JavaScript object containing key:value pairs to a Java Map, following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}
     * @param jsObj the JavaScript object to convert
     * @param listAsArray Should arrays be converted to Object[] or List
     * @return the map
     * @throws IllegalArgumentException if unable to convert the passed JavaScript object to a map
     */
    public static Map convertToMap(JavaScriptObject jsObj, boolean listAsArray) {
        Object javaObj = convertToJava(jsObj, listAsArray, true);
        if (javaObj == null) {
            return (Map) null;
        } else if (javaObj instanceof Map) {
            return (Map) javaObj;
        } else {
            throw new IllegalArgumentException("convertToMap - unable to convert the passed "
                    + "JavaScript object to a Map.  JavaScript is: " + SC.echo(jsObj));
        }
    }

    /**
     * Convert a JavaScript object containing key:value pairs to a Java Map, following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}.
     * Any arrays encountered during the conversion process will be converted to Java <code>ArrayList</code>s
     * @param jsObj the JavaScript object to convert
     * @return the map
     * @throws IllegalArgumentException if unable to convert the passed JavaScript object to a map
     */
    public static Map convertToMap(JavaScriptObject jsObj) {
        return convertToMap(jsObj, false);
    }

    /**
     * Convert a JavaScript object to an Object[], following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}. 
     * If the JavaScript object is not an array in Javascript, a new array will be created 
     * containing the converted object as the only entry.
     * @param object the JavaScript object to convert
     * @return the JavaScript object converted to a Java Object array
     */
    public static Object[] convertToArray(JavaScriptObject object) {
        Object javaObj = convertToJava(object, true);
        if (!(javaObj instanceof Object[])) {
            javaObj = new Object[] { javaObj };
        }
        return (Object[]) javaObj;
    }

    /**
     * Convert a JavaScript object to a List, following the rules described
     * in the {@link com.smartgwt.client.docs.JavaScriptToJavaConversion JS -> Java conversion article}. 
     * If the JavaScript object is not an array in JavaScript, a new List will be created containing 
     * the converted object as the only entry.
     * @param object the JavaScript object to convert
     * @return the JavaScript object converted to a Java List
     */
    public static List convertToList(JavaScriptObject object) {
        Object javaObj = convertToJava(object, false);
        if (!(javaObj instanceof List)) {
            List list = new ArrayList();
            list.add(javaObj);
            javaObj = list;
        }
        return (List) javaObj;
    }

    private static native JsDate createJavaScriptDate(double time) /*-{
                                                                   // Use $wnd.Date.create() instead of JsDate.create() so that instance methods like
                                                                   // duplicate() are added to the resulting JavaScript date object.
                                                                   var jsD = $wnd.Date.create();
                                                                   jsD.setTime(time);
                                                                   return jsD;
                                                                   }-*/;

    public static JsDate convertToJavaScriptDate(Date date) {
        if (date == null)
            return null;
        if (date instanceof LogicalDate) {
            return ((LogicalDate) date).toJavaScriptDate();
        } else if (date instanceof LogicalTime) {
            return ((LogicalTime) date).toJavaScriptDate();
        } else {
            return createJavaScriptDate(date.getTime());
        }
    }

    public static native Date convertToJavaDate(JavaScriptObject val) /*-{
                                                                      if (!$wnd.isc.isA.Date(val)) return null;
                                                                      if (val.logicalDate) {
                                                                      return @com.smartgwt.client.util.LogicalDate::new(D)(val.getTime());
                                                                      } else if (val.logicalTime) {
                                                                      return @com.smartgwt.client.util.LogicalTime::new(D)(val.getTime());
                                                                      } else {
                                                                      return @com.smartgwt.client.util.JSOHelper::toDate(D)(val.getTime());
                                                                      }
                                                                      }-*/;

    //explicitly cast Object to String to workaround GWT hosted mode but in certain browsers when originating string is obtained
    //by calling object.toString(). http://code.google.com/p/google-web-toolkit/issues/detail?id=4301
    public static String convertToString(Object obj) {
        if (obj instanceof String) {
            return (String) obj;
        } else {
            throw new IllegalArgumentException("Object " + obj + " is not of type String");
        }
    }

    /**
     * @param obj the object
     * @return true if object is a Java Date
     */
    public static boolean isJavaDate(Object obj) {
        return obj instanceof Date;
    }

    /**
     * @param obj the object
     * @return true if object is a Java Number
     */
    public static boolean isJavaNumber(Object obj) {
        return obj instanceof Number;
    }

    /**
     * @param obj the object
     * @return true if object is a Java Integer
     */
    public static boolean isJavaInteger(Object obj) {
        return obj instanceof Integer;
    }

    /**
     * @param obj the object
     * @return true if object is a Java Float
     */
    public static boolean isJavaFloat(Object obj) {
        return obj instanceof Float;
    }

    /**
     * @param obj the object
     * @return true if object is a Java Double
     */
    public static boolean isJavaDouble(Object obj) {
        return obj instanceof Double;
    }

    /**
     * @param obj the object
     * @return true if object is a Java String
     */
    public static boolean isJavaString(Object obj) {
        return obj instanceof String;
    }

    /**
     * @param obj the object
     * @return true if object is a Java Integer
     */
    public static boolean isJavaBoolean(Object obj) {
        return obj instanceof Boolean;
    }

    public static boolean isJavaList(Object obj) {
        return obj instanceof List;
    }

    public static boolean isJavaMap(Object obj) {
        return obj instanceof Map;
    }

    /**
     * @param obj the object
     * @return true if object is a Java Array
     */
    public static boolean isJavaArray(Object obj) {
        return obj == null ? false : obj.getClass().isArray();
    }

    public static <O extends JavaScriptObject> JsArray<O> convertToJsArray(O[] array) {
        if (array == null)
            return null;
        else {
            final JsArray<O> ret = (JsArray<O>) JavaScriptObject.createArray();
            ret.setLength(array.length);
            for (int i = 0; i < array.length; ++i) {
                ret.set(i, array[i]);
            }
            return ret;
        }
    }

    /**
     * Converts the parameter Object array to an equivalent Javascript array - see 
     * {@link com.smartgwt.client.docs.JavaToJavaScriptConversion} for details of the conversions
     * that take place.
     *
     * @param array the Java Object array to convert
     */
    public static JavaScriptObject convertToJavaScriptArray(Object[] array) {
        return convertToJavaScriptArray(array, false);
    }

    public static JavaScriptObject convertToJavaScriptArray(Object[] array, boolean strict) {

        if (array == null)
            return null;
        JavaScriptObject jsArray = createJavaScriptArray();
        for (int i = 0; i < array.length; i++) {
            Object val = array[i];

            if (val == null) {
                setArrayValue(jsArray, i, (JavaScriptObject) val);
            } else if (val instanceof String || val instanceof Character) {
                setArrayValue(jsArray, i, val.toString());
            } else if (val instanceof Number) {
                if (val instanceof Long)
                    setArrayValue(jsArray, i, ((Long) val).longValue());
                else
                    setArrayValue(jsArray, i, doubleValue((Number) val));
            } else if (val instanceof Boolean) {
                JSOHelper.setArrayValue(jsArray, i, ((Boolean) val).booleanValue());
            } else if (val instanceof Date) {
                JSOHelper.setArrayValue(jsArray, i, (Date) val);
            } else if (val instanceof ValueEnum) {
                JSOHelper.setArrayValue(jsArray, i, ((ValueEnum) val).getValue());
            } else if (val instanceof JavaScriptObject) {
                JSOHelper.setArrayValue(jsArray, i, (JavaScriptObject) val);
            } else if (val instanceof RecordList) {
                if (val instanceof ResultSet) {
                    val = ((ResultSet) val).getAllCachedRows();
                }
                val = ((RecordList) val).getRange(0, ((RecordList) val).getLength());
                setArrayValue(jsArray, i, convertToJavaScriptArray((Record[]) val, strict));

            } /*else if (val instanceof JsObject) {
              JSOHelper.setArrayValue(jsArray, i, ((JsObject) val).getJsObj());
              } */
            else if (val instanceof DataClass) {
                JSOHelper.setArrayValue(jsArray, i, ((DataClass) val).getJsObj());
            } else if (val instanceof BaseClass) {
                JSOHelper.setArrayValue(jsArray, i, ((BaseClass) val).getOrCreateJsObj());
            } else if (val instanceof BaseWidget) {
                JSOHelper.setArrayValue(jsArray, i, ((BaseWidget) val).getOrCreateJsObj());
            } else if (val instanceof Record) {
                JSOHelper.setArrayValue(jsArray, i, ((Record) val).getJsObj());
            } else if (val.getClass().isArray()) {
                if (val instanceof Object[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((Object[]) val, strict));
                } else if (val instanceof int[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((int[]) val));
                } else if (val instanceof double[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((double[]) val));
                } else if (val instanceof float[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((float[]) val));
                } else if (val instanceof boolean[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((boolean[]) val));
                } else if (val instanceof char[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((char[]) val));
                } else if (val instanceof byte[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((byte[]) val));
                } else if (val instanceof short[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((short[]) val));
                } else if (val instanceof long[]) {
                    setArrayValue(jsArray, i, convertToJavaScriptArray((long[]) val));
                } else {
                    assert false : val.getClass() + " should not be an array class.";
                    setArrayValue(jsArray, i, (JavaScriptObject) null);
                }
            } else if (val instanceof List) {
                setArrayValue(jsArray, i, JSOHelper.convertToJavaScriptArray(((List<?>) val).toArray(), strict));
            } else if (val instanceof Iterator) {
                List listVal = new ArrayList();
                while (((Iterator) val).hasNext())
                    listVal.add(((Iterator) val).next());

                setArrayValue(jsArray, i,
                        JSOHelper.convertToJavaScriptArray(((List<?>) listVal).toArray(), strict));
            } else if (val instanceof Set) {
                setArrayValue(jsArray, i, JSOHelper.convertToJavaScriptArray(((Set<?>) val).toArray(), strict));
            } else if (val instanceof Map) {
                setArrayValue(jsArray, i, convertMapToJavascriptObject((Map) val, strict));
            } else {
                assert val != null;
                assert !(val instanceof JavaScriptObject);
                if (strict) {
                    throwUnconvertibleObjectException(val,
                            "Object is a member of an Array being converted to JavaScript. "
                                    + "Please see the SmartClient documentation of "
                                    + "RPCRequest.data for a table of Java types that can be converted automatically.");
                } else {
                    setArrayValue(jsArray, i, val);
                }
            }
        }
        return jsArray;
    }

    // Helper to throw an exception if we want to convert a method to JS but it's not a convertible type
    // This is also called from JSNI when attempting to serialize a Java object as part of an RPC request
    // within the SmartClient JS code. See isc.SmartGWT.throwUnconvertibleObjectException() defined in
    // SmartGwtEntryPoint
    public static void throwUnconvertibleObjectException(Object object, String messageDetail) {

        String message;
        if (object == null)
            message = "Null!!!";
        else if (object.getClass() == null)
            message = "NO Class??!!";
        else
            message = "Attempt to convert instance of class " + (object.getClass().getName())
                    + " to a JavaScript object failed.";
        if (messageDetail != null)
            message += " " + messageDetail;
        throw new UnsupportedOperationException(message);
    }

    public static Integer toInteger(int value) {
        return value;
    }

    public static Long toLong(double value) {
        return (long) value;
    }

    public static Float toFloat(float value) {
        return value;
    }

    public static Double toDouble(double value) {
        return value;
    }

    private static double getTime(Date date) {
        return date.getTime();
    }

    /**
     * Converts a time to a Java {@link java.util.Date} object.
     * 
     * <p>Note: If needing to convert a JavaScript object, it is preferable to use
     * {@link #convertToJavaDate(JavaScriptObject)} instead because that function is able to
     * convert to {@link com.smartgwt.client.util.LogicalDate} and {@link com.smartgwt.client.util.LogicalTime}
     * instances as needed.
     * 
     * @param millis number of milliseconds since January 1, 1970, 00:00:00 GMT.
     * @return a new {@link java.util.Date} representing the given time.
     */
    public static Date toDate(double millis) {
        return new Date((long) millis);
    }

    public static JavaScriptObject toDateJS(Date date) {
        return convertToJavaScriptDate(date);
    }

    // Helper to get logical JS date / time objects. These objects will be recognized
    // as logical date / times for formatting / serialization purposes by the SmartClient system.
    public static JsDate getJSLogicalDate(Date date) {
        return getJSLogicalDate(date.getYear(), date.getMonth(), date.getDate());
    }

    public static native JsDate getJSLogicalDate(int year, int month, int date) /*-{
                                                                                return $wnd.Date.createLogicalDate(year, month, date);
                                                                                }-*/;

    public static native JsDate getJSLogicalTime(Date date) /*-{
                                                            var time = @com.smartgwt.client.util.JSOHelper::getTime(Ljava/util/Date;)(date);
                                                            var jsDate = $wnd.Date.create().setTime(time);
                                                            return $wnd.Date.createLogicalTime(jsDate.getHours(), jsDate.getMinutes(), jsDate.getSeconds(), jsDate.getMilliseconds());
                                                            }-*/;

    public static native JsDate getJSLogicalTime(int hour, int minute, int second, int millisecond) /*-{
                                                                                                    return $wnd.Date.createLogicalTime(hour, minute, second, millisecond);
                                                                                                    }-*/;

    public static Boolean toBoolean(boolean value) {
        return value;
    }

    public static native JavaScriptObject createJavaScriptArray() /*-{
                                                                  //Important : constructing an array from JSNI using [] or new Array() results in a
                                                                  //corrupted array object in the final javascript. The array ends up having the correct elements
                                                                  //but the test (myarr instanceof Array) fails because the JSNI-created array constructor is different.
                                                                  //Need to construct array within the scope of the application's IFRAME by using `new $wnd.Array'.
                                                                  return $wnd.Array.create();
                                                                  }-*/;

    public static void setArrayValue(JavaScriptObject array, int index, Date value) {
        setArrayValue(array, index, convertToJavaScriptDate(value));
    }

    public static native void setArrayValue(JavaScriptObject array, int index, String value) /*-{
                                                                                             // See the comments in setAttribute(JavaScriptObject, String, String).
                                                                                             array[index] = (value == null ? null : String(value));
                                                                                             }-*/;

    public static native void setArrayValue(JavaScriptObject array, int index, double value) /*-{
                                                                                             array[index] = value;
                                                                                             }-*/;

    public static void setArrayValue(JavaScriptObject array, int index, long value) {
        final double d = (double) value;
        if (Math.abs(value) > 9007199254740992L) {
            SC.logWarn("The long value " + Long.toString(value)
                    + " cannot be exactly represented in JavaScript. It will be truncated to: "
                    + Long.toString((long) d));
        }
        setArrayValue(array, index, d);
    }

    public static native void setArrayValue(JavaScriptObject array, int index, boolean value) /*-{
                                                                                              array[index] = value;
                                                                                              }-*/;

    public static void setArrayValue(JavaScriptObject array, int index, char value) {
        setArrayValue(array, index, Character.toString(value));
    }

    public static native void setArrayValue(JavaScriptObject array, int index, JavaScriptObject value) /*-{
                                                                                                       array[index] = value;
                                                                                                       }-*/;

    public static native void setArrayValue(JavaScriptObject array, int index, Object value) /*-{
                                                                                             array[index] = value;
                                                                                             }-*/;

    public static native String getArrayValue(JavaScriptObject array, int index) /*-{
                                                                                 var result = array[index];
                                                                                 return (result == null ? null : String(result));
                                                                                 }-*/;

    public static native JavaScriptObject getJSOArrayValue(JavaScriptObject array, int index) /*-{
                                                                                              var result = array[index];
                                                                                              return (result == null ? null : result);
                                                                                              }-*/;

    public static native Object getObjectArrayValue(JavaScriptObject array, int index) /*-{
                                                                                       var result = array[index];
                                                                                       return (result == null ? null : result);
                                                                                       }-*/;

    public static native Boolean getBooleanArrayValue(JavaScriptObject array, int index) /*-{
                                                                                         var ret = array[index];
                                                                                         return ret == null ? null : @com.smartgwt.client.util.JSOHelper::toBoolean(Z)(ret);
                                                                                         }-*/;

    public static native int getIntArrayValue(JavaScriptObject array, int index) /*-{
                                                                                 return array[index];
                                                                                 }-*/;

    public static native float getfloatArrayValue(JavaScriptObject array, int index) /*-{
                                                                                     return array[index];
                                                                                     }-*/;

    public static native double getdoubleArrayValue(JavaScriptObject array, int index) /*-{
                                                                                       return array[index];
                                                                                       }-*/;

    public static native Integer getIntegerArrayValue(JavaScriptObject array, int index) /*-{
                                                                                         var ret = array[index];
                                                                                         return (ret == null ? null : @com.smartgwt.client.util.JSOHelper::toInteger(I)(ret));
                                                                                         }-*/;

    public static native Float getFloatArrayValue(JavaScriptObject array, int index) /*-{
                                                                                     var ret = array[index];
                                                                                     return (ret == null ? null : @com.smartgwt.client.util.JSOHelper::toFloat(F)(ret));
                                                                                     }-*/;

    public static native Double getDoubleArrayValue(JavaScriptObject array, int index) /*-{
                                                                                       var ret = array[index];
                                                                                       return (ret == null ? null : @com.smartgwt.client.util.JSOHelper::toDouble(D)(ret));
                                                                                       }-*/;

    public static native Date getDateArrayValue(JavaScriptObject array, int i) /*-{
                                                                               if (array == null || !$wnd.isc.isAn.Array(array)) return null;
                                                                               var val = array[i];
                                                                               return @com.smartgwt.client.util.JSOHelper::convertToJavaDate(Lcom/google/gwt/core/client/JavaScriptObject;)(val);
                                                                               }-*/;

    public static native int getArrayLength(JavaScriptObject array) /*-{
                                                                    return array.length;
                                                                    }-*/;

    public static Boolean[] convertToJavaBooleanArray(JavaScriptObject array) {
        int length = getArrayLength(array);
        Boolean[] arr = new Boolean[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getBooleanArrayValue(array, i);
        }
        return arr;
    }

    public static int[] convertToJavaIntArray(JavaScriptObject array) {
        int length = getArrayLength(array);
        int[] arr = new int[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getIntArrayValue(array, i);
        }
        return arr;
    }

    /**
     * @deprecated deprecated in favor of {@link #convertToJavaIntegerArray}
     */
    public static Integer[] convertToJavaInterArray(JavaScriptObject array) {
        return convertToJavaIntegerArray(array);
    }

    public static Integer[] convertToJavaIntegerArray(JavaScriptObject array) {
        int length = getArrayLength(array);
        Integer[] arr = new Integer[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getIntegerArrayValue(array, i);
        }
        return arr;
    }

    public static String[] convertToJavaStringArray(JavaScriptObject array) {
        if (array == null)
            return new String[] {};
        int length = getArrayLength(array);
        String[] arr = new String[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getArrayValue(array, i);
        }
        return arr;
    }

    public static Float[] convertToJavaFloatArray(JavaScriptObject array) {
        int length = getArrayLength(array);
        Float[] arr = new Float[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getFloatArrayValue(array, i);
        }
        return arr;
    }

    public static Double[] convertToJavaDoubleArray(JavaScriptObject array) {
        int length = getArrayLength(array);
        Double[] arr = new Double[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getDoubleArrayValue(array, i);
        }
        return arr;
    }

    public static Date[] convertToJavaDateArray(JavaScriptObject array) {
        int length = getArrayLength(array);
        Date[] arr = new Date[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getDateArrayValue(array, i);
        }
        return arr;
    }

    public static Object[] convertToJavaObjectArray(JavaScriptObject array) {
        if (array == null)
            return new Object[] {};
        int length = getArrayLength(array);
        Object[] arr = new Object[length];
        for (int i = 0; i < length; i++) {
            arr[i] = getObjectArrayValue(array, i);
        }
        return arr;
    }

    public static RefDataClass[] convertToJavaRefDataClassArray(JavaScriptObject nativeArray) {
        if (nativeArray == null) {
            return new RefDataClass[] {};
        }
        JavaScriptObject[] componentsj = JSOHelper.toArray(nativeArray);
        RefDataClass[] objects = new RefDataClass[componentsj.length];
        for (int i = 0; i < componentsj.length; i++) {
            JavaScriptObject componentJS = componentsj[i];
            RefDataClass obj = RefDataClass.getRef(componentJS);
            objects[i] = obj;
        }
        return objects;
    }

    public static native void apply(JavaScriptObject config, JavaScriptObject jsObj) /*-{
                                                                                     for(var k in config) {
                                                                                     jsObj[k] = config[k];
                                                                                     }
                                                                                     }-*/;

    /**
     * Sets attribute "attr" on Javascript object "jsObj" to the JavaScript equivalent of "valueMap".
     * See {@link com.smartgwt.client.docs.JavaToJavaScriptConversion} 
     * for details of the conversions that take place.
     *
     * @param jsObj the JavaScript object on which to set the attribute
     * @param attr the name of the attribute to set
     * @param valueMap the Java Map to convert and apply as the attribute value
     */
    public static void setAttribute(JavaScriptObject jsObj, String attr, Map valueMap) {
        JavaScriptObject valueJS = convertMapToJavascriptObject(valueMap);
        setAttribute(jsObj, attr, valueJS);
    }

    /**
     * Converts the parameter Map to an equivalent Javascript object - see 
     * {@link com.smartgwt.client.docs.JavaToJavaScriptConversion} 
     * for details of the conversions that take place.
     *
     * @param array the Java Map to convert
     */
    public static JavaScriptObject convertMapToJavascriptObject(Map valueMap) {
        return convertMapToJavascriptObject(valueMap, false);
    }

    public static JavaScriptObject convertMapToJavascriptObject(Map valueMap, boolean strict) {
        if (valueMap == null)
            return null;
        JavaScriptObject valueJS = JSOHelper.createObject();
        for (Iterator iterator = valueMap.keySet().iterator(); iterator.hasNext();) {
            String key = (String) iterator.next();
            if (key == null) {
                SC.logWarn("JSO::convertMapToJavascriptObject : Map contains null key - dropping this entry.");
                continue;
            }
            if (key.equals("__ref")) {
                SC.logWarn("JSO::convertMapToJavascriptObject : skipping __ref in map");
                continue;
            }
            if (key.equals("__module")) {
                SC.logWarn("JSO::convertMapToJavascriptObject : skipping __module in map");
                continue;
            }

            Object value = valueMap.get(key);

            if (value instanceof JavaScriptObject) {
                setAttribute(valueJS, key, (JavaScriptObject) value);
            } else if (value instanceof Date) {
                setAttribute(valueJS, key, ((Date) value));
            } else if (value instanceof Number) {
                setAttribute(valueJS, key, doubleValue((Number) value));
            } else if (value instanceof String) {
                setAttribute(valueJS, key, ((String) value));
            } else if (value instanceof Boolean) {
                setAttribute(valueJS, key, ((Boolean) value).booleanValue());
            } else if (value == null) {
                setNullAttribute(valueJS, key);
            } else if (value instanceof String[]) {
                setAttribute(valueJS, key, convertToJavaScriptArray((String[]) value));
            } else if (value instanceof Object[]) {
                setAttribute(valueJS, key, convertToJavaScriptArray((Object[]) value, strict));
            } else if (value instanceof int[]) {
                setAttribute(valueJS, key, convertToJavaScriptArray((int[]) value));
            } else if (value instanceof float[]) {
                setAttribute(valueJS, key, convertToJavaScriptArray((float[]) value));
            } else if (value instanceof double[]) {
                setAttribute(valueJS, key, convertToJavaScriptArray((double[]) value));
            } else if (value instanceof long[]) {
                setAttribute(valueJS, key, convertToJavaScriptArray((long[]) value));
            } else if (value instanceof Map) {
                JavaScriptObject innerMapJS = convertMapToJavascriptObject((Map) value, strict);
                setAttribute(valueJS, key, innerMapJS);
            } else if (value instanceof List) {
                setAttribute(valueJS, key, JSOHelper.convertToJavaScriptArray(((List) value).toArray(), strict));
            } else if (value instanceof DataClass) {
                setAttribute(valueJS, key, ((DataClass) value).getJsObj());
            } else if (value instanceof BaseClass) {
                setAttribute(valueJS, key, ((BaseClass) value).getJsObj());
            } else if (value instanceof BaseWidget) {
                setAttribute(valueJS, key, ((BaseWidget) value).getOrCreateJsObj());
            } else if (value instanceof ValueEnum) {
                setAttribute(valueJS, key, ((ValueEnum) value).getValue());
            } else {
                assert value != null;
                if (strict) {
                    throwUnconvertibleObjectException(value, "Object is stored as attribute " + key
                            + " of a Map being converted to JavaScript. "
                            + "Please see the SmartClient documentation of "
                            + "RPCRequest.data for a table of Java types that can be converted automatically.");
                } else {
                    setObjectAttribute(valueJS, key, value);
                }
            }
        }
        return valueJS;
    }

    public static native String[] getProperties(JavaScriptObject jsObj) /*-{
                                                                        var props = @com.smartgwt.client.util.JSOHelper::createJavaScriptArray()();
                                                                        for(var k in jsObj) {
                                                                        props.push(k);
                                                                        }
                                                                        return @com.smartgwt.client.util.JSOHelper::convertToJavaStringArray(Lcom/google/gwt/core/client/JavaScriptObject;)(props);
                                                                        }-*/;

    public static native String getPropertiesAsString(JavaScriptObject jsObj) /*-{
                                                                              var props = '{';
                                                                              for(var k in jsObj) {
                                                                              props += '\n' + k;
                                                                              }
                                                                              return props + '}';
                                                                              }-*/;

    /**
     * Adds all properties and methods from the propertiesObject to the destination object.
     *
     * @param destination the destination object
     * @param propertiesObject the propertiesObject
     */
    public static native void addProperties(JavaScriptObject destination, JavaScriptObject propertiesObject)
    /*-{
    $wnd.isc.addProperties(destination, propertiesObject);
    }-*/;

    /**
     * Removes non-transferable properties from the supplied object
     * (e.g. widget ID), performing a copy first if requested.
     *
     * @param properties the supplied object to clean
     * @param copyProperties whether to copy the object first
     * @return the cleaned properties object
     */
    public static native JavaScriptObject cleanProperties(JavaScriptObject properties, boolean copyProperties)
    /*-{
    if (properties != null) {
        if (copyProperties) properties = $wnd.isc.addProperties({}, properties);
        delete properties.AUTOIDClass;
        delete properties.ID;
        delete properties.__ref;
        delete properties.__module;
        delete properties._autoAssignedID;
    }
    return properties;
    }-*/;

    // Invoking functions in JavaScript scope
    // Convert Java Object Array to JavaScript array of appropriate types and use function.apply()
    /**
     * Invoke a method on some JavaScript object, passing in the specified array of parameters.
     * <P> 
     * This method converts the specified parameters to a equivalent 
     * objects in JavaScript before calling the method in JavaScript scope, and will convert any return
     * value to Java before returning it - see 
     * {@link com.smartgwt.client.docs.JavaToJavaScriptConversion} for details of the conversions
     * that take place.<br>
     * If the invoked method has no return value, this method will return null.
     * 
     * @param target
     * @param methodName
     * @param arguments
     * @return
     */
    public static Object callMethod(JavaScriptObject target, String methodName, Object[] arguments) {

        JavaScriptObject argumentsJS = convertToJavaScriptArray(arguments);
        JavaScriptObject result = callMethodJS(target, methodName, argumentsJS);
        if (result == null)
            return null;
        // Use convertToJava to appropriately wrap things in the right object type.
        return convertToJava(result);
    }

    private static native JavaScriptObject callMethodJS(JavaScriptObject target, String methodName,
            JavaScriptObject arguments) /*-{
                                        var returnVal = null;
                                        if (target != null && target[methodName] != null) {
                                        returnVal = target[methodName].apply(target, arguments);
                                        }
                                        return returnVal;
                                            
                                        }-*/;

}