package com.drawbridge.jsengine.jsobjects; import java.awt.Dimension; import java.awt.Point; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedList; import java.util.List; import com.drawbridge.dm.DMImageFactory.DMImage; import com.drawbridge.dm.DMPanel; import com.drawbridge.dm.DMSimplePanel; import com.drawbridge.dm.ManipulationSource; import com.drawbridge.dm.animation.NativeAnimationItem; import com.drawbridge.dm.animation.NativeAnimationProcessor; import com.drawbridge.dm.animation.NativeAnimationTween; import com.drawbridge.jsengine.ast.Evaluator; import com.drawbridge.utils.ASTModification; import com.drawbridge.utils.Utils; /** * Allows specification of native functions that can used in the same way as * JSFunctions * * @author Alistair Stead * */ public class JSNativeFunction extends JSFunction { private String mMethodName; private JSType mObject; private Class<?>[] mParams; private static Evaluator mLastNativeCaller = null; public JSNativeFunction(JSType object, String methodName, Class<?>[] params) { super(null); mObject = object; mMethodName = methodName; mParams = params; } @Override public JSType executeFunction(Evaluator caller, JSType[] methodParams) throws ExecutionException { mLastNativeCaller = caller; mEvaluator = null; Method mMethod = null; try { if (mObject != null) mMethod = JSNativeFunction.class.getDeclaredMethod(mMethodName, mParams); else { // Try to use a static method mMethod = JSNativeFunction.class.getMethod(mMethodName, mParams); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } if (mMethod != null) { if (mParams != null && methodParams != null && mParams.length == methodParams.length) { // Check params are correct for (int i = 0; i < mParams.length; i++) { if (!(mParams[i] == null || methodParams[i] == null || mParams[i].equals(methodParams[i].getClass()))) { throw new ExecutionException("Cannot run native method, params are not equal: " + mParams[i] + ", " + methodParams[i] + mParams.length + " vs. " + methodParams.length); } } } else { throw new ExecutionException("Cannot run native method - incorrect number of paramaters"); } } JSType result = null; try { result = (JSType) mMethod.invoke(this, (Object[]) methodParams); // Remember all native functions need to be statically declared in // this class } catch (IllegalArgumentException e) { Utils.out.println(this.getClass(), "IllegalArgumentException: mMethod: " + mMethod.getName() + " + " + mMethodName + " methodParams:" + methodParams.toString()); throw new ExecutionException("Cannot run native method - illegal arguments!"); } catch (IllegalAccessException e) { Utils.out.println(this.getClass(), "IllegalAccessException: mMethod: " + mMethod.getName() + " + " + mMethodName); throw new ExecutionException("Cannot run native method - illegal access!"); } catch (InvocationTargetException e) { Utils.out.println(this.getClass(), "InvocationTargetException: mMethod: " + mMethod.getName() + " + " + mMethodName); throw new ExecutionException("Cannot run native method - illegal target!"); } return result; } @Override public String toString() { return "Native:" + mMethodName; } // DOCUMENT FUNCTIONS // ************************************************************************************ // ******************************************************************************************************* public Canvas getElementById(JSString name) { if (name.mValue.equals("myCanvas")) { return new Canvas(); } else throw new RuntimeException("No element named:" + name.mValue); } public Context getContext(JSString name) { if (name.mValue.equals("2d")) { return new Context(); } else throw new RuntimeException("No context named:" + name.mValue); } // OBJECT FUNCTIONS // ************************************************************************************** // ******************************************************************************************************* public static JSBoolean hasOwnProperty(JSObject jsObject, JSString name) { if (jsObject.getProperty(name.mValue) != null) { return new JSBoolean(true); } return new JSBoolean(false); } // JSString FUNCTIONS // ************************************************************************************ // ******************************************************************************************************* public static JSNumber charAt(JSString jsString, JSNumber position) { return new JSNumber(jsString.mValue.charAt((int) java.lang.Math.round(position.mValue))); } public static JSString toUpperCase(JSString jsString) { return new JSString(jsString.mValue.toUpperCase()); } // JSImage FUNCTIONS // ************************************************************************************* // ******************************************************************************************************* public static void drawImage(JSImage image, JSNumber x, JSNumber y, JSNumber w, JSNumber h) { // Here we're assuming there's only one context (DMPanel). DMPanel.getInstance().drawImage(image.mValue, (int) java.lang.Math.round(x.mValue), (int) java.lang.Math.round(y.mValue), (int) java.lang.Math.round(w.mValue), (int) java.lang.Math.round(h.mValue)); } // MATH FUNCTIONS // **************************************************************************************** // ******************************************************************************************************* public static JSNumber abs(Double x) { return new JSNumber(java.lang.Math.abs(x)); } public static JSNumber acos(Double x) { return new JSNumber(java.lang.Math.acos(x)); } public static JSNumber asin(Double x) { return new JSNumber(java.lang.Math.asin(x)); } public static JSNumber atan(Double x) { return new JSNumber(java.lang.Math.atan(x)); } public static JSNumber atan2(Double x, Double y) { return new JSNumber(java.lang.Math.atan2(x, y)); } public static JSNumber ceil(Double x) { return new JSNumber(java.lang.Math.ceil(x)); } public static JSNumber cos(Double x) { return new JSNumber(java.lang.Math.cos(x)); } public static JSNumber exp(Double x) { return new JSNumber(java.lang.Math.exp(x)); } public static JSNumber floor(Double x) { return new JSNumber(java.lang.Math.floor(x)); } public static JSNumber log(Double x) { return new JSNumber(java.lang.Math.log(x)); } public static JSNumber max(Double x, Double y) { return new JSNumber(java.lang.Math.max(x, y)); } public static JSNumber min(Double x, Double y) { return new JSNumber(java.lang.Math.min(x, y)); } public static JSNumber pow(Double x, Double y) { return new JSNumber(java.lang.Math.pow(x, y)); } public static JSNumber random() { return new JSNumber(java.lang.Math.random()); } public static JSNumber round(Double x) { return new JSNumber((double) java.lang.Math.round(x)); } public static JSNumber sin(Double x) { return new JSNumber(java.lang.Math.sin(x)); } public static JSNumber sqrt(Double x) { return new JSNumber(java.lang.Math.sqrt(x)); } public static JSNumber tan(Double x) { return new JSNumber(java.lang.Math.tan(x)); } // ANIMATION FUNCTIONS // *********************************************************************************** // ******************************************************************************************************* public static JSType loadImage(JSNumber index, JSNumber x, JSNumber y, JSNumber width, JSNumber height) { List<DMImage> list = DMPanel.getInstance().mModel.getModelObjects(); if (list.size() > index.mValue) { DMImage image = list.get(index.intValue()); if (image != null) { Dimension newSize = new Dimension(width.intValue(), height.intValue()); image.setPreferredSize(newSize); image.setPreferredLocation(new Point(x.intValue(), y.intValue()), ManipulationSource.SOURCE_EXECUTION); image.setEvaluatorNode(mLastNativeCaller, ASTModification.ASTModificationType.MOD_LOCATION); image.setEvaluatorNode(mLastNativeCaller, ASTModification.ASTModificationType.MOD_SIZE); if (!DMSimplePanel.getInstance().isPlaying()) { image.setSize(newSize); image.setLocation(image.getPreferredLocation()); } return new JSImage(image); } } try { throw new Exception("loadImage Index not valid (should be 0-" + list.size() + ")"); } catch (Exception e) { e.printStackTrace(); return new JSUndefined(); } } public static LinkedList<NativeAnimationItem> animationItems = new LinkedList<NativeAnimationItem>(); public static NativeAnimationProcessor processor = new NativeAnimationProcessor(); public static int tweensCalled = 0; public static int tweensProcessed = -1; public static void tweenProcessingCallback() { if (tweensProcessed == -1) tweensProcessed = 1; else { tweensProcessed++; } if (tweensProcessed == tweensCalled) { DMSimplePanel.getInstance().finishedProcessing(); } Utils.out.println(JSNativeFunction.class, "tweenProcessingCallback ->" + tweensCalled + " called and " + tweensProcessed + " processed"); } public void setTweenPosition(JSNumber x, JSNumber y) { // add the animation item to the list if (mObject instanceof JSImage) { JSImage image = (JSImage) mObject; animationItems.add(new NativeAnimationItem(image.mValue, x.intValue(), y.intValue(), null, null, null)); } } public void setTweenSize(JSNumber width, JSNumber height) { // add the animation item to the list if (mObject instanceof JSImage) { JSImage image = (JSImage) mObject; animationItems.add(new NativeAnimationItem(image.mValue, null, null, width.intValue(), height.intValue(), null)); } } public static void setTimeToDestination(JSNumber timeDelay) { Utils.out.println(JSNativeFunction.class, "setTimeToDestination"); tweensCalled++; try { NativeAnimationItem[] items = new NativeAnimationItem[animationItems.size()]; for (int i = 0; i < animationItems.size(); i++) items[i] = animationItems.get(i); NativeAnimationTween tween = new NativeAnimationTween(items, timeDelay.intValue()); processor.add(tween); if (!processor.isAlive()) processor.start(); animationItems = new LinkedList<NativeAnimationItem>(); } catch (Exception e) { e.printStackTrace(); } } // CONTEXT FUNCTIONS // ************************************************************************************ // ******************************************************************************************************* public void setBackgroundColour(JSString string) { // Do nothing right now try { // Utils.out.println(this.getClass(),"Running setBackgroundColor: " // + string.mValue); java.awt.Color color = java.awt.Color.decode(string.mValue); DMPanel.getInstance().getCanvas().setBackground(color); DMSimplePanel.getInstance().getCanvas().setBackground(color); } catch (Exception e) { e.printStackTrace(); } } public JSString getRGB(JSNumber r, JSNumber g, JSNumber b) { // Utils.out.println(this.getClass(),"Running getRGB:" + r + ", " + g + // ", " + b); Utils.out.println(this.getClass(), ""); String result = String.format("#%02x%02x%02x", r.intValue(), g.intValue(), b.intValue()); // Utils.out.println(this.getClass(),"returning:" + result); return new JSString(result); } }