com.adobe.xfa.service.canonicalize.Canonicalize Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* 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 extends Node> 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 super Node> 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 extends Node> 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 extends Node> 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