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

com.adobe.xfa.Model Maven / Gradle / Ivy

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show 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;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.xml.sax.Attributes;

import com.adobe.xfa.content.Content;
import com.adobe.xfa.ut.Assertions;
import com.adobe.xfa.ut.BooleanHolder;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.LcLocale;
import com.adobe.xfa.ut.MsgFormatPos;
import com.adobe.xfa.ut.Numeric;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.ResourceLoader;
import com.adobe.xfa.ut.StringUtils;
import com.adobe.xfa.ut.SymbolTable;

//Adobe Patent or Adobe Patent Pending Invention Included Within this File

/**
 * An abstract class from which to derive all other models.
 */
public abstract class Model extends Element implements Element.DualDomNode {
	
	/**
	 * A marker interface that indicates that a Model's contents are dual-DOM,
	 * as opposed to the model Element itself, which is always dual-DOM. 
	 * @exclude from published api.
	 */
	public interface DualDomModel {
	}
	
	/**
	 * @exclude from published api.
	 */
	public static abstract class Publisher {
		/**
		 * See {@link Model#publish(Publisher)} for a description of this method.
		 */
		public abstract String updateExternalRef(Node oNode, int eAttribute,
				String sExternalRefValue);
		public abstract int getTargetVersion();
		public abstract int getTargetAvailability();
	}

	/**
	 * Helper function for isCompatibleNS
	 * @param aNS - namespace of node being checked
     * @param aModelNS - namespace of model being checked against.
	 * @exclude from published api.
	 */
	public static boolean checkforCompatibleNS(String aNS, String aModelNS) {
		if (aNS == null)
			return false;		// atom doesn't exist

		if (aNS.length() == 0)
			return false;

		int nNSLen = aModelNS.length();

		if (aNS.length() >= nNSLen) {

			if (aNS.startsWith(aModelNS))
				return true;
		}

		return false;
	}

	/**
	 * For use by derived classes only.
	 * @exclude from published api.
	 */
	@FindBugsSuppress(code="ES")
	protected static Model getNamedModel(AppModel oAppModel, String aModelName) {
		Node child = oAppModel.getFirstXFAChild();
		while (child != null) {
			if (child.getClassName() == aModelName)
				return (Model) child;
			child = child.getNextXFASibling();
		}

		return null;
	}

	/**
	 * @exclude from published api.
	 */
	public ScriptTable getScriptTable() {
		return ModelScript.getScriptTable();
	}

	/**
	 * @param startModel
	 * @param aShortCutName.
	 * 
	 * @exclude from published api.
	 */
	static Node lookupShortCut(Model startModel, String sShortCutName) {
		if (startModel.shortCutName().equals(sShortCutName))
			return startModel.getAliasNode();

		// Hunt for children which are models.
		
		for (Node child = startModel.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) {
			if (child instanceof Model) {
				Node retNode = lookupShortCut((Model) child, sShortCutName);
				if (retNode != null)
					return retNode;
			}			
		}

		return null;
	}
	
	static void resolveList(List oPendingProtos, boolean bResolveExternalOnly) {
	  	int nPos = 0;
		int nLen = oPendingProtos.size();
		while(nLen > 0 && nPos != nLen) {
			ProtoableNode oObject = oPendingProtos.get(nPos);
			
			boolean bRemoved = oObject.performResolveProtos(bResolveExternalOnly);
			
			// Note: if someone removes a node from oPendingProtos it will be after the current node
			if (!bRemoved)
				nPos++;

			// make sure the length is correct because a node maybe removed from oPendingProtos
			// by performResolveProtos
			nLen =  oPendingProtos.size();
		}
	}
	
	
	private static final boolean ACROBAT_PLUGIN = ResourceLoader.loadProperty("ACROBAT_PLUGIN").equalsIgnoreCase("true");
	
	
	private boolean mbLoading; // true if in loadchildren

	/**
	 * Normalize the namespaces of the nodes under this model
	 *
	 * @exclude from published api.
	 */
	protected boolean mbNormalizedNameSpaces;

	private boolean mbReady; // true if ready() has been called
	/**
	 * @exclude from published api.
	 */
	protected boolean mbValidateTextOnLoad = true;

	private boolean mbWillDirtyDoc;
	
	private final List mErrorContextList = new ArrayList();

	/**
	 * List of errors encountered during load
	 */
	private final List mErrorList = new ArrayList();

	private Generator mGenerator;
	
	private IDValueMap mIDValueMap;
	
	private String mName;

	private int mnCurrentVersion;

	private Element mAliasNode; // The node referred to by the alias (eg. $data)

	private ProcessingInstruction moOriginalVersion;
	
	/**
	 * For performance save the protos.
	 */
	private final List moProtoList = new ArrayList(); 

	private Node moSOMContext; // set by setContext & used in resolveNode

	/**
	 * Similar to moUseList, but for USEHREF nodes.
	 */
	private final List moUseHRefList = new ArrayList();
	
	/**
	 * For performance save the use (reference) nodes
	 */
	private final List moUseList = new ArrayList();
	
	private AppModel mAppModel;
	
	private String msCachedLocale; // see getCachedLocale

	private final Schema mSchema;
	private final String mShortCutName; // eg. $data

	/**
	 * used to perform SOM parsing
	 */
	private SOMParser mSOMParser; 
	
	
	/**
	 * This is used to make serialization behaviour consistent with C++.
	 * In some cases, whether the AppModel node is transient is specific
	 * to a particular model (i.e., the DOM that model is associated with).
	 */
	private boolean mbAppModelIsTransient;
	
	/**
	 * Used to optimize the interning of attribute values.
	 */
	private SymbolTable mSymbolTable;
	
	private boolean mbAllowUpdates;
	
	private Element mXmlPeer;			// Our XML tree peer.

	/**
	 * @exclude from published api.
	 */
	protected int mnOriginalVersion = 0;
	
	/**
	 * @exclude from published api.
	 */
	@FindBugsSuppress(code="ES")
	protected Model(Element parent, Node prevSibling, String uri, String qName,
			String name, String aShortCutName, int classTag, String className,
			Schema schema) {

		super(parent, prevSibling, uri, name, qName, null, classTag, className);
		
		// AppModel always creates with a null parent
		if (getClass() == AppModel.class)
			assert parent == null;
		
		if (parent != null) {
			
			// If a Model has a parent, it must be an AppModel
			if (parent.getClass() != AppModel.class)
				throw new IllegalArgumentException("parent is not an AppModel.");
		}
		
		mShortCutName = aShortCutName;
		mSchema = schema;

		// setClassAtom not called since this class doesn't exist on its own
		setModel(this);
		if (name.length() > 0)
			setName(name);

		//
		// Move up the hierarchy until we reach the top level model.
		//
	    Element currentParent = parent;
		if (currentParent != null) {
			parent = parent.getXFAParent();
			while (parent != null) {
				currentParent = parent;
				parent = parent.getXFAParent();
			}
			assert(currentParent instanceof AppModel);
			if (currentParent instanceof AppModel)
				setAppModel((AppModel)currentParent);
		}
		
		// default the version on the model to the head
		setCurrentVersion(getHeadVersion());

		// JavaPort: In C++ this is done in the loadChildren method
		if (uri != "" && uri != null) {
			int nVersion = getVersion(uri);
			setCurrentVersion(nVersion);
		}
	}
	
	/** @exclude */
	protected void addAttributes(Element pParent, Attribute[] oAttributes, Generator generator) {
		// do nothing
	}
	
	/**
	 * @exclude from published api.
	 */
	public final void addErrorList(ExFull error,
			int /* LogMessage.LogMessageSeverity */ eSeverity,
			Element context) {
		mErrorList.add(error);
		// Keep track of all the nodes where errors occurred.
		// That way if we want, we can try and correct some errors
		// in a post-load fixup operation.
		mErrorContextList.add(context);
		LogMessage oMsg = new LogMessage();
		oMsg.insertMessage(error, eSeverity, getCachedLocale());
		getLogMessenger().sendMessage(oMsg);

	}
	
	/**
	 * @exclude from public api.
	 */
	public final void removeLastError() {
		if (mErrorList.size() > 0)
			mErrorList.remove(mErrorList.size() - 1);
	}
	
	/**
	 * @exclude from published api.
	 */
	protected void removeOriginalVersionNode() {
		if (moOriginalVersion != null) {
			// remove the node from the tree
			moOriginalVersion.remove();
		}
	}

	void addProto(ProtoableNode poProto) {
		moProtoList.add(poProto);
	}

	/**
	 * add a new new node to the useNodeHrefList
	 * @exclude from published api.
	 */
	public void addUseHRefNode(Element poUseHRefNode) {
		//
		// Watson 1799140: discard non-protoable elements from list.
		//
		if (poUseHRefNode instanceof ProtoableNode)
		   moUseHRefList.add((ProtoableNode)poUseHRefNode);
	}


	/**
	 * add a new new node to the useNodeList
	 * @exclude from published api.
	 */
	public void addUseNode(Element poUse) {
		// Javaport: new for Java.  Ensure entries are unique!
		for (int i = 0; i < moUseList.size(); i++)
			if (poUse == moUseList.get(i))
				return;
		//
		// Watson 1799140: discard non-protoable elements from list.
		//
		if (poUse instanceof ProtoableNode)
    		moUseList.add((ProtoableNode)poUse);
	}
	
	/**
	 * This method is called by derived classes to output the filename and line
	 * number when an error occurs during the load of an XML File.
	 *
	 * @exclude from published api.
	 */
	protected final void addXMLLoadErrorContext(int lineNumber,
			String fileName, ExFull oEx) {

		if (!StringUtils.isEmpty(fileName) && lineNumber != 0) {
			MsgFormatPos oMessage = new MsgFormatPos(
					ResId.XMLFileLineNumberLoadException);
			oMessage.format(fileName);
			oMessage.format(Integer.toString(lineNumber));
			ExFull err2 = new ExFull(oMessage);
			oEx.insert(err2, true);
		} 
		else if (!StringUtils.isEmpty(fileName)) {
			MsgFormatPos oMessage = new MsgFormatPos(ResId.XMLFileLoadException);
			oMessage.format(fileName);
			ExFull err2 = new ExFull(oMessage);
			oEx.insert(err2, true);
		} 
		else if (lineNumber > 0) {
			MsgFormatPos oMessage = new MsgFormatPos(
					ResId.XMLLineNumberLoadException);
			oMessage.format(Integer.toString(lineNumber));
			ExFull err2 = new ExFull(oMessage);
			oEx.insert(err2, true);
		}
	}

	/**
	 * This method is called by derived classes to output the filename and
	 * line number when an error occurs during the load of an XML File.
	 * @exclude from published api.
	 */
	public void addXMLLoadErrorContext(Node oSrc, ExFull oEx) {

		Node oNode = oSrc;
		// If possible give more context by providing the filename and
		// the line number of the offending operation.
		String sFileName = oNode.getOwnerDocument().getParseFileName();
		if (sFileName == null)
			sFileName = "";
		// Could be a #text node
		if (oNode instanceof Chars) {
			oNode = oNode.getXMLParent();
		}
		String sLineNumber = "";
		if (oNode instanceof Element) {
			int nLineNumber = ((Element) oNode).getLineNumber();
			sLineNumber = Integer.toString(nLineNumber);
		}

		if (sFileName.length() > 0 && sLineNumber.length() > 0) {
			MsgFormatPos oMessage = new MsgFormatPos(
					ResId.XMLFileLineNumberLoadException);
			oMessage.format(sFileName);
			oMessage.format(sLineNumber);
			ExFull pErr2 = new ExFull(oMessage);
			oEx.insert(pErr2, true);
		} else if (sFileName.length() > 0) {
			MsgFormatPos oMessage = new MsgFormatPos(ResId.XMLFileLoadException);
			oMessage.format(sFileName);
			ExFull pErr2 = new ExFull(oMessage.resId(), oMessage.toString());
			oEx.insert(pErr2, true);
		} else if (sLineNumber.length() > 0) {
			MsgFormatPos oMessage = new MsgFormatPos(
					ResId.XMLLineNumberLoadException);
			oMessage.format(sLineNumber);
			ExFull pErr2 = new ExFull(oMessage.resId(), oMessage.toString());
			oEx.insert(pErr2, true);
		}
	}
	
	/*
	 * Does this model notify protoable nodes of any changes from their source nodes?
	 * this should be TRUE only for models that are allowed to change at runtime.
	 * In Designer the template model should turn this on
	 * In Acrobat and XMLFormAgent/PresentationAgent the form model should have this on.
	 * @return true if this model can be modified at runtime, otherwise false.
	 * @exclude from published api. 
	 */
	boolean allowUpdates() {
		return mbAllowUpdates;
	}

	/**
	 * Sets whether this model can be updated at runtime.
	 * This should only be true for Designer.
	 * @param bAllowUpdates - true if this model can be modified at runtime, otherwise false.
	 * @exclude from published api.
	 */
	public void allowUpdates(boolean bAllowUpdates) {
		mbAllowUpdates = bAllowUpdates;
	}

	/**
	 * Clears the model's current list of errors.
	 */
	public void clearErrorList() {
		mErrorList.clear();
		mErrorContextList.clear();
	}

	/**
	 * @see Element#clone(Element)
	 *
	 * @exclude from published api.
	 */
	public Element clone(Element parent, boolean bDeep) {
		// Models are not to be cloned
		MsgFormatPos oMessage = new MsgFormatPos(ResId.InvalidMethodException, getClassAtom());
		oMessage.format("clone");
		throw new ExFull(oMessage);
	}

	/**
	 * Runs through all the installed factories, calling createDOM on each, and
	 * appending those nodes to parent.
	 * @param parent
	 *            root of the XML DOM
	 *
	 * @exclude from published api.
	 */
	protected void createDOM(Element parent) {
		List factoryList = parent.getAppModel().factories();
		int count = factoryList.size();
		for (int i = 0; i < count; i++) {
			ModelFactory factory = factoryList.get(i);
			factory.createDOM(parent);
		}
	}

	/**
	 * Creates an element with the given parent, previous sibling,
	 * namespace uri and qualified name.
	 * @param parent the element's parent, if any.
     * @param prevSibling the element's previous sibling, if any.
     * @param uri the element's namespace URI. This string must be interned.
	 * @param qName the element's qualified name. This string must be interned.
	 * @return a new element conformant to our schema.
	 */
	public Element createElement(Element parent, Node prevSibling, String uri, String qName) {
		return createElement(parent, prevSibling, uri, qName, qName, null, 0, null);
	}

	/**
	 * Creates an element with the given parent, sibling, namespace uri,
	 * local name and SAX attributes.
	 * @param parent the element's parent, if any.
     * @param prevSibling the element's previous sibling, if any.
	 * @param uri the element's namespace. This string must be interned.
	 * @param localName the element's name. This string must be interned.
	 * @param qName the element's qualified name. This string must be interned.
	 * @param attributes the element's (SAX) attribute definitions.
	 * @return a new element conformant to our schema.
	 *
	 * @exclude from published api.
	 */
	public Element createElement(Element parent, Node prevSibling, String uri,
			String localName, String qName, Attributes attributes,
			int lineNumber, String fileName) {
		super.setLineNumber(lineNumber);
		Element retVal = null;
		int eTag = XFA.INVALID_ELEMENT;
		
		//
		// Ensure we use the proper model schema.
		//
		Schema oSchema = getSchema();
		Model model = null;
		if (parent != null)
			model = parent.getModel();
		
		if (parent instanceof Model) {
			model = (Model)parent;
			oSchema = model.getSchema();
		}
		else if (parent instanceof Element) {
			model = parent.getModel();
			oSchema = model.getSchema();
		}

		try {

			if (parent != null && 
				(parent.getElementClass() == XFA.INVALID_ELEMENT || 
				 parent.getElementClass() == XFA.PACKETTAG)) {
				// We hit a schema error, or an exceptional case,
				// so create a plain element node that's not
				// part of the XFA Schema
				retVal = new Element(parent, prevSibling, uri, localName,
						qName, attributes, XFA.INVALID_ELEMENT, localName);
				return retVal;
			}


			eTag = oSchema.getElementTag(uri, localName);
			if (eTag < 0) {
				//
				// If invalid then give the schema one last opportunity
				// to determine if the tag, in the context of its parent,
				// is potentially valid.  This is used by configuration in the
				// places where we allow arbitrary syntax in the config file.
				//
				eTag = oSchema.getElementTag(parent);
			}
			if (eTag < 0)
				throw new ExFull(ResId.InvalidNodeTypeException, localName);
			boolean bValid = true;
			if (parent != null && ! parent.isValidChild(eTag, 0, true, false)) {
				bValid = false;
				int eRemappedTag = remapTag(eTag);
				if (eTag != eRemappedTag && parent.isValidChild(eRemappedTag, 0, true, false)) {
					eTag = eRemappedTag;
					bValid = true;
				}
			}
			retVal = oSchema.getInstance(eTag, this, parent, prevSibling, true);

			// Validate the parent/child relationship.
			if ( ! bValid) {

				MsgFormatPos msg
						= new MsgFormatPos(ResId.InvalidChildAppendException,
														parent.getClassName());
				msg.format(localName);
				ExFull err = new ExFull(msg);

				// See if we can further qualify the error.
				// Was it a case of "wrong number of occurrences"?
				// If the schema says the relationship is ok, then it must have
				// been an occurrence problem.
				if (null != parent.getChildReln(retVal.getClassTag())) {
					ExFull err2 = new ExFull(
							ResId.OccurrenceViolationException, localName);
					err.insert(err2, true);
				}
				addXMLLoadErrorContext(lineNumber, fileName, err);

				// Mark this instance with an invalid class tag so that we'll
				// know
				// to exclude it from normal XFA Node processing.
				retVal.setClass(retVal.getClassName(), XFA.INVALID_ELEMENT);
				// TODO figure out what the error severity is
				addErrorList(err, LogMessage.MSG_WARNING, retVal);
			}
		} catch (ExFull e) {
			boolean bReportError = true;
			//ignore any element belonging to an unrecognized namespace
			if (uri != null && uri.length() > 0 && model != null) {
				if (!model.isCompatibleNS(uri)) {
					//
					// Watson #1354332
					//
					// Make sure elements in the template packet that are not in the XFA template namespace
					// are not pretty printed. Outputting elements from unrecognized namespaces without pretty
					// printing could be generalized to all models in XFAModelImpl::loadChildren, using a check with
					// isCompatibleNS, but it would mean a performance hit for every model and every element,
					// as XFAModelImpl::checkforCompatibleNS calls strncmp
					if (retVal != null)
    					retVal.inhibitPrettyPrint (true);
					bReportError = false;
				}
			}
			// We hit a schema error, so create a plain element node that's not
			// part of the XFA Schema
			
			//Bug#2940586: In case of schema error, DONOT pass in the attributes since they'll anyway be
			//created while setting DOM properties (scroll down a bit).
			retVal = new Element(parent, prevSibling, uri, localName, qName,
					null, XFA.INVALID_ELEMENT, localName);

			// TODO figure out what the error severity is
			if (bReportError) {
				addXMLLoadErrorContext(lineNumber, fileName, e);
				addErrorList(e, LogMessage.MSG_WARNING, retVal);
			}
		}
		retVal.setLineNumber(lineNumber);
		//
		// Re-classify element if the context demands it.
		//
		retVal.setClass(parent, eTag);
		retVal.setDOMProperties(uri, localName, qName, attributes);

		return retVal;
	}

	/**
	 * Create an element with the given element tag and name.
	 * @param eTag the element's tag.
	 * @param name the element's name, if known. This string must be interned.
	 * @return a new element conformant to the XFA schema.
	 */
	final public Element createElement(int eTag, String name) {
		String className = XFA.getAtom(eTag);
		Element e = createElement(null, null, null, className, className, null, 0, null);
		if (name != null && name.length()> 0)
			e.setName(name);
		return e;
	}

	/**
	 * Creates an element with the given class, name and parent.
	 * @param className the element's class name. This string must be interned.
	 * @param name the element's name, if known. This string must be interned.
	 * @param parent the element's parent.
	 * @return a new element conformant to the XFA schema.
	 */
	final public Element createElement(String className, String name, Element parent) {
		// Javaport: This method exists to provide consistency with the C++:
		// XFAModelImpl::createNode(size_t, XFANodeImpl*, jfAtom, jfAtom, jfBool)
		// That method ignores the uri parameter, resulting in a node that
		// inherits its Model's namespace.
		// This gets called from ProtoableNode.createProto
		Element e = createElement(parent, null, parent.getModel().getNS(), className, className, null, 0, null);
		if (name != null && name.length()> 0)
			e.setName(name);
		return e;
	}
	
	/**
	 * Creates an element with the given name.
	 * @param name the element's name. This string must be interned.
	 * @return a new element conformant to our schema.
	 *
	 * @exclude from published api.
	 */
	public Element createElement(String name) {
		return createElement(null, null, null, name, name, null, 0, null);
	}

    /**
     * Create an element with the given tag, parent, name and uri.
	 * @param eTag the element's tag.
	 * @param parent the element's parent.
	 * @param aName the element's name.
	 * @param aNS the element's namespace.
	 * @param bDoVersionCheck check the element's version.
     * @return a new element.
	 * @exclude from published api.
     */
	public abstract Node createNode(
			int eTag, 
			Element parent, 
			String aName /* = "" */, 
			String aNS /* = "" */, 
			boolean bDoVersionCheck /* = true */);

	/**
	 * @exclude from published api.
	 */
	final public TextNode createTextNode(Element parent, Node prevSibling,
			char[] ch, int start, int length) {
		return new TextNode(parent, prevSibling, ch, start, length);
	}

	/**
	 * Creates a text node with the given text.
	 * @param parent the node's parent, if any.
	 * @param prevSibling the node's previous sibling, if any.
	 * @param text the node's text.
	 * @return a new node conformant to our schema.
	 */
	final public TextNode createTextNode(Element parent, Node prevSibling,
			String text) {
		return new TextNode(parent, prevSibling, text);
	}

	/**
	 * Returns the node that is represented by the alias for this model.
	 *
	 * The returned model is normally the model itself, but not always.
	 * For example, while $template refers to the template model, by
	 * default, $data refers to the first child of the data model.
	 *
	 * @return an Node that corresponds to the alias for this model.
	 * @exclude from public api.
	 */
	public final Element getAliasNode() {
		if (mAliasNode == null)
			return this;
		return mAliasNode;
	}
	
	/**
	 *
	 * @exclude from published api.
	 */
	public AppModel getAppModel() {
		return mAppModel;
	}
	
	/**
	 * @exclude from public api.
	 */
	public boolean getAppModelIsTransient() {
		return mbAppModelIsTransient;
	}

	/**
	 * @exclude from published api.
	 */
	public abstract String getBaseNS();
	
	/**
	 * @exclude from published api.
	 */
	public String getCachedLocale() {
		if (StringUtils.isEmpty(msCachedLocale)) {
			LcLocale oLocale = new LcLocale(LcLocale.getLocale());
			if (! oLocale.isValid())
				oLocale = new LcLocale(LcLocale.DEFAULT_LOCALE);
			msCachedLocale = oLocale.getIsoName();
		}
		return msCachedLocale;
	}

	/**
	 * Retrieves the current node, which is the starting node for calls to
	 * resolveNode() and resolveNodes()
	 * @return The current node.
	 *
	 * @exclude from published api.
	 */
	public final Node getContext() {
		if (moSOMContext == null)
			return this;
		return moSOMContext;
	}

	/**
	 * Gets the current version of this model.
	 * @return the version number (times 10).
	 */
	public int getCurrentVersion() {
		if (mnCurrentVersion == 0)
			return getHeadVersion();
		if (mnCurrentVersion > getHeadVersion())
			return getHeadVersion();
		return mnCurrentVersion;
	}
	
	/**
	 * @exclude from published api.
	 */
	public Obj getDeltas(Node oNode) {
		return new XFAList();
	}
	
	/**
	 * Returns this model's document.
	 * @return the document node.
	 */
	public final Document getDocument() {
		return getOwnerDocument();
	}

	/**
	 * @exclude from published api.
	 */
	public Obj getDelta(Element node, String sSOM) {
		return new Delta(node, null, sSOM);
	}

	/**
	 * Gets all the context nodes that correspond to entries in the error list.
	 * 
	 * @return A list of Element objects where the load discovered a problem.
	 */
	public List getErrorContextList() {
		return mErrorContextList;
	}

	/**
	 * Gets all the errors that have been generated by this model
	 * since the last method call to clear the error list.
	 * Note that these are not fatal errors.
	 * They are typically syntax problems discovered when loading the
	 * collateral.  Some applications may choose to sift through the list
	 * and stop processing if they recognize specific problems.  However
	 * most applications should simply dump the messages into the log file
	 * and continue processing.  i.e. treat this as a list of warnings.
	 * @return the current list of {@link ExFull} error objects.
	 */
	public List getErrorList() {
		return mErrorList;
	}
	
	/**
	 * the EventManager manages xfe:script scripts and their
	 * associated events (ie. events that cause the scripts to execute)
	 * @exclude from public api.
	 */
	public EventManager getEventManager() {
		return getAppModel().getEventManager();
	}

	/**
	 * @exclude from published api.
	 */
	public Generator getGenerator() {
		return mGenerator;
	}

	/**
	 * @exclude from published api.
	 */
	public abstract String getHeadNS();

	/**
	 * @exclude from published api.
	 */
	public int getHeadVersion() {
		return Schema.XFAVERSION_HEAD;
	}

	/**
	 * @exclude from published api.
	 */
	public IDValueMap getIDValueMap() {
		return mIDValueMap;
	}
	
	/**
	 * Gets the boolean value of a particular legacy setting.
	 * @param nLegacyFlag the specific legacy setting to check
	 * @return true if the legacy setting is set, or is the default the original version.
	 * @exclude from published api.
	 */
	public boolean getLegacySetting(AppModel.LegacyMask nLegacyFlag) {
		// overridden by AppModel and TemplateModel 
		// no one should be calling this version
		assert(false);
		return false;
	}

	/**
	 * Retrieves the log messenger associated with this model.
	 * @return The log messenger.
	 *
	 * @exclude from published api.
	 */
	public LogMessenger getLogMessenger() {
		return getAppModel().getLogMessenger();
	}

	/**
	 * @see Element#getName()
	 *
	 * @exclude from published api.
	 */
	final public String getName() {
		if (mName != null)
			return mName;
		return super.getName();
	}

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

	/**
	 * Look up an XFA ID.  Since XFA IDs are only guaranteed to be unique within the scope of
	 * their model (to prevent conflicts with customer-controlled data), the lookup is done
	 * by namespace.
	 * 

* Note: jfDomDocument::getElementByXFAId handles the conversion of versioned model namespace * strings to unversioned ones. * @param aID * The node id * @return The node if found, null if not found. * * @exclude from published api. */ public Element getNode(String aID) { String aModelNamespace = getNSInternal(); Element oElement = getOwnerDocument().getElementByXFAId(aModelNamespace, aID); if (oElement == null) return null; else return oElement.getXfaPeer(); } /** * @see Element#getNS() * * @exclude from published api. */ public String getNS() { int nVersion = getCurrentVersion(); // if we have a version set use it. if (nVersion > 0) return getNS(nVersion); else if (getAppModel().getSourceBelow() == EnumAttr.SOURCEBELOW_UPDATE) return getHeadNS(); else return super.getNS(); } /** * @exclude from published api. */ protected String getNS(int nVersion) { String sNS = ""; if (nVersion > 0) { StringBuilder sNSBuf = new StringBuilder(getBaseNS()); sNSBuf.append(nVersion); sNSBuf.insert(sNSBuf.length() - 1, '.'); sNSBuf.append('/'); sNS = sNSBuf.toString().intern(); // JavaPort: Original version creates does many object allocations // double dVersion = ((double)nVersion)/10.0; // String sNSBuf = getBaseNS() + Numeric.doubleToString(dVersion, 1, false) + '/'; //sNS = sNSBuf.intern(); } return sNS; } /** * @exclude from published api. */ public String getOriginalVersion(boolean bDefault) { if (moOriginalVersion != null) { // moOriginalVersion is not connected to the tree, delete it if (moOriginalVersion.getXMLParent() == null) { moOriginalVersion = null; } else return moOriginalVersion.getData(); } if (moOriginalVersion == null && bDefault) { //This part is being handled in TemplateModel class. //Instead of current NameSpace it should return empty like it does on C++ side. return ""; //return getNS(); } return ""; } /** * Get the processing instruction that holds our version number. * @param bCreate - if true, create the PI if it doesn't exist * @param sValue - the value to use for the PI if we create it * @return the version node PI * @exclude from published api. */ public ProcessingInstruction getOriginalVersionNode(boolean bCreate, String sValue /* "" */) { if ((moOriginalVersion == null || moOriginalVersion.getXMLParent() == null) && bCreate) { moOriginalVersion = new ProcessingInstruction(null, null, STRS.ORIGINALXFAVERSION, sValue); //watson 1217329 do not append the PI if we are in the plugin, if we do we will //break document signatures. if (!ACROBAT_PLUGIN) appendChild(moOriginalVersion, false); } return moOriginalVersion; } /** * Keep track of proto Nodes and 'use' tags for quick lookup later our * @return our list of protos * * @exclude from published api. */ public List getProtoList() { return moProtoList; } /** * Return the schema definition for this model. * @return a class derived from Schema * * @exclude from published api. */ final public Schema getSchema() { return mSchema; } /** * Get the ScriptHandler associated with the specified language. * @param sLanguageName the language name (should not include any "application/x-" prefix). * @return The ScriptHandler registered for the specified language, or null if no * handler has been registered for that language. * * @exclude from published api. */ ScriptHandler getScriptHandler(String sLanguageName) { return getAppModel().getScriptHandler(sLanguageName); } /** * @exclude from published api. */ public int getSourceBelow() { return getAppModel().getSourceBelow(); } /** * @exclude from published api. */ @FindBugsSuppress(code="ES") public int getVersion(String sNS) { int nRet = 0; if (!StringUtils.isEmpty(sNS)) { // search for the template namespace version String sBaseNS = getBaseNS(); int nStart = sNS.indexOf(sBaseNS); if (nStart >= 0) { nStart = nStart + sBaseNS.length(); int nLast = sNS.lastIndexOf('/'); if (nLast > nStart) { String sNum = sNS.substring(nStart,nLast); double dValue = Double.parseDouble(sNum); nRet = (int) (dValue * 10); } } } return nRet; } /** * @exclude from public api. */ public void setXmlPeer(Node peer) { mXmlPeer = (Element)peer; } /** * @exclude from public api. */ public Node getXmlPeer() { return mXmlPeer; } /** * Initialize the symbol table that is used to optimize the * interning of Attribute values. *

* The {@link Model#intern(String)} method will work correctly * whether or not there is a SymbolTable initialized, but can * significantly speed up loading of a document from file, so it * will typically only be enabled during loading. * * @exclude from published api. */ final void initializeSymbolTable() { mSymbolTable = new SymbolTable(); } /** * Save memory used by the SymbolTable once it is no longer needed * (i.e., no longer loading a document). * * @exclude from published api. */ final void disposeSymbolTable() { mSymbolTable = null; } /** * Load a node from one DOM into another. * In the C++ implementation, this would load a node from the XML DOM into the XFA DOM. * In this implementation, this method exists to handle the case of a form packet used * to restore state that has been parsed into a generic packet, but now needs to be loaded * into an XFA FormModel DOM. * @param parent the XFA Form parent of the node that is to be loaded * @param node the generic packet node that is to be loaded * @param genTag * @return the loaded node * @exclude from published api. */ protected Node doLoadNode(Element parent, Node node, Generator genTag) { Model model = parent.getModel(); if (node instanceof Chars) { return model.createTextNode(parent, null, ((Chars)node).getData()); } assert node instanceof Element; Element element = (Element)node; Element newNode; try { int eTag = XFA.getElementTag(element.getLocalName()); if (eTag == -1) throw new ExFull(new MsgFormatPos(ResId.InvalidNodeTypeException, element.getLocalName())); newNode = getSchema().getInstance(eTag, this, parent, null, true); newNode.setDOMProperties(element.getNS(), element.getLocalName(), element.getXMLName(), null); } catch (ExFull exception) { // This method is called by derived classes to output the // filename and line number when an error occurs during the // load of an XML File. addXMLLoadErrorContext(node, exception); addErrorList(exception, LogMessage.MSG_WARNING, null); return null; } doLoadAttributes(element, newNode); // iterate though all the children and load them Node nextChild; for (Node child = node.getFirstXMLChild(); child != null; child = nextChild) { nextChild = preLoadNode(newNode, child, genTag); // preLoad could remove the child if (child.getXFAParent() == null) break; if (child instanceof Element || child instanceof Chars) { doLoadNode(newNode, child, genTag); } } if (newNode instanceof Content && newNode.getFirstXFAChild() == null) new TextNode(newNode, null, ""); return newNode; } /** * Load the attributes from one node to another. * This method was extracted from {@link #doLoadNode(Element, Node, Generator)} * @param fromElement the element that attributes should be copied from * @param toElement the elemen that attributes should be copied to * @exclude from published api. */ protected void doLoadAttributes(Element fromElement, Element toElement) { final int numAttrs = fromElement.getNumAttrs(); for (int i = 0; i < numAttrs; i++) { Attribute attr = fromElement.getAttr(i); String aName = attr.getLocalName(); // JavaPort: This is different in Java since in C++, any xsi:nil attribute will have // already been "copied" to the loaded node because it was peered with an XML DOM element. // Ignore XSI nil attr; allow the node to control the use of the attribute. // (Watson 1321484). if (attr.isXSINilAttr()) { toElement.updateAttribute(attr); continue; } // load Attribute checks for eval attributes // check for proto-related attributes boolean bHandled = saveProtoInformation(toElement, aName, i == 0); // Nothing we recognize yet? Check if it fits our schema. if (!bHandled) { int eTag = XFA.getAttributeTag(aName); if (eTag == -1 || !toElement.isValidAttr(eTag, true, null)) { if (!attr.isNameSpaceAttr()) { //ignore the namespace attr MsgFormatPos message = new MsgFormatPos(ResId.InvalidAttributeLoadException); message.format(aName); message.format(fromElement.getName()); message.format(fromElement.getLineNumber()); addErrorList(new ExFull(message), LogMessage.MSG_WARNING, null); } } else { // Copy the Attribute - we don't need to duplicate since Attributes are immutable toElement.setAttribute(attr, eTag); } } } } /** * Determine if a specified namespace string is compatible with the * namespace of this model. Essentially this determines if the two * namespaces are equivalent (though the strings that represent them may not * be identical). * @param aNS * The namespace to compare. * * @exclude from published api. */ public boolean isCompatibleNS(String aNS) { return checkforCompatibleNS(aNS,getBaseNS()); } /** * @exclude from published api. */ public final boolean isContainer() { return true; } /** * @exclude from published api. */ public final boolean isLoading() { return mbLoading; } /** * @exclude from published api. */ protected final void isLoading(boolean bIsLoading) { mbLoading = bIsLoading; } /** * @exclude from published api. */ public boolean isVersionCompatible(int nVersion, int nTargetVersion) { return nVersion <= nTargetVersion; } /** * @exclude from published api. */ public void loadNode(Element parent, Node node, Generator genTag) { // // Search for a model factory which can initialize itself // on this node. // if (parent instanceof AppModel) { AppModel appModel = (AppModel)parent; List factoryList = appModel.factories(); int nFactoryCount = factoryList.size(); for (int i = 0; i < nFactoryCount; i++) { ModelFactory factory = (ModelFactory) factoryList.get(i); if (factory.isRootNode((AppModel)node, null, node.getName())) { // // Strip any unneccesary white space before processing // the node. We do not strip white space if any children // that are text or cdata section node's have // non-whitespace values. // boolean stripWhiteSpace = true; Node oNode = node.getFirstXMLChild(); while (oNode != null) { if (oNode instanceof TextNode) { if (! ((TextNode) oNode).isXMLSpace()) { stripWhiteSpace = false; break; } } else if (oNode instanceof Chars) { if (! ((Chars) oNode).isXMLSpace()) { stripWhiteSpace = false; break; } } oNode = oNode.getNextXMLSibling(); } // watson bug 2324810, if we remove the whitespace node while incremental loading it can // cause problems for XML parser which may be holding references to these whitespace nodes. if (stripWhiteSpace && node instanceof Element && !node.getOwnerDocument().isIncrementalLoad()) { ((Element) node).removeWhiteSpace(); } Node oParentNode = parent; // // first check to see if a model already exists. // If so, call add, not loadChildren // Model oNewModel = factory.findModel(oParentNode); if (oNewModel == null) { oNewModel = factory.newModel(appModel, node, null, node.getName(), "", null); // JavaPort: This is incomplete. // oNewModel.domDocument(node.getOwnerDocument()); // oNewModel.scanForOriginalVersionNode(node); // load all our children // oNewModel.loadChildren(node, genTag); } else { // load all our children // JavaPort: This is incomplete. // oNewModel.add(node, genTag); } oNewModel.postLoad(); return; } } // // Everything below that's not a model is a packet. // We no longer create generic XFANodes when we load. // //getAppModel().loadPacket(node); return; } } /** * loadSpecialAttribute should be called by the loadNode method. It scans for * special attributes. Currently it only looks for event-related attributes * (for automatic execution of script). The bCheckOnly flag can be set to TRUE * to cause this routine to do nothing except return whether or not the * attribute would be handled. * @return true if the attribute is handled * @exclude */ @FindBugsSuppress(code="ES") public boolean loadSpecialAttribute(Attribute attr, String aLocalName, Element element, boolean bCheckOnly) { // Ignore XSI nil attr; allow the node to control the use of the attribute. if (attr.isXSINilAttr()) return true; // check namespace String ns = attr.getNS(); if (ns == null || !attr.getNS().startsWith(STRS.XFAEVENTSNS)) return false; if (aLocalName == XFA.SCRIPT) { if (bCheckOnly) return true; EventManager tm = getEventManager(); String sEventName = element.getEvent().trim(); if (!StringUtils.isEmpty(sEventName)) { // parse sEvent (individual events are separated by blanks). // just look at the first event... we use to be able to wait for // multiple events to happen but this functionality has been dropped int index = sEventName.indexOf(' '); if (index != -1) sEventName = sEventName.substring(0, index); assert !StringUtils.isEmpty(sEventName); if (StringUtils.isEmpty(sEventName)) return true; String sEventContext = "$"; index = sEventName.indexOf(':'); if (index != -1) { sEventContext = sEventName.substring(0, index); sEventName = sEventName.substring(index + 1); } int nEventID = tm.getEventID(sEventName); ScriptDispatcher sd = new ScriptDispatcher(element, sEventContext, nEventID, tm, element.getEventScript(null), element.getEventContentType(null)); tm.registerEvents(sd); } return true; } else if (aLocalName == XFA.EVENT) { return true; } else if (aLocalName == XFA.CONTENTTYPE) { return true; } return false; } /** * loadSpecialNode should be called by the loadNode method. It checks * for special nodes. Currently it only looks for event-related nodes * (for automatic execution of script). * @return true if the node is handled * @exclude from published api. */ public boolean loadSpecialNode(Element parent, Node node, boolean bCheckOnly) { if (!(node instanceof Element)) return false; Element element = (Element)node; String aNodeLocalName = element.getLocalName(); if (aNodeLocalName != XFA.SCRIPT) return false; // Having efficiently checked that the node name is not "script", // do a more costly check of the namespace. String ns = element.getNS(); if (ns == null || !element.getNS().startsWith(STRS.XFAEVENTSNS)) return false; if (bCheckOnly) return true; if (element.getNumAttrs() != 0) { EventManager tm = getEventManager(); final int len = element.getNumAttrs(); for (int i = 0; i < len; i++) { Attribute attr = element.getAttr(i); if (attr.getLocalName() != XFA.EVENT) continue; // check namespace if (!attr.getNS().startsWith(STRS.XFAEVENTSNS)) continue; String sEventName = attr.getAttrValue().trim(); // parse sEvent (individual events are separated by blanks). // just look at the first event... we used to be able to wait for // multiple events to happen but this functionality has been dropped int index = sEventName.indexOf(' '); if (index != -1) sEventName = sEventName.substring(0, index); assert !StringUtils.isEmpty(sEventName); if (StringUtils.isEmpty(sEventName)) continue; String sEventContext = "$"; index = sEventName.indexOf(':'); if (index != -1) { sEventContext = sEventName.substring(0, index); sEventName = sEventName.substring(index + 1); } int nEventID = tm.getEventID(sEventName); ScriptDispatcher sd = new ScriptDispatcher(parent, sEventContext, nEventID, tm, parent.getEventScript(element), parent.getEventContentType(element)); tm.registerEvents(sd); break; } } return true; } /** * Load and append the XML document to pParent * @param parent the parent node to the xml content being loaded * @param is XML document * @param bIgnoreAggregatingTag - TRUE if the root node of the loaded * XML should be ignored. In this case, the children of the root node * will be appended to pParent. FALSE if the root node * itself will be appended to pParent. * @param eReplaceContent ReplaceContent.AllContent if all XFA and non-XFA DOM content * under e is to be replaced with the result of the load. ReplaceContent.XFAContent * if XFA content only is to be cleared before adding the result of the load. * ReplaceContent.None if the result of the load is to be appended to the content under e * without clearing any existing content. * * @exclude from public api. */ @FindBugsSuppress(code="ES") protected void loadXMLImpl(Element parent, InputStream is, boolean bIgnoreAggregatingTag, ReplaceContent eReplaceContent) { if (parent instanceof DualDomNode && !(parent instanceof Model || parent instanceof Packet)) { assert false; // JAVAPORT_DATA: in order to support loadXML correctly, we load into an existing // model in-place. This is in contrast to the C++ where the XML is parsed into // an XML DOM, then loaded into the XFA DOM. Any DualDomModel implementations // must override this method. MsgFormatPos msg = new MsgFormatPos(ResId.UnsupportedOperationException); msg.format("loadXML"); msg.format(""); throw new ExFull(msg); } if (eReplaceContent == ReplaceContent.AllContent) { Node child = parent.getFirstXMLChild(); while (child != null) { Node nextChild = child.getNextXMLSibling(); child.remove(); child = nextChild; } } else if (eReplaceContent == ReplaceContent.XFAContent) { Node child = parent.getFirstXFAChild(); while (child != null) { Node nextChild = child.getNextXFASibling(); child.remove(); child = nextChild; } } Document doc = getDocument(); doc.setContext(this, parent, bIgnoreAggregatingTag); doc.load(is, null, false); // call virtual function to reset any cached data parent.resetPostLoadXML(); // resolve protos resolveProtos(false); // Watson 1915148: for new documents, ensure we track dependencies for loadXML. AppModel appModel = getAppModel(); if (appModel != null && !appModel.getLegacySetting(AppModel.XFA_LEGACY_V29_SCRIPTING)) // Watson 1928585: Pass pParent, not NULL, so that peers don't crash. parent.notifyPeers(DESCENDENT_VALUE_CHANGED, "", parent); } /** * @exclude from published api. */ public boolean loadRootAttributes() { return false; } /** * @exclude from published api. */ public void modelCleanup(Node node) { int nodeTag = node.getClassTag(); if (node instanceof ProtoableNode) { ProtoableNode proto = (ProtoableNode)node; // Don't reduce fragment references - we may have a default override to a non-default fragment property. // Cleaning up defaults would potentially lose local overrides. if (proto.hasExternalProto()) return; // Watson 1592035: Ditto for proto references, for the same reason as above. if (proto.hasProto()) return; } // Don't attempt to reduce rich text nodes. // They may have non-standard attributes. if (XFA.RICHTEXTNODETAG == nodeTag || XFA.XMLMULTISELECTNODETAG == nodeTag || XFA.INVALID_ELEMENT == nodeTag) return; if (node instanceof TextNode) nodeCleanup(node, false, false); // If there are comments or processing instructions, don't clean up. if (!(node instanceof Element)) return; Element pNode = (Element) node; int nChildren = pNode.getXFAChildCount(); for (int i = nChildren; i > 0; i--) { Node pChild = pNode.getXFAChild(i - 1); // process the grandchildren first. When that's done, this // node may end up empty as well. modelCleanup(pChild); } boolean bHasNonDefaultAttributes = false; try { for (int j = pNode.getNumAttrs(); j > 0; j--) { String aName = pNode.getAttrName(j - 1); String sVal = pNode.getAttrVal(j - 1); String aNS = pNode.getAttrNS(j-1); if (aNS == "") { aNS = getNS(); } //Certain attributes we do not want to cleanup int eTag = getSchema().getAttributeTag(aNS, aName); if (eTag != -1 && doAttributeCleanup(pNode, eTag, sVal)) { pNode.removeAttr(j - 1); } else { bHasNonDefaultAttributes = true; } } } catch (ExFull e) { // If we encounter attributes that don't conform to the schema, // there will // be an exception thrown. We don't want to stop processing in this // case... } // if (pNode.getLocalName() == XFA.EXTRAS || // pNode.getLocalName() == XFA.DESC || // pNode.getLocalName() == XFA.AREA ) { // nodeCleanup(pNode, bHasNonDefaultAttributes, false); // return; // } Element pParent = pNode.getXFAParent(); int parentTag = pParent.getClassTag(); if (pNode.getFirstXFAChild() == null && !bHasNonDefaultAttributes) { ChildReln pSchema = pParent.getChildReln(nodeTag); if (pSchema.getOccurrence() == ChildReln.zeroOrOne) { // Call the virtual method to see if the model can do any // schema-specific cleanup nodeCleanup(pNode, bHasNonDefaultAttributes, false); return; } if (pSchema.getOccurrence() == ChildReln.zeroOrMore) { if ((parentTag == XFA.BORDERTAG) && (nodeTag == XFA.CORNERTAG)) { nodeCleanup(pNode, bHasNonDefaultAttributes, false); return; } } if (pSchema.getOccurrence() == ChildReln.oneOfChild) { // watson 1081879: the UI oneOfChild tells us what the object // is... // it should never be stripped out and should never be transient // (if transient - won't get saved - and value get stipped out // we have no way telling what kind of object it is) if ((parentTag == XFA.UITAG) && (pNode.isDefault(false)) && (nodeTag != XFA.DEFAULTUITAG)) { pNode.makeNonDefault(false); return; } if ((parentTag != XFA.UITAG) && // Don't process #text data... (pNode instanceof Element) && (pParent.defaultElement() == nodeTag)) { nodeCleanup(pNode, bHasNonDefaultAttributes, false); return; } } return; } nodeCleanup(pNode, bHasNonDefaultAttributes, pNode.getFirstXFAChild() != null); } /** * @exclude from published api. */ public void nodeCleanup(Node pNode, boolean bHasAttrs, boolean bHasChildren) { // To be implemented by derived models. } /** * Return TRUE if the given attribute can be removed, aka cleaned up from a given node. * Return FALSE otherwise. * @exclude from published api. */ public boolean doAttributeCleanup(Node node, int eAttributeTag, String sAttrValue) { // If there's a colon character inside, ignore the attribute. // it's likely something like xmlns:xfa or xfa:APIVersion or something else we want // to ignore. // We also want to ignore activity because 'click' is now the default (need a default // value for enum validation on load) and it look strange to strip it from Element pNode = (Element) node; if (eAttributeTag != XFA.XMLNSTAG && eAttributeTag != XFA.ACTIVITYTAG && eAttributeTag != XFA.COMMITONTAG && eAttributeTag != XFA.HIGHLIGHTTAG && null != pNode && pNode.isValidAttr(eAttributeTag, false, null) && // Check for valid schema. defaultAttribute() will throw if it's not valid. pNode.newAttribute(eAttributeTag, sAttrValue).toString().equals(pNode.defaultAttribute(eAttributeTag).toString())) { return true; //ok to cleanup this attribute } return false; //do not cleanup this attribute } /** * @exclude from published api. */ public void normalizeNameSpaces() { String aNS = getNS(); normalizeNameSpaces(aNS); } /** * Helper function for normalizeNameSpaces(). * @param poNode the node to normalize. * @param aURI the new namespace. * * @exclude from published api. */ public void normalizeNameSpaces(Element poNode, String aURI) { String aNS = poNode.getNS(); if (aNS == "" || isCompatibleNS(aNS)) { // this call to createElementNS goes right to the Impl class and is thus not protected by XFAPerms // we must check perms manually in this case // make sure that namespace prefix on elements used within packet are removed poNode.setNameSpaceURI(aURI, false, false, true); } if (poNode.getLocalName() != XFA.EXDATA) { Node poChild = poNode.getFirstXMLChild(); while (poChild != null) { if (poChild instanceof Element) { normalizeNameSpaces((Element)poChild, aURI); } poChild = poChild.getNextXMLSibling(); } } } /** * Walks through the XFA DOM and * normalizes the namespaces of all the nodes. * * @param nTargetVersion * the version of the schema desired. One of * {@link Schema#XFAVERSION_10 XFAVERSION_10}, ... * {@link Schema#XFAVERSION_HEAD XFAVERSION_HEAD}. * @param oResult * a list of {@link NodeValidationInfo NodeValidationInfo} objects: * invalid children, attributes and attribute values based upon the * target version. If this model is not a valid child of its * parent, this model will be the first entry of oResult. * If oResult is not null, this method will ensure all child nodes * attributes and attribute values are valid for given the target * version. * @return true if successful, else false. * */ public boolean normalizeNameSpaces(int nTargetVersion, List oResult) { boolean bRet = false; // if the target version is >= the current don't need to call // validateSchema if (nTargetVersion >= getCurrentVersion()) bRet = true; // else validate the schema else if (oResult != null) bRet = validateSchema(nTargetVersion, Schema.XFAAVAILABILITY_ALL, true, oResult); else bRet = true; // manual if (bRet) { // If the target version is greater than the head version for // a particular model, then set the target version to the head. if (nTargetVersion > getHeadVersion()) nTargetVersion = getHeadVersion(); setCurrentVersion(nTargetVersion); normalizeNameSpaces(); } return bRet; } /** * @exclude from published api. */ protected void normalizeNameSpaces(String aNewNS) { String sNewNS = aNewNS; // update the version number int nVersion = getVersion(sNewNS); setCurrentVersion(nVersion); // watson bug 1470736 : We used to have the rest of this method wrapped // in a #ifndef ACROBAT_PLUGIN (see watson 1229534). This is no longer // needed as we now maintain the current version of the document (we don't // always update it to the head version like in the past). String sOldNS = getOriginalVersion(true); String aNS = getNS(); if (aNS == "" || isCompatibleNS(aNS)) { // make sure that namespace prefix on elements used within packet are removed setNameSpaceURI(aNewNS, false, false, true); } for (Node child = getFirstXMLChild(); child != null; child = child.getNextXMLSibling()) { if (child instanceof Element) { normalizeNameSpaces((Element) child, aNewNS); } } if (moOriginalVersion == null && !StringUtils.isEmpty(sOldNS) && !sOldNS.equals(sNewNS)) { // when designer changes the target version of a new document the call normalize namespaces // however they don't want the originalXFAVersion PI created. // if we don't do this all new documents have the incorrect version. if (getAppModel().updateOriginalVersion()) getOriginalVersionNode(true, sOldNS); } mnOriginalVersion = 0; } /** * postLoad is called after we've stopped receiving DOM events to load the * DOM * * @exclude from published api. */ abstract protected void postLoad(); /** * Preprocess the node. The node may be removed. * @return the next XML sibling to be processed, or null if * all siblings have been processed. * * @exclude from published api. */ public Node preLoadNode( Element parent, Node node, Generator genTag) { Node nextSibling = node.getNextXMLSibling(); // search for the PI this corresponds to the // original xfa namespace used. if (node instanceof ProcessingInstruction) { // todo cs move into the xtg namespance if (node.getName() == STRS.ORIGINALXFAVERSION) { //Bug#2940586: //Due to difference in parsing mechanism of C++ and Java, it may be possible that moOriginalVersion //has already been created and appended as a child of Template Model. //Now that actual version is found, remove the previously added PI and update originalXFAVersionPI { if (moOriginalVersion != null && moOriginalVersion.getXFAParent() == this){ this.removeChild(moOriginalVersion); } } moOriginalVersion = (ProcessingInstruction) node; } } return nextSibling; } /** * Prepares this Model to be saved. Any model maintenance that * may have been deferred is performed to ensure that the serialized form of * the Model is correct. This method is called automatically * before a Model is serialized to a stream, so it does not * normally need to be called directly. The exception is when a * Document is saved using * {@link Document#saveAs(java.io.OutputStream, Node, DOMSaveOptions)} or * {@link Document#saveXML(java.io.OutputStream, DOMSaveOptions)}. * @exclude from published api. */ public void preSave(boolean bSaveXMLScript /* = false */) { // watson bug 1757392, don't dirty the document when saving final boolean previousWillDirty = getWillDirty(); setWillDirty(false); try { if (mbNormalizedNameSpaces || isDirty()) normalizeNameSpaces(); } finally { setWillDirty(previousWillDirty); } } /** * Publish the model to an Application Storage facility. This involves * updating all external references (such as image hrefs) such that they * point to local collateral. The actual details of this are left up to the * implementer of the class derived from Model.Publisher specified in the * oPublisher parameter. * * What publish() itself does is recursively traverse the tree structure of * the model. When an external reference is encountered (for example, the * href attribute of an image node), the updateExternalRef() method is * called. The node, attribute identifier and original value of the external * reference are passed to updateExternalRef(). The derived class should * copy the collateral to the application storage area (if desired), and * should update the value of sExternalRefValue to indicate the new value * for the external reference (probably some sort of relative path). If an * external reference stored as an element value (for example, a uri node) * is encountered, updateExternalRef is called with node and the eAttribute * arg is set to XFA.TEXTNODETAG. * * Two or more instances of an identical external reference result in only * one call to updateExternalRef(), because returned values are cached and * reused. * * If called on the AppModel, this method recursively calls publish * on all contained models. * * @param publisher * an instance of a class derived from TemplateModel.Publisher. * * @exclude from published api. */ public boolean publish(Publisher publisher) { int nVersion = publisher.getTargetVersion(); int nAvailability = publisher.getTargetAvailability(); return validateSchema(nVersion, nAvailability, false, null); } /** * Indicates that the model is ready. This causes the "ready" * event to be issued so that script waiting on that event will * execute. When applied to an AppModel, this * call recursively calls ready() on each child * model (as a convenience). Some applications may choose to call * the ready() on each model individually. Calling * ready() a second time on a given model has no * effect. * @return true if ready scripts fire * @exclude from published api. */ public boolean ready(boolean bForced /* = false */) { boolean bRet = false; if (!bForced && mbReady) return bRet; EventManager tm = getEventManager(); int nId = tm.getEventID("ready"); bRet |= tm.eventOccurred(nId, getAliasNode()); mbReady = true; return bRet; } /** * @see Node#remove() * @exclude from published api. */ public void remove() { setAppModel(null); super.remove(); } /** * removes poRefNode from both the UseNodeList and UseHRefNodeList * @exclude from published api. */ void removeIDReferenceNode(Element poRefNode) { moUseList.remove(poRefNode); moUseHRefList.remove(poRefNode); } /** * remove references to a given node. * @exclude from published api. */ public void removeReferences(Node node) { if (node == null) return; // bump the ref count to ensure poNodeImpl doesn't go away on us. // JavaPort: not needed: jfObjWrap oWrapper(poNodeImpl); // depth first traversal // go through backwards in case nodes are removed // Javaport: We move forward through the children, // but be careful to pre-fetch the next child. Node child = node.getFirstXFAChild(); while (child != null) { Node nextChild = child.getNextXFASibling(); removeReferences(child); child = nextChild; } // All descendants of a model have a model pointer to that model // (and no other model), so for performance don't traverse the // children if the model isn't "this". Call verifyModel to // confirm this in debug mode. // Javaport: only Elements have a reference to their model. if (node instanceof Element) { Element element = (Element)node; if (element.getModel() != this) { if (Assertions.isEnabled) verifyModel(node, this); return; } element.setModel(null); } EventManager.EventTable eventTable = node.getEventTable(false); EventManager.resetEventTable(eventTable); // Remove all peers of this node node.clearPeers(); } // This helper function verifies the assumption that no descendants of // node have a model pointer of model. private static void verifyModel(Node node, Model model) { for (Node child = node.getFirstXFAChild(); child != null; child = child.getNextXFASibling()) { assert child.getModel() != model; verifyModel(child, model); } } /** * Remap a given tag to a new value. This gives a model a chance to remap a tag * to a new value after it's determined that the original wasn't valid in the current * context. The default implementation returns the input tag, which is a no-op. * * @param eTag the original input tag. * * @return the new remapped tag. * * @exclude from published api. */ public int remapTag(int eTag) { return eTag; } /** * @see Node#resolveNodes(String, boolean, boolean, boolean) */ public NodeList resolveNodes(String somNodes, boolean bPeek /* = false */, boolean bLastInstance /* = false */, boolean bNoProperties /* = false */, DependencyTracker oDependencyTracker /* = null */, BooleanHolder isAssociation /* = null */) { // Note that in these routines involving current context, moSOMContext is // never set to this to avoid a cyclic dependency. if (mSOMParser == null) mSOMParser = new SOMParser(null); ArrayNodeList oResult = new ArrayNodeList(); // If a resolveNodes is done on a clone, for instance, then the // model's refCount will be zero. When creating oModel (below) // the refCount will be incremented to one and when oModel goes // out of scope the model will be destroyed prematurely. // assert(poThis->refCount() != 0); mSOMParser.setOptions(bPeek, bLastInstance, bNoProperties); mSOMParser.setDependencyTracker(oDependencyTracker); if (moSOMContext == null || moSOMContext.getModel() == null) mSOMParser.resolve(this, somNodes, oResult, isAssociation); else mSOMParser.resolve(moSOMContext, somNodes, oResult, isAssociation); return oResult; } /** * @exclude from published api. */ public void resolveProtos(boolean bForceExternalProtoResolve/*false*/) { // Two passes -- first for external references (usehref), and one for internal (use). if (bForceExternalProtoResolve || getAppModel().getResolveAllExternalProtos()) { resolveList(moUseHRefList, true); } // only processes the use list if the document isn't a fragment if (!getAppModel().getIsFragmentDoc()) { resolveList(moUseList, false); } } /** * @exclude from published api. */ @FindBugsSuppress(code="ES") protected boolean saveProtoInformation(Element poXfaNode, String aLocalName, boolean bCheckParent) { // Check for any proto definitions (parent tag ) // if (bCheckParent && poXfaNode.getXFAParent() != null && poXfaNode.getXFAParent() instanceof Proto) { addProto((ProtoableNode)poXfaNode); } // Check for node with at least one attribute being use= or usehref= if (aLocalName == XFA.USE) { addUseNode(poXfaNode); return true; } else if (aLocalName == XFA.USEHREF) { addUseHRefNode(poXfaNode); return true; } return false; } /** * @throws IOException * @exclude */ public void serialize(OutputStream outStream, DOMSaveOptions options, int level, Node prevSibling) throws IOException, IOException { // Ensure that we serialize from the XML DOM side. getXmlPeer().serialize(outStream, options, level, prevSibling); } /** * Specify the node that is represented by the alias for this model. * @param aliasNode * The node that will represent this model. * * @exclude from published api. */ final public void setAliasNode(Element aliasNode) { // don't store a copy to self to avoid circular dependencies if (aliasNode == this) mAliasNode = null; else mAliasNode = aliasNode; } /** * @param pNewModel * * @exclude from published api. */ public void setAppModel(AppModel pNewModel) { mAppModel = pNewModel; } /** * @exclude from public api. */ public void setAppModelIsTransient(boolean bAppModelIsTransient) { mbAppModelIsTransient = bAppModelIsTransient; } /** * Specifies the current node, which is the starting node for calls to * resolveNode and resolveNodes. * @param node * the new current node. *

resolveNode and resolveNodes * differ from Node.resolveNode() and * Node.resolveNodes() in that the current node is * the node set via setContext(), instead of "this" * node. * * @exclude from published api. */ public void setContext(Node node) { if (node == this) moSOMContext = null; else moSOMContext = node; } /** * @exclude from published api. */ public void setCurrentVersion(int nVersion) { mnCurrentVersion = nVersion; } /** * @see Element#setDOMProperties(String, String, String, Attributes) * @exclude from published api. */ public final void setDOMProperties(String uri, String localName, String qName, Attributes attributes) { super.setDOMProperties(uri, localName, qName, attributes); // JavaPort: In C++ this is done in the loadChildren method int nVersion = getVersion(uri); if (nVersion > getHeadVersion()) { String sHostName = ""; HostPseudoModel oHost = (HostPseudoModel)getAppModel().lookupPseudoModel(STRS.DOLLARHOST); if (oHost != null) sHostName = oHost.getName(); // Fix for Watson 1706661. // Make sure the host name has a value, otherwise the message doesn't help. if (StringUtils.isEmpty(sHostName)) sHostName = Numeric.doubleToString(getHeadVersion() / 10.0, 1, false); MsgFormatPos oMessage = new MsgFormatPos(ResId.InvalidModelVersionException, localName); oMessage.format(sHostName); if (getAppModel().getSourceAbove() == EnumAttr.SOURCEABOVE_WARN) { // log an warning addErrorList(new ExFull(oMessage), LogMessage.MSG_WARNING, this); } else if (getAppModel().getSourceAbove() == EnumAttr.SOURCEABOVE_ERROR) { // throw an error and stop processing. throw new ExFull(oMessage); } } // set the version setCurrentVersion(nVersion); } /** * @exclude from published api. */ void setGenerator(Generator gen) { mGenerator = gen; } /** * @exclude from published api. */ public void setIDValueMap(IDValueMap idValueMap) { mIDValueMap = idValueMap; } /** * Specifies the log messenger that this model should use. * @exclude from published api. */ void setLogMessenger(LogMessenger oMessenger) { getAppModel().setLogMessenger(oMessenger); } /** * Set the name for this model * @param name * the model name * * @exclude from published api. */ final public void setName(String name) { mName = name; } /** * @exclude from published api. */ public void setNeedsNSNormalize(boolean bNormalize) { mbNormalizedNameSpaces = bNormalize; } /** * @exclude from published api. */ public String shortCutName() { return mShortCutName; } /** * append "_copyi" where i increments from 1 until the ID value is unique * @param psID the starting id value * @return the unique id * @exclude from published api. */ protected String uniquifyID (String psID) { String sTest = psID; int iCopyIndex = 1; while (getNode(sTest) != null) { String sCopyIndex = Integer.toString(iCopyIndex++); sTest = psID + "_copy" + sCopyIndex; } return sTest; } /** * @see Node#validateUsage(int, int, boolean) * @exclude from published api. */ public boolean validateUsage(int nXFAVersion, int nAvailability, boolean bUpdateVersion) { return validateUsage(nXFAVersion, nAvailability, false, bUpdateVersion); } /** * @see Node#validateUsageFailedIsFatal(int, int) * @exclude from published api. */ public boolean validateUsageFailedIsFatal(int nXFAVersion, int nAvailability) { return validateUsage(nXFAVersion, nAvailability, true, false); } /** * Determine if a version is allowed or if a disallowed version is a fatal error. *

* This method previously combined the two tests in a single call, and used a Scalar * parameter as a way of passing an output parameter by reference. Since this method * is called so frequently, creating the Scalar instances was a performance issue. * In this implementation, the isFatalTest parameter selects which test to perform. * @param nVersion the version to be tested. * @param isFatalTest indicates whether to perform the fatal disallowance test. * @param bUpdateVersion allow the current version to be updated if necessary. * @return if isFatalTest is false, returns true if nVersion is allowed; * if isFatalTest is true, returns true if disallowing nVersion is a fatal error. */ private boolean validateUsage(int nVersion, int nAvailability, boolean bFatalTest, boolean bUpdateVersion) { AppModel poAppModel = getAppModel(); // first check if an output version is set int eOutputBelow = poAppModel.getOutputBelow(); int nTargetOutputVer = poAppModel.getVersionRestriction(); boolean bRet = bFatalTest ? false : true; // check if source below has been set if (!isVersionCompatible(nVersion, getCurrentVersion()) && getSourceBelow() == EnumAttr.SOURCEBELOW_MAINTAIN) { if (bFatalTest) { bRet = !mbLoading; // fatal error if we are not loading } else { bRet = false; } } else if (getHeadVersion() < nVersion) { // last option check what should be done if we exceed the head version if (bFatalTest) { if (poAppModel.getSourceAbove() == EnumAttr.SOURCEABOVE_ERROR) bRet = true; } else { bRet = false; } } else if (nTargetOutputVer != 0 && !isVersionCompatible(nVersion, nTargetOutputVer) && eOutputBelow != EnumAttr.OUTPUTBELOW_UPDATE) { if (bFatalTest) { if (eOutputBelow == EnumAttr.OUTPUTBELOW_ERROR) bRet = true; } else { bRet = false; } } else if (bUpdateVersion && poAppModel.getSourceBelow() == EnumAttr.SOURCEBELOW_UPDATE && nVersion <= getHeadVersion() ) { // bump up the version if needed if (mnCurrentVersion < nVersion) //setCurrentVersion(nVersion); mnCurrentVersion = nVersion; } // todo check availability when the appModel stores this info // size_t nTargetAvail = poAppModel->getTargetAvailability() // bRet &= nTargetAvail & nAvailability return bRet; } /** * @exclude from published api. */ public void willDirtyDoc(boolean bWillDirty) { mbWillDirtyDoc = bWillDirty; } /** * @exclude from published api. */ public boolean willDirtyDoc() { return mbWillDirtyDoc; } /** * Intern a string. *

* The implementation is optimized to use a cache of previously interned symbols to avoid * the overhead of calling intern on symbols that have already been seen. This is intended * to optimize the case when we need to optimize the value of many strings where we expect * the values to be frequently repeated (e.g., Attribute values). * * @param value the string to be interned. * @return the interned string value. * * @exclude from published api. */ final String intern(String value) { return mSymbolTable != null ? mSymbolTable.internSymbol(value) : value.intern(); } /** * @exclude from published api. */ public int getOriginalXFAVersion() { if (mnOriginalVersion == 0) { // watson bug 1521181, don't cache the default value because this could change at runtime // without updating moLegacyInfo.nXFAVersion, this only happens in designer String sPIValue = getOriginalVersion(false); // If there isn't an original XFA version PI in the template and behaviorOverride is empty, then get // the current version of the template. int nVersion; if (StringUtils.isEmpty(sPIValue)) { sPIValue = getOriginalVersion(true); nVersion = getVersion(sPIValue); mnOriginalVersion = nVersion; return nVersion; } else { // read in the settings // IF YOU CHANGE A DEFAULT OR ADD A NEW VALUE HERE // MAKE SURE YOU UPDATE THE XFA_LEGACY_XX_DEFAULT setting in XFATemplateModel.h // Get the defaults depending on the XFA version. nVersion = getVersion(sPIValue); mnOriginalVersion = nVersion; } } return mnOriginalVersion; } /** * @exclude from published api. */ public void setOriginalXFAVersion(int nXFAVersion) { // if 0 restore the defaults and remove the PI if (nXFAVersion == 0) { mnOriginalVersion = 0; removeOriginalVersionNode(); } else if (nXFAVersion <= getCurrentVersion()) { // call getOriginalXFAVersion first to ensure that mnLegacyInfo are updated // this will ensure updateOriginalXFAVersionPI does the right thing getOriginalXFAVersion(); // now update the version number mnOriginalVersion = nXFAVersion; // update the PI updateOriginalXFAVersionPI(); } } /** * @exclude from published api. */ protected void updateOriginalXFAVersionPI() { String sPIValue = getNS(mnOriginalVersion); getVersion(sPIValue); ProcessingInstruction oPI = getOriginalVersionNode(false, ""); // update the pi if (oPI == null) getOriginalVersionNode(true, sPIValue); // create else oPI.setData(sPIValue); } /** * search for the PI. This corresponds to the * original xfa namespace used. Only checks immediate children of root node. * * @exclude from published api. */ // JavaPort TODO: The code calling this function is currently incomplete // (Model.loadNode()). /* private void scanForOriginalVersionNode(Node oRootNode) { assert(moOriginalVersion == null); Node oNode = oRootNode.getFirstXMLChild(); while (oNode != null) { if (oNode instanceof ProcessingInstruction) { if (oNode.getName() == STRS.ORIGINALXFAVERSION) { moOriginalVersion = (ProcessingInstruction)oNode; break; } } oNode = oNode.getNextXMLSibling(); } } */ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy