/*
* 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
}
}