/*
* Scriptographer
*
* This file is part of Scriptographer, a Scripting Plugin for Adobe Illustrator
* http://scriptographer.org/
*
* Copyright (c) 2002-2010, Juerg Lehni
* http://scratchdisk.com/
*
* All rights reserved. See LICENSE file for details.
*
* File created on Apr 10, 2007.
*/
package com.scriptographer.script.rhino;
import java.util.IdentityHashMap;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.MemberBox;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import com.scratchdisk.script.rhino.ExtendedJavaObject;
import com.scriptographer.ai.Color;
import com.scriptographer.ai.RGBColor;
import com.scriptographer.ai.Style;
import com.scriptographer.script.EnumUtils;
/**
* @author lehni
*
*/
public class RhinoWrapFactory extends
com.scratchdisk.script.rhino.RhinoWrapFactory {
public Object wrap(Context cx, Scriptable scope, Object obj,
Class<?> staticType) {
// By default, Rhino converts chars to integers. In Scriptographer, we
// want a string of length 1:
if (staticType == Character.TYPE)
return obj.toString();
else if (obj instanceof Enum)
// TODO: Could this be moved to wrapCustom?
return EnumUtils.getScriptName((Enum) obj);
return super.wrap(cx, scope, obj, staticType);
}
IdentityHashMap<Class, Function> mappedJavaClasses =
new IdentityHashMap<Class, Function>();
/**
* Maps a Java class to a JavaScript prototype, so this can be used instead
* for wrapping of returned java types. So far this is only used for
* java.io.File in Scriptographer.
*/
public void mapJavaClass(Class cls, Function ctor) {
mappedJavaClasses.put(cls, ctor);
}
public Scriptable wrapCustom(Context cx, Scriptable scope,
Object javaObj, Class<?> staticType, boolean newObject) {
if (javaObj instanceof Style)
return new StyleWrapper(scope, (Style) javaObj, staticType, true);
else if (javaObj instanceof Color)
return new ColorWrapper(scope, (Color) javaObj, staticType, true);
else if (javaObj instanceof java.awt.Color)
return new ColorWrapper(scope, new RGBColor(
(java.awt.Color) javaObj), staticType, true);
else {
Function ctor = mappedJavaClasses.get(staticType);
if (ctor != null) {
// If this native object was explicitly created from JS,
// lets wrap it in a uncached ExtendedJavaObject.
if (newObject)
return null;
scope = ScriptableObject.getTopLevelScope(scope);
// Do not go through Context.javaToJS as this would again
// end up here in wrapCustom for a native object.
// Just wrap it as an ExtendedJavaObject.
return ctor.construct(cx, scope, new Object[] {
new ExtendedJavaObject(scope, javaObj, javaObj.getClass(),
false)
});
}
}
return new ExtendedJavaObject(scope, javaObj, staticType, true);
}
public int calculateConversionWeight(Object from, Object unwrapped,
Class<?> to, int defaultWeight) {
int weight = super.calculateConversionWeight(from, unwrapped, to,
defaultWeight);
if (weight == defaultWeight) {
if (unwrapped instanceof String
&& (Enum.class.isAssignableFrom(to) || to.isArray()))
weight = CONVERSION_TRIVIAL + 1;
else if (unwrapped instanceof Color
&& java.awt.Color.class.equals(to))
weight = CONVERSION_TRIVIAL;
else if (unwrapped instanceof java.awt.Color
&& Color.class.equals(to))
weight = CONVERSION_TRIVIAL;
}
return weight;
}
@SuppressWarnings("unchecked")
public Object coerceType(Class<?> type, Object value, Object unwrapped) {
Object res = super.coerceType(type, value, unwrapped);
if (res == null) {
if (unwrapped instanceof String) {
if (Enum.class.isAssignableFrom(type)) {
return EnumUtils.get((Class<Enum>) type,
(String) unwrapped);
} else if (type.isArray()) {
// Convert a string to an array by splitting into words
// and trying to convert each to the desired type
String[] parts = ((String) unwrapped).split("\\s");
Class componentType = type.getComponentType();
Object[] values = (Object[])
java.lang.reflect.Array.newInstance(
componentType, parts.length);
for (int i = 0; i < values.length; i++) {
String part = parts[i];
values[i] = coerceType(componentType, part, part);
}
return values;
}
} else if (unwrapped instanceof java.awt.Color
&& Color.class.equals(type)) {
return new RGBColor((java.awt.Color) unwrapped);
} else if (unwrapped instanceof Color
&& java.awt.Color.class.equals(type)) {
return ((Color) unwrapped).toAWTColor();
}
}
return res;
}
public boolean shouldAddBean(Class<?> cls, boolean isStatic,
MemberBox getter, MemberBox setter) {
Package pkg = cls.getPackage();
// Only control adding of beans if we're inside com.scriptographer
// packages. Outside, we always add the bean. Inside, we add it except
// if it's a read-only bean of which the getter name starts with is, to
// force isMethodName() style methods to be called as methods.
return getter != null && !(pkg != null
&& pkg.getName().startsWith("com.scriptographer."))
|| setter != null || !getter.getName().startsWith("is");
}
public boolean shouldRemoveGetterSetter(Class<?> cls, boolean isStatic,
MemberBox getter, MemberBox setter) {
Package pkg = cls.getPackage();
// Only remove getter / setters of beans that were added within
// com.scriptographer packages. Outside we use the old convention of
// never removing them.
return pkg != null && pkg.getName().startsWith("com.scriptographer.");
}
}