/* * 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.core; import java.util.Date; import java.util.Map; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.event.shared.EventHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.event.shared.HasHandlers; import com.smartgwt.client.bean.BeanFactory; import com.smartgwt.client.data.Record; import com.smartgwt.client.types.ValueEnum; import com.smartgwt.client.util.JSOHelper; import com.smartgwt.client.widgets.BaseWidget; import com.smartgwt.client.util.ConfigUtil; import com.smartgwt.client.util.SC; public class DataClass extends JsObject implements HasHandlers { // Properties stashed by BeanFactory when calling the no-arg constructor. // We pick them up immediately in the constructor so that they don't get // applied to the wrong object (in case the constructor of a subclass // triggers the construction of some other object -- which, admittedly, // is unlikely in the case of DataClass, but better safe than sorry). protected Map<String, Object> factoryProperties; // Called by the generated BeanFactory once the instance is fully // constructed. Unlike the implementation in BaseWidget, we don't call // this before creating the jsObj, because we're not supplying any // properties to the SmartClient constructor. So, this is here just so that // BeanFactory doesn't need to care whether it's dealing with a BaseWidget // or a DataClass. public void applyFactoryProperties () { if (factoryProperties != null) { // Make sure that this is re-entrant without infinite loop Map<String, Object> properties = factoryProperties; factoryProperties = null; BeanFactory.setProperties(this, properties); } } // Tracks whether this object was created by a BeanFactory. The BeanFactory // code will set this property via the reflection mechanism when creating // an instance. Thus, it can check whether the property has been correctly // applied. (That is, if factoryCreated is false for an object which // BeanFactory creates, then BeanFactory knows something went wrong). // // There is one known case where properties are not correctly applied via // reflection: when (a) a class has a static initializer; (b) the static // initializer is not triggered before the use of reflection to create an // object of that class; and (c) the static initializer itself creates an // object of that class. // // We can't detect that case directly, but we can at least detect the // resulting failure and try to recover (and generate a useful error // message). protected boolean factoryCreated; public void setFactoryCreated (boolean createdByBeanFactory) { factoryCreated = createdByBeanFactory; } public boolean isFactoryCreated () { return factoryCreated; } // if this instance has been used to set the properties of another object, mark it as // read-only so that no further changes can be made (lest a warning be generated). protected boolean readOnly; public void setReadOnly() { readOnly = true; } public boolean getReadOnly() { return readOnly; } public DataClass() { super(JSOHelper.createObject()); // Stash any properties supplied by BeanFactory, if intended for an // object of this class. The properties will be applied by generated // BeanFactory code once the object is fully constructed. if (getClass() == BeanFactory.getFactoryPropertiesClass()) { factoryProperties = BeanFactory.getFactoryProperties(); BeanFactory.clearFactoryProperties(); } } public DataClass(JavaScriptObject jsObj) { super(jsObj); } /** * Set attribute value to a String * @param property * @param value */ public void setAttribute(String property, String value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Returns attribute value as a String * @param property * @return */ public String getAttribute(String property) { return JSOHelper.getAttribute(jsObj, property); } /** * Returns attribute value as a String. * @param property * @return */ public String getAttributeAsString(String property) { return JSOHelper.getAttribute(jsObj, property); } /** * Returns attribute value as an Integer. * Applies to values stored as a JavaScript number on the underlying data object. * @param property * @return */ public Integer getAttributeAsInt(String property) { return JSOHelper.getAttributeAsInt(jsObj, property); } /** * 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 two parameter * variant of this API {@link #getAttributeAsBoolean(String, boolean)} if you want * <code>null</code> returned for <code>null</code> attribute values. * @param property the property name * @return the property value */ public Boolean getAttributeAsBoolean(String property) { return getAttributeAsBoolean(property, 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 one parameter variant of this API * {@link #getAttributeAsBoolean(String)}. * @param property the property name * @param allowNull whether to allow null * @return the property value */ public Boolean getAttributeAsBoolean(String property, boolean allowNull) { return JSOHelper.getAttributeAsBoolean(jsObj, property, allowNull); } /** * Returns attribute as a Double. * Applies to values stored as a JavaScript Number on the underlying data object. * @param property * @return */ public Double getAttributeAsDouble(String property) { return JSOHelper.getAttributeAsDouble(jsObj, property); } /** * Returns attribute as a Long. * Applies to values stored as a JavaScript Number on the underlying data object. * @param property * @return */ public Long getAttributeAsLong(String property) { Double dVal = this.getAttributeAsDouble(property); return dVal == null ? null : dVal.longValue(); } /** * Returns attribute as a double array. * Applies to values stored as a JavaScript Array of Numbers on the underlying data object. * @param property * @return */ public double[] getAttributeAsDoubleArray(String property) { return JSOHelper.getAttributeAsDoubleArray(jsObj, property); } /** * Set attribute value to a DataClass array. * Value will be stored as a JavaScript Array of the underlying JavaScript objects for each entry. * @param property * @param value */ public void setAttribute(String property, DataClass[] value) { JSOHelper.setAttribute(jsObj, property, JSOHelper.convertToJavaScriptArray(value)); } /** * Set attribute value to a BaseClass array. * Value will be stored as a JavaScript Array of the underlying JavaScript objects for each entry. * @param property * @param value */ public void setAttribute(String property, BaseClass[] value) { JSOHelper.setAttribute(jsObj, property, JSOHelper.convertToJavaScriptArray(value)); } /** * Set attribute value to a BaseWidget array. * Value will be stored as a JavaScript Array of the underlying JavaScript objects for each entry. * @param property * @param value */ public void setAttribute(String property, BaseWidget[] value) { JSOHelper.setAttribute(jsObj, property, JSOHelper.convertToJavaScriptArray(value)); } /** * Returns attribute as an int array. * Applies to values stored as a JavaScript Array of Numbers on the underlying data object. * @param property * @return */ public int[] getAttributeAsIntArray(String property) { return JSOHelper.getAttributeAsIntArray(jsObj, property); } /** * Set attribute value to a String array. * Value will be stored as a JavaScript Array of Strings on the underlying data object. * @param property * @param value */ public void setAttribute(String property, String[] value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Returns attribute as a String array. * Applies to values stored as a JavaScript Array of Strings on the underlying data object. * @param property * @return */ public String[] getAttributeAsStringArray(String property) { return JSOHelper.getAttributeAsStringArray(jsObj, property); } /** * Set attribute value to a DataClass. * Value will be stored as the underlying JavaScript object for the DataClass * instance passed in. * * @param property * @param value */ public void setAttribute(String property, DataClass value) { JSOHelper.setAttribute(jsObj, property, value == null ? null : value.getJsObj()); } /** * Set attribute value to a BaseClass. * Value will be stored as the underlying JavaScript object for the BaseClass * instance passed in. * * @param property * @param value */ public void setAttribute(String property, BaseClass value) { JSOHelper.setAttribute(jsObj, property, value == null ? null : value.getOrCreateJsObj()); } /** * Set attribute value to a JavaScriptObject. * * @param property * @param value */ public void setAttribute(String property, JavaScriptObject value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a Date. * Value will be stored as a JavaScript Date on the underlying data object * * @param property * @param value */ public void setAttribute(String property, Date value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a Map. * Value will be stored as a JavaScript Object on the underlying data object, * with property/value pairs matching the keys/values specified on the Map. * Note that this is a recursive conversion - each value will also be converted to * the equivalent JavaScript type where appropriate. * * @param property * @param value */ public void setAttribute(String property, Map value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a ValueEnum array. * Value will be stored as a JavaScript Array containing the each Enum value. * * @param property * @param value */ public void setAttribute(String property, ValueEnum[] value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a ValueEnum. * The value of the ValueEnum will be stored on the underlying data object. * * @param property * @param value */ public void setAttribute(String property, ValueEnum value) { JSOHelper.setAttribute(jsObj, property, value == null ? null : value.getValue()); } /** * Set the attribute value to an Object. * <P> * The values provided as attributes are generally expected to contain valid values for a field * of a DataBoundComponent. Therefore this method converts the value passed in to an equivalent object in JavaScript before * storing on underlying data object. <br> * Developers can use {@link #setAttributeAsJavaObject(String, Object)} to store Java objects * without converting to JavaScript. * * <P> * Conversions that occur include: * <ul> * <li>Numeric data types will be represented as JavaScript Number</li> * <li>Dates (and subclasses) will be stored as JavaScript Date</li> * <li>Java Arrays will be stored as JavaScript Array, with members converted to the * equivalent JavaScript type where appropriate</li> * <li>Java Collections (including List, Set) will be stored as JavaScript Array, with members converted to the * equivalent JavaScript object (recursively) where appropriate</li> * <li>Maps are stored as JavaScript Objects with each key mapped to a property on the object. * Property values are converted to the equivalent JavaScript object (recursively) where appropriate</li> * </ul> * Other Java Objects, including POJOs are stored on the underlying data object unconverted. Developers can * retrieve such values via {@link #getAttributeAsObject(String)}. * * @param property the attribute name * @param value the attribute value. */ public void setAttribute(String property, Object value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set the attribute value to an Object. * <P> * Unlike {@link #setAttribute(String, Object)}, this method will store the value passed in as-is * rather than converting to an equivalent object in JavaScript. * * @param property * @param value */ public void setAttributeAsJavaObject(String property, Object value) { JSOHelper.setObjectAttribute(jsObj, property, value); } /** * Set attribute value to a boolean. * @param property * @param value */ public void setAttribute(String property, boolean value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a Boolean. * * @param property * @param value */ public void setAttribute(String property, Boolean value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to an int. * Value will be stored as a JavaScript Number on the underlying data object * @param property * @param value */ public void setAttribute(String property, int value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to an Integer. * Value will be stored as a JavaScript Number on the underlying data object * @param property * @param value */ public void setAttribute(String property, Integer value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a long. * Value will be stored as a JavaScript Number on the underlying data object. * @param property * @param value */ public void setAttribute(String property, long value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a Float. * Value will be stored as a JavaScript Number on the underlying data object * @param property * @param value */ public void setAttribute(String property, Float value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a double. * Value will be stored as a JavaScript Number on the underlying data object * @param property * @param value */ public void setAttribute(String property, double value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a Double. * Value will be stored as a JavaScript Number on the underlying data object * @param property * @param value */ public void setAttribute(String property, Double value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to an int array. * Value will be stored as a JavaScript Array of Numbers on the underlying data object. * @param property * @param value */ public void setAttribute(String property, int[] value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to an Integer array. * Value will be stored as a JavaScript Array of Numbers on the underlying data object. * @param property * @param value */ public void setAttribute(String property, Integer[] value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Set attribute value to a double array. * Value will be stored as a JavaScript Array of Numbers on the underlying data object * * @param property * @param value */ public void setAttribute(String property, double[] value) { JSOHelper.setAttribute(jsObj, property, value); } /** * Returns attribute value as a Float. * Applies to values stored as a JavaScript number on the underlying data object. * @param property * @return */ public Float getAttributeAsFloat(String property) { return JSOHelper.getAttributeAsFloat(jsObj, property); } /** * Returns attribute value as a Date. * Applies to values stored as a JavaScript Date on the underlying data object. * @param property * @return */ public Date getAttributeAsDate(String property) { return JSOHelper.getAttributeAsDate(jsObj, property); } /** * Returns attribute value as a Java Object. * <P> * If the attribute has been set as a Java Object (typically via a call to * {@link #setAttributeAsJavaObject(String, Object)}) it will be returned as is. * <P> * Values specified as JavaScript types will be converted to the equivalent * Java type if possible. * @param property * @return */ public Object getAttributeAsObject(String property) { return JSOHelper.getAttributeAsObject(jsObj, property); } /** * Returns attribute value as a Map. * Applies to values stored as a JavaScript Object on the underlying data object. * @param property * @return */ public Map getAttributeAsMap(String property) { return JSOHelper.getAttributeAsMap(jsObj, property); } /** * Get the attribute value as a Record. * * @param property the property name * @return the record value */ public Record getAttributeAsRecord(String property) { return Record.getOrCreateRef(getAttributeAsJavaScriptObject(property)); } /** * Returns attribute value as a JavaScript Object. * @param property * @return */ public JavaScriptObject getAttributeAsJavaScriptObject(String property) { return JSOHelper.getAttributeAsJavaScriptObject(jsObj, property); } public String[] getAttributes() { return JSOHelper.getProperties(jsObj); } //event handling code private HandlerManager manager = null; //@Override public void fireEvent(GwtEvent<?> event) { if (manager != null) { manager.fireEvent(event); } } protected final <H extends EventHandler> HandlerRegistration doAddHandler( final H handler, GwtEvent.Type<H> type) { return ensureHandlers().addHandler(type, handler); } /** * Ensures the existence of the handler manager. * * @return the handler manager **/ HandlerManager ensureHandlers() { return manager == null ? manager = new HandlerManager(this) : manager; } HandlerManager getManager() { return manager; } public int getHandlerCount(GwtEvent.Type<?> type) { return manager == null? 0 : manager.getHandlerCount(type); } public void logConfiguration(Class callerClass, String callerMethodName) { String configTypeName = ConfigUtil.getSimpleClassName(this.getClass()); if (readOnly) { ConfigUtil.warnOfReconfiguration(callerClass, callerMethodName, configTypeName); } else { ConfigUtil.debugInitialConfiguration(callerClass, callerMethodName, configTypeName); } } }