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

com.adobe.xfa.form.CalculateDispatcher Maven / Gradle / Ivy

The newest version!
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2005 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.form;

import com.adobe.xfa.AppModel;
import com.adobe.xfa.Arg;
import com.adobe.xfa.Attribute;
import com.adobe.xfa.Element;
import com.adobe.xfa.EnumAttr;
import com.adobe.xfa.EventManager;
import com.adobe.xfa.Int;
import com.adobe.xfa.Node;
import com.adobe.xfa.ProtoableNode;
import com.adobe.xfa.Schema;
import com.adobe.xfa.ScriptHandler;
import com.adobe.xfa.XFA;
import com.adobe.xfa.template.TemplateModel;
import com.adobe.xfa.template.containers.Container;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.Numeric;
import com.adobe.xfa.ut.Peer;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.StringUtils;


/**
 * @exclude from public api.
 */
class CalculateDispatcher extends com.adobe.xfa.ScriptDispatcher {
	
	private final int meRunAt;		
	
	CalculateDispatcher(Element scriptContextNode,
			String sEventContext,
			int nEventID,
			EventManager eventManager,
			String sScript,
			String sContentType,
			int eRunAt) {
		super(scriptContextNode,
				sEventContext, 
				nEventID, 
				eventManager,
				sScript,
				sContentType);
		
		meRunAt = eRunAt;
	}
	
	public void dispatch() {
		Element node = getActionContextNode();
		if (node == null)
			return;
		String sScript = getScript();
		String sContentType = getContentType();

		assert(node instanceof FormField ||
				node instanceof FormSubform ||
					node instanceof FormExclGroup);

		// note would be nice to directly access mpoNodeImpl.mpModel
		FormModel formModel = (FormModel) node.getModel();

		if (formModel.isActivityExcluded("calculate"))
			return;
		
		// As of //https://zerowing.corp.adobe.com/display/xtg/InactivePresenceXFAProposal,
		// event processing associated with inactive containers must not occur.
		// To mitigate performance on older forms, only do this check for XFA 3.0 documents and higher.
		AppModel appModel = node.getAppModel();
		if (appModel != null) {
			TemplateModel templateModel = TemplateModel.getTemplateModel(appModel, false);
			if ((templateModel != null) &&
				(templateModel.getOriginalXFAVersion() >= Schema.XFAVERSION_30) &&
				(node instanceof Container)) {
				Container container = (Container)node;
				int ePresence = container.getRuntimePresence(EnumAttr.UNDEFINED);
				if (EnumAttr.PRESENCE_INACTIVE == ePresence)
					return;
			}
		}

		int eModelRunAtSetting = formModel.getRunScripts();
		int eRunAt = getRunAt();

		// ensure we can run the script
		if (eModelRunAtSetting == EnumAttr.RUNSCRIPTS_NONE)
			return;
		else if (eModelRunAtSetting == EnumAttr.RUNSCRIPTS_SERVER && eRunAt == EnumAttr.RUNAT_CLIENT)
			return;
		else if (eModelRunAtSetting == EnumAttr.RUNSCRIPTS_CLIENT && eRunAt == EnumAttr.RUNAT_SERVER)
			return;
		
		int nNumErrorsBefore = formModel.getErrorList().size();

		// remove old Dependencies
		formModel.removeDependency(node, true);

		FormDependencyTracker tracker = new FormDependencyTracker(node, true);
		try {			
			Arg returnCode = new Arg();
	
			boolean bCalcEnabled = formModel.getCalculationsEnabled();
	
			if (bCalcEnabled && node instanceof FormField) {
				FormField field = (FormField)node;

				// check for override setting on the value node.
				// if set to true don't run the script
				Element value = field.getElement(XFA.VALUETAG, true, 0, false, false);
				if (value != null && value.getEnum(XFA.OVERRIDETAG) == EnumAttr.BOOL_TRUE) {
					// watson bug 1440694  old 7.0 forms don't respect the override attribute, 
					// must continue to respect this
					if (!node.getAppModel().getLegacySetting(AppModel.XFA_LEGACY_CALCOVERRIDE))
						return;
				}

				returnCode = field.evaluate(sScript, sContentType, ScriptHandler.CALCULATE, true);
		
				String sReturnValue = null;
		
				if ((Arg.EXCEPTION != returnCode.getArgType()) &&
						(Arg.EMPTY != returnCode.getArgType())) {
				
					if (Arg.NULL != returnCode.getArgType())
						sReturnValue = returnCode.getAsString(false);
				
					// watson 1776934: Do not detect permissions violations for calculations unless the 
					// calculation is modifying the existing value.
					String sRawValueOld = field.getRawValue();

					//Bug#3018085
					boolean doContinueSettingNewRawValue=true;
					{
						// get the value
						Element pValueNode = field.getElement(XFA.VALUETAG, true, 0, false, false);

						// get the value's content
						if (pValueNode != null) {
							Node pContentNode = pValueNode.getOneOfChild();

							//Continue only if content is of type Decimal OR Float
							if (pContentNode != null && (pContentNode.isSameClass(XFA.DECIMALTAG) || pContentNode.isSameClass(XFA.FLOATTAG))) {

								//Verify that both the old and new values are of double type and comparable
								if (!StringUtils.isEmpty(sReturnValue) && !StringUtils.isEmpty(sRawValueOld)){
									double dReturnValue = Numeric.stringToDouble(sReturnValue, true);
									double dRawValueOld = Numeric.stringToDouble(sRawValueOld, true);

									//If both values are comparable and equal by applying double semantics (IEEE-754 1985)
									//then just consider these values as equivalent and skip setting rawValue and avoid triggering peer notifications
									if (!Double.isInfinite(dReturnValue) && !Double.isNaN(dReturnValue) && 
											!Double.isInfinite(dRawValueOld) && !Double.isNaN(dRawValueOld)){
										//fracDigits exist only in case of DecimalValue.
										//Assume it to be 8 (in case of FloatValue it is hardcoded to 8 (ref FloatValue.class))
										int fracDigits = 8;
										if (pContentNode.isSameClass(XFA.DECIMALTAG)){								
											Attribute fracDigitAttr = ((Element)pContentNode).getAttribute(XFA.FRACDIGITSTAG);
											//fracDigitAttr can't be null for DecimalValue. No need to check for null value.
											fracDigits = ((Int)fracDigitAttr).getValue();
										}
										String sReturnValueNormalized = Numeric.doubleToStringUsingBigDecimal(dReturnValue, fracDigits, false);
										String sRawValueOldNormalized = Numeric.doubleToStringUsingBigDecimal(dRawValueOld, fracDigits, false);
										doContinueSettingNewRawValue = !sReturnValueNormalized.equals(sRawValueOldNormalized);
									}
								}
							}
						}
					}
			
					if (doContinueSettingNewRawValue && sReturnValue != null && ! sReturnValue.equals(sRawValueOld)) {
						field.setRawValue(sReturnValue);
						String sRawValueNew = field.getRawValue();
						// bug 1816666 (cover cases like 24.2499 != 24.25, in sReturnValue check 
						//      above, though setRawVal/getRawVal treat it that way)
						// added another condition for that below apart from check permissions
						//   more details in bug notes / review notes (reviewid=224101)						
						// check permissions
						if ((null!=sRawValueNew || null!=sRawValueOld) && (null==sRawValueNew || ! sRawValueNew.equals(sRawValueOld)) &&
								!node.checkAncestorPerms()){
							field.setRawValue(sRawValueOld);							
							MsgFormatPos message = new MsgFormatPos(ResId.PermissionsViolationExceptionMethod);
							message.format("calculate");
							throw new ExFull(message);
						}					
					} 	
				}
			}
	
			if (bCalcEnabled && node instanceof FormExclGroup) {
				FormExclGroup exclGroup = (FormExclGroup)node;

				// ensure none of the field children have the value overwritten
				for (Node child = exclGroup.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
					if (child instanceof FormField) {
						FormField formField = (FormField) child; 
					
						// check for override setting on the value node.
						// if set to true don't run the script
						Element value = formField.getElement(XFA.VALUETAG, true, 0, false, false);
						if (value != null && value.getEnum(XFA.OVERRIDETAG) == EnumAttr.BOOL_TRUE) {
							// watson bug 1440694  old 7.0 forms don't respect the override attribute, 
							// must continue to respect this
							if (!node.getAppModel().getLegacySetting(AppModel.XFA_LEGACY_CALCOVERRIDE))
								return;
						}
					}
				}

				returnCode = exclGroup.evaluate(sScript, sContentType, ScriptHandler.CALCULATE, true);
		
				String sReturnValue = "";
		
				if ((Arg.EXCEPTION != returnCode.getArgType()) &&
						(Arg.EMPTY != returnCode.getArgType())) {
				
					if (Arg.NULL != returnCode.getArgType())
						sReturnValue = returnCode.getAsString(false);
				
					// watson 1776934: Do not detect permissions violations for calculations unless the 
					// calculation is modifying the existing value.
					if (sReturnValue != null && ! sReturnValue.equals(exclGroup.getRawValue())) {
						// check permissions
						if (! node.checkAncestorPerms()) {
							MsgFormatPos message = new MsgFormatPos(ResId.PermissionsViolationExceptionMethod);
							message.format("calculate");
							throw new ExFull(message);
						}
					
						exclGroup.setRawValue(sReturnValue);
					} 	
				}
			}
			else if (bCalcEnabled && node instanceof FormSubform) {
				FormSubform subform = (FormSubform)node;
				returnCode = subform.evaluate(sScript, sContentType, ScriptHandler.CALCULATE, true);
			}
	
			if (Arg.EXCEPTION == returnCode.getArgType()) {
				//Vantive 567857
				//An exception occurred when evaluating this script but it's possible 
				//that this script will pass if run again later. If we're able to requeue it
				//(i.e. not cyclic dependency) then we'll remove the errors it just generated.
				//If will only be allowed to fail a finite number of times before 
				//we stop giving it another chance.

				int nNumErrorsOccurred = formModel.getErrorList().size() - nNumErrorsBefore;
				if (0 < nNumErrorsOccurred) {
					// Queue it up again
					if ( formModel.queueCalculate((ProtoableNode)node) ) {
						//Remove the errors logged this time.
						for (int e = 0; e < nNumErrorsOccurred; e++) {
							formModel.removeLastError();
						}
						
						assert nNumErrorsBefore == formModel.getErrorList().size();
					}
				}
			}
		}
		finally {
			tracker.dispose();
		}
	}
	
	int getRunAt() {
		return meRunAt;
	}
	
	@Override
	public void updateFromPeer(Object peerNode, 
						int eventType, 
						String arg1, 
						Object arg2) {
		
		Element node = getActionContextNode();
		if (node == null)
			return;
		
		// Change of presence means we need to queue calcs 
		// (In case the object was inactive before)
		// This won't do anything if the object is still inactive.
		if (peerNode == node && 
			node instanceof Container &&
		    eventType == Peer.ATTR_CHANGED && 
		    arg1.equals(XFA.PRESENCE) ) {
			
			// note would be nice to directly access mpoNodeImpl->mpModel
			FormModel formModel = (FormModel)node.getModel();
			formModel.queueCalculatesAndValidates(node, true);
		}	
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy