All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.scripthandler.rhino.RhinoScriptHandler Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2007 Adobe Systems Incorporated All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */
package com.adobe.xfa.scripthandler.rhino;


import java.util.IdentityHashMap;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.WrappedException;

import com.adobe.xfa.AppModel;
import com.adobe.xfa.Arg;
import com.adobe.xfa.Element;
import com.adobe.xfa.Obj;
import com.adobe.xfa.ScriptDebugger;
import com.adobe.xfa.ScriptHandler;
import com.adobe.xfa.TextNode;
import com.adobe.xfa.XFA;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.MsgFormat;
import com.adobe.xfa.ut.ResId;


/**
 * A class to enable scripting support for the JavaScript language.
 * 
 * @author mtardif
 */
public class RhinoScriptHandler extends ScriptHandler implements ScriptHandlerIF {

    /**
     * Instantiates a JavaScript script handler.
     * The no-argument ctor ensures that a script host 
     * is initialized.
     * 
     * @exclude from published api.
     */
    protected RhinoScriptHandler() {
        mScriptHost = this;
        mAppModel = null;
        mAppModelLiveObject = null;
        mObjectMap = new IdentityHashMap();
    }

    /**
     * Instantiates a JavaScript script handler.
     * @param appModel the application model.
     */
    public RhinoScriptHandler(AppModel appModel) {
        this(appModel, null);
    }

    /**
     * Instantiates a JavaScript script handler.
     * @param appModel an application model.
     * @param scriptDebugger an script debugger.
     * 
     * @exclude from published api.
     */
    public RhinoScriptHandler(AppModel appModel, ScriptDebugger scriptDebugger /* = null */) {
        this(appModel, null, 0);
    }
    
    /**
     * @param appModel the application's XFAAppModel.
     * @param scriptDebugger the application's XFAScriptDebugger-derived class.
     * @param javaScriptTimeout the maximum amount of time, in milliseconds, a
     * single script is allowed to execute before it gets timed out. 
     * A value of '0' means infinite which also is the default.
     */
    public RhinoScriptHandler(AppModel appModel,
                                ScriptDebugger scriptDebugger /* = null */,
                                    int javaScriptTimeout /* = 0 */) {
        super(scriptDebugger);
        mAppModel = appModel;
        mScriptHost = this;
        mJavaScriptTimeout = javaScriptTimeout;
        // Don't want a negative value for the timeout
        if (mJavaScriptTimeout < 0)
            mJavaScriptTimeout = 0;
        LiveComponent oLiveComponent = mScriptHost.newLiveComponent(this, mAppModel);
        //
        // Add this live component to the global scope.
        //
        RhinoEngine.setTopLevelScope(oLiveComponent);
        //
        // Set the value of the "xfa" object.
        //
        mAppModelLiveObject = mScriptHost.newLiveObject(this, mAppModel);
        mObjectMap = new IdentityHashMap();
    }

    /**
     * Instantiates a JavaScript script handler form the given.
     * @param appModel an application model.
     * @param scriptHost a Rhino script host.
     * @param liveObject a live object.
     * @param scriptDebugger an script debugger.
     * @param objectMap an object map.
     * 
     * @exclude from published api.
	 */
    protected RhinoScriptHandler(AppModel appModel, RhinoScriptHandler scriptHost, LiveObject liveObject, ScriptDebugger scriptDebugger /* = null */, IdentityHashMap objectMap) {
        this(appModel, scriptHost, liveObject, scriptDebugger, 0, objectMap);
    }

    /**
     * Instantiates a JavaScript script handler form the given.
     * @param appModel an application model.
     * @param scriptHost a Rhino script host.
     * @param liveObject a live object.
     * @param scriptDebugger an script debugger.
     * @param javaScriptTimeout the maximum amount of time, in milliseconds, a
     * single script is allowed to exeucte before it gets timed out. 
     * @param objectMap an object map.
     * 
     * @exclude from published api.
	 */
    protected RhinoScriptHandler(AppModel appModel,
						        RhinoScriptHandler scriptHost,
						        LiveObject liveObject,
                                ScriptDebugger scriptDebugger /* = null */,
                                int javaScriptTimeout /* = 0 */,
                                IdentityHashMap objectMap) {
        super(scriptDebugger);
        mAppModel = appModel;
        mScriptHost = scriptHost;
        mJavaScriptTimeout = javaScriptTimeout;
        // Don't want a negative value for the timeout
        if (mJavaScriptTimeout < 0)
            mJavaScriptTimeout = 0;
        mAppModelLiveObject = liveObject;
        mObjectMap = objectMap;
    }

    /**
	 * Clones this JavaScript handler.
     * 
     * @exclude from published api.
	 */
    public RhinoScriptHandler clone() {
        RhinoScriptHandler oNewScriptHandler
                = new RhinoScriptHandler(mAppModel, mScriptHost, mAppModelLiveObject, getDebugger(), mObjectMap);
//      oNewScriptHandler.mbFatalError = mbFatalError;
        oNewScriptHandler.mError = mError;
        return oNewScriptHandler;
    }

    public LiveObject newLiveObject(RhinoScriptHandler handler, Obj xfaObject) {
        return new LiveObject(handler, xfaObject);
    }

    public LiveComponent newLiveComponent(RhinoScriptHandler handler, Obj xfaObject) {
        return new LiveComponent(handler, xfaObject);
    }

    /**
     * @exclude from published api.
     */
    @FindBugsSuppress(code="RCN")	// oDebugger
    public void executeOrSyntaxCheck(String sScript, Arg oReturnCode, int eReason, boolean bSyntaxCheckOnly) {
            clearError();
            int nScriptID = -1;
            ScriptDebugger oDebugger = getDebugger();
            if (oDebugger != null) {
            // Javaport: not needed.
            //    nScriptID = oDebugger.getScriptID(this, sScript, moAppModel.getContext(), eReason);
            //    if (moRhinoDebugger == null) {
            //        moRhinoDebugger = moEngine.getContext().getDebugger();
            //        moEngine.getContext().setDebugger(moRhinoDebugger, oDebugger);
            //    }
                assert(false);
            }
            //
            // Set the context for the script to be the current XFANode.
            //
            String oScriptID = "";
            if (oDebugger != null) {
                //
                // Form a string "#n" where n is the script ID.  This
                // can be parsed back out in break-point callbacks.
                //
                oScriptID = "#" + nScriptID;
            }
            Script oScript = null;
            try {
                oScript = RhinoEngine.getThreadLocalRuntimeContext().compileString(sScript, oScriptID, 1, null);
            } catch (RhinoException oError) {
                setError(oError);
            }
            boolean bSuccess = (oScript != null);
            if (oScript != null) {
                if ( ! bSyntaxCheckOnly) {
                    if (oDebugger != null) {
                    // Javaport: not needed.
                    //    moEsDebugger->addScript(nScriptID, oScript);
                //    oDebugger->willExecuteScript(nScriptID);
                //    bSuccess = mpEngine->eval(*poScript, &oResult, -1, ScScript::Engine::kEvalBreakOnEntry, poThis);
                //    // Allow script to be thrown away if it contains no function declarations.  We check for 2 because
                //    // we have a reference in poScript (about to be released, below), and there's one in mpoEsDebugger's
                //    // map.  If there's a function declaration, then the ExtendScript engine holds onto a reference
                //    // in case someone debugs into it, in which case the ref-count is 3.
                //    if (poScript->getRefCount() == 2)
                //        mpoEsDebugger->clearScript(nScriptID, poScript);
                    assert(false);
                }
                else {
                    bSuccess = false;
                    try {
                        //
                        // Javaport: the Rhino API says this must be the top level
                        // scope, and not the context object, contrary to C++!
                        //
                    	Context ctx = RhinoEngine.getThreadLocalRuntimeContext();
                    	Scriptable topScope = RhinoEngine.getTopLevelScope();
                        Object oResult = oScript.exec(ctx, topScope);
                        if (! (oResult instanceof Undefined)) {
                            oReturnCode.assign(variantToArg(oResult));
                            bSuccess = true;
                        }
                    } catch (RhinoException f) {
                        setError(f);
                    } catch (ExFull g) {
                        setError(g);
                    }
                }
            }
        }
        if (! bSuccess) {
            Exception error = getError();
            String sErrorText = null;
            //
            // Watson 1252722: a comment-only script causes bSuccess to be false, but
            // there's no associated error message.  Workaround is to double-check here
            // that error.getCode() != ScCore::kErrOK.
            //
            if (error instanceof JavaScriptException) {
                StringBuilder sError = new StringBuilder(((JavaScriptException) error).getMessage());
                sErrorText = sError.toString();
            }
            else if (error instanceof WrappedException) {
                StringBuilder sError = new StringBuilder(((WrappedException) error).getMessage());
                final String sRhinoPrefix = "Wrapped ";
                int nErrorPrefix = sError.indexOf(sRhinoPrefix);
                if (nErrorPrefix >= 0)
                    sError.replace(0, nErrorPrefix + sRhinoPrefix.length(), "");
                final String sRhinoSuffix = "\n (#";
                int nLineNumberSuffix = sError.indexOf(sRhinoSuffix);
                if (nLineNumberSuffix >= 0)
                    sError.delete(nLineNumberSuffix + 1, sError.length());
                if (sError.charAt(sError.length() - 1) == '\n')
                    sError.setLength(sError.length() - 1);
                sErrorText = sError.toString();
            }
            else if (error instanceof RhinoException) {
                StringBuilder sError = new StringBuilder(((RhinoException) error).getMessage());
                final String sRhinoPrefix = "Error: ";
                int nErrorPrefix = sError.indexOf(sRhinoPrefix);
                if (nErrorPrefix >= 0)
                    sError.delete(0, nErrorPrefix + sRhinoPrefix.length());
                sErrorText = sError.toString();
                int nErrorLine = ((RhinoException) error).lineNumber();
                //    
                // There's no reason why a scriptException shouldn't be thrown regardless
                // of bSyntaxCheckOnly, except that FormCalc won't necessarily be as reliable
                // about line numbers in the non-syntax-error case.  So for consistency keep
                // that behaviour.
                //
                if (bSyntaxCheckOnly) {
                    MsgFormat oFmt = new MsgFormat(ResId.ScriptHandlerError, sErrorText);
                    ScriptException oScriptEx = new ScriptException(oFmt, nErrorLine, RhinoErrorIdToErrorCode(0));
                    throw oScriptEx;
                }
            }
            else if (error instanceof ExFull) {
                sErrorText = error.toString();
            }
            if (sErrorText != null) {
                Arg arg = variantToArg(sErrorText);
                oReturnCode.assign(arg);
                throw new ExFull(new MsgFormat(ResId.ScriptHandlerError, sErrorText));
            }
        }
        if (bSyntaxCheckOnly)
            return;        // Compiled successfully
        if (oDebugger != null) {
        // Javaport: not needed.
        //  oDebugger.didExecuteScript(nScriptID, oReturnCode);
            assert(false);
        }
    }

    /**
     * @exclude from published api.
     */
    public AppModel getAppModel() {
        return mAppModel;
    }

    /**
     * @exclude from published api.
     */
    protected RhinoScriptHandler getScriptHost() {
        return mScriptHost;
    }

    /**
     * @exclude from published api.
     */
    protected IdentityHashMap getObjectMap() {
        return mObjectMap;
    }

    /**
     * @exclude from published api.
     */
    protected LiveObject getAppModelLiveObject() {
        return mAppModelLiveObject;
    }

    public String languageName() {
        return "javascript";
    }

    /**
     * @exclude from published api.
     */
    public void throwError(ExFull oError) {
        StringBuilder sErrorBuf =  new StringBuilder(oError.toString());
        int nLen = sErrorBuf.length();
        while (sErrorBuf.charAt(nLen - 1) == '\n')
            nLen--;
        sErrorBuf.setLength(nLen);
        RhinoEngine.throwException(sErrorBuf.toString());
    }
    
    /**
     * @exclude from published api.
     */
    public boolean wasFatalError() {
        //return mbFatalError;
    	return false;
    }

    /**
     * Deletes any accumulated script execution contexts.
     *
     * @exclude from published api.
     */
    protected void clearExecutionContexts() {
        mObjectMap.clear();
        RhinoEngine.destroy();
    }

// JavaPort: not needed in Rhino.
//  public String getOption(String sOptionName) {
//  }
//
//  public void setOption(String sOptionName, String sOptionValue) {
//  }

    void clearError() {
        setError(null);
    }

    Exception getError() {
        return mError;
    }

    void setError(Exception error) {
        mError = error;
    }

// JavaPort: not needed for XFA4J.
//  public ScriptDebugger getDebugger() {
//  }
//
//  public boolean debugCommand(int eCmd) {
//  }
//
//  public boolean debugBreakPoint(int nScriptID, int nLine, int eSetType) {
//  }
//
//  public boolean debugGetStack(Storage oStack) {
//  }
//
//  public boolean debugGetVariables(Storage oVarNames, Storage oVarValues) {
//  }
//
//  public void setDebugger(ScriptDebugger poDebugger) {
//  }
//
//  public void removeReference(Node oNode) {
//  }
    
    private final RhinoScriptHandler mScriptHost;
    private final AppModel mAppModel;
    private final LiveObject mAppModelLiveObject;
    
//  private boolean mbFatalError;
    private Exception mError;
    private int mJavaScriptTimeout; // for futures.
    
    private final IdentityHashMap mObjectMap; // List of scripted XFA objects.
// JavaPort: not needed for XFA4J.
//  private Debugger moRhinoDebugger;
    
    LiveObject ObjectToLiveObject(Obj object) {
        LiveObject liveObject = lookupObject(object);
        if (liveObject == null) {
            //
            // not found; create a new entry
            //
            liveObject = mScriptHost.newLiveObject(this, object);
            addObject(object, liveObject);
        }
        return liveObject;
    }
    
    LiveObject lookupObject(Obj object) {
        //
        // Special handling to make sure we don't add a circular dependency.
        //
        if (object == mAppModel) {
            return mAppModelLiveObject;
        }
        return mObjectMap.get(object);
    }

    void addObject(Obj object, LiveObject liveObject) {
        // Make sure we don't add a circular dependency.
        assert(object != mAppModel);
        mObjectMap.put(object, liveObject);
        //
        // check if it is a script element
        //
        if (object.isSameClass(XFA.SCRIPTTAG)) {
            Element oNode = (Element) object;
            Element oParent = oNode.getXFAParent();
            if (oParent != null && oParent.isSameClass(XFA.VARIABLESTAG)) {
                TextNode oText = (TextNode) oNode.getProperty(XFA.TEXTNODETAG, 0);
                if (oText != null) {
                    String sScript = oText.getValue();        
                    compileObject(object, sScript);
                }
            }
        }
    }

    boolean compileObject(Obj object, String sScript) {
        clearError();
        Script oScript = null;
    	Context ctx = RhinoEngine.getThreadLocalRuntimeContext();
    	int nLevel = ctx.getOptimizationLevel();
        try {
        	ctx.setOptimizationLevel(-1);
            oScript = ctx.compileString(sScript, null, 1, null);
        } catch (RhinoException oError) {
            setError(oError);
        } finally {
        	ctx.setOptimizationLevel(nLevel);
        }
        boolean bSuccess = (oScript != null);
        boolean bRet = false;
        if (bSuccess) {
            LiveObject liveObject = lookupObject(object);
            //
            // create our script object.
            //
            ScriptObject oScriptObj = new ScriptObject(oScript);
            //
            // Set the live object's script object.
            //
            liveObject.setScriptObject(oScriptObj);
            bRet = true;
        }
        else {
            String sError = "";
            Exception error = getError();
            if (error instanceof RhinoException) {
                sError = ((RhinoException) error).getMessage();
            }
            throw new ExFull(new MsgFormat(ResId.ScriptHandlerError, sError));
        }
        return bRet;
    }

    public Object argToVariant(Arg arg) {
        Object variant = null;
        switch (arg.getArgType()) {
        case Arg.EMPTY:
            variant = Undefined.instance;
            break;
        case Arg.NULL:
            variant = null;
            break;
        case Arg.BOOL:
            variant = Boolean.valueOf(arg.getBool().booleanValue());
            break;
        case Arg.INTEGER:
            variant = Integer.valueOf(arg.getInteger().intValue());
            break;
        case Arg.DOUBLE:
            variant = new Double(arg.getDouble(false).doubleValue());
            break;
        case Arg.STRING:
            variant = arg.getString();
            break;
        case Arg.OBJECT:
            Obj oObj = arg.getObject();
            if (oObj == null) {
                variant = null;
                break;
            }
            LiveObject oXFALiveObject = (LiveObject) ObjectToLiveObject(oObj);
            variant = oXFALiveObject;
            break;
        case Arg.EXCEPTION:
            throw new EvaluatorException(arg.getException().toString());
        default:
            assert(false);
            break;
        }
        return variant;
    }

    public Arg variantToArg(Object variant) {
        Arg arg = new Arg();
        if (variant == null)
            arg.setNull();
        else if (variant instanceof Undefined)
            arg.empty();
        else if (variant instanceof Boolean)
            arg.setBool(Boolean.valueOf(((Boolean) variant).booleanValue()));
        else if (variant instanceof Integer)
            arg.setInteger(Integer.valueOf(((Integer) variant).intValue()));
        else if (variant instanceof Double)
            arg.setDouble(new Double(((Double) variant).doubleValue()));
        else if (variant instanceof String)
            arg.setString((String) variant);
        else if (variant instanceof LiveObject)
            arg.setObject(((LiveObject) variant).getXFAObject());
        else
            arg.setVoid(variant);
        return arg;
    }

    /*
     * Map ExtendScript error codes to the common error codes.  This is only
     * intended to handle those that may occur during syntax checking, not execution.
     */
    static int RhinoErrorIdToErrorCode(int nErrorCode) {
    // Javaport: there's no such thing in Rhino.
    //    switch (nErrorCode) {
    //    case ScCore::kErrGeneral:
    //        return ScriptHandler.ERR_General;
    //    case ScCore::kErrUndefined:
    //        return ScriptHandler.ERR_Undefined;
    //    case ScCore::kErrNoLvalue:
    //        return ScriptHandler.ERR_NoLvalue;
    //    case ScCore::kErrOpenString:
    //        return ScriptHandler.ERR_OpenString;
    //    case ScCore::kErrOpenComment:
    //        return ScriptHandler.ERR_OpenComment;
    //    case ScCore::kErrBadDigit:
    //        return ScriptHandler.ERR_BadDigit;
    //    case ScCore::kErrUnsupported:
    //        return ScriptHandler.ERR_Unsupported;
    //    case ScCore::kErrSyntax:
    //        return ScriptHandler.ERR_Syntax;
    //    case ScCore::kErrKeyword:
    //        return ScriptHandler.ERR_Keyword;
    //    case ScCore::kErrBadBreakContinue:
    //        return ScriptHandler.ERR_BadBreakContinue;
    //    case ScCore::kErrBadLabel:
    //        return ScriptHandler.ERR_BadLabel;
    //    case ScCore::kErrExpressionNotConst:
    //        return ScriptHandler.ERR_ExpressionNotConst;
    //    case ScCore::kErrClosedBlock:
    //        return ScriptHandler.ERR_ClosedBlock;
    //    case ScCore::kErrOpenBlock:
    //        return ScriptHandler.ERR_OpenBlock;
    //    case ScCore::kErrNoCatch:
    //        return ScriptHandler.ERR_NoCatch;
    //    case ScCore::kErrNoTry:
    //        return ScriptHandler.ERR_NoTry;
    //    case ScCore::kErrVarExpected:
    //        return ScriptHandler.ERR_VarExpected;
    //    case ScCore::kErrScalarExpected:
    //        return ScriptHandler.ERR_ScalarExpected;
    //    case ScCore::kErrBadArgument:
    //        return ScriptHandler.ERR_BadArgument;
    //    case ScCore::kErrBadArgumentList:
    //        return ScriptHandler.ERR_BadArgumentList;
    //    case ScCore::kErrObjectExpected:
    //        return ScriptHandler.ERR_ObjectExpected;
    //    case ScCore::kErrNoCtor:
    //        return ScriptHandler.ERR_NoCtor;
    //    case ScCore::kErrNoValue:
    //        return ScriptHandler.ERR_NoValue;
    //    case ScCore::kErrNoFunction:
    //        return ScriptHandler.ERR_NoFunction;
    //    case ScCore::kErrExpected:
    //        return ScriptHandler.ERR_Expected;
    //    case ScCore::kErrWrongClass:
    //        return ScriptHandler.ERR_WrongClass;
    //    case ScCore::kErrBadReturn:
    //        return ScriptHandler.ERR_BadReturn;
    //    case ScCore::kErrCharConversion:
    //        return ScriptHandler.ERR_CharConversion;
    //    case ScCore::kErrCharPartial:
    //        return ScriptHandler.ERR_CharPartial;
    //    case ScCore::kErrDuplicateDefault:
    //        return ScriptHandler.ERR_DuplicateDefault;
    //    case ScCore::kErrRedeclared:
    //        return ScriptHandler.ERR_Redeclared;
    //    case ScCore::kErrRange:
    //        return ScriptHandler.ERR_Range;
    //    case ScCore::kErrCatchAfterCatch:
    //        return ScriptHandler.ERR_CatchAfterCatch;
    //    }
    //    //
    //    // Not a serious problem, but a new case should be handled if we
    //    // hit this assert.  Default to a syntax error.
    //    //
    //    assert(false);
        return ScriptHandler.ERR_Syntax;
    }

}
    




© 2015 - 2024 Weber Informatics LLC | Privacy Policy