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

com.adobe.xfa.scripthandler.formcalc.FormCalcScriptHandler 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.formcalc;


import com.adobe.xfa.AppModel;
import com.adobe.xfa.Arg;
import com.adobe.xfa.DependencyTracker;
import com.adobe.xfa.Node;
import com.adobe.xfa.Obj;
import com.adobe.xfa.ScriptDebugger;
import com.adobe.xfa.ScriptHandler;
import com.adobe.xfa.SOMParser;
import com.adobe.xfa.formcalc.CalcException;
import com.adobe.xfa.formcalc.CalcSymbol;
import com.adobe.xfa.formcalc.CalcParser;
import com.adobe.xfa.formcalc.DebugHost;
import com.adobe.xfa.formcalc.Frame;
import com.adobe.xfa.formcalc.FrameTable;
import com.adobe.xfa.formcalc.ProtocolHost;
import com.adobe.xfa.formcalc.ScriptHost;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.MsgFormat;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.ResourceLoader;
import com.adobe.xfa.ut.IntegerHolder;
import com.adobe.xfa.ut.StringUtils;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.StringTokenizer;


/**
 * A class to enable scripting support for the FormCalc language.
 *
 * @author Darren Burns
 */
public class FormCalcScriptHandler extends ScriptHandler {

	/**
     * @exclude from published api.
	 */
	public static class FormCalcParser extends CalcParser implements ScriptHost, ProtocolHost, DebugHost {

		public FormCalcParser(AppModel oAppModel, FormCalcScriptHandler oScriptHandler) {
			super();
			moAppModel = oAppModel;
			moScriptHandler = oScriptHandler;
			moParser = new CalcParser();
			moParser.setScriptHost(this);
			//
			// Fix for Watson 1100043.  There's a need to initialize a
			// protocol host at this stage, for otherwise the functions
			// Get, Put and Post don't get defined as FormCalc builtin
			// functions.  A real protocol host will be installed at
			// a later stage.
			//
			moParser.setProtocolHost(this);
			moParser.setDebugHost(this);
						
			EnumSet oLegacyScripting = EnumSet.noneOf(CalcParser.LegacyVersion.class);			
			if (moAppModel.getLegacySetting(AppModel.XFA_LEGACY_V30_SCRIPTING))
				oLegacyScripting.add(CalcParser.LegacyVersion.V30_SCRIPTING);
			if (moAppModel.getLegacySetting(AppModel.XFA_LEGACY_V32_SCRIPTING))
				oLegacyScripting.add(CalcParser.LegacyVersion.V32_SCRIPTING);
			if (oLegacyScripting.size() == 0) oLegacyScripting.add(CalcParser.LegacyVersion.CUR_SCRIPTING);
			moParser.setLegacyScripting(oLegacyScripting);
		}

		String execute(String script, String locale, int nScriptID, Arg returnCode, boolean bSyntaxCheckOnly) {
			CalcParser oCalcParser = moParser;
			//
			// Vantive 661756, 662032
			// the parser is already in use (and FormCalc is not re-entrant)
			// so we need to create a temporaty parser (that is basically a 
			// clone of one already in use) to use to execute the script.
			//
			if (moParser.inUse()) {
				oCalcParser = (CalcParser) moParser.clone();
			}
			oCalcParser.putScript(script);
			oCalcParser.setScriptID(nScriptID);
			if (!StringUtils.isEmpty(locale))
				oCalcParser.setLocale(locale);
			ScriptDebugger oDebugger = moScriptHandler.getDebugger();
			if (oDebugger != null)
				oCalcParser.debugEnable(true);
			//
			// Don't save declarations and code if we're just syntax-checking
			//
			boolean bSave = ! bSyntaxCheckOnly;
			boolean bSuccess = oCalcParser.compile(bSave, bSave, bSyntaxCheckOnly ? true : false);
			if (bSuccess && ! bSyntaxCheckOnly) {
				if (oDebugger != null)
					oDebugger.willExecuteScript(nScriptID);
				oCalcParser.evaluate(true, true);
			}
			CalcSymbol oResult = oCalcParser.getCalcResult();
			if (oResult == null)
				return "";	// When doing an error-free syntax check.
			String sReturnMessage = null;
			if (oResult.getType() == CalcSymbol.TypeError) {
				IntegerHolder oErrorLine = new IntegerHolder();
				IntegerHolder oErrorCode = new IntegerHolder();
				String sErr = oResult.getErrorValue(oErrorLine, oErrorCode);
				//
				// It's been decided that an empty (or all-comment) script
				// should return undefined (Arg.EMPTY).  This is essentially
				//	so that users can comment out scripts without generating
				// a syntax error.
				//
				String sNoExpr
					= ResourceLoader.loadResource(ResId.FC_ERR_NO_EXPR);
				if (sErr.equals(sNoExpr)) {
					returnCode.empty();  
					return sReturnMessage;
				}
				//
				// There's no reason why a scriptException shouldn't be thrown
				// regardless of bSyntaxCheckOnly, except that FormCalc won't
				// necessarily be very reliable about line numbers in the
				// non-syntax-error case.  So maintain old behaviour
				// until a change is deemed necessary.
				//
				MsgFormat oFmt = new MsgFormat(ResId.ScriptHandlerError, sErr);
				if (bSyntaxCheckOnly)
					throw new ScriptHandler.ScriptException(oFmt, oErrorLine.value, FCResIdToErrorCode(oErrorCode.value));
				throw new ExFull(oFmt);
			}	
			else if (oResult.getType() == CalcSymbol.TypeReturn) {
				//
				// Store returned string for a derived class to use if desired.
				// This class quietly ignores it.
				//
				sReturnMessage = oResult.getStringValue();
				returnCode.empty();  
				return sReturnMessage;
			}
			FCValToXFAArg(oResult, returnCode);
			return sReturnMessage;
		}

		/*
		 * Overridden from ScriptHost
		 */
		public Obj getItem(String sItem, Obj[] oObj) {
			try {
				DependencyTracker oDependencyTracker
											= moAppModel.dependencyTracker();
				SOMParser oParser = new SOMParser(oDependencyTracker);
				List oResultList = new ArrayList();
				int resultListLength = 0;
				if (oParser.resolve(moAppModel.getContext(), sItem, oObj, oResultList, null))
					resultListLength = oResultList.size();
				if (resultListLength == 0) {
					moScriptHandler.setFatalError(false);
					//
					// Not handled.  Supply an error return value.
					// Load "unknown accessor" error message from resources.
					//
					MsgFormatPos sFmt = new MsgFormatPos(ResId.FC_ERR_ACCESSOR);
					sFmt.format(sItem);
					CalcSymbol calcsymbol
									= new CalcSymbol(sFmt.toString(), true, 0, 0);
					throw new CalcException(calcsymbol);
				}
				Arg arg = oResultList.get(0).value;
				return arg.getObject();
			} catch (ExFull oError) {
				//
				// Exception thrown.  Convert it into an error return value
				//
				CalcSymbol calcsymbol
								= new CalcSymbol(oError.toString(), true, 0, 0);
				throw new CalcException(calcsymbol);
			}
		}

		/*
		 * Overridden from ScriptHost
		 */
		public CalcSymbol[] getItemValue(String sItem, Obj[] oObj) {
			try {
				DependencyTracker oDependencyTracker
											= moAppModel.dependencyTracker();
				SOMParser oParser = new SOMParser(oDependencyTracker);
				List oResultList = new ArrayList();
				int resultListLength = 0;
				if (oParser.resolve(moAppModel.getContext(), sItem, oObj, oResultList, null))
					resultListLength = oResultList.size();
				if (resultListLength == 0) {
					moScriptHandler.setFatalError(false);
					//
					// Not handled.  Supply an error return value.
					// Load "unknown accessor" error message from resources.
					//
					MsgFormatPos sFmt = new MsgFormatPos(ResId.FC_ERR_ACCESSOR);
					sFmt.format(sItem);
					CalcSymbol calcsymbol
									= new CalcSymbol(sFmt.toString(), true, 0, 0);
					throw new CalcException(calcsymbol);
				}
				CalcSymbol[] oSym = new CalcSymbol[resultListLength];
				for (int i = 0; i < resultListLength; i++) {
					Obj obj = oResultList.get(i).object;
					Arg arg = oResultList.get(i).value;
					boolean bNullValue = false;
					if (arg.getArgType() == Arg.OBJECT) {
						//
						// attempt to get the default value ... the default
						// value could in turn be another Object, theoretically,
						// but the logic to avoid looping endlessly
						// is troublesome
						//
						Obj o = arg.getObject();
						bNullValue = ! o.getScriptProperty(arg,
											"", oDependencyTracker, true, true);
					}
					//
					// Trace to the debugger if attached
					//
					ScriptDebugger oDebugger = moScriptHandler.getDebugger();
					if (oDebugger != null)
						oDebugger.resolvedValue(sItem, arg);
					if (arg.getArgType() == Arg.EXCEPTION)
						throw arg.getException();
					if (bNullValue) {
						oSym[i] = new CalcSymbol();
					}
					else if (arg.getArgType() == Arg.STRING) {
						oSym[i] = new CalcSymbol(arg.getString());
					}
					else if (arg.getArgType() == Arg.DOUBLE) {
						oSym[i] = new CalcSymbol(arg.getDouble(false).doubleValue());
					}
					else if (arg.getArgType() == Arg.INTEGER) {
						oSym[i] = new CalcSymbol(arg.getInteger().doubleValue());
					}
					else if (arg.getArgType() == Arg.BOOL) {
						oSym[i] = new CalcSymbol(arg.getBool().booleanValue() ? 1.0 : 0.0);
					}
					else if (arg.getArgType() == Arg.OBJECT) {
						oSym[i] = new CalcSymbol(obj, null);
					}
					else {
						oSym[i] = new CalcSymbol();
					}
				}
				return oSym;
			} catch (ExFull oError) {
				//
				// Exception thrown.  Convert it into an error return value
				//
				CalcSymbol calcsymbol
						= new CalcSymbol(oError.toString(), true, 0, 0);
				throw new CalcException(calcsymbol);
			}
		}

		/*
		 * Overridden from ScriptHost
		 */
		public int putItemValue(String sItem, Obj[] oObj, CalcSymbol oValue) {
			try {
				String sValue = null;
				if (oValue.getType() != CalcSymbol.TypeNull)
					sValue = oValue.getStringValue();
				Node oNode = moAppModel.getContext();
				if ( ! oNode.performSOMAssignment(sItem, sValue, oObj)) {
					moScriptHandler.setFatalError(false);
					//
					// Not handled.  Supply an error return value.
					// Load "unknown accessor" error message from resources.
					//
					MsgFormatPos sFmt = new MsgFormatPos(ResId.FC_ERR_ACCESSOR);
					sFmt.format(sItem);
					CalcSymbol calcsymbol
								= new CalcSymbol(sFmt.toString(), true, 0, 0);
					throw new CalcException(calcsymbol);
				}
				return 0;
			} catch (ExFull oError) {
				//
				// Exception thrown.  Convert it into an error return value
				//
				CalcSymbol calcsymbol
								= new CalcSymbol(oError.toString(), true, 0, 0);
				throw new CalcException(calcsymbol);
			}
		}

		/*
		 * Overridden from ScriptHost
		 */
		public int putItem(Obj[] oObj, CalcSymbol oValue) {
			return 0;		// needs work -- TBD
		}

		/*
		 * Overridden from ProtocolHost
		 */
		public CalcSymbol getUrl(String sUrl) throws CalcException {
			MsgFormatPos sFmt = new MsgFormatPos(ResId.PROTOCOL_ERR_SYS);
			sFmt.format(sUrl);
			CalcSymbol oErr = new CalcSymbol(sFmt.toString(), true, 0, 0);
			throw new CalcException(oErr);
		}
		
		/*
		 * Overridden from ProtocolHost
		 */
		public CalcSymbol putUrl(String sUrl, String sData, String sEnc)
		                                        throws CalcException {
			MsgFormatPos sFmt = new MsgFormatPos(ResId.PROTOCOL_ERR_SYS);
			sFmt.format(sUrl);
			CalcSymbol oErr = new CalcSymbol(sFmt.toString(), true, 0, 0);
			throw new CalcException(oErr);
		}

		/*
		 * Overridden from ProtocolHost
		 */
		public CalcSymbol postUrl(String sUrl, String sSoapHeader,
									String sData, String sContentType, String sEnc)
																throws CalcException {
			MsgFormatPos sFmt = new MsgFormatPos(ResId.PROTOCOL_ERR_SYS);
			sFmt.format(sUrl);
			CalcSymbol oErr = new CalcSymbol(sFmt.toString(), true, 0, 0);
			throw new CalcException(oErr);
		}

		/*
		 * Overridden from DebugHost
		 */
		public int stopped(int nScriptID, int nLine) {
			ScriptDebugger oDebugger = moScriptHandler.getDebugger();
			if (oDebugger != null)
				oDebugger.stopped(nScriptID, nLine);
			return -1;
		}

		/*
		 * Overridden from DebugHost
		 */
		public void poll() {
			ScriptDebugger oDebugger = moScriptHandler.getDebugger();
			if (oDebugger != null)
				oDebugger.poll(moScriptHandler);
		}

		/*
		 * Overridden from ScriptHost
		 */
		public boolean breakPoint(CalcParser oParser, int nScriptID, int nLine, boolean bSet) {
			return oParser.moCode.debugBreakPoint(nScriptID, nLine, bSet);
		}

		/*
		 * Overridden from ScriptHost
		 */
		public boolean command(CalcParser oParser, int eCmd) {
			return oParser.moCode.debugCommand(oParser, eCmd);
		}

		/*
		 * Overridden from ScriptHost
		 */
		public String getStackTrace(CalcParser oParser) {
			return oParser.moFrame.getStackTrace(oParser.mStack);
		}

		/*
		 * Overridden from ScriptHost
		 */
		public void getVariables(CalcParser oParser, List oNames, List oValues) {
			List oSymbols = new ArrayList();
			oParser.moData.enumerate(oParser.moScope, oSymbols);
			//
			// Peek at the stack to see if we're in a function.
			// If not, ignore parameters.
			//
			Frame oTopFrame = null;
			CalcSymbol oFuncSym = null;
			String sFuncPrefix = "";
			FrameTable oFrameTable = oParser.moFrame;
			if (oFrameTable.getDepth() > 0) {
				oTopFrame = oFrameTable.peek();
				oFuncSym = oTopFrame.getFuncSym();
				sFuncPrefix = oFuncSym.getName();
				sFuncPrefix += '`';
			}
			for (CalcSymbol oSym : oSymbols) {
				String sName = oSym.getName();
				if (oSym.getType() == CalcSymbol.TypeParameter) {
					if (oFuncSym == null)
						continue;	// not in  function
					if (! sFuncPrefix.startsWith(sName))
						continue;	// not a parameter for current function
					else
						sName = sName.substring(sFuncPrefix.length());
					int nStackAddr = oTopFrame.getStackAddr();
					int nArgCount = oTopFrame.getArgCount();
					int nStackIdx = oSym.getIdxValue();
					oSym = oParser.mStack.peek(nStackAddr
														- nArgCount + nStackIdx);
				}
				oNames.add(sName);
				oValues.add(oSym);
			}
		}

		/**
		 * @exclude from published api.
		 */
		public boolean cancelActionOccured()
		{
			return moScriptHandler.getCancelOrTimeState();	
		}			
		//
		// Fix for Watson 1100043.  There's no need to overide ProtocolHost
		// methods GetUrl(), PutUrl() and PostUrl() herein because the base
		// class implementation suffices at this stage.
		//

		private final CalcParser			moParser;
		private final AppModel				moAppModel;
		private final FormCalcScriptHandler	moScriptHandler;
	}

	/**
	 * Instantiates a script handler for the FormCalc language.
	 *
	 * @param oAppModel the application AppModel.
	 */
	public FormCalcScriptHandler(AppModel oAppModel) {
		this(oAppModel, null);
	}

	/**
	 * Instantiates a script handler for the FormCalc language.
	 *
	 * @param oAppModel the application model.
	 * @param oScriptDebugger the option script debugger.
	 *
     * @exclude from published api.
	 */
	public FormCalcScriptHandler(AppModel oAppModel,
								ScriptDebugger oScriptDebugger /* = null */) {
		super(oScriptDebugger);
		moParser = null;
		moAppModel = oAppModel;
		mbFatalError = true;
	}

	/**
	 * @exclude from published api.
	 */
	public void executeOrSyntaxCheck(String script,
			                         Arg returnCode,
						             int eReason /* = UNSPECIFIED */,
									 boolean bSyntaxCheckOnly) {
		if (moParser == null)
			moParser = new FormCalcParser(moAppModel, this);
		//
		// Assume fatal error unless told otherwise.
		//
		mbFatalError = true;
		msReturnMessage = "";
		int nScriptID = -1;
		ScriptDebugger oDebugger = getDebugger();
		if (oDebugger != null)
			nScriptID = oDebugger.getScriptID(this, script,
										moAppModel.getContext(), eReason);
		msReturnMessage = moParser.execute(script, null,
										nScriptID, returnCode, false);
		if (oDebugger != null)
			oDebugger.didExecuteScript(nScriptID, returnCode);
	}

	/**
	 * @exclude from published api.
	 */
	public void syntaxCheck(String script) {
		if (moParser == null)
			moParser = new FormCalcParser(moAppModel, this);
		//
		// Assume fatal error unless told otherwise.
		//
		mbFatalError = true;
		msReturnMessage = "";
		int nScriptID = -1;
		Arg returnCode = null;	// Not used in this case.
		msReturnMessage = moParser.execute(script, "",
										nScriptID, returnCode, true);
	}

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

	/**
	 * Method to check for the Esc Key press to halt the execution.
	 * May be we can extend it to set server side time-out for FormCalc execution.
	 * @exclude from published api.
	 */
	public boolean getCancelOrTimeState() {
		return false;
	}
	
	/**
	 * @exclude from published api.
	 */
	public void setOption(String sOptionName, String sOptionValue) {
		if (moParser == null)
			moParser = new FormCalcParser(moAppModel, this);
		if (sOptionName.equals("DupsMode"))
			moParser.setDupsMode((sOptionValue.length() >= 1 ? sOptionValue.charAt(0) : 0) == '1');
	}

	/**
	 * @exclude from published api.
	 */
	public String getOption(String sOptionName) {
		if (sOptionName.equals("DupsMode") && moParser != null)
			return moParser.getDupsMode() ? "1" : "0";
		return "";
	}

	public FormCalcScriptHandler clone() {
		FormCalcScriptHandler oNewScriptHandler
					= new FormCalcScriptHandler(moAppModel, getDebugger());
		oNewScriptHandler.moParser = moParser;
		oNewScriptHandler.msReturnMessage = msReturnMessage;
		return oNewScriptHandler;
	}

	/**
	 * @exclude from published api.
	 */
	public boolean wasFatalError() {
		return mbFatalError;
	}

	/**
	 * @exclude from published api.
	 */
	void setFatalError(boolean bFatal) {
		mbFatalError = bFatal;
	}

	// Debugging support

	/**
	 * @exclude from published api.
	 */
	public boolean debugCommand(int eCmd) {
		switch(eCmd) {
		case ScriptHandler.STEP_OVER:
			moParser.command(getParser(true), DebugHost.STEP_OVER);
			return true;
		case ScriptHandler.STEP_INTO:
			moParser.command(getParser(true), DebugHost.STEP_INTO);
			return true;
		case ScriptHandler.STEP_OUT:
			moParser.command(getParser(true), DebugHost.STEP_OUT);
			return true;
		case ScriptHandler.BREAK:
			moParser.command(getParser(true), DebugHost.STEP_INTO);
			return true;
		case ScriptHandler.HALT:
			moParser.command(getParser(true), DebugHost.STOP);
			return true;
		}
		return false;
	}

	/**
	 * @exclude from published api.
	 */
	public boolean debugBreakPoint(int nScriptID, int nLine, int eSetType) {
		if (eSetType == ScriptHandler.BP_SET
									|| eSetType == ScriptHandler.BP_CLEAR)
			return moParser.breakPoint(getParser(true), nScriptID,
								nLine, eSetType == ScriptHandler.BP_SET);
		return false;
	}

	/**
	 * @exclude from published api.
	 */
	public boolean debugGetStack(List oStack) {
		oStack.clear();
		String sStackTrace = moParser.getStackTrace(getParser(true));
		StringTokenizer sToke = new StringTokenizer(sStackTrace, "\n");
		while (sToke.hasMoreTokens()) {
			String sEntry = sToke.nextToken();
			oStack.add(sEntry);
		}
		return true;
	}

	/**
	 * @exclude from published api.
	 */
	public boolean debugGetVariables(List oVarNames, List oVarValues) {
		oVarNames.clear();
		oVarValues.clear();
		List oCalcSymbols = new ArrayList();
		moParser.getVariables(getParser(true), oVarNames, oCalcSymbols);
		for (int i = 0; i < oVarNames.size(); i++) {
			Arg oArg = new Arg();
			FCValToXFAArg(oCalcSymbols.get(i), oArg);
			oVarValues.add(oArg);
		}
		return true;
	}

	/**
	 * @exclude from published api.
	 */
	protected int debugFeatures() {
		int nFeatures = 
				ScriptHandler.FEATURE_CAN_SET_BREAKPOINT |
				ScriptHandler.FEATURE_CAN_STEP_OVER	   |
				ScriptHandler.FEATURE_CAN_STEP_INTO	   |
				ScriptHandler.FEATURE_CAN_STEP_OUT;
		return nFeatures;
	}

	/*
	 * ArgToFCVal existed but was unused as of rev 34 of this file,
	 * in case we ever need to retrieve it...
	 */
	static void FCValToXFAArg(CalcSymbol oFCVal, Arg oArgVal) {
		switch (oFCVal.getType()) {
		case CalcSymbol.TypeDouble:
			oArgVal.setDouble(new Double(oFCVal.getNumericValue()));
			break;
		case CalcSymbol.TypeReference:
			oArgVal.setObject(oFCVal.getObjValue());
			break;
		case CalcSymbol.TypeVariable:
		case CalcSymbol.TypeString:
			oArgVal.setString(oFCVal.getStringValue());
			break;
		case CalcSymbol.TypeNull:
			oArgVal.setNull();
			break;
		default:
			oArgVal.empty();
			// TBD -- failure
			break;
		}
	}

	/*
	 * Map FormCalc resource IDs to common error codes.  This is only
	 * intended to handle those that may occur during syntax checking,
     * not execution.
	 */
	static int FCResIdToErrorCode(int nErrorResId) {
		if (nErrorResId == ResId.FC_ERR_SYNTAX)
			return ScriptHandler.ERR_Syntax;
		else if (nErrorResId == ResId.FC_ERR_LOOP)
			return ScriptHandler.ERR_BadBreakContinue;
		else if (nErrorResId == ResId.FC_ERR_FUNC_USED)
			return ScriptHandler.ERR_FuncBuiltIn;
		else if (nErrorResId == ResId.FC_ERR_FUNC_UNKN)
			return ScriptHandler.ERR_FuncUnknown;
		//
		// Not a serious problem, but a new case should be handled if we
		// hit this assert.  Default to a syntax error.
		//
	// Javaport:  asserts in Java are a problem.
    //	assert(false);
		return ScriptHandler.ERR_Syntax;
	}

	/**
	 * If the script does a return("foo"), then the returnCode from
	 * execute will be Arg.EMPTY, and
	 * getReturnMessage() will return "foo".  Otherwise getReturnMessage()
	 * returns an empty string.
	 *
	 * @exclude from published api.
	 */
	protected String getReturnMessage() {
		return msReturnMessage;
	}

	/**
	 * Provides access to the FormCalc Parser for derived classes. 
	 * If bForceCreation is false, the
	 * return value may be null if execute() has not been called
	 * (the parser is normally only
	 * instantiated on the first call to execute()).  If bForceCreation
	 * is true, the parser will be created and returned.
	 *
	 * @exclude from published api.
	 */
	protected CalcParser getParser(boolean bForceCreation) {
		if (moParser == null) {
			if (bForceCreation)
				moParser = new FormCalcParser(moAppModel, this);
			else
				return null;
		}
		return moParser;
	}

	private String			msReturnMessage;
	private FormCalcParser 	moParser;
	private final AppModel	moAppModel;
	private boolean			mbFatalError;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy