001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit.javascript;
039:
040: import java.lang.reflect.Method;
041:
042: import org.apache.commons.lang.ArrayUtils;
043: import org.mozilla.javascript.Context;
044: import org.mozilla.javascript.Function;
045: import org.mozilla.javascript.FunctionObject;
046: import org.mozilla.javascript.Scriptable;
047: import org.mozilla.javascript.ScriptableObject;
048:
049: /**
050: * Wraps a java method to make it available as a javascript function
051: * (more flexible than Rhino's {@link FunctionObject}.
052: *
053: * @version $Revision: 2132 $
054: * @author Marc Guillemot
055: */
056: public class MethodWrapper extends ScriptableObject implements Function {
057:
058: private static final long serialVersionUID = 6106771000496895783L;
059: private final Class clazz_;
060: private final Method method_;
061: private final int[] jsTypeTags_;
062:
063: /**
064: * Facility constructor to wrap a method without arguments
065: * @param methodName the name of the method to wrap
066: * @param clazz the class declaring the method
067: * @throws NoSuchMethodException if the method is no found
068: */
069: MethodWrapper(final String methodName, final Class clazz)
070: throws NoSuchMethodException {
071: this (methodName, clazz, ArrayUtils.EMPTY_CLASS_ARRAY);
072: }
073:
074: /**
075: * Wraps a method as a javascript function.
076: * @param methodName the name of the method to wrap
077: * @param clazz the class declaring the method
078: * @param parameterTypes the types of the method's parameter
079: * @throws NoSuchMethodException if the method is no found
080: */
081: MethodWrapper(final String methodName, final Class clazz,
082: final Class[] parameterTypes) throws NoSuchMethodException {
083:
084: clazz_ = clazz;
085: method_ = clazz.getMethod(methodName, parameterTypes);
086: jsTypeTags_ = new int[parameterTypes.length];
087: for (int i = 0; i < parameterTypes.length; i++) {
088: jsTypeTags_[i] = FunctionObject
089: .getTypeTag(parameterTypes[i]);
090: }
091: }
092:
093: /**
094: * @see org.mozilla.javascript.ScriptableObject#getClassName()
095: * @return a name based on the method name
096: */
097: public String getClassName() {
098: return "function " + method_.getName();
099: }
100:
101: /**
102: * @see org.mozilla.javascript.Function#call(Context, Scriptable, Scriptable, Object[])
103: * {@inheritDoc}
104: */
105: public Object call(final Context context, final Scriptable scope,
106: final Scriptable this Obj, final Object[] args) {
107: final Object javaResp;
108: if (this Obj instanceof ScriptableWrapper) {
109: final ScriptableWrapper wrapper = (ScriptableWrapper) this Obj;
110: final Object wrappedObject = wrapper.getWrappedObject();
111: if (clazz_.isInstance(wrappedObject)) {
112: // convert arguments
113: final Object[] javaArgs = convertJSArgsToJavaArgs(
114: context, scope, args);
115: try {
116: javaResp = method_.invoke(wrappedObject, javaArgs);
117: } catch (final Exception e) {
118: throw Context
119: .reportRuntimeError("Exception calling wrapped function "
120: + method_.getName()
121: + ": "
122: + e.getMessage());
123: }
124: } else {
125: throw buildInvalidCallException(this Obj);
126: }
127:
128: } else {
129: throw buildInvalidCallException(this Obj);
130: }
131:
132: final Object jsResp = Context.javaToJS(javaResp,
133: ScriptableObject.getTopLevelScope(scope));
134: return jsResp;
135: }
136:
137: private RuntimeException buildInvalidCallException(
138: final Scriptable this Obj) {
139: return Context.reportRuntimeError("Function "
140: + method_.getName()
141: + " called on incompatible object: " + this Obj);
142: }
143:
144: /**
145: * Converts js arguments to java arguments
146: * @param context the current context
147: * @param scope the current scope
148: * @param jsArgs the javascript arguments
149: * @return the java arguments
150: */
151: Object[] convertJSArgsToJavaArgs(final Context context,
152: final Scriptable scope, final Object[] jsArgs) {
153: if (jsArgs.length != jsTypeTags_.length) {
154: throw Context
155: .reportRuntimeError("Bad number of parameters for function "
156: + method_.getName()
157: + ": expected "
158: + jsTypeTags_.length
159: + " got "
160: + jsArgs.length);
161: }
162: final Object[] javaArgs = new Object[jsArgs.length];
163: for (int i = 0; i < jsArgs.length; i++) {
164: javaArgs[i] = FunctionObject.convertArg(context, scope,
165: jsArgs[i], jsTypeTags_[i]);
166: }
167: return javaArgs;
168: }
169:
170: /**
171: * @see org.mozilla.javascript.Function#construct(Context, Scriptable, Object[])
172: * {@inheritDoc}
173: */
174: public Scriptable construct(final Context context,
175: final Scriptable scope, final Object[] args) {
176: throw Context
177: .reportRuntimeError("Function " + method_.getName()
178: + " can't be used as a constructor");
179: }
180:
181: }
|