/* * Smart GWT (GWT for SmartClient) * Copyright 2008 and beyond, Isomorphic Software, Inc. * * Smart GWT is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 * is published by the Free Software Foundation. Smart GWT is also * available under typical commercial license terms - see * http://smartclient.com/license * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package com.smartgwt.client.bean.types; import com.google.gwt.core.client.JavaScriptObject; import com.smartgwt.client.bean.BeanValueType; import com.smartgwt.client.bean.BeanValueType.Convertability; import com.smartgwt.client.bean.types.OtherValueType; import com.smartgwt.client.util.SC; import com.smartgwt.client.util.JSOHelper; import java.util.Map; // This class deals with ValueTypes which wrap an underlying JavaScriptObject // which can be used to construct the ValueType. Essentially, that means // BaseWidget, BaseClass, DataClass, JsObject, RefDataClass and their // descendants. // // The generator tries to keep some track of which JavaScriptObjects can be // used to construct the ValueType -- for instance, BaseWidgets can only be // constructed from SmartClient Canvas objects. But we can't get at the // scClassName (easily) until we actually construct the object, so we aren't // super-precise -- we could let through some illegal states. // // Note that if the ValueType literally takes a JavaScriptObject as a // parameter, then we would use JsoValueType instead. public abstract class JsoWrapperValueType<ValueType> extends BeanValueType<ValueType> { @Override public Convertability convertabilityFrom (Object value) { // If it is a JavaScriptObject, then check whether we can wrap it if (value instanceof JavaScriptObject) { if (canWrapJavaScriptObject((JavaScriptObject) value)) { return Convertability.SUPPORTED; } } // If it is a Map, then it may have arrived as a POJO and been turned // into a Map. In that case, we could wrap it if we aren't looking for // a SmartClient instance. In principle, I suppose we can turn anything // into a JavaScriptObject and then wrap it ... will need to look at // the methods that take JavaScriptObjects and see what they tend to // really be expecting. if (value instanceof Map) { if (getScClassName() == null) return Convertability.SUPPORTED; } if (value == null) return Convertability.SUPPORTED; if (value.getClass() == getValueType()) return Convertability.EXACT; if (isAssignableFrom(value)) return Convertability.PREFERRED; return super.convertabilityFrom(value); } // Tests the scClassName to see whether the object can be wrapped by the // constructor. protected boolean canWrapJavaScriptObject (JavaScriptObject value) { String scClassName = getScClassName(); if (scClassName == null) { // If null, we take it that the JavaScriptObject need not be any // particular class, or that it needs to be tested in another way return true; } else { return isA(value, scClassName); } } // If the ValueType should only wrap a certain SmartClient class (or // subclases thereof), then the generated subclass should implement // getScClassName. Returning null indicates that the SmartClient class // doesn't matter. protected String getScClassName () { return null; } // Generated subclasses must implement in order to return a new instance // constructed from the JavaScriptObject value protected abstract ValueType newInstance (JavaScriptObject value); @Override @SuppressWarnings("unchecked") public ValueType convertFrom (Object value) { if (isAssignableFrom(value)) { // The cast should be safe, since isAssignableFrom is the check return (ValueType) value; } else if (value instanceof JavaScriptObject) { if (canWrapJavaScriptObject((JavaScriptObject) value)) { Object existingRef = JSOHelper.getAttributeAsObject((JavaScriptObject) value, SC.REF); if (existingRef != null) { // If there is an existing __ref, we check whether it is assignable to // our ValueType ... if so, we just return it ... otherwise, we fall // through to creating a new ValueType instance. if (isAssignableFrom(existingRef)) return (ValueType) existingRef; } return newInstance((JavaScriptObject) value); } else { throw new IllegalArgumentException("Could not wrap JavaScriptObject"); } } else if (value instanceof Map && getScClassName() == null) { // In theory, we can turn anything into a JavaScriptObject and then // wrap it. But the value type is probably expecting a particular // kind of JavaScriptObject ... we'll probably need to work on this // some more. return convertFrom(convertToJavaScriptObject(value)); } else { return super.convertFrom(value); } // We could consider a conversion routine for strings ... for instance, // checking whether a string is the ID of an existing SmartClient instance, // and then wrapping that } }