/******************************************************************************* | |
* Copyright (c) 2008 Standards for Technology in Automotive Retail | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* David Carver - STAR - bug 224197 - initial API and implementation | |
* based on work from Apache Xalan 2.7.0 | |
*******************************************************************************/ | |
/* | |
* Copyright 1999-2004 The Apache Software Foundation. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/* | |
* $Id: ElemTemplateElement.java,v 1.3 2008/03/28 02:38:15 dacarver Exp $ | |
*/ | |
package org.eclipse.wst.xsl.core.internal.compiler.xslt10.templates; | |
import java.io.Serializable; | |
import java.util.Enumeration; | |
import java.util.Vector; | |
import javax.xml.transform.SourceLocator; | |
import javax.xml.transform.TransformerException; | |
import org.eclipse.wst.xsl.core.compiler.xslt10.res.Messages; | |
import org.eclipse.wst.xsl.core.compiler.xslt10.res.XSLTErrorResources; | |
import org.eclipse.wst.xsl.core.internal.compiler.xslt10.transformer.TransformerImpl; | |
import org.apache.xml.serializer.SerializationHandler; | |
import org.apache.xml.utils.PrefixResolver; | |
import org.apache.xml.utils.UnImplNode; | |
import org.apache.xpath.ExpressionNode; | |
import org.apache.xpath.WhitespaceStrippingElementMatcher; | |
import org.w3c.dom.DOMException; | |
import org.w3c.dom.Document; | |
import org.w3c.dom.Node; | |
import org.w3c.dom.NodeList; | |
import org.xml.sax.helpers.NamespaceSupport; | |
/** | |
* An instance of this class represents an element inside an xsl:template class. | |
* It has a single "execute" method which is expected to perform the given | |
* action on the result tree. This class acts like a Element node, and | |
* implements the Element interface, but is not a full implementation of that | |
* interface... it only implements enough for basic traversal of the tree. | |
* | |
* @see Stylesheet | |
* @xsl.usage advanced | |
*/ | |
public class ElemTemplateElement extends UnImplNode implements PrefixResolver, | |
Serializable, ExpressionNode, WhitespaceStrippingElementMatcher, | |
XSLTVisitable { | |
static final long serialVersionUID = 4440018597841834447L; | |
/** | |
* Construct a template element instance. | |
* | |
*/ | |
public ElemTemplateElement() { | |
} | |
/** | |
* Tell if this template is a compiled template. | |
* | |
* @return Boolean flag indicating whether this is a compiled template | |
*/ | |
public boolean isCompiledTemplate() { | |
return false; | |
} | |
/** | |
* Get an integer representation of the element type. | |
* | |
* @return An integer representation of the element, defined in the | |
* Constants class. | |
* @see org.apache.xalan.templates.Constants | |
*/ | |
public int getXSLToken() { | |
return Constants.ELEMNAME_UNDEFINED; | |
} | |
/** | |
* Return the node name. | |
* | |
* @return An invalid node name | |
*/ | |
@Override | |
public String getNodeName() { | |
return "Unknown XSLT Element"; | |
} | |
/** | |
* For now, just return the result of getNodeName(), which the local name. | |
* | |
* @return The result of getNodeName(). | |
*/ | |
@Override | |
public String getLocalName() { | |
return getNodeName(); | |
} | |
/** | |
* This function will be called on top-level elements only, just before the | |
* transform begins. | |
* | |
* @param transformer | |
* The XSLT TransformerFactory. | |
* | |
* @throws TransformerException | |
*/ | |
public void runtimeInit(TransformerImpl transformer) | |
throws TransformerException { | |
} | |
/** | |
* Execute the element's primary function. Subclasses of this function may | |
* recursivly execute down the element tree. | |
* | |
* @param transformer | |
* The XSLT TransformerFactory. | |
* | |
* @throws TransformerException | |
* if any checked exception occurs. | |
*/ | |
public void execute(TransformerImpl transformer) | |
throws TransformerException { | |
} | |
/** | |
* Get the owning "composed" stylesheet. This looks up the inheritance chain | |
* until it calls getStylesheetComposed on a Stylesheet object, which will | |
* Get the owning aggregated stylesheet, or that stylesheet if it is | |
* aggregated. | |
* | |
* @return the owning "composed" stylesheet. | |
*/ | |
public StylesheetComposed getStylesheetComposed() { | |
return m_parentNode.getStylesheetComposed(); | |
} | |
/** | |
* Get the owning stylesheet. This looks up the inheritance chain until it | |
* calls getStylesheet on a Stylesheet object, which will return itself. | |
* | |
* @return the owning stylesheet | |
*/ | |
public Stylesheet getStylesheet() { | |
return (null == m_parentNode) ? null : m_parentNode.getStylesheet(); | |
} | |
/** | |
* Get the owning root stylesheet. This looks up the inheritance chain until | |
* it calls StylesheetRoot on a Stylesheet object, which will return a | |
* reference to the root stylesheet. | |
* | |
* @return the owning root stylesheet | |
*/ | |
public StylesheetRoot getStylesheetRoot() { | |
return m_parentNode.getStylesheetRoot(); | |
} | |
/** | |
* This function is called during recomposition to control how this element | |
* is composed. | |
*/ | |
public void recompose(StylesheetRoot root) throws TransformerException { | |
} | |
/** | |
* This function is called after everything else has been recomposed, and | |
* allows the template to set remaining values that may be based on some | |
* other property that depends on recomposition. | |
*/ | |
public void compose(StylesheetRoot sroot) throws TransformerException { | |
resolvePrefixTables(); | |
ElemTemplateElement t = getFirstChildElem(); | |
m_hasTextLitOnly = ((t != null) | |
&& (t.getXSLToken() == Constants.ELEMNAME_TEXTLITERALRESULT) && (t | |
.getNextSiblingElem() == null)); | |
StylesheetRoot.ComposeState cstate = sroot.getComposeState(); | |
cstate.pushStackMark(); | |
} | |
/** | |
* This after the template's children have been composed. | |
*/ | |
public void endCompose(StylesheetRoot sroot) throws TransformerException { | |
StylesheetRoot.ComposeState cstate = sroot.getComposeState(); | |
cstate.popStackMark(); | |
} | |
/** | |
* Throw a template element runtime error. (Note: should we throw a | |
* TransformerException instead?) | |
* | |
* @param msg | |
* key of the error that occured. | |
* @param args | |
* Arguments to be used in the message | |
*/ | |
@Override | |
public void error(String msg, Object[] args) { | |
String themsg = Messages.createMessage(msg, args); | |
throw new RuntimeException(Messages.createMessage( | |
XSLTErrorResources.ER_ELEMTEMPLATEELEM_ERR, | |
new Object[] { themsg })); | |
} | |
/* | |
* Throw an error. | |
* | |
* @param msg Message key for the error | |
* | |
*/ | |
@Override | |
public void error(String msg) { | |
error(msg, null); | |
} | |
// Implemented DOM Element methods. | |
/** | |
* Add a child to the child list. NOTE: This presumes the child did not | |
* previously have a parent. Making that assumption makes this a less | |
* expensive operation -- but requires that if you *do* want to reparent a | |
* node, you use removeChild() first to remove it from its previous context. | |
* Failing to do so will damage the tree. | |
* | |
* @param newChild | |
* Child to be added to child list | |
* | |
* @return Child just added to the child list | |
* @throws DOMException | |
*/ | |
@Override | |
public Node appendChild(Node newChild) throws DOMException { | |
if (null == newChild) { | |
error(XSLTErrorResources.ER_NULL_CHILD, null); // "Trying to add a | |
// null child!"); | |
} | |
ElemTemplateElement elem = (ElemTemplateElement) newChild; | |
if (null == m_firstChild) { | |
m_firstChild = elem; | |
} else { | |
ElemTemplateElement last = (ElemTemplateElement) getLastChild(); | |
last.m_nextSibling = elem; | |
} | |
elem.m_parentNode = this; | |
return newChild; | |
} | |
/** | |
* Add a child to the child list. NOTE: This presumes the child did not | |
* previously have a parent. Making that assumption makes this a less | |
* expensive operation -- but requires that if you *do* want to reparent a | |
* node, you use removeChild() first to remove it from its previous context. | |
* Failing to do so will damage the tree. | |
* | |
* @param elem | |
* Child to be added to child list | |
* | |
* @return Child just added to the child list | |
*/ | |
public ElemTemplateElement appendChild(ElemTemplateElement elem) { | |
if (null == elem) { | |
error(XSLTErrorResources.ER_NULL_CHILD, null); // "Trying to add a | |
// null child!"); | |
} | |
if (null == m_firstChild) { | |
m_firstChild = elem; | |
} else { | |
ElemTemplateElement last = getLastChildElem(); | |
last.m_nextSibling = elem; | |
} | |
elem.setParentElem(this); | |
return elem; | |
} | |
/** | |
* Tell if there are child nodes. | |
* | |
* @return True if there are child nodes | |
*/ | |
@Override | |
public boolean hasChildNodes() { | |
return (null != m_firstChild); | |
} | |
/** | |
* Get the type of the node. | |
* | |
* @return Constant for this node type | |
*/ | |
@Override | |
public short getNodeType() { | |
return org.w3c.dom.Node.ELEMENT_NODE; | |
} | |
/** | |
* Return the nodelist (same reference). | |
* | |
* @return The nodelist containing the child nodes (this) | |
*/ | |
@Override | |
public NodeList getChildNodes() { | |
return this; | |
} | |
/** | |
* Remove a child. ADDED 9/8/200 to support compilation. TODO: ***** | |
* Alternative is "removeMe() from my parent if any" ... which is less well | |
* checked, but more convenient in some cases. Given that we assume only | |
* experts are calling this class, it might be preferable. It's less DOMish, | |
* though. | |
* | |
* @param childETE | |
* The child to remove. This operation is a no-op if oldChild is | |
* not a child of this node. | |
* | |
* @return the removed child, or null if the specified node was not a child | |
* of this element. | |
*/ | |
public ElemTemplateElement removeChild(ElemTemplateElement childETE) { | |
if (childETE == null || childETE.m_parentNode != this) | |
return null; | |
// Pointers to the child | |
if (childETE == m_firstChild) | |
m_firstChild = childETE.m_nextSibling; | |
else { | |
ElemTemplateElement prev = childETE.getPreviousSiblingElem(); | |
prev.m_nextSibling = childETE.m_nextSibling; | |
} | |
// Pointers from the child | |
childETE.m_parentNode = null; | |
childETE.m_nextSibling = null; | |
return childETE; | |
} | |
/** | |
* Replace the old child with a new child. | |
* | |
* @param newChild | |
* New child to replace with | |
* @param oldChild | |
* Old child to be replaced | |
* | |
* @return The new child | |
* | |
* @throws DOMException | |
*/ | |
@Override | |
public Node replaceChild(Node newChild, Node oldChild) throws DOMException { | |
if (oldChild == null || oldChild.getParentNode() != this) | |
return null; | |
ElemTemplateElement newChildElem = ((ElemTemplateElement) newChild); | |
ElemTemplateElement oldChildElem = ((ElemTemplateElement) oldChild); | |
// Fix up previous sibling. | |
ElemTemplateElement prev = (ElemTemplateElement) oldChildElem | |
.getPreviousSibling(); | |
if (null != prev) | |
prev.m_nextSibling = newChildElem; | |
// Fix up parent (this) | |
if (m_firstChild == oldChildElem) | |
m_firstChild = newChildElem; | |
newChildElem.m_parentNode = this; | |
oldChildElem.m_parentNode = null; | |
newChildElem.m_nextSibling = oldChildElem.m_nextSibling; | |
oldChildElem.m_nextSibling = null; | |
// newChildElem.m_stylesheet = oldChildElem.m_stylesheet; | |
// oldChildElem.m_stylesheet = null; | |
return newChildElem; | |
} | |
/** | |
* Unimplemented. See org.w3c.dom.Node | |
* | |
* @param newChild | |
* New child node to insert | |
* @param refChild | |
* Insert in front of this child | |
* | |
* @return null | |
* | |
* @throws DOMException | |
*/ | |
@Override | |
public Node insertBefore(Node newChild, Node refChild) throws DOMException { | |
if (null == refChild) { | |
appendChild(newChild); | |
return newChild; | |
} | |
if (newChild == refChild) { | |
// hmm... | |
return newChild; | |
} | |
Node node = m_firstChild; | |
Node prev = null; | |
boolean foundit = false; | |
while (null != node) { | |
// If the newChild is already in the tree, it is first removed. | |
if (newChild == node) { | |
if (null != prev) | |
((ElemTemplateElement) prev).m_nextSibling = (ElemTemplateElement) node | |
.getNextSibling(); | |
else | |
m_firstChild = (ElemTemplateElement) node.getNextSibling(); | |
node = node.getNextSibling(); | |
continue; // prev remains the same. | |
} | |
if (refChild == node) { | |
if (null != prev) { | |
((ElemTemplateElement) prev).m_nextSibling = (ElemTemplateElement) newChild; | |
} else { | |
m_firstChild = (ElemTemplateElement) newChild; | |
} | |
((ElemTemplateElement) newChild).m_nextSibling = (ElemTemplateElement) refChild; | |
((ElemTemplateElement) newChild).setParentElem(this); | |
prev = newChild; | |
node = node.getNextSibling(); | |
foundit = true; | |
continue; | |
} | |
prev = node; | |
node = node.getNextSibling(); | |
} | |
if (!foundit) | |
throw new DOMException(DOMException.NOT_FOUND_ERR, | |
"refChild was not found in insertBefore method!"); | |
else | |
return newChild; | |
} | |
/** | |
* Replace the old child with a new child. | |
* | |
* @param newChildElem | |
* New child to replace with | |
* @param oldChildElem | |
* Old child to be replaced | |
* | |
* @return The new child | |
* | |
* @throws DOMException | |
*/ | |
public ElemTemplateElement replaceChild(ElemTemplateElement newChildElem, | |
ElemTemplateElement oldChildElem) { | |
if (oldChildElem == null || oldChildElem.getParentElem() != this) | |
return null; | |
// Fix up previous sibling. | |
ElemTemplateElement prev = oldChildElem.getPreviousSiblingElem(); | |
if (null != prev) | |
prev.m_nextSibling = newChildElem; | |
// Fix up parent (this) | |
if (m_firstChild == oldChildElem) | |
m_firstChild = newChildElem; | |
newChildElem.m_parentNode = this; | |
oldChildElem.m_parentNode = null; | |
newChildElem.m_nextSibling = oldChildElem.m_nextSibling; | |
oldChildElem.m_nextSibling = null; | |
// newChildElem.m_stylesheet = oldChildElem.m_stylesheet; | |
// oldChildElem.m_stylesheet = null; | |
return newChildElem; | |
} | |
/** | |
* NodeList method: Count the immediate children of this node | |
* | |
* @return The count of children of this node | |
*/ | |
@Override | |
public int getLength() { | |
// It is assumed that the getChildNodes call synchronized | |
// the children. Therefore, we can access the first child | |
// reference directly. | |
int count = 0; | |
for (ElemTemplateElement node = m_firstChild; node != null; node = node.m_nextSibling) { | |
count++; | |
} | |
return count; | |
} // getLength():int | |
/** | |
* NodeList method: Return the Nth immediate child of this node, or null if | |
* the index is out of bounds. | |
* | |
* @param index | |
* Index of child to find | |
* @return org.w3c.dom.Node: the child node at given index | |
*/ | |
@Override | |
public Node item(int index) { | |
// It is assumed that the getChildNodes call synchronized | |
// the children. Therefore, we can access the first child | |
// reference directly. | |
ElemTemplateElement node = m_firstChild; | |
for (int i = 0; i < index && node != null; i++) { | |
node = node.m_nextSibling; | |
} | |
return node; | |
} // item(int):Node | |
/** | |
* Get the stylesheet owner. | |
* | |
* @return The stylesheet owner | |
*/ | |
@Override | |
public Document getOwnerDocument() { | |
return getStylesheet(); | |
} | |
/** | |
* Get the owning xsl:template element. | |
* | |
* @return The owning xsl:template element, this element if it is a | |
* xsl:template, or null if not found. | |
*/ | |
public ElemTemplate getOwnerXSLTemplate() { | |
ElemTemplateElement el = this; | |
int type = el.getXSLToken(); | |
while ((null != el) && (type != Constants.ELEMNAME_TEMPLATE)) { | |
el = el.getParentElem(); | |
if (null != el) | |
type = el.getXSLToken(); | |
} | |
return (ElemTemplate) el; | |
} | |
/** | |
* Return the element name. | |
* | |
* @return The element name | |
*/ | |
@Override | |
public String getTagName() { | |
return getNodeName(); | |
} | |
/** | |
* Tell if this element only has one text child, for optimization purposes. | |
* | |
* @return true of this element only has one text literal child. | |
*/ | |
public boolean hasTextLitOnly() { | |
return m_hasTextLitOnly; | |
} | |
/** | |
* Return the base identifier. | |
* | |
* @return The base identifier | |
*/ | |
public String getBaseIdentifier() { | |
// Should this always be absolute? | |
return this.getSystemId(); | |
} | |
/** | |
* line number where the current document event ends. | |
* | |
* @serial | |
*/ | |
private int m_lineNumber; | |
/** | |
* line number where the current document event ends. | |
* | |
* @serial | |
*/ | |
private int m_endLineNumber; | |
/** | |
* Return the line number where the current document event ends. Note that | |
* this is the line position of the first character after the text | |
* associated with the document event. | |
* | |
* @return The line number, or -1 if none is available. | |
* @see #getColumnNumber | |
*/ | |
public int getEndLineNumber() { | |
return m_endLineNumber; | |
} | |
/** | |
* Return the line number where the current document event ends. Note that | |
* this is the line position of the first character after the text | |
* associated with the document event. | |
* | |
* @return The line number, or -1 if none is available. | |
* @see #getColumnNumber | |
*/ | |
public int getLineNumber() { | |
return m_lineNumber; | |
} | |
/** | |
* the column number where the current document event ends. | |
* | |
* @serial | |
*/ | |
private int m_columnNumber; | |
/** | |
* the column number where the current document event ends. | |
* | |
* @serial | |
*/ | |
private int m_endColumnNumber; | |
/** | |
* Return the column number where the current document event ends. Note that | |
* this is the column number of the first character after the text | |
* associated with the document event. The first column in a line is | |
* position 1. | |
* | |
* @return The column number, or -1 if none is available. | |
* @see #getLineNumber | |
*/ | |
public int getEndColumnNumber() { | |
return m_endColumnNumber; | |
} | |
/** | |
* Return the column number where the current document event ends. Note that | |
* this is the column number of the first character after the text | |
* associated with the document event. The first column in a line is | |
* position 1. | |
* | |
* @return The column number, or -1 if none is available. | |
* @see #getLineNumber | |
*/ | |
public int getColumnNumber() { | |
return m_columnNumber; | |
} | |
/** | |
* Return the public identifier for the current document event. | |
* <p> | |
* This will be the public identifier | |
* | |
* @return A string containing the public identifier, or null if none is | |
* available. | |
* @see #getSystemId | |
*/ | |
public String getPublicId() { | |
return (null != m_parentNode) ? m_parentNode.getPublicId() : null; | |
} | |
/** | |
* Return the system identifier for the current document event. | |
* | |
* <p> | |
* If the system identifier is a URL, the parser must resolve it fully | |
* before passing it to the application. | |
* </p> | |
* | |
* @return A string containing the system identifier, or null if none is | |
* available. | |
* @see #getPublicId | |
*/ | |
public String getSystemId() { | |
Stylesheet sheet = getStylesheet(); | |
return (sheet == null) ? null : sheet.getHref(); | |
} | |
/** | |
* Set the location information for this element. | |
* | |
* @param locator | |
* Source Locator with location information for this element | |
*/ | |
public void setLocaterInfo(SourceLocator locator) { | |
m_lineNumber = locator.getLineNumber(); | |
m_columnNumber = locator.getColumnNumber(); | |
} | |
/** | |
* Set the end location information for this element. | |
* | |
* @param locator | |
* Source Locator with location information for this element | |
*/ | |
public void setEndLocaterInfo(SourceLocator locator) { | |
m_endLineNumber = locator.getLineNumber(); | |
m_endColumnNumber = locator.getColumnNumber(); | |
} | |
/** | |
* Tell if this element has the default space handling turned off or on | |
* according to the xml:space attribute. | |
* | |
* @serial | |
*/ | |
private boolean m_defaultSpace = true; | |
/** | |
* Tell if this element only has one text child, for optimization purposes. | |
* | |
* @serial | |
*/ | |
private boolean m_hasTextLitOnly = false; | |
/** | |
* Tell if this element only has one text child, for optimization purposes. | |
* | |
* @serial | |
*/ | |
protected boolean m_hasVariableDecl = false; | |
public boolean hasVariableDecl() { | |
return m_hasVariableDecl; | |
} | |
/** | |
* Set the "xml:space" attribute. A text node is preserved if an ancestor | |
* element of the text node has an xml:space attribute with a value of | |
* preserve, and no closer ancestor element has xml:space with a value of | |
* default. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text | |
* in XSLT Specification</a> | |
* | |
* @param v | |
* Enumerated value, either Constants.ATTRVAL_PRESERVE or | |
* Constants.ATTRVAL_STRIP. | |
*/ | |
public void setXmlSpace(int v) { | |
m_defaultSpace = ((Constants.ATTRVAL_STRIP == v) ? true : false); | |
} | |
/** | |
* Get the "xml:space" attribute. A text node is preserved if an ancestor | |
* element of the text node has an xml:space attribute with a value of | |
* preserve, and no closer ancestor element has xml:space with a value of | |
* default. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#section-Creating-Text">section-Creating-Text | |
* in XSLT Specification</a> | |
* | |
* @return The value of the xml:space attribute | |
*/ | |
public boolean getXmlSpace() { | |
return m_defaultSpace; | |
} | |
/** | |
* The list of namespace declarations for this element only. | |
* | |
* @serial | |
*/ | |
private Vector m_declaredPrefixes; | |
/** | |
* Return a table that contains all prefixes available within this element | |
* context. | |
* | |
* @return Vector containing the prefixes available within this element | |
* context | |
*/ | |
public Vector getDeclaredPrefixes() { | |
return m_declaredPrefixes; | |
} | |
/** | |
* From the SAX2 helper class, set the namespace table for this element. | |
* Take care to call resolveInheritedNamespaceDecls. after all namespace | |
* declarations have been added. | |
* | |
* @param nsSupport | |
* non-null reference to NamespaceSupport from the | |
* ContentHandler. | |
* | |
* @throws TransformerException | |
*/ | |
public void setPrefixes(NamespaceSupport nsSupport) | |
throws TransformerException { | |
setPrefixes(nsSupport, false); | |
} | |
/** | |
* Copy the namespace declarations from the NamespaceSupport object. Take | |
* care to call resolveInheritedNamespaceDecls. after all namespace | |
* declarations have been added. | |
* | |
* @param nsSupport | |
* non-null reference to NamespaceSupport from the | |
* ContentHandler. | |
* @param excludeXSLDecl | |
* true if XSLT namespaces should be ignored. | |
* | |
* @throws TransformerException | |
*/ | |
public void setPrefixes(NamespaceSupport nsSupport, boolean excludeXSLDecl) | |
throws TransformerException { | |
Enumeration decls = nsSupport.getDeclaredPrefixes(); | |
while (decls.hasMoreElements()) { | |
String prefix = (String) decls.nextElement(); | |
if (null == m_declaredPrefixes) | |
m_declaredPrefixes = new Vector(); | |
String uri = nsSupport.getURI(prefix); | |
if (excludeXSLDecl | |
&& uri | |
.equals(org.apache.xml.utils.Constants.S_XSLNAMESPACEURL)) | |
continue; | |
// System.out.println("setPrefixes - "+prefix+", "+uri); | |
XMLNSDecl decl = new XMLNSDecl(prefix, uri, false); | |
m_declaredPrefixes.addElement(decl); | |
} | |
} | |
/** | |
* Fullfill the PrefixResolver interface. Calling this for this class will | |
* throw an error. | |
* | |
* @param prefix | |
* The prefix to look up, which may be an empty string ("") for | |
* the default Namespace. | |
* @param context | |
* The node context from which to look up the URI. | |
* | |
* @return null if the error listener does not choose to throw an exception. | |
*/ | |
public String getNamespaceForPrefix(String prefix, org.w3c.dom.Node context) { | |
this.error(XSLTErrorResources.ER_CANT_RESOLVE_NSPREFIX, null); | |
return null; | |
} | |
/** | |
* Given a namespace, get the corrisponding prefix. 9/15/00: This had been | |
* iteratively examining the m_declaredPrefixes field for this node and its | |
* parents. That makes life difficult for the compilation experiment, which | |
* doesn't have a static vector of local declarations. Replaced a recursive | |
* solution, which permits easier subclassing/overriding. | |
* | |
* @param prefix | |
* non-null reference to prefix string, which should map to a | |
* namespace URL. | |
* | |
* @return The namespace URL that the prefix maps to, or null if no mapping | |
* can be found. | |
*/ | |
public String getNamespaceForPrefix(String prefix) { | |
// if (null != prefix && prefix.equals("xmlns")) | |
// { | |
// return Constants.S_XMLNAMESPACEURI; | |
// } | |
Vector nsDecls = m_declaredPrefixes; | |
if (null != nsDecls) { | |
int n = nsDecls.size(); | |
if (prefix.equals(Constants.ATTRVAL_DEFAULT_PREFIX)) { | |
prefix = ""; | |
} | |
for (int i = 0; i < n; i++) { | |
XMLNSDecl decl = (XMLNSDecl) nsDecls.elementAt(i); | |
if (prefix.equals(decl.getPrefix())) | |
return decl.getURI(); | |
} | |
} | |
// Not found; ask our ancestors | |
if (null != m_parentNode) | |
return m_parentNode.getNamespaceForPrefix(prefix); | |
// JJK: No ancestors; try implicit | |
// %REVIEW% Are there literals somewhere that we should use instead? | |
// %REVIEW% Is this really the best place to patch? | |
if ("xml".equals(prefix)) | |
return "http://www.w3.org/XML/1998/namespace"; | |
// No parent, so no definition | |
return null; | |
} | |
/** | |
* The table of {@link XMLNSDecl}s for this element and all parent | |
* elements, screened for excluded prefixes. | |
* | |
* @serial | |
*/ | |
Vector m_prefixTable; | |
/** | |
* Return a table that contains all prefixes available within this element | |
* context. | |
* | |
* @return reference to vector of {@link XMLNSDecl}s, which may be null. | |
*/ | |
public Vector getPrefixes() { | |
return m_prefixTable; | |
} | |
/** | |
* Get whether or not the passed URL is contained flagged by the | |
* "extension-element-prefixes" property. This method is overridden by | |
* {@link ElemLiteralResult#containsExcludeResultPrefix}. | |
* | |
* @see <a | |
* href="http://www.w3.org/TR/xslt#extension-element">extension-element | |
* in XSLT Specification</a> | |
* | |
* @param prefix | |
* non-null reference to prefix that might be excluded. | |
* | |
* @return true if the prefix should normally be excluded. | |
*/ | |
public boolean containsExcludeResultPrefix(String prefix, String uri) { | |
ElemTemplateElement parent = this.getParentElem(); | |
if (null != parent) | |
return parent.containsExcludeResultPrefix(prefix, uri); | |
return false; | |
} | |
/** | |
* Tell if the result namespace decl should be excluded. Should be called | |
* before namespace aliasing (I think). | |
* | |
* @param prefix | |
* non-null reference to prefix. | |
* @param uri | |
* reference to namespace that prefix maps to, which is protected | |
* for null, but should really never be passed as null. | |
* | |
* @return true if the given namespace should be excluded. | |
* | |
* @throws TransformerException | |
*/ | |
private boolean excludeResultNSDecl(String prefix, String uri) | |
throws TransformerException { | |
if (uri != null) { | |
if (uri.equals(org.apache.xml.utils.Constants.S_XSLNAMESPACEURL) | |
|| getStylesheet().containsExtensionElementURI(uri)) | |
return true; | |
if (containsExcludeResultPrefix(prefix, uri)) | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Combine the parent's namespaces with this namespace for fast processing, | |
* taking care to reference the parent's namespace if this namespace adds | |
* nothing new. (Recursive method, walking the elements depth-first, | |
* processing parents before children). Note that this method builds | |
* m_prefixTable with aliased namespaces, *not* the original namespaces. | |
* | |
* @throws TransformerException | |
*/ | |
public void resolvePrefixTables() throws TransformerException { | |
// Always start with a fresh prefix table! | |
m_prefixTable = null; | |
// If we have declared declarations, then we look for | |
// a parent that has namespace decls, and add them | |
// to this element's decls. Otherwise we just point | |
// to the parent that has decls. | |
if (null != this.m_declaredPrefixes) { | |
StylesheetRoot stylesheet = this.getStylesheetRoot(); | |
// Add this element's declared prefixes to the | |
// prefix table. | |
int n = m_declaredPrefixes.size(); | |
for (int i = 0; i < n; i++) { | |
XMLNSDecl decl = (XMLNSDecl) m_declaredPrefixes.elementAt(i); | |
String prefix = decl.getPrefix(); | |
String uri = decl.getURI(); | |
if (null == uri) | |
uri = ""; | |
boolean shouldExclude = excludeResultNSDecl(prefix, uri); | |
// Create a new prefix table if one has not already been | |
// created. | |
if (null == m_prefixTable) | |
m_prefixTable = new Vector(); | |
NamespaceAlias nsAlias = stylesheet | |
.getNamespaceAliasComposed(uri); | |
if (null != nsAlias) { | |
// Should I leave the non-aliased element in the table as | |
// an excluded element? | |
// The exclusion should apply to the non-aliased prefix, so | |
// we don't calculate it here. -sb | |
// Use stylesheet prefix, as per xsl WG | |
decl = new XMLNSDecl(nsAlias.getStylesheetPrefix(), nsAlias | |
.getResultNamespace(), shouldExclude); | |
} else | |
decl = new XMLNSDecl(prefix, uri, shouldExclude); | |
m_prefixTable.addElement(decl); | |
} | |
} | |
ElemTemplateElement parent = this.getParentNodeElem(); | |
if (null != parent) { | |
// The prefix table of the parent should never be null! | |
Vector prefixes = parent.m_prefixTable; | |
if (null == m_prefixTable && !needToCheckExclude()) { | |
// Nothing to combine, so just use parent's table! | |
this.m_prefixTable = parent.m_prefixTable; | |
} else { | |
// Add the prefixes from the parent's prefix table. | |
int n = prefixes.size(); | |
for (int i = 0; i < n; i++) { | |
XMLNSDecl decl = (XMLNSDecl) prefixes.elementAt(i); | |
boolean shouldExclude = excludeResultNSDecl(decl | |
.getPrefix(), decl.getURI()); | |
if (shouldExclude != decl.getIsExcluded()) { | |
decl = new XMLNSDecl(decl.getPrefix(), decl.getURI(), | |
shouldExclude); | |
} | |
// m_prefixTable.addElement(decl); | |
addOrReplaceDecls(decl); | |
} | |
} | |
} else if (null == m_prefixTable) { | |
// Must be stylesheet element without any result prefixes! | |
m_prefixTable = new Vector(); | |
} | |
} | |
/** | |
* Add or replace this namespace declaration in list of namespaces in scope | |
* for this element. | |
* | |
* @param newDecl | |
* namespace declaration to add to list | |
*/ | |
void addOrReplaceDecls(XMLNSDecl newDecl) { | |
int n = m_prefixTable.size(); | |
for (int i = n - 1; i >= 0; i--) { | |
XMLNSDecl decl = (XMLNSDecl) m_prefixTable.elementAt(i); | |
if (decl.getPrefix().equals(newDecl.getPrefix())) { | |
return; | |
} | |
} | |
m_prefixTable.addElement(newDecl); | |
} | |
/** | |
* Return whether we need to check namespace prefixes against and exclude | |
* result prefixes list. | |
*/ | |
boolean needToCheckExclude() { | |
return false; | |
} | |
/** | |
* Send startPrefixMapping events to the result tree handler for all | |
* declared prefix mappings in the stylesheet. | |
* | |
* @param transformer | |
* non-null reference to the the current transform-time state. | |
* | |
* @throws TransformerException | |
*/ | |
void executeNSDecls(TransformerImpl transformer) | |
throws TransformerException { | |
executeNSDecls(transformer, null); | |
} | |
/** | |
* Send startPrefixMapping events to the result tree handler for all | |
* declared prefix mappings in the stylesheet. | |
* | |
* @param transformer | |
* non-null reference to the the current transform-time state. | |
* @param ignorePrefix | |
* string prefix to not startPrefixMapping | |
* | |
* @throws TransformerException | |
*/ | |
void executeNSDecls(TransformerImpl transformer, String ignorePrefix) | |
throws TransformerException { | |
try { | |
if (null != m_prefixTable) { | |
SerializationHandler rhandler = transformer | |
.getResultTreeHandler(); | |
int n = m_prefixTable.size(); | |
for (int i = n - 1; i >= 0; i--) { | |
XMLNSDecl decl = (XMLNSDecl) m_prefixTable.elementAt(i); | |
if (!decl.getIsExcluded() | |
&& !(null != ignorePrefix && decl.getPrefix() | |
.equals(ignorePrefix))) { | |
rhandler.startPrefixMapping(decl.getPrefix(), decl | |
.getURI(), true); | |
} | |
} | |
} | |
} catch (org.xml.sax.SAXException se) { | |
throw new TransformerException(se); | |
} | |
} | |
/** | |
* Send endPrefixMapping events to the result tree handler for all declared | |
* prefix mappings in the stylesheet. | |
* | |
* @param transformer | |
* non-null reference to the the current transform-time state. | |
* | |
* @throws TransformerException | |
*/ | |
void unexecuteNSDecls(TransformerImpl transformer) | |
throws TransformerException { | |
unexecuteNSDecls(transformer, null); | |
} | |
/** | |
* Send endPrefixMapping events to the result tree handler for all declared | |
* prefix mappings in the stylesheet. | |
* | |
* @param transformer | |
* non-null reference to the the current transform-time state. | |
* @param ignorePrefix | |
* string prefix to not endPrefixMapping | |
* | |
* @throws TransformerException | |
*/ | |
void unexecuteNSDecls(TransformerImpl transformer, String ignorePrefix) | |
throws TransformerException { | |
try { | |
if (null != m_prefixTable) { | |
SerializationHandler rhandler = transformer | |
.getResultTreeHandler(); | |
int n = m_prefixTable.size(); | |
for (int i = 0; i < n; i++) { | |
XMLNSDecl decl = (XMLNSDecl) m_prefixTable.elementAt(i); | |
if (!decl.getIsExcluded() | |
&& !(null != ignorePrefix && decl.getPrefix() | |
.equals(ignorePrefix))) { | |
rhandler.endPrefixMapping(decl.getPrefix()); | |
} | |
} | |
} | |
} catch (org.xml.sax.SAXException se) { | |
throw new TransformerException(se); | |
} | |
} | |
/** | |
* The *relative* document order number of this element. | |
* | |
* @serial | |
*/ | |
protected int m_docOrderNumber = -1; | |
/** | |
* Set the UID (document order index). | |
* | |
* @param i | |
* Index of this child. | |
*/ | |
public void setUid(int i) { | |
m_docOrderNumber = i; | |
} | |
/** | |
* Get the UID (document order index). | |
* | |
* @return Index of this child | |
*/ | |
public int getUid() { | |
return m_docOrderNumber; | |
} | |
/** | |
* Parent node. | |
* | |
* @serial | |
*/ | |
protected ElemTemplateElement m_parentNode; | |
/** | |
* Get the parent as a Node. | |
* | |
* @return This node's parent node | |
*/ | |
@Override | |
public Node getParentNode() { | |
return m_parentNode; | |
} | |
/** | |
* Get the parent as an ElemTemplateElement. | |
* | |
* @return This node's parent as an ElemTemplateElement | |
*/ | |
public ElemTemplateElement getParentElem() { | |
return m_parentNode; | |
} | |
/** | |
* Set the parent as an ElemTemplateElement. | |
* | |
* @param p | |
* This node's parent as an ElemTemplateElement | |
*/ | |
public void setParentElem(ElemTemplateElement p) { | |
m_parentNode = p; | |
} | |
/** | |
* Next sibling. | |
* | |
* @serial | |
*/ | |
ElemTemplateElement m_nextSibling; | |
/** | |
* Get the next sibling (as a Node) or return null. | |
* | |
* @return this node's next sibling or null | |
*/ | |
@Override | |
public Node getNextSibling() { | |
return m_nextSibling; | |
} | |
/** | |
* Get the previous sibling (as a Node) or return null. Note that this may | |
* be expensive if the parent has many kids; we accept that price in | |
* exchange for avoiding the prev pointer TODO: If we were sure parents and | |
* sibs are always ElemTemplateElements, we could hit the fields directly | |
* rather than thru accessors. | |
* | |
* @return This node's previous sibling or null | |
*/ | |
@Override | |
public Node getPreviousSibling() { | |
Node walker = getParentNode(), prev = null; | |
if (walker != null) | |
for (walker = walker.getFirstChild(); walker != null; prev = walker, walker = walker | |
.getNextSibling()) { | |
if (walker == this) | |
return prev; | |
} | |
return null; | |
} | |
/** | |
* Get the previous sibling (as a Node) or return null. Note that this may | |
* be expensive if the parent has many kids; we accept that price in | |
* exchange for avoiding the prev pointer TODO: If we were sure parents and | |
* sibs are always ElemTemplateElements, we could hit the fields directly | |
* rather than thru accessors. | |
* | |
* @return This node's previous sibling or null | |
*/ | |
public ElemTemplateElement getPreviousSiblingElem() { | |
ElemTemplateElement walker = getParentNodeElem(); | |
ElemTemplateElement prev = null; | |
if (walker != null) | |
for (walker = walker.getFirstChildElem(); walker != null; prev = walker, walker = walker | |
.getNextSiblingElem()) { | |
if (walker == this) | |
return prev; | |
} | |
return null; | |
} | |
/** | |
* Get the next sibling (as a ElemTemplateElement) or return null. | |
* | |
* @return This node's next sibling (as a ElemTemplateElement) or null | |
*/ | |
public ElemTemplateElement getNextSiblingElem() { | |
return m_nextSibling; | |
} | |
/** | |
* Get the parent element. | |
* | |
* @return This node's next parent (as a ElemTemplateElement) or null | |
*/ | |
public ElemTemplateElement getParentNodeElem() { | |
return m_parentNode; | |
} | |
/** | |
* First child. | |
* | |
* @serial | |
*/ | |
ElemTemplateElement m_firstChild; | |
/** | |
* Get the first child as a Node. | |
* | |
* @return This node's first child or null | |
*/ | |
@Override | |
public Node getFirstChild() { | |
return m_firstChild; | |
} | |
/** | |
* Get the first child as a ElemTemplateElement. | |
* | |
* @return This node's first child (as a ElemTemplateElement) or null | |
*/ | |
public ElemTemplateElement getFirstChildElem() { | |
return m_firstChild; | |
} | |
/** | |
* Get the last child. | |
* | |
* @return This node's last child | |
*/ | |
@Override | |
public Node getLastChild() { | |
ElemTemplateElement lastChild = null; | |
for (ElemTemplateElement node = m_firstChild; node != null; node = node.m_nextSibling) { | |
lastChild = node; | |
} | |
return lastChild; | |
} | |
/** | |
* Get the last child. | |
* | |
* @return This node's last child | |
*/ | |
public ElemTemplateElement getLastChildElem() { | |
ElemTemplateElement lastChild = null; | |
for (ElemTemplateElement node = m_firstChild; node != null; node = node.m_nextSibling) { | |
lastChild = node; | |
} | |
return lastChild; | |
} | |
/** DOM backpointer that this element originated from. */ | |
transient private org.w3c.dom.Node m_DOMBackPointer; | |
/** | |
* If this stylesheet was created from a DOM, get the DOM backpointer that | |
* this element originated from. For tooling use. | |
* | |
* @return DOM backpointer that this element originated from or null. | |
*/ | |
public org.w3c.dom.Node getDOMBackPointer() { | |
return m_DOMBackPointer; | |
} | |
/** | |
* If this stylesheet was created from a DOM, set the DOM backpointer that | |
* this element originated from. For tooling use. | |
* | |
* @param n | |
* DOM backpointer that this element originated from. | |
*/ | |
public void setDOMBackPointer(org.w3c.dom.Node n) { | |
m_DOMBackPointer = n; | |
} | |
/** | |
* Compares this object with the specified object for precedence order. The | |
* order is determined by the getImportCountComposed() of the containing | |
* composed stylesheet and the getUid() of this element. Returns a negative | |
* integer, zero, or a positive integer as this object is less than, equal | |
* to, or greater than the specified object. | |
* | |
* @param o | |
* The object to be compared to this object | |
* @return a negative integer, zero, or a positive integer as this object is | |
* less than, equal to, or greater than the specified object. | |
* @throws ClassCastException | |
* if the specified object's type prevents it from being | |
* compared to this Object. | |
*/ | |
public int compareTo(Object o) throws ClassCastException { | |
ElemTemplateElement ro = (ElemTemplateElement) o; | |
int roPrecedence = ro.getStylesheetComposed().getImportCountComposed(); | |
int myPrecedence = this.getStylesheetComposed() | |
.getImportCountComposed(); | |
if (myPrecedence < roPrecedence) | |
return -1; | |
else if (myPrecedence > roPrecedence) | |
return 1; | |
else | |
return this.getUid() - ro.getUid(); | |
} | |
/** | |
* Get information about whether or not an element should strip whitespace. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* | |
* @param support | |
* The XPath runtime state. | |
* @param targetElement | |
* Element to check | |
* | |
* @return true if the whitespace should be stripped. | |
* | |
* @throws TransformerException | |
*/ | |
public boolean shouldStripWhiteSpace(org.apache.xpath.XPathContext support, | |
org.w3c.dom.Element targetElement) throws TransformerException { | |
StylesheetRoot sroot = this.getStylesheetRoot(); | |
return (null != sroot) ? sroot.shouldStripWhiteSpace(support, | |
targetElement) : false; | |
} | |
/** | |
* Get information about whether or not whitespace can be stripped. | |
* | |
* @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT | |
* Specification</a> | |
* | |
* @return true if the whitespace can be stripped. | |
*/ | |
public boolean canStripWhiteSpace() { | |
StylesheetRoot sroot = this.getStylesheetRoot(); | |
return (null != sroot) ? sroot.canStripWhiteSpace() : false; | |
} | |
/** | |
* Tell if this element can accept variable declarations. | |
* | |
* @return true if the element can accept and process variable declarations. | |
*/ | |
public boolean canAcceptVariables() { | |
return true; | |
} | |
// =============== ExpressionNode methods ================ | |
/** | |
* Set the parent of this node. | |
* | |
* @param n | |
* Must be a ElemTemplateElement. | |
*/ | |
public void exprSetParent(ExpressionNode n) { | |
// This obviously requires that only a ElemTemplateElement can | |
// parent a node of this type. | |
setParentElem((ElemTemplateElement) n); | |
} | |
/** | |
* Get the ExpressionNode parent of this node. | |
*/ | |
public ExpressionNode exprGetParent() { | |
return getParentElem(); | |
} | |
/** | |
* This method tells the node to add its argument to the node's list of | |
* children. | |
* | |
* @param n | |
* Must be a ElemTemplateElement. | |
*/ | |
public void exprAddChild(ExpressionNode n, int i) { | |
appendChild((ElemTemplateElement) n); | |
} | |
/** | |
* This method returns a child node. The children are numbered from zero, | |
* left to right. | |
*/ | |
public ExpressionNode exprGetChild(int i) { | |
return (ExpressionNode) item(i); | |
} | |
/** Return the number of children the node has. */ | |
public int exprGetNumChildren() { | |
return getLength(); | |
} | |
/** | |
* Accept a visitor and call the appropriate method for this class. | |
* | |
* @param visitor | |
* The visitor whose appropriate method will be called. | |
* @return true if the children of the object should be visited. | |
*/ | |
protected boolean accept(XSLTVisitor visitor) { | |
return visitor.visitInstruction(this); | |
} | |
/** | |
* @see XSLTVisitable#callVisitors(XSLTVisitor) | |
*/ | |
public void callVisitors(XSLTVisitor visitor) { | |
if (accept(visitor)) { | |
callChildVisitors(visitor); | |
} | |
} | |
/** | |
* Call the children visitors. | |
* | |
* @param visitor | |
* The visitor whose appropriate method will be called. | |
*/ | |
protected void callChildVisitors(XSLTVisitor visitor, boolean callAttributes) { | |
for (ElemTemplateElement node = m_firstChild; node != null; node = node.m_nextSibling) { | |
node.callVisitors(visitor); | |
} | |
} | |
/** | |
* Call the children visitors. | |
* | |
* @param visitor | |
* The visitor whose appropriate method will be called. | |
*/ | |
protected void callChildVisitors(XSLTVisitor visitor) { | |
callChildVisitors(visitor, true); | |
} | |
/** | |
* @see PrefixResolver#handlesNullPrefixes() | |
*/ | |
public boolean handlesNullPrefixes() { | |
return false; | |
} | |
} |