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

com.adobe.xfa.data.DataModelFactory 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.data;

import org.xml.sax.Attributes;

import com.adobe.xfa.AppModel;
import com.adobe.xfa.Element;
import com.adobe.xfa.Model;
import com.adobe.xfa.ModelFactory;
import com.adobe.xfa.ModelPeer;
import com.adobe.xfa.Node;
import com.adobe.xfa.Option;
import com.adobe.xfa.STRS;
import com.adobe.xfa.XFA;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.ResId;


/**
 * A class to represent XFA data model factories.  A data model factory
 * is used by an application model to create a data model where
 * appropriate, when loading an XML file.
 */
public final class DataModelFactory extends ModelFactory {

	private static final String gsAttrOption = "delegate|ignore|preserve";

	private static final String gsRecordOption = "%d|%s";

	private Option mAttributesOption;

	private Option mDataWindowOption; // mDataWindowOption is stored as a string, but is parsed into

	private Option mExcludeNSOption;

	private int mnDataWindowRecordsAfter;

	private int mnDataWindowRecordsBefore; // mnDataWindowRecordsBefore and mnDataWindowRecordsAfter.

	private Option mRangeOption;

	private Option mRecordOption;

	private Option mStartNodeOption;

	private DataTransformations mTransformations;

	private Option mTransformOption;

	/**
	 * Instantiates a data model factory.
	 */
	public DataModelFactory() {
		super(XFA.DATAMODELTAG, XFA.DATA, STRS.DOLLARDATA);

		mAttributesOption = new Option(XFA.ATTRIBUTES, gsAttrOption);
		mRecordOption = new Option(XFA.RECORD, gsRecordOption); // integer | name
		mTransformOption = new Option(XFA.TRANSFORM, STRS.PERCENTS); // transformations
		mRangeOption = new Option(XFA.RANGE, STRS.PERCENTS); // num,num-num,num-*,...
		mStartNodeOption = new Option(XFA.STARTNODE, STRS.PERCENTS); // path
		mExcludeNSOption = new Option(XFA.EXCLUDENS, STRS.PERCENTS); // string (list of namespaces separated by blanks)
		mDataWindowOption = new Option(XFA.WINDOW, STRS.PERCENTS); // string ("number", or "number,number")
		mnDataWindowRecordsBefore = 0;
		mnDataWindowRecordsAfter = 0;
		mTransformations = null;
		attributesAreValues(true);
	}

	/**
	 * Return a boolean indicating whether XML attributes are to be treated as
	 * XFADataValues or ignored. The default is false.
	 * 
	 * @return true if attributes are to be XFADataValues
	 * 
	 * @exclude from published api.
	 */
	public boolean attributesAreValues() {
		// This may have to be revisited if the meaning of "delegate" ever changes.
		// Right now it's equivalent to "ignore".

		return (mAttributesOption.getMatchingIndex() == 2); // 2 == "preserve"
	}

	/**
	 * Set the behavior for interpreting XML attributes.
	 * 
	 * @param bSetting
	 *            if true, non-namespaced attributes are to be interpreted as
	 *            XFADataValues. If false, attributes are essentially ignored
	 *            within the context of the XFA-Data DOM. The default is false.
	 * 
	 * @exclude from published api.
	 */
	public void attributesAreValues(boolean bSetting) {
		String sSetting = bSetting ? STRS.PRESERVE : STRS.IGNORE;
		mAttributesOption.setValue(sSetting, false);
	}

	/** 
	 * @exclude from published api.
	 */
	public Model createDOM(Element parent) {
		
		DataModel m = new DataModel(parent.getAppModel(), null);
		// The model might not be created in the same document as the AppModel,
		// so make sure that the document is correct now.
		m.setDocument(parent.getOwnerDocument());
		
		ModelPeer modelPeer = new ModelPeer(
				parent, null, 
				STRS.XFADATANS_CURRENT, STRS.DATASETS, STRS.XFADATASETS, 
				null, m);
		
		m.setXmlPeer(modelPeer);
		
		Element dataPeer = new Element(modelPeer, null, STRS.XFADATANS_CURRENT, XFA.DATA, STRS.XFADATA, null, XFA.INVALID_ELEMENT, "");
		dataPeer.setModel(m);
		
		// Set xfa:dataNode="dataGroup" so that data is always a dataGroup, never a dataValue.
		dataPeer.setAttribute(STRS.XFADATANS_CURRENT, STRS.XFADATANODE, XFA.DATANODE, XFA.DATAGROUP, false);
		
		DataNode dataNode = new DataNode(m, null, null, null, null, null);
		dataNode.setClass(XFA.DATAGROUP, XFA.DATAGROUPTAG);
		dataNode.setModel(m);		
		dataNode.setXmlPeer(dataPeer);
		dataPeer.setXfaPeer(dataNode);
		
		m.setAliasNode(dataNode);
		return m;
	}
	
	/**
	 * DataModel knows how to merge a duplicate model into an existing one
	 * during postLoad processing.
	 * @exclude from published api.
	 */
	public boolean getAllowAdd() {
		return true;
	}
	
	/** 
	 * @param name the name to be tested. This String must be interned.
	 * @exclude from published api.
	 */
	@FindBugsSuppress(code="ES")
	public boolean isRootName(String name) {
		return (name == XFA.DATA) || (name == STRS.DATASETS);
	}

	/** 
	 * @param parent
	 * @param uri
	 * @param localName This String must be interned.
	 * @exclude from published api.
	 */
	@FindBugsSuppress(code="ES")
	protected boolean isRootNode(AppModel parent, String uri, String localName) {
		
		// Accept anything, unless the parent node is "xfa", in which
		// case accept only the datasets node.
		
		if (!parent.getAllowThirdPartyXml()) {
			// only accept "datasets" under the "xfa" node
			return localName == STRS.DATASETS;
		}
		
		// accept anything else
		return true;
	}

	/** 
	 * @exclude from published api.
	 */
	@FindBugsSuppress(code="ES")
	protected Model newModel(AppModel parent, Node prevSibling, String uri, String localName,
			String qName, Attributes a) {
		
		// JAVAPORT_DATA - We may be creating a duplicate DataModel, but it will get
		// merged into any existing one during postLoad processing.
		
		DataModel dataModel = new DataModel(parent, prevSibling);
		
		// Create an XML peer for the DataModel.
		// This peer may only be temporary since during loading, the DataModel
		// may need to rearrange peers as it loads the XML.
		// To make things a little easier, we can detect whether the element we
		// are creating represents an xfa:datasets element or some third-party
		// node, and create the appropriate class of peer.
		// We create the peer as an orphan - the SaxHandler will look after
		// wiring it into the XFA document.
		
		Element dataModelPeer;
		if (uri.equals(STRS.XFADATANS_CURRENT) && localName.equals(STRS.DATASETS)) {
			dataModelPeer = new ModelPeer(null, null, uri, localName, qName, a, dataModel);
		}
		else {
			dataModelPeer = new Element(null, null, uri, localName, qName, a, XFA.INVALID_ELEMENT, "");
			dataModelPeer.setDocument(dataModel.getOwnerDocument());
		}

		dataModel.setXmlPeer(dataModelPeer);

		if (mAttributesOption.isSet()) {
			// don't specify the value if getMatchingIndex() == 0 ("delegate")
			if (mAttributesOption.getMatchingIndex() == 1) 		// "ignore"
				dataModel.setAttributesAreValues(false);
			else if (mAttributesOption.getMatchingIndex() == 2) // "preserve"
				dataModel.setAttributesAreValues(true);
		}
		if (mRecordOption.isSet()) {
			int recordLevel = 1;
			String recordName = null;
			if (mRecordOption.getMatchingIndex() == 0)
				recordLevel = mRecordOption.getInteger();
			else
				// string
				recordName = mRecordOption.getString();

			dataModel.setRecordOptions(recordLevel, recordName);
		}
		if (mRangeOption.isSet()) {
			String sRange = mRangeOption.getString();
			dataModel.setRangeOptions(sRange);
		}

		if (mStartNodeOption.isSet()) {
			// The format of mStartNodeSOMString was verified when the option
			// was set via setOption.
			// Parse out the SOM string by removing "xfasom(" and trailing ")".
			int nLength = mStartNodeOption.getString().length();
			dataModel.setStartNodeSOMString(mStartNodeOption.getString().substring(7, nLength - 1));
		}
		if (mExcludeNSOption.isSet()) {
			dataModel.setExcludeNSList(mExcludeNSOption.getString());
		}
		if (mDataWindowOption.isSet()) {
			dataModel.setDataWindowParameters(mnDataWindowRecordsBefore, mnDataWindowRecordsAfter);
		}
		if (mTransformations != null && mTransformations.size() > 0) {
			// ?? remove
			// mTransformations.setAttributesAreValues(attributesAreValues());
			dataModel.setTransformations(mTransformations);
		}
		return dataModel;
	}

	/** 
	 * Sets a data loading option.
	 * @param optionName the name of the option
	 * @param optionValue the value of the option
	 * @param bCritical if true, then any future attempts to set this option
	 * on this DataModelFactory are rejected.
	 */
	public void setOption(String optionName, String optionValue, boolean bCritical) {
		
		// JavaPort: This method becomes part of the published API in XFA4J since it is
		// used in piCosDocument::setDataLoadOptions, which Gibson code emulates.
		// The set of supported options and their domains should be documented.
		
		final Option optionArray[] = new Option[] {
			mAttributesOption, 	// 0
			mRecordOption, 		// 1
			mStartNodeOption, 	// 2
			mExcludeNSOption, 	// 3
			mDataWindowOption, 	// 4
			mRangeOption, 		// 5
			mTransformOption, 	// 6
			null
		};

		// We now support either "datasets_" or "data_" as a prefix for our options.
		// We can only specify one package name to XFAOption::setOptionByArray, which
		// must match the prefix in the option name IF THERE IS ONE. So what we do
		// is check the prefix ourselves, and remove it.
		String name = optionName;
		int nPackageSeparatorOffset = optionName.indexOf('_');
		if (nPackageSeparatorOffset != -1) {
			if (nPackageSeparatorOffset == 0)
				throw new ExFull(ResId.OptionWrongPackageException, optionName);

			// package specified; verify that it's the right one
			String packageName = optionName.substring(0, nPackageSeparatorOffset);
			if (!isRootName(packageName.intern()))
				// wrong package
				throw new ExFull(ResId.OptionWrongPackageException, optionName);

			// package is OK; remove it from the option name
			name = optionName.substring(nPackageSeparatorOffset + 1);
		}

		int nOption = Option.setOptionByArray("", optionArray, name,
				optionValue, bCritical);

		if (nOption == 2) { // mStartNodeOption
			// Validate string passed to mStartNodeOption. Currently, it must be a SOM
			// expression in the form xfasom(som expression).

			String sStartNodeString = mStartNodeOption.getString(); // for convenience

			if (!sStartNodeString.startsWith("xfasom(")
					|| !sStartNodeString.endsWith(")")) {
				MsgFormatPos oMessage = new MsgFormatPos(ResId.InvalidOptionValueException);
				oMessage.format(mStartNodeOption.getName());
				oMessage.format(sStartNodeString);
				throw new ExFull(oMessage);
			}
		}
		else if (nOption == 4) { // mDataWindowOption
			// Validate string passed to mDataWindowOption. It's either "number" or
			// "number,number"

			String sWindowString = mDataWindowOption.getString(); // for convenience

			int before = 0;
			int after = 0;

			int iteration = 1;
			boolean bValid = false;
			String[] tokens = sWindowString.split(",");
			for (int i = 0; i < tokens.length; i++) {
				String sToken = tokens[i];
				int num;
				try {
					num = Integer.parseInt(sToken);
				} catch (NumberFormatException e) {
					bValid = false;
					break;
				}
				if (iteration == 1) {
					bValid = true;
					before = num;
					after = num;
				}
				else if (iteration == 2) {
					after = num;
				}
				else {
					bValid = false; // too many numbers
					break;
				}
				iteration++;
			}

			if (!bValid) {
				MsgFormatPos oMessage = new MsgFormatPos(
						ResId.InvalidOptionValueException);
				oMessage.format(mDataWindowOption.getName());
				oMessage.format(sWindowString);
				throw new ExFull(oMessage);
			}

			mnDataWindowRecordsBefore = before;
			mnDataWindowRecordsAfter = after;

		}
		else if (nOption == 6) { // mTransformOption
			if (mTransformations == null)
				mTransformations = new DataTransformations();
			mTransformations.add(mTransformOption.getString());
		}
	}
	
	public void reset(){
		if (mTransformations == null) mTransformations.reset();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy