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

com.adobe.xfa.service.canonicalize.Canonicalize Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
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.service.canonicalize;


import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map;


import com.adobe.xfa.AppModel;
import com.adobe.xfa.Attribute;
import com.adobe.xfa.Chars;
import com.adobe.xfa.Comment;
import com.adobe.xfa.Document;
import com.adobe.xfa.DOMSaveOptions;
import com.adobe.xfa.Element;
import com.adobe.xfa.Model;
import com.adobe.xfa.Node;
import com.adobe.xfa.STRS;
import com.adobe.xfa.TextNode;
import com.adobe.xfa.Element.DualDomNode;
import com.adobe.xfa.ut.ExFull;
import com.adobe.xfa.ut.FindBugsSuppress;
import com.adobe.xfa.ut.ResId;
import com.adobe.xfa.ut.StringUtils;


/**
 * A service class to canonicalize XML DOM nodes into their
 * standard representations as defined by the
 * Canonical XML
 * and the
 * Exclusive XML Canonicalization
 * specifications.
 *
 * @author Chris Solc
 */
public class Canonicalize {

	/**
	 * Canonicalization type: generic canonicalization.
	 */
	public static final int	CANONICALWITH = 1;
	/**
	 * Canonicalization type: generic canonicalization
	 * with all comments removed.
	 */
	public static final int	CANONICALWITHOUT = 2;
	/**
	 * Canonicalization type: exclusive canonicalization.
	 */
	public static final int	EXCLUSIVEWITH = 3;
	/**
	 * Canonicalization type: exclusive canonicalization
	 * with all comments removed.
	 */
	public static final int	EXCLUSIVEWITHOUT = 4;
	/**
	 * Canonicalization type: generic canonicalization
	 * without the use of context namespaces.
 	 * @exclude from published API.
	 */
	public static final int	SAVEWITH = 5;
	/**
	 * Canonicalization type: generic canonicalization
	 * without the use of context namespaces and
	 * with all comments removed.
 	 * @exclude from published API.
	 */
	public static final int	SAVEWITHOUT = 6;

	// indication to process children nodes
	private boolean				mbDocumentSubSet;

	private boolean				mbWithComments;
	private boolean				mbExclusive;
	private boolean				mbInPlace;
	private boolean 			mbLegacy_V32_Canonicalization;
	
	// the document on which the new DOM is built.
	private Document 			mRootDoc = null;

	// contains the cloned nodes to be canonicalized
	private final List		mNodeList = new ArrayList();

	// structure for keeping track of namespaces so superfluous
	// ones can be  removed
	private XMLNameSpaceStack	mNSStack;

	private List		mInclusiveNSPrefixList;

	private DOMSaveOptions		mOptions;

	private static class AttrList {
		private final List mAttrs = new ArrayList();

		AttrList() {
		}

		@FindBugsSuppress(code = "ES")
		void add(Attribute newAttr) {
			int nSize = size();
			for (int nIndex = 0; nIndex < nSize; nIndex++) {
				Attribute attr = mAttrs.get(nIndex);
				if (attr.getLocalName() == newAttr.getLocalName()) {
					if (attr.getNS() == newAttr.getNS()) {
						mAttrs.set(nIndex, newAttr);
						return;
					}
					
					if ((newAttr.getNS() == "") && 
							((attr.getPrefix() != null) && (attr.getPrefix() == ""))) {
						mAttrs.set(nIndex, newAttr); // this is considered a match
						return;
					}
				}
			}
			
			mAttrs.add(newAttr);
		}

		Attribute get(int i) {
			return mAttrs.get(i);
		}

		int size() {
			return mAttrs.size();
		}
	}

	private static class CanonNode {
		final Object mClone;
		final Object mOriginal;

		CanonNode(Object clone, Object original) {
			mClone = clone;
			mOriginal = original;
		}
	}

	/*
	 * Helper Class to keep track of the namespaces that are declared
	 * This will allow the detection of superfluous namespace declarations
	 */
	private static class XMLNameSpaceStack {

		private NameSpaceList mStack;

		static class NameSpaceList {
			final String msNameSpace;
			final List mURIList = new ArrayList();
			final NameSpaceList mNextNS;

			NameSpaceList(String nameSpace, String aUri, NameSpaceList nextNS /* =null */) {
				msNameSpace = nameSpace;
				mNextNS = nextNS;
				mURIList.add(aUri);
			}
		}

		XMLNameSpaceStack() {
			mStack = new NameSpaceList(STRS.XMLNS, "", null);
		}

		/*
		 * returns true if added else false
		 * intended to be used in a recursive traversal of a DOM tree,
		 * thus adding means narrowing the scope of namespace
		 */
		boolean push(String sNameSpace, String sNamespaceURI) {
			if (mStack == null) {
				// create a new list
				mStack = new NameSpaceList(sNameSpace, sNamespaceURI, null);
				return true;
			}
			NameSpaceList currentNS = mStack;
			while (currentNS != null) {
				// find the matching namespace
				if (currentNS.msNameSpace.equals(sNameSpace)) {
					if (currentNS.mURIList.size() > 0
					&& (currentNS.mURIList.get(currentNS.mURIList.size() - 1)).equals(sNamespaceURI)) {
						return false;	// don't add; all ready at the head
					}
					//
					// the last entry for the namespace doesn't match the
					// current URI so append it to the list
					//
					currentNS.mURIList.add(sNamespaceURI);
					return true;
				}
				currentNS = currentNS.mNextNS;
			}
			//
			// never found the namespace so create a new one and add the URI
			//
			NameSpaceList newNSList = new NameSpaceList(sNameSpace, sNamespaceURI, mStack);
			mStack = newNSList;
			return true;
		}

		/*
		 * removes the first instance of the namespace given
		 * doesn't remove namesapaces only uri's and increases
		 * the namespace scope
		 */
		void pop(String sNameSpace) {
			NameSpaceList currentNS = mStack;
			while (currentNS != null) {
				if (currentNS.msNameSpace.equals(sNameSpace)) {
					if (currentNS.mURIList.size() > 0)
						currentNS.mURIList.remove(currentNS.mURIList.size() - 1);
					break;
				}
				currentNS = currentNS.mNextNS;
			}
		}

		/*
		 * doesn't remove namesapaces only uri's and
		 * increases the namespace scope
		 */
		void clear() {
			while (mStack != null)
				mStack = mStack.mNextNS;
			mStack = null;
		}
	}


	/**
	 * Instantiates a canonicalization service.
 	 * @exclude from published API.
	 */
	public Canonicalize() {
		mbDocumentSubSet = false;
		mbWithComments = false;
		mbExclusive = false;
		mInclusiveNSPrefixList = null;
		mbInPlace = false;
		mbLegacy_V32_Canonicalization = true;
		mOptions = new DOMSaveOptions(DOMSaveOptions.RAW_OUTPUT,
							true, false, true, 3, true, false,
								"", '\0', '\0', "&", false, false, true);
		mNSStack = new XMLNameSpaceStack();
	}
	
	/**
	 * Instantiates a canonicalization service.
 	 * @exclude from published API.
	 * @param bLegacy_V32_Canonicalization legacy protected canonicalization flag
	 */
	public Canonicalize(boolean bLegacy_V32_Canonicalization) {
		mbDocumentSubSet = false;
		mbWithComments = false;
		mbExclusive = false;
		mInclusiveNSPrefixList = null;
		mbInPlace = false;
		mbLegacy_V32_Canonicalization = bLegacy_V32_Canonicalization;
		mOptions = new DOMSaveOptions(DOMSaveOptions.RAW_OUTPUT,
							true, false, true, 3, true, false,
								"", '\0', '\0', "&", false, false, true);
		mNSStack = new XMLNameSpaceStack();
	}
	
	/**
	 * Instantiates a canonicalization service.
	 * @param node a node to be canonicalized along with its children.
	 * @param bInPlace whether to perform canonicalization on the node specified
	 * @param bLegacy_V32_Canonicalization legacy protected canonicalization flag	 * 
	 * or on a copy.
	 */
	public Canonicalize(Node node, boolean bInPlace /* =false */, boolean bLegacy_V32_Canonicalization /*=true*/) {
		mbDocumentSubSet = false;
		mbWithComments = false;
		mbExclusive = false;
		mInclusiveNSPrefixList = null;
		mbInPlace = bInPlace;
		mbLegacy_V32_Canonicalization = bLegacy_V32_Canonicalization;
		mOptions = new DOMSaveOptions(DOMSaveOptions.RAW_OUTPUT,
							true, false, true, 3, true, false,
								"", '\0', '\0', "&", false, false, true);
		setData(node);
		mNSStack = new XMLNameSpaceStack();
	}
	
	/**
	 * Instantiates a canonicalization service.
	 * @param store a list of nodes to be canonicalized.
	 * @param bWithDescendants when true, canonicalize the descendants and
	 * @param bLegacy_V32_Canonicalization legacy protected canonicalization flag	 *  
	 * attributes of each node in the list; otherwise, do not.
	 */
	public Canonicalize(List store, boolean bWithDescendants, boolean bLegacy_V32_Canonicalization /*=true*/) {
		mbDocumentSubSet = false;
		mbWithComments = false;
		mbExclusive = false;
		mInclusiveNSPrefixList = null;
		mbInPlace = false;
		mbLegacy_V32_Canonicalization = bLegacy_V32_Canonicalization;		
		mOptions = new DOMSaveOptions(DOMSaveOptions.RAW_OUTPUT,
							true, false, true, 3, true, false,
								"", '\0', '\0', "&", false, false, true);
		setData(store, bWithDescendants);
		mNSStack = new XMLNameSpaceStack();
	}

	private void addNSToList(AttrList map, Object node, Document doc, boolean bExclusive, List inclusiveNSPrefixList) {

		boolean bAdd = true;
		String sPrefix = null;
		String sNS = null;
		if (node instanceof Element /* || node instanceof Document */) {
			Element elementOrDocumentNode = (Element)node;
			sPrefix = elementOrDocumentNode.getPrefix();
			sNS = elementOrDocumentNode.getNS();
		}
		else if (node instanceof Attribute) {
			Attribute attributeNode = (Attribute)node;
			sPrefix = attributeNode.getPrefix();
			sNS = attributeNode.getNS();
		}
		
		String sQualifiedName = sPrefix;

		if (StringUtils.isEmpty(sPrefix) && node instanceof Attribute)
			return;

		if (bExclusive)	{
			bAdd = false;
			if (inclusiveNSPrefixList != null) {
				for (int k = 0; k < inclusiveNSPrefixList.size(); k++) {				
					if (sQualifiedName.equals(inclusiveNSPrefixList.get(k))) {
						bAdd = true;
						break;
					}
				}
			}
		}

		if (bAdd) {
			if (StringUtils.isEmpty(sQualifiedName))
				sQualifiedName = "xmlns";
			else {
				sQualifiedName =  "xmlns:" + sQualifiedName;
			}

			// create a new attribute and update the attribute value
			Attribute oNewAttr = doc.setAttribute("", sQualifiedName, sQualifiedName, sNS);
			map.add(oNewAttr);
		}
	}

	/**
	 * Performs text normalization of a set of nodes. This implies that:
	 * 
    *
  • CDATA sections are replaced with their character content. *
  • XML declaration and document type declaration (DTD) are removed. *
  • empty elements are converted to start-end tag pairs. *
  • whitespace outside of the document element and within start and end * tags is normalized. *
  • all whitespace in character content is retained (excluding * characters removed during line feed normalization). *
  • special characters in attribute values and character content are * replaced by character references. *
  • encoding of special characters as character references in attribute * values (&, <, ", CR, NL, TAB). *
  • encoding of special characters as character references in text * (&, <, >, CR). *
  • superfluous namespace declarations are removed from each element. *
  • default attributes are added to each element. *
  • lexicographic order is imposed on the namespace declarations and * attributes of each element. *
* @param retList a list given by the caller to be populated with * canonicalized {@link Node node}s, by this method. * @param eCanonicalType the canonicalization type to use. * @param inclusiveNSPrefixList a list of namespace prefixes that are * handled as though performing non-exclusive canonicalization, * when exclusive canonicalization type is specified. * @return the document. * * @exclude from published API. */ public Document canonicalize(List retList, int eCanonicalType /* =CANONICALWITHOUT */, List inclusiveNSPrefixList /* =null */) { mbWithComments = false; mbExclusive = false; mInclusiveNSPrefixList = inclusiveNSPrefixList; if (eCanonicalType == CANONICALWITH || eCanonicalType == EXCLUSIVEWITH || eCanonicalType == SAVEWITH) mbWithComments = true; if (eCanonicalType == EXCLUSIVEWITH || eCanonicalType == EXCLUSIVEWITHOUT) mbExclusive = true; for (int i = 0; i < mNodeList.size(); i++) { mNSStack.push(STRS.LOWERXMLSTR, STRS.XMLNSURI); // // this is to remove any unneeded namespace declaration // mNSStack.push("", ""); Object node = mNodeList.get(i).mClone; if (node instanceof Chars) { // replace CData section with the encoded content String sValue = StringUtils.toXML(((Chars) node).getText(), false); CanonNode oCanon = mNodeList.get(i); mNodeList.set(i, new CanonNode(new Chars(null, null, sValue), oCanon.mOriginal)); } else if (node instanceof Comment) { // remove comment nodes if we are trimming comments if (! mbWithComments) { mNodeList.remove(i); i--; } } else if (node instanceof Document) { processTree(node); } else if (node instanceof Element) { processTree(node); // all others process normally break; } else { processTree(node); // all others process normally } mNSStack.clear(); } for (int i = 0; i < mNodeList.size(); i++) { Node currentNode = (Node)mNodeList.get(i).mClone; retList.add(currentNode); } return mRootDoc; } /** * Performs text normalization of a set of nodes. This implies that: *
    *
  • CDATA sections are replaced with their character content. *
  • XML declaration and document type declaration (DTD) are removed. *
  • empty elements are converted to start-end tag pairs. *
  • whitespace outside of the document element and within start and end * tags is normalized. *
  • all whitespace in character content is retained (excluding * characters removed during line feed normalization). *
  • special characters in attribute values and character content are * replaced by character references. *
  • encoding of special characters as character references in attribute * values (&, <, ", CR, NL, TAB). *
  • encoding of special characters as character references in text * (&, <, >, CR). *
  • superfluous namespace declarations are removed from each element. *
  • default attributes are added to each element. *
  • lexicographic order is imposed on the namespace declarations and * attributes of each element. *
* @param eCanonicalType the canonicalization type to use: one of * {@link #CANONICALWITH CANONICALWITH}, * {@link #CANONICALWITHOUT CANONICALWITHOUT}, * {@link #EXCLUSIVEWITH EXCLUSIVEWITH} or * {@link #EXCLUSIVEWITHOUT EXCLUSIVEWITHOUT}. * @param inclusiveNSPrefixList a list of namespace prefixes that are * handled as though performing non-exclusive canonicalization, * when exclusive canonicalization type is specified. * @return byte array containing the canonicalized, UTF-8 encoded, data. */ public byte[] canonicalize(int eCanonicalType /* =CANONICALWITHOUT */, List inclusiveNSPrefixList /* =null */) { ByteArrayOutputStream stream = new ByteArrayOutputStream(); canonicalize(stream, eCanonicalType, inclusiveNSPrefixList); return stream.toByteArray(); } /** * Performs text normalization of a set of nodes. This implies that: *
    *
  • CDATA sections are replaced with their character content. *
  • XML declaration and document type declaration (DTD) are removed. *
  • empty elements are converted to start-end tag pairs. *
  • whitespace outside of the document element and within start and end * tags is normalized. *
  • all whitespace in character content is retained (excluding * characters removed during line feed normalization). *
  • special characters in attribute values and character content are * replaced by character references. *
  • encoding of special characters as character references in attribute * values (&, <, ", CR, NL, TAB). *
  • encoding of special characters as character references in text * (&, <, >, CR). *
  • superfluous namespace declarations are removed from each element. *
  • default attributes are added to each element. *
  • lexicographic order is imposed on the namespace declarations and * attributes of each element. *
* @param out the stream to be populated with the the * canonicalized data. The stream will be UTF-8 encoded. * @param eCanonicalType the canonicalization type to use: one of * {@link #CANONICALWITH CANONICALWITH}, * {@link #CANONICALWITHOUT CANONICALWITHOUT}, * {@link #EXCLUSIVEWITH EXCLUSIVEWITH} or * {@link #EXCLUSIVEWITHOUT EXCLUSIVEWITHOUT}. * @param inclusiveNSPrefixList a list of namespace prefixes that are * handled as though performing non-exclusive canonicalization, * when exclusive canonicalization type is specified. */ public void canonicalize(OutputStream out, int eCanonicalType /* =CANONICALWITHOUT */, List inclusiveNSPrefixList /* =null */) { List retList = new ArrayList(); Document doc = canonicalize(retList, eCanonicalType, inclusiveNSPrefixList); if (out == null) throw new ExFull(ResId.InvalidInputObject); for (int i = 0, n = retList.size(); i < n; i++) { doc.saveAs(out, retList.get(i), mOptions); } } private void getElementAttrs(AttrList map, Element element) { Element parent = element.getXMLParent(); if (parent instanceof DualDomNode) parent = (Element)((DualDomNode)parent).getXmlPeer(); Element original = getOriginal(element); // walk up the tree and populate the chain; if (original != null) getInheritedAttrs(map, original, parent); while (element.getNumAttrs() > 0) { Attribute attribute = element.getAttr(0); // copy all the attributes into the list unless we are running in exclusive mode. boolean bAdd = true; // only ns nodes that are included in the unsuppressed list are copied over if (mbExclusive && attribute.isNameSpaceAttr()) { bAdd = false; if (mInclusiveNSPrefixList != null) { for (int k = 0; k < mInclusiveNSPrefixList.size(); k++) { String sLocalName = attribute.getLocalName(); if (sLocalName.equals(mInclusiveNSPrefixList.get(k))) { bAdd = true; break; } } } } if (bAdd) { // update the attribute value String sValue = StringUtils.toXML(attribute.getAttrValue(), true); attribute = attribute.newAttribute(sValue); // add the attribute to our list map.add(attribute); } // remove the attribute element.removeAttr(attribute.getNS(), attribute.getName()); } } private void getInheritedAttrs(AttrList map, Element original, Element target) { // no more nodes or not a subset if (original == null || !mbDocumentSubSet) return; // got the clone, need to find the original Element originalTarget = getOriginal(target); // found our target node so stop searching up if (original == originalTarget) { // once we reach the target node only search for xml:XXX attrs // since it will have had all the xml:XXX attrs copied over. // see section 2.4 Document Subsets in http://www.w3.org/TR/xml-c14n if (!mbExclusive) { int nLen = target.getNumAttrs(); for (int i = 0; i < nLen; i++) { Attribute attribute = target.getAttr(i); if (attribute.getPrefix() == STRS.LOWERXMLSTR) { // update the attribute value String sValue = StringUtils.toXML(attribute.getAttrValue(), true); Attribute clone = attribute.newAttribute(sValue); map.add(clone); } } } return; } // in exclusive mode with no prefixList just return if (mbExclusive && (mInclusiveNSPrefixList == null || mInclusiveNSPrefixList.size() == 0)) return; Element parent = original.getXMLParent(); if (parent instanceof DualDomNode) parent = (Element)((DualDomNode)parent).getXmlPeer(); getInheritedAttrs(map, parent, target); int nLen = original.getNumAttrs(); // copy all the nodes into our list for (int i = 0; i < nLen; i++) { Attribute attribute = original.getAttr(i); boolean bAdd = false; if (!mbExclusive) { if ( attribute.isNameSpaceAttr() || attribute.getPrefix() == STRS.LOWERXMLSTR) bAdd = true; } // only ns nodes that are included in the unsuppressed list are copied over if (!bAdd && mbExclusive && (mInclusiveNSPrefixList != null) && attribute.isNameSpaceAttr()) { for (int k = 0; k < mInclusiveNSPrefixList.size(); k++) { String sLocalName = attribute.getLocalName(); if (sLocalName.equals(mInclusiveNSPrefixList.get(k))) { bAdd = true; break; } } } if (bAdd) { // update the attribute value String sValue = StringUtils.toXML(attribute.getAttrValue(), true); Attribute oClone = attribute.newAttribute(sValue); map.add(oClone); } else { // not a NS node, if the node has a prefix make sure it is added // // only needed if we have xml that doesn't have the namespace nodes defined addNSToList(map, attribute, mRootDoc, mbExclusive, mInclusiveNSPrefixList); } } // only needed if we have xml that doesn't have the namespace nodes defined addNSToList(map, original, mRootDoc, mbExclusive, mInclusiveNSPrefixList); } private Element getOriginal(Element element) { for (int i = 0; i < mNodeList.size(); i++) { if (mNodeList.get(i).mClone == element) { return (Element)mNodeList.get(i).mOriginal; } } return null; } /** * Gets the save options for canonicalized data. * @return the save options for canonicalized data. * @exclude from published API. */ public DOMSaveOptions getSaveOptions() { return mOptions; } /* * process the nodes in the DOM tree * This is exclusive Canonicalization. * the namespace declarations above there root * that are pertinent are considered */ private void processTree(Object node) { if (node == null) { return; } else if (node instanceof TextNode) { // // encode text // String sValue = StringUtils.toXML(((TextNode) node).getValue(), false); ((TextNode) node).setValue(sValue,true,false); } else if (node instanceof Document) { // // process children // Node child = ((Document) node).getFirstXMLChild(); while (child != null) { Node nextSib = child.getNextXMLSibling(); // // if we are dealing with textnodes on the DOM document // they are whitespace. According to the canonicalization // spec all whitespace is removed except for line feeds // which are normalized // if (child instanceof TextNode) { Node previousSib = child.getPreviousXMLSibling(); if (previousSib == null || previousSib instanceof TextNode) { ((Document) node).removeChild(child); } else if (((TextNode) child).getValue().indexOf('\n') >= 0) { ((TextNode) child).setValue("\n",true,false); } } else { processTree(child); } child = nextSib; } child = ((Document) node).getLastXMLChild(); // // remove any trailing whitespace // while (child instanceof TextNode) { ((Document) node).removeChild(child); child = ((Document) node).getLastXMLChild(); } } else if (node instanceof Element) { Element element = (Element) node; SortedMap neededNS = new TreeMap(StringUtils.UCS_CODEPOINT_COMPARATOR); SortedMap> attList = new TreeMap>(StringUtils.UCS_CODEPOINT_COMPARATOR); // // get the element properties // String sElementPrefix = element.getPrefix(); String sElementNSURI = element.getNS(); // // ensure strings aren't null // if (sElementPrefix == null) sElementPrefix = ""; if (sElementNSURI == null) sElementNSURI = ""; // // if the namespace isn't on the Stack add it // if (mNSStack.push(sElementPrefix, sElementNSURI)) neededNS.put(sElementPrefix, sElementNSURI); AttrList attributes = new AttrList(); getElementAttrs(attributes, element); int nAttributes = attributes.size(); // // Process any attributes on this element // for (int i = 0, n = nAttributes; i < n; i++) { String sNSURI; String sNSPrefix; boolean bNSRequired = true; // // update the attribute value // Attribute attribute = attributes.get(i); // // if the attribute is a namespace declaration then // add it to the namespace list // else add it to an attribute list // if (attribute.isNameSpaceAttr()) { sNSURI = attribute.getAttrValue(); if (attribute.getPrefix() == "") sNSPrefix = ""; else sNSPrefix = attribute.getName(); if (sNSURI == null) sNSURI = ""; } else { sNSPrefix = attribute.getPrefix(); // if we don't have a prefix, then use the default namespace for attributes if (StringUtils.isEmpty(sNSPrefix)) { bNSRequired = false; sNSURI = ""; } else { sNSURI = attribute.getNS(); // ensure the string isn't null if (StringUtils.isEmpty(sNSURI)) sNSURI = ""; } // // store attribute in a sorted list // SortedMap storeAttList = attList.get(sNSURI); if (storeAttList == null) { storeAttList = new TreeMap(StringUtils.UCS_CODEPOINT_COMPARATOR); storeAttList.put(attribute.getName(), attribute); attList.put(sNSURI, storeAttList); } else { storeAttList.put(attribute.getName(), attribute); } } // // if not on the stack and it is required added to the list // of namespaces // if (bNSRequired && mNSStack.push(sNSPrefix, sNSURI) ) { neededNS.put(sNSPrefix, sNSURI); } } // // add the sorted NameSpaces first // for (Map.Entry entry : neededNS.entrySet()) { String sQualifiedName = entry.getKey(); String sNSValue = entry.getValue(); // sanity check // if code creates an xml:... attr check if the empty namespace // is used. If so skip it. if (sQualifiedName.equals("xml") && StringUtils.isEmpty(sNSValue)) continue; if (StringUtils.isEmpty(sQualifiedName)) sQualifiedName = "xmlns"; else if (! sQualifiedName.startsWith("xmlns:")) sQualifiedName = ("xmlns:" + sQualifiedName).intern(); element.setAttribute("", sQualifiedName, sQualifiedName, sNSValue, false); } // // add attributes so that they are in the right order // (sorted by NamespaceURI then by name) // for (SortedMap storeAttList: attList.values()) { for (Attribute attr: storeAttList.values()) { if (attr != null) { // JavaPort: // deviated from C++, instead of using // oElement.setAttributeNodeNS(oAttr); element.setAttribute(attr.getNS(), attr.getQName(), attr.getName(), attr.getAttrValue(), false); } } storeAttList.clear(); } attList.clear(); // // Test for the presence of children, which includes both // text content and nested elements. and process the children // Node child = element.getFirstXMLChild(); Node nextSib; while (child != null) { nextSib = child.getNextXMLSibling(); processTree(child); child = nextSib; } // // cleanup the stack so that we maintain the proper // namespace scope // for (String sNS : neededNS.keySet()) { if (sNS != null) mNSStack.pop(sNS); } neededNS.clear(); } else if (node instanceof Chars) { // // replace CData section with the encoded content // String sValue = StringUtils.toXML(((Chars) node).getText(), false); Node newText = new Chars(null, null, sValue); ((Chars) node).getXMLParent().replaceChild(newText, (Chars) node); } else if (node instanceof Comment) { // // remove comment nodes if we are trimming comments // if (! mbWithComments) { if (((Comment) node).getXMLParent() != null) ((Comment) node).getXMLParent().removeChild((Comment) node); } } } /** * Set the data for this canonicalize to work on. * @param node a node to be canonicalized along with its children. * @exclude from published API. */ public void setData(Object node) { prepareSourceDocument(node); mNodeList.clear(); if (node == null) return; mbDocumentSubSet = !(node instanceof Document); Object newNode = null; if (! mbInPlace) { AppModel app = new AppModel(null); mRootDoc = new Document(app); // clone data so that original isn't modified if (!mbDocumentSubSet) { newNode = ((Document) node).cloneNode(true); mRootDoc = (Document) newNode; } else if (node instanceof Attribute) { // JavaPort: Attributes should be immutable, so there is no need to import. newNode = node; } else if (node instanceof Node) { newNode = mRootDoc.importNode((Node)node, true); } else { throw new ExFull(ResId.UNSUPPORTED_OPERATION, "Canonicalize#setData"); } } else { if (node instanceof Node) mRootDoc = ((Node) node).getOwnerDocument(); newNode = node; } CanonNode canonNode = new CanonNode(newNode, node); mNodeList.add(canonNode); } /** * Set the data for this canonicalize to work on. * @param store a list of nodes to be canonicalized. * @param bWithDescendants when true, canonicalize the descendants and * attributes of each node in the list; otherwise, do not. * XPATH NODE-SET if bWithDescendants = false, if that is the case * reestablish hierarchy, namespace scopes and xml attributes. * @exclude from published API. */ private void setData(List store, boolean bWithDescendants) { prepareSourceDocument(store); // // Don't support in place canonicalization // at this time when passing a list of nodes // assert (! mbInPlace); mbDocumentSubSet = true; mNodeList.clear(); List workingList = new ArrayList(); AppModel appModel = new AppModel(null); mRootDoc = appModel.getDocument(); // // clone data so that original isn't modified // for (int i = 0; i < store.size(); i++) { Node node = store.get(i); // // don't process if null // if (node == null) continue; // next node in the given array Node newNode = null; Node original = node; // // if the node is the first one in the list and // it is a document node we have to clone it // if (i == 0 && node instanceof Document) { newNode = ((Document) node).cloneNode(bWithDescendants); // // use this document to perform the imports // mRootDoc = (Document) newNode; CanonNode canonNode = new CanonNode(newNode, original); if (bWithDescendants) mNodeList.add(canonNode); else workingList.add(canonNode); continue; // next node in the given array } // if bWithDescendants = true, just import // the node with a deep copy, and you are done if (bWithDescendants) { newNode = mRootDoc.importNode(node, true); CanonNode canonNode = new CanonNode(newNode, original); mNodeList.add(canonNode); continue; // next node in the given array } // // If it is an element create a new element with no attributes. // if (node instanceof Element){ newNode = mRootDoc.createElementNS(((Element) node).getNS(), ((Element) node).getName(), null); if (!mbLegacy_V32_Canonicalization){ // bug 2447873 transient and default flags ((Element)newNode).isTransient(newNode.isTransient(), false); ((Element)newNode).setDefaultFlag(newNode.isDefault(false), false); } } else newNode = mRootDoc.importNode(node, false); // // grab the real parent // // Element oOriginalParent = oOriginal.getParent(); // // If it is an attribute search for it's parent element in the // passed in node-set // (must be the last node append to the list we are creating) // // JavaPort: Attributes not supported // if (oNewNode instanceof Attribute) { // if (oWorkingList.size() > 0) { // Object oLast = oWorkingList.last(); // Object oLastOriginal // = ((CanonNode) oLast).moOriginal; // if (oOriginalParent == oLastOriginal) { // Object oElement = ((CanonNode) oLast).moClone; // // JavaPort: // // deviated from C++, instead of using // // oElement.setAttributeNodeNS(oNewNode); // Attribute oAttr = (Attribute) oNewNode; // ((Element) oElement).setAttribute(oAttr.getNS(), // oAttr.getQName(), // oAttr.getName(), // oAttr.getAttrValue(), false); // continue; // next node in the given array // } // } // } CanonNode canonNode = new CanonNode(newNode, original); workingList.add(canonNode); } while (workingList.size() > 0) { CanonNode currentNode = workingList.get(workingList.size() - 1); Node original = (Node) currentNode.mOriginal; Node originalParent = original.getXMLParent(); boolean appended = false; workingList.remove(workingList.size() - 1); // // check if any of the nodes in the list is an ancestor // of the current node // while (originalParent != null && ! appended) { for (int i = workingList.size(); i > 0; i--) { Object listItem = workingList.get(i - 1); Object originalListItem = ((CanonNode) listItem).mOriginal; if (originalParent == originalListItem) { Object target = workingList.get(i - 1).mClone; if (target instanceof Element) { ((Element) target).insertChild( (Node) currentNode.mClone, ((Node) target).getFirstXMLChild(), false); appended = true; } break; } } originalParent = originalParent.getXMLParent(); } if (! appended) mNodeList.add(0, currentNode); } } /** * Prepares the source document for canonicalization. * Since canonicalization is a form of serialization, * we need to give the source document a chance to prepare * for saving. * @param node a node to be canonicalized * @see #prepareSourceDocument(List) */ private void prepareSourceDocument(Object node) { if (node instanceof Element) { ((Element)node).getModel().preSave(false); } } /** * Prepares the source document for canonicalization. * @param nodes a list of nodes to be canonicalized * @see #prepareSourceDocument(Object) */ private void prepareSourceDocument(List nodes) { // Attempt to minimize the number of nodes that preSave is called on. // If we find a Document or AppModel in the list, then everything needs to // be processed. However, we could find a minimal list that lets us get // away with doing a preSave on a subset of the Models. List modelsToPrepare = new ArrayList(nodes.size()); for (int i = 0; i < nodes.size(); i++) { Node node = nodes.get(i); if (node instanceof Document) { ((Document)node).getAppModel().preSave(false); return; } else if (node instanceof AppModel) { ((AppModel)node).preSave(false); return; } else if (node instanceof Element) { Model model = ((Element)node).getModel(); if (model != null && !modelsToPrepare.contains(model)) modelsToPrepare.add(model); } } for (int i = 0; i < modelsToPrepare.size(); i++) modelsToPrepare.get(i).preSave(false); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy