http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/NodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/NodeModel.java b/src/main/java/freemarker/ext/dom/NodeModel.java deleted file mode 100644 index 1cff441..0000000 --- a/src/main/java/freemarker/ext/dom/NodeModel.java +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.lang.ref.WeakReference; -import java.net.MalformedURLException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Attr; -import org.w3c.dom.CDATASection; -import org.w3c.dom.CharacterData; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.ProcessingInstruction; -import org.w3c.dom.Text; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; -import freemarker.ext.util.WrapperTemplateModel; -import freemarker.template.AdapterTemplateModel; -import freemarker.template.Configuration; -import freemarker.template.DefaultObjectWrapper; -import freemarker.template.SimpleScalar; -import freemarker.template.TemplateBooleanModel; -import freemarker.template.TemplateDateModel; -import freemarker.template.TemplateHashModel; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateNodeModel; -import freemarker.template.TemplateNodeModelEx; -import freemarker.template.TemplateNumberModel; -import freemarker.template.TemplateSequenceModel; - -/** - * A base class for wrapping a single W3C DOM Node as a FreeMarker template model. - * - * <p> - * Note that {@link DefaultObjectWrapper} automatically wraps W3C DOM {@link Node}-s into this, so you may need do that - * with this class manually. However, before dropping the {@link Node}-s into the data-model, you certainly want to - * apply {@link NodeModel#simplify(Node)} on them. - * - * <p> - * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as shared variable ( - * {@link Configuration#setSharedVariable(String, Object)}). - * - * <p> - * To represent a node sequence (such as a query result) of exactly 1 nodes, this class should be used instead of - * {@link NodeListModel}, as it adds extra capabilities by utilizing that we have exactly 1 node. If you need to wrap a - * node sequence of 0 or multiple nodes, you must use {@link NodeListModel}. - */ -abstract public class NodeModel -implements TemplateNodeModelEx, TemplateHashModel, TemplateSequenceModel, - AdapterTemplateModel, WrapperTemplateModel, _UnexpectedTypeErrorExplainerTemplateModel { - - static private final Logger LOG = LoggerFactory.getLogger("freemarker.dom"); - - private static final Object STATIC_LOCK = new Object(); - - static private DocumentBuilderFactory docBuilderFactory; - - static private final Map xpathSupportMap = Collections.synchronizedMap(new WeakHashMap()); - - static private XPathSupport jaxenXPathSupport; - - static private ErrorHandler errorHandler; - - static Class xpathSupportClass; - - static { - try { - useDefaultXPathSupport(); - } catch (Exception e) { - // do nothing - } - if (xpathSupportClass == null && LOG.isWarnEnabled()) { - LOG.warn("No XPath support is available."); - } - } - - /** - * The W3C DOM Node being wrapped. - */ - final Node node; - private TemplateSequenceModel children; - private NodeModel parent; - - /** - * Sets the DOM parser implementation to be used when building {@link NodeModel} objects from XML files or from - * {@link InputStream} with the static convenience methods of {@link NodeModel}. Otherwise FreeMarker itself doesn't - * use this. - * - * @see #getDocumentBuilderFactory() - * - * @deprecated It's a bad practice to change static fields, as if multiple independent components do that in the - * same JVM, they unintentionally affect each other. Therefore it's recommended to leave this static - * value at its default. - */ - @Deprecated - static public void setDocumentBuilderFactory(DocumentBuilderFactory docBuilderFactory) { - synchronized (STATIC_LOCK) { - NodeModel.docBuilderFactory = docBuilderFactory; - } - } - - /** - * Returns the DOM parser implementation that is used when building {@link NodeModel} objects from XML files or from - * {@link InputStream} with the static convenience methods of {@link NodeModel}. Otherwise FreeMarker itself doesn't - * use this. - * - * @see #setDocumentBuilderFactory(DocumentBuilderFactory) - */ - static public DocumentBuilderFactory getDocumentBuilderFactory() { - synchronized (STATIC_LOCK) { - if (docBuilderFactory == null) { - DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance(); - newFactory.setNamespaceAware(true); - newFactory.setIgnoringElementContentWhitespace(true); - docBuilderFactory = newFactory; // We only write it out when the initialization was full - } - return docBuilderFactory; - } - } - - /** - * Sets the error handler to use when parsing the document with the static convenience methods of {@link NodeModel}. - * - * @deprecated It's a bad practice to change static fields, as if multiple independent components do that in the - * same JVM, they unintentionally affect each other. Therefore it's recommended to leave this static - * value at its default. - * - * @see #getErrorHandler() - */ - @Deprecated - static public void setErrorHandler(ErrorHandler errorHandler) { - synchronized (STATIC_LOCK) { - NodeModel.errorHandler = errorHandler; - } - } - - /** - * @since 2.3.20 - * - * @see #setErrorHandler(ErrorHandler) - */ - static public ErrorHandler getErrorHandler() { - synchronized (STATIC_LOCK) { - return NodeModel.errorHandler; - } - } - - /** - * Convenience method to create a {@link NodeModel} from a SAX {@link InputSource}; please see the security warning - * further down. Adjacent text nodes will be merged (and CDATA sections are considered as text nodes) as with - * {@link #mergeAdjacentText(Node)}. Further simplifications are applied depending on the parameters. If all - * simplifications are turned on, then it applies {@link #simplify(Node)} on the loaded DOM. - * - * <p> - * Note that {@code parse(...)} is only a convenience method, and FreeMarker itself doesn't use it (except when you - * call the other similar static convenience methods, as they may build on each other). In particular, if you want - * full control over the {@link DocumentBuilderFactory} used, create the {@link Node} with your own - * {@link DocumentBuilderFactory}, apply {@link #simplify(Node)} (or such) on it, then call - * {@link NodeModel#wrap(Node)}. - * - * <p> - * <b>Security warning:</b> If the XML to load is coming from a source that you can't fully trust, you shouldn't use - * this method, as the {@link DocumentBuilderFactory} it uses by default supports external entities, and so it - * doesn't prevent XML External Entity (XXE) attacks. Note that XXE attacks are not specific to FreeMarker, they - * affect all XML parsers in general. If that's a problem in your application, OWASP has a cheat sheet to set up a - * {@link DocumentBuilderFactory} that has limited functionality but is immune to XXE attacks. Because it's just a - * convenience method, you can just use your own {@link DocumentBuilderFactory} and do a few extra steps instead - * (see earlier). - * - * @param removeComments - * Whether to remove all comment nodes (recursively); this is like calling {@link #removeComments(Node)} - * @param removePIs - * Whether to remove all processing instruction nodes (recursively); this is like calling - * {@link #removePIs(Node)} - */ - static public NodeModel parse(InputSource is, boolean removeComments, boolean removePIs) - throws SAXException, IOException, ParserConfigurationException { - DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder(); - ErrorHandler errorHandler = getErrorHandler(); - if (errorHandler != null) builder.setErrorHandler(errorHandler); - final Document doc; - try { - doc = builder.parse(is); - } catch (MalformedURLException e) { - // This typical error has an error message that is hard to understand, so let's translate it: - if (is.getSystemId() == null && is.getCharacterStream() == null && is.getByteStream() == null) { - throw new MalformedURLException( - "The SAX InputSource has systemId == null && characterStream == null && byteStream == null. " - + "This is often because it was created with a null InputStream or Reader, which is often because " - + "the XML file it should point to was not found. " - + "(The original exception was: " + e + ")"); - } else { - throw e; - } - } - if (removeComments && removePIs) { - simplify(doc); - } else { - if (removeComments) { - removeComments(doc); - } - if (removePIs) { - removePIs(doc); - } - mergeAdjacentText(doc); - } - return wrap(doc); - } - - /** - * Same as {@link #parse(InputSource, boolean, boolean) parse(is, true, true)}; don't miss the security warnings - * documented there. - */ - static public NodeModel parse(InputSource is) throws SAXException, IOException, ParserConfigurationException { - return parse(is, true, true); - } - - - /** - * Same as {@link #parse(InputSource, boolean, boolean)}, but loads from a {@link File}; don't miss the security - * warnings documented there. - */ - static public NodeModel parse(File f, boolean removeComments, boolean removePIs) - throws SAXException, IOException, ParserConfigurationException { - DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder(); - ErrorHandler errorHandler = getErrorHandler(); - if (errorHandler != null) builder.setErrorHandler(errorHandler); - Document doc = builder.parse(f); - if (removeComments && removePIs) { - simplify(doc); - } else { - if (removeComments) { - removeComments(doc); - } - if (removePIs) { - removePIs(doc); - } - mergeAdjacentText(doc); - } - return wrap(doc); - } - - /** - * Same as {@link #parse(InputSource, boolean, boolean) parse(source, true, true)}, but loads from a {@link File}; - * don't miss the security warnings documented there. - */ - static public NodeModel parse(File f) throws SAXException, IOException, ParserConfigurationException { - return parse(f, true, true); - } - - protected NodeModel(Node node) { - this.node = node; - } - - /** - * @return the underling W3C DOM Node object that this TemplateNodeModel - * is wrapping. - */ - public Node getNode() { - return node; - } - - @Override - public TemplateModel get(String key) throws TemplateModelException { - if (key.startsWith("@@")) { - if (key.equals(AtAtKey.TEXT.getKey())) { - return new SimpleScalar(getText(node)); - } else if (key.equals(AtAtKey.NAMESPACE.getKey())) { - String nsURI = node.getNamespaceURI(); - return nsURI == null ? null : new SimpleScalar(nsURI); - } else if (key.equals(AtAtKey.LOCAL_NAME.getKey())) { - String localName = node.getLocalName(); - if (localName == null) { - localName = getNodeName(); - } - return new SimpleScalar(localName); - } else if (key.equals(AtAtKey.MARKUP.getKey())) { - StringBuilder buf = new StringBuilder(); - NodeOutputter nu = new NodeOutputter(node); - nu.outputContent(node, buf); - return new SimpleScalar(buf.toString()); - } else if (key.equals(AtAtKey.NESTED_MARKUP.getKey())) { - StringBuilder buf = new StringBuilder(); - NodeOutputter nu = new NodeOutputter(node); - nu.outputContent(node.getChildNodes(), buf); - return new SimpleScalar(buf.toString()); - } else if (key.equals(AtAtKey.QNAME.getKey())) { - String qname = getQualifiedName(); - return qname != null ? new SimpleScalar(qname) : null; - } else { - // As @@... would cause exception in the XPath engine, we throw a nicer exception now. - if (AtAtKey.containsKey(key)) { - throw new TemplateModelException( - "\"" + key + "\" is not supported for an XML node of type \"" + getNodeType() + "\"."); - } else { - throw new TemplateModelException("Unsupported @@ key: " + key); - } - } - } else { - XPathSupport xps = getXPathSupport(); - if (xps != null) { - return xps.executeQuery(node, key); - } else { - throw new TemplateModelException( - "Can't try to resolve the XML query key, because no XPath support is available. " - + "This is either malformed or an XPath expression: " + key); - } - } - } - - @Override - public TemplateNodeModel getParentNode() { - if (parent == null) { - Node parentNode = node.getParentNode(); - if (parentNode == null) { - if (node instanceof Attr) { - parentNode = ((Attr) node).getOwnerElement(); - } - } - parent = wrap(parentNode); - } - return parent; - } - - @Override - public TemplateNodeModelEx getPreviousSibling() throws TemplateModelException { - return wrap(node.getPreviousSibling()); - } - - @Override - public TemplateNodeModelEx getNextSibling() throws TemplateModelException { - return wrap(node.getNextSibling()); - } - - @Override - public TemplateSequenceModel getChildNodes() { - if (children == null) { - children = new NodeListModel(node.getChildNodes(), this); - } - return children; - } - - @Override - public final String getNodeType() throws TemplateModelException { - short nodeType = node.getNodeType(); - switch (nodeType) { - case Node.ATTRIBUTE_NODE : return "attribute"; - case Node.CDATA_SECTION_NODE : return "text"; - case Node.COMMENT_NODE : return "comment"; - case Node.DOCUMENT_FRAGMENT_NODE : return "document_fragment"; - case Node.DOCUMENT_NODE : return "document"; - case Node.DOCUMENT_TYPE_NODE : return "document_type"; - case Node.ELEMENT_NODE : return "element"; - case Node.ENTITY_NODE : return "entity"; - case Node.ENTITY_REFERENCE_NODE : return "entity_reference"; - case Node.NOTATION_NODE : return "notation"; - case Node.PROCESSING_INSTRUCTION_NODE : return "pi"; - case Node.TEXT_NODE : return "text"; - } - throw new TemplateModelException("Unknown node type: " + nodeType + ". This should be impossible!"); - } - - public TemplateModel exec(List args) throws TemplateModelException { - if (args.size() != 1) { - throw new TemplateModelException("Expecting exactly one arguments"); - } - String query = (String) args.get(0); - // Now, we try to behave as if this is an XPath expression - XPathSupport xps = getXPathSupport(); - if (xps == null) { - throw new TemplateModelException("No XPath support available"); - } - return xps.executeQuery(node, query); - } - - /** - * Always returns 1. - */ - @Override - public final int size() { - return 1; - } - - @Override - public final TemplateModel get(int i) { - return i == 0 ? this : null; - } - - @Override - public String getNodeNamespace() { - int nodeType = node.getNodeType(); - if (nodeType != Node.ATTRIBUTE_NODE && nodeType != Node.ELEMENT_NODE) { - return null; - } - String result = node.getNamespaceURI(); - if (result == null && nodeType == Node.ELEMENT_NODE) { - result = ""; - } else if ("".equals(result) && nodeType == Node.ATTRIBUTE_NODE) { - result = null; - } - return result; - } - - @Override - public final int hashCode() { - return node.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - return other.getClass() == this.getClass() - && ((NodeModel) other).node.equals(this.node); - } - - static public NodeModel wrap(Node node) { - if (node == null) { - return null; - } - NodeModel result = null; - switch (node.getNodeType()) { - case Node.DOCUMENT_NODE : result = new DocumentModel((Document) node); break; - case Node.ELEMENT_NODE : result = new ElementModel((Element) node); break; - case Node.ATTRIBUTE_NODE : result = new AttributeNodeModel((Attr) node); break; - case Node.CDATA_SECTION_NODE : - case Node.COMMENT_NODE : - case Node.TEXT_NODE : result = new CharacterDataNodeModel((org.w3c.dom.CharacterData) node); break; - case Node.PROCESSING_INSTRUCTION_NODE : result = new PINodeModel((ProcessingInstruction) node); break; - case Node.DOCUMENT_TYPE_NODE : result = new DocumentTypeModel((DocumentType) node); break; - } - return result; - } - - /** - * Recursively removes all comment nodes from the subtree. - * - * @see #simplify - */ - static public void removeComments(Node parent) { - Node child = parent.getFirstChild(); - while (child != null) { - Node nextSibling = child.getNextSibling(); - if (child.getNodeType() == Node.COMMENT_NODE) { - parent.removeChild(child); - } else if (child.hasChildNodes()) { - removeComments(child); - } - child = nextSibling; - } - } - - /** - * Recursively removes all processing instruction nodes from the subtree. - * - * @see #simplify - */ - static public void removePIs(Node parent) { - Node child = parent.getFirstChild(); - while (child != null) { - Node nextSibling = child.getNextSibling(); - if (child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { - parent.removeChild(child); - } else if (child.hasChildNodes()) { - removePIs(child); - } - child = nextSibling; - } - } - - /** - * Merges adjacent text nodes (where CDATA counts as text node too). Operates recursively on the entire subtree. - * The merged node will have the type of the first node of the adjacent merged nodes. - * - * <p>Because XPath assumes that there are no adjacent text nodes in the tree, not doing this can have - * undesirable side effects. Xalan queries like {@code text()} will only return the first of a list of matching - * adjacent text nodes instead of all of them, while Jaxen will return all of them as intuitively expected. - * - * @see #simplify - */ - static public void mergeAdjacentText(Node parent) { - mergeAdjacentText(parent, new StringBuilder(0)); - } - - static private void mergeAdjacentText(Node parent, StringBuilder collectorBuf) { - Node child = parent.getFirstChild(); - while (child != null) { - Node next = child.getNextSibling(); - if (child instanceof Text) { - boolean atFirstText = true; - while (next instanceof Text) { // - if (atFirstText) { - collectorBuf.setLength(0); - collectorBuf.ensureCapacity(child.getNodeValue().length() + next.getNodeValue().length()); - collectorBuf.append(child.getNodeValue()); - atFirstText = false; - } - collectorBuf.append(next.getNodeValue()); - - parent.removeChild(next); - - next = child.getNextSibling(); - } - if (!atFirstText && collectorBuf.length() != 0) { - ((CharacterData) child).setData(collectorBuf.toString()); - } - } else { - mergeAdjacentText(child, collectorBuf); - } - child = next; - } - } - - /** - * Removes all comments and processing instruction, and unites adjacent text nodes (here CDATA counts as text as - * well). This is similar to applying {@link #removeComments(Node)}, {@link #removePIs(Node)}, and finally - * {@link #mergeAdjacentText(Node)}, but it does all that somewhat faster. - */ - static public void simplify(Node parent) { - simplify(parent, new StringBuilder(0)); - } - - static private void simplify(Node parent, StringBuilder collectorTextChildBuff) { - Node collectorTextChild = null; - Node child = parent.getFirstChild(); - while (child != null) { - Node next = child.getNextSibling(); - if (child.hasChildNodes()) { - if (collectorTextChild != null) { - // Commit pending text node merge: - if (collectorTextChildBuff.length() != 0) { - ((CharacterData) collectorTextChild).setData(collectorTextChildBuff.toString()); - collectorTextChildBuff.setLength(0); - } - collectorTextChild = null; - } - - simplify(child, collectorTextChildBuff); - } else { - int type = child.getNodeType(); - if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE ) { - if (collectorTextChild != null) { - if (collectorTextChildBuff.length() == 0) { - collectorTextChildBuff.ensureCapacity( - collectorTextChild.getNodeValue().length() + child.getNodeValue().length()); - collectorTextChildBuff.append(collectorTextChild.getNodeValue()); - } - collectorTextChildBuff.append(child.getNodeValue()); - parent.removeChild(child); - } else { - collectorTextChild = child; - collectorTextChildBuff.setLength(0); - } - } else if (type == Node.COMMENT_NODE) { - parent.removeChild(child); - } else if (type == Node.PROCESSING_INSTRUCTION_NODE) { - parent.removeChild(child); - } else if (collectorTextChild != null) { - // Commit pending text node merge: - if (collectorTextChildBuff.length() != 0) { - ((CharacterData) collectorTextChild).setData(collectorTextChildBuff.toString()); - collectorTextChildBuff.setLength(0); - } - collectorTextChild = null; - } - } - child = next; - } - - if (collectorTextChild != null) { - // Commit pending text node merge: - if (collectorTextChildBuff.length() != 0) { - ((CharacterData) collectorTextChild).setData(collectorTextChildBuff.toString()); - collectorTextChildBuff.setLength(0); - } - } - } - - NodeModel getDocumentNodeModel() { - if (node instanceof Document) { - return this; - } else { - return wrap(node.getOwnerDocument()); - } - } - - /** - * Tells the system to use (restore) the default (initial) XPath system used by - * this FreeMarker version on this system. - */ - static public void useDefaultXPathSupport() { - synchronized (STATIC_LOCK) { - xpathSupportClass = null; - jaxenXPathSupport = null; - try { - useXalanXPathSupport(); - } catch (Exception e) { - ; // ignore - } - if (xpathSupportClass == null) try { - useSunInternalXPathSupport(); - } catch (Exception e) { - ; // ignore - } - if (xpathSupportClass == null) try { - useJaxenXPathSupport(); - } catch (Exception e) { - ; // ignore - } - } - } - - /** - * Convenience method. Tells the system to use Jaxen for XPath queries. - * @throws Exception if the Jaxen classes are not present. - */ - static public void useJaxenXPathSupport() throws Exception { - Class.forName("org.jaxen.dom.DOMXPath"); - Class c = Class.forName("freemarker.ext.dom.JaxenXPathSupport"); - jaxenXPathSupport = (XPathSupport) c.newInstance(); - synchronized (STATIC_LOCK) { - xpathSupportClass = c; - } - if (LOG.isDebugEnabled()) { - LOG.debug("Using Jaxen classes for XPath support"); - } - } - - /** - * Convenience method. Tells the system to use Xalan for XPath queries. - * @throws Exception if the Xalan XPath classes are not present. - */ - static public void useXalanXPathSupport() throws Exception { - Class.forName("org.apache.xpath.XPath"); - Class c = Class.forName("freemarker.ext.dom.XalanXPathSupport"); - synchronized (STATIC_LOCK) { - xpathSupportClass = c; - } - if (LOG.isDebugEnabled()) { - LOG.debug("Using Xalan classes for XPath support"); - } - } - - static public void useSunInternalXPathSupport() throws Exception { - Class.forName("com.sun.org.apache.xpath.internal.XPath"); - Class c = Class.forName("freemarker.ext.dom.SunInternalXalanXPathSupport"); - synchronized (STATIC_LOCK) { - xpathSupportClass = c; - } - if (LOG.isDebugEnabled()) { - LOG.debug("Using Sun's internal Xalan classes for XPath support"); - } - } - - /** - * Set an alternative implementation of freemarker.ext.dom.XPathSupport to use - * as the XPath engine. - * @param cl the class, or <code>null</code> to disable XPath support. - */ - static public void setXPathSupportClass(Class cl) { - if (cl != null && !XPathSupport.class.isAssignableFrom(cl)) { - throw new RuntimeException("Class " + cl.getName() - + " does not implement freemarker.ext.dom.XPathSupport"); - } - synchronized (STATIC_LOCK) { - xpathSupportClass = cl; - } - } - - /** - * Get the currently used freemarker.ext.dom.XPathSupport used as the XPath engine. - * Returns <code>null</code> if XPath support is disabled. - */ - static public Class getXPathSupportClass() { - synchronized (STATIC_LOCK) { - return xpathSupportClass; - } - } - - static private String getText(Node node) { - String result = ""; - if (node instanceof Text || node instanceof CDATASection) { - result = ((org.w3c.dom.CharacterData) node).getData(); - } else if (node instanceof Element) { - NodeList children = node.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - result += getText(children.item(i)); - } - } else if (node instanceof Document) { - result = getText(((Document) node).getDocumentElement()); - } - return result; - } - - XPathSupport getXPathSupport() { - if (jaxenXPathSupport != null) { - return jaxenXPathSupport; - } - XPathSupport xps = null; - Document doc = node.getOwnerDocument(); - if (doc == null) { - doc = (Document) node; - } - synchronized (doc) { - WeakReference ref = (WeakReference) xpathSupportMap.get(doc); - if (ref != null) { - xps = (XPathSupport) ref.get(); - } - if (xps == null) { - try { - xps = (XPathSupport) xpathSupportClass.newInstance(); - xpathSupportMap.put(doc, new WeakReference(xps)); - } catch (Exception e) { - LOG.error("Error instantiating xpathSupport class", e); - } - } - } - return xps; - } - - - String getQualifiedName() throws TemplateModelException { - return getNodeName(); - } - - @Override - public Object getAdaptedObject(Class hint) { - return node; - } - - @Override - public Object getWrappedObject() { - return node; - } - - @Override - public Object[] explainTypeError(Class[] expectedClasses) { - for (int i = 0; i < expectedClasses.length; i++) { - Class expectedClass = expectedClasses[i]; - if (TemplateDateModel.class.isAssignableFrom(expectedClass) - || TemplateNumberModel.class.isAssignableFrom(expectedClass) - || TemplateBooleanModel.class.isAssignableFrom(expectedClass)) { - return new Object[] { - "XML node values are always strings (text), that is, they can't be used as number, " - + "date/time/datetime or boolean without explicit conversion (such as " - + "someNode?number, someNode?datetime.xs, someNode?date.xs, someNode?time.xs, " - + "someNode?boolean).", - }; - } - } - return null; - } - -}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/NodeOutputter.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/NodeOutputter.java b/src/main/java/freemarker/ext/dom/NodeOutputter.java deleted file mode 100644 index e4417a2..0000000 --- a/src/main/java/freemarker/ext/dom/NodeOutputter.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - -import java.util.Iterator; -import java.util.LinkedHashMap; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import freemarker.core.BugException; -import freemarker.core.Environment; -import freemarker.template.Template; -import freemarker.template.utility.StringUtil; - -class NodeOutputter { - - private Element contextNode; - private Environment env; - private String defaultNS; - private boolean hasDefaultNS; - private boolean explicitDefaultNSPrefix; - private LinkedHashMap<String, String> namespacesToPrefixLookup = new LinkedHashMap<String, String>(); - private String namespaceDecl; - int nextGeneratedPrefixNumber = 1; - - NodeOutputter(Node node) { - if (node instanceof Element) { - setContext((Element) node); - } else if (node instanceof Attr) { - setContext(((Attr) node).getOwnerElement()); - } else if (node instanceof Document) { - setContext(((Document) node).getDocumentElement()); - } - } - - private void setContext(Element contextNode) { - this.contextNode = contextNode; - this.env = Environment.getCurrentEnvironment(); - this.defaultNS = env.getDefaultNS(); - this.hasDefaultNS = defaultNS != null && defaultNS.length() > 0; - namespacesToPrefixLookup.put(null, ""); - namespacesToPrefixLookup.put("", ""); - buildPrefixLookup(contextNode); - if (!explicitDefaultNSPrefix && hasDefaultNS) { - namespacesToPrefixLookup.put(defaultNS, ""); - } - constructNamespaceDecl(); - } - - private void buildPrefixLookup(Node n) { - String nsURI = n.getNamespaceURI(); - if (nsURI != null && nsURI.length() > 0) { - String prefix = env.getPrefixForNamespace(nsURI); - if (prefix == null) { - prefix = namespacesToPrefixLookup.get(nsURI); - if (prefix == null) { - // Assign a generated prefix: - do { - prefix = StringUtil.toLowerABC(nextGeneratedPrefixNumber++); - } while (env.getNamespaceForPrefix(prefix) != null); - } - } - namespacesToPrefixLookup.put(nsURI, prefix); - } else if (hasDefaultNS && n.getNodeType() == Node.ELEMENT_NODE) { - namespacesToPrefixLookup.put(defaultNS, Template.DEFAULT_NAMESPACE_PREFIX); - explicitDefaultNSPrefix = true; - } else if (n.getNodeType() == Node.ATTRIBUTE_NODE && hasDefaultNS && defaultNS.equals(nsURI)) { - namespacesToPrefixLookup.put(defaultNS, Template.DEFAULT_NAMESPACE_PREFIX); - explicitDefaultNSPrefix = true; - } - NodeList childNodes = n.getChildNodes(); - for (int i = 0; i < childNodes.getLength(); i++) { - buildPrefixLookup(childNodes.item(i)); - } - } - - private void constructNamespaceDecl() { - StringBuilder buf = new StringBuilder(); - if (explicitDefaultNSPrefix) { - buf.append(" xmlns=\""); - buf.append(defaultNS); - buf.append("\""); - } - for (Iterator<String> it = namespacesToPrefixLookup.keySet().iterator(); it.hasNext(); ) { - String nsURI = it.next(); - if (nsURI == null || nsURI.length() == 0) { - continue; - } - String prefix = namespacesToPrefixLookup.get(nsURI); - if (prefix == null) { - throw new BugException("No xmlns prefix was associated to URI: " + nsURI); - } - buf.append(" xmlns"); - if (prefix.length() > 0) { - buf.append(":"); - buf.append(prefix); - } - buf.append("=\""); - buf.append(nsURI); - buf.append("\""); - } - this.namespaceDecl = buf.toString(); - } - - private void outputQualifiedName(Node n, StringBuilder buf) { - String nsURI = n.getNamespaceURI(); - if (nsURI == null || nsURI.length() == 0) { - buf.append(n.getNodeName()); - } else { - String prefix = namespacesToPrefixLookup.get(nsURI); - if (prefix == null) { - //REVISIT! - buf.append(n.getNodeName()); - } else { - if (prefix.length() > 0) { - buf.append(prefix); - buf.append(':'); - } - buf.append(n.getLocalName()); - } - } - } - - void outputContent(Node n, StringBuilder buf) { - switch(n.getNodeType()) { - case Node.ATTRIBUTE_NODE: { - if (((Attr) n).getSpecified()) { - buf.append(' '); - outputQualifiedName(n, buf); - buf.append("=\"") - .append(StringUtil.XMLEncQAttr(n.getNodeValue())) - .append('"'); - } - break; - } - case Node.COMMENT_NODE: { - buf.append("<!--").append(n.getNodeValue()).append("-->"); - break; - } - case Node.DOCUMENT_NODE: { - outputContent(n.getChildNodes(), buf); - break; - } - case Node.DOCUMENT_TYPE_NODE: { - buf.append("<!DOCTYPE ").append(n.getNodeName()); - DocumentType dt = (DocumentType) n; - if (dt.getPublicId() != null) { - buf.append(" PUBLIC \"").append(dt.getPublicId()).append('"'); - } - if (dt.getSystemId() != null) { - buf.append(" \"").append(dt.getSystemId()).append('"'); - } - if (dt.getInternalSubset() != null) { - buf.append(" [").append(dt.getInternalSubset()).append(']'); - } - buf.append('>'); - break; - } - case Node.ELEMENT_NODE: { - buf.append('<'); - outputQualifiedName(n, buf); - if (n == contextNode) { - buf.append(namespaceDecl); - } - outputContent(n.getAttributes(), buf); - NodeList children = n.getChildNodes(); - if (children.getLength() == 0) { - buf.append(" />"); - } else { - buf.append('>'); - outputContent(n.getChildNodes(), buf); - buf.append("</"); - outputQualifiedName(n, buf); - buf.append('>'); - } - break; - } - case Node.ENTITY_NODE: { - outputContent(n.getChildNodes(), buf); - break; - } - case Node.ENTITY_REFERENCE_NODE: { - buf.append('&').append(n.getNodeName()).append(';'); - break; - } - case Node.PROCESSING_INSTRUCTION_NODE: { - buf.append("<?").append(n.getNodeName()).append(' ').append(n.getNodeValue()).append("?>"); - break; - } - /* - case Node.CDATA_SECTION_NODE: { - buf.append("<![CDATA[").append(n.getNodeValue()).append("]]>"); - break; - }*/ - case Node.CDATA_SECTION_NODE: - case Node.TEXT_NODE: { - buf.append(StringUtil.XMLEncNQG(n.getNodeValue())); - break; - } - } - } - - void outputContent(NodeList nodes, StringBuilder buf) { - for (int i = 0; i < nodes.getLength(); ++i) { - outputContent(nodes.item(i), buf); - } - } - - void outputContent(NamedNodeMap nodes, StringBuilder buf) { - for (int i = 0; i < nodes.getLength(); ++i) { - Node n = nodes.item(i); - if (n.getNodeType() != Node.ATTRIBUTE_NODE - || (!n.getNodeName().startsWith("xmlns:") && !n.getNodeName().equals("xmlns"))) { - outputContent(n, buf); - } - } - } - - String getOpeningTag(Element element) { - StringBuilder buf = new StringBuilder(); - buf.append('<'); - outputQualifiedName(element, buf); - buf.append(namespaceDecl); - outputContent(element.getAttributes(), buf); - buf.append('>'); - return buf.toString(); - } - - String getClosingTag(Element element) { - StringBuilder buf = new StringBuilder(); - buf.append("</"); - outputQualifiedName(element, buf); - buf.append('>'); - return buf.toString(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/PINodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/PINodeModel.java b/src/main/java/freemarker/ext/dom/PINodeModel.java deleted file mode 100644 index 18bccdb..0000000 --- a/src/main/java/freemarker/ext/dom/PINodeModel.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - -import org.w3c.dom.ProcessingInstruction; - -import freemarker.template.TemplateScalarModel; - -class PINodeModel extends NodeModel implements TemplateScalarModel { - - public PINodeModel(ProcessingInstruction pi) { - super(pi); - } - - public String getAsString() { - return ((ProcessingInstruction) node).getData(); - } - - public String getNodeName() { - return "@pi$" + ((ProcessingInstruction) node).getTarget(); - } - - public boolean isEmpty() { - return true; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/SunInternalXalanXPathSupport.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/SunInternalXalanXPathSupport.java b/src/main/java/freemarker/ext/dom/SunInternalXalanXPathSupport.java deleted file mode 100644 index 593a031..0000000 --- a/src/main/java/freemarker/ext/dom/SunInternalXalanXPathSupport.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - -import java.util.List; - -import javax.xml.transform.TransformerException; - -import org.w3c.dom.Node; -import org.w3c.dom.traversal.NodeIterator; - -import com.sun.org.apache.xml.internal.utils.PrefixResolver; -import com.sun.org.apache.xpath.internal.XPath; -import com.sun.org.apache.xpath.internal.XPathContext; -import com.sun.org.apache.xpath.internal.objects.XBoolean; -import com.sun.org.apache.xpath.internal.objects.XNodeSet; -import com.sun.org.apache.xpath.internal.objects.XNull; -import com.sun.org.apache.xpath.internal.objects.XNumber; -import com.sun.org.apache.xpath.internal.objects.XObject; -import com.sun.org.apache.xpath.internal.objects.XString; - -import freemarker.core.Environment; -import freemarker.template.SimpleNumber; -import freemarker.template.SimpleScalar; -import freemarker.template.Template; -import freemarker.template.TemplateBooleanModel; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; - -/** - * This is just the XalanXPathSupport class using the sun internal - * package names - */ - -class SunInternalXalanXPathSupport implements XPathSupport { - - private XPathContext xpathContext = new XPathContext(); - - private static final String ERRMSG_RECOMMEND_JAXEN - = "(Note that there is no such restriction if you " - + "configure FreeMarker to use Jaxen instead of Xalan.)"; - - private static final String ERRMSG_EMPTY_NODE_SET - = "Cannot perform an XPath query against an empty node set." + ERRMSG_RECOMMEND_JAXEN; - - synchronized public TemplateModel executeQuery(Object context, String xpathQuery) throws TemplateModelException { - if (!(context instanceof Node)) { - if (context != null) { - if (isNodeList(context)) { - int cnt = ((List) context).size(); - if (cnt != 0) { - throw new TemplateModelException( - "Cannot perform an XPath query against a node set of " + cnt - + " nodes. Expecting a single node." + ERRMSG_RECOMMEND_JAXEN); - } else { - throw new TemplateModelException(ERRMSG_EMPTY_NODE_SET); - } - } else { - throw new TemplateModelException( - "Cannot perform an XPath query against a " + context.getClass().getName() - + ". Expecting a single org.w3c.dom.Node."); - } - } else { - throw new TemplateModelException(ERRMSG_EMPTY_NODE_SET); - } - } - Node node = (Node) context; - try { - XPath xpath = new XPath(xpathQuery, null, customPrefixResolver, XPath.SELECT, null); - int ctxtNode = xpathContext.getDTMHandleFromNode(node); - XObject xresult = xpath.execute(xpathContext, ctxtNode, customPrefixResolver); - if (xresult instanceof XNodeSet) { - NodeListModel result = new NodeListModel(node); - result.xpathSupport = this; - NodeIterator nodeIterator = xresult.nodeset(); - Node n; - do { - n = nodeIterator.nextNode(); - if (n != null) { - result.add(n); - } - } while (n != null); - return result.size() == 1 ? result.get(0) : result; - } - if (xresult instanceof XBoolean) { - return ((XBoolean) xresult).bool() ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; - } - if (xresult instanceof XNull) { - return null; - } - if (xresult instanceof XString) { - return new SimpleScalar(xresult.toString()); - } - if (xresult instanceof XNumber) { - return new SimpleNumber(Double.valueOf(((XNumber) xresult).num())); - } - throw new TemplateModelException("Cannot deal with type: " + xresult.getClass().getName()); - } catch (TransformerException te) { - throw new TemplateModelException(te); - } - } - - private static PrefixResolver customPrefixResolver = new PrefixResolver() { - - public String getNamespaceForPrefix(String prefix, Node node) { - return getNamespaceForPrefix(prefix); - } - - public String getNamespaceForPrefix(String prefix) { - if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) { - return Environment.getCurrentEnvironment().getDefaultNS(); - } - return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix); - } - - public String getBaseIdentifier() { - return null; - } - - public boolean handlesNullPrefixes() { - return false; - } - }; - - /** - * Used for generating more intelligent error messages. - */ - private static boolean isNodeList(Object context) { - if (context instanceof List) { - List ls = (List) context; - int ln = ls.size(); - for (int i = 0; i < ln; i++) { - if (!(ls.get(i) instanceof Node)) { - return false; - } - } - return true; - } else { - return false; - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/XPathSupport.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/XPathSupport.java b/src/main/java/freemarker/ext/dom/XPathSupport.java deleted file mode 100644 index e51d812..0000000 --- a/src/main/java/freemarker/ext/dom/XPathSupport.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; - -public interface XPathSupport { - - // [2.4] Add argument to pass down the ObjectWrapper to use - TemplateModel executeQuery(Object context, String xpathQuery) throws TemplateModelException; - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/XalanXPathSupport.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/XalanXPathSupport.java b/src/main/java/freemarker/ext/dom/XalanXPathSupport.java deleted file mode 100644 index d48e11f..0000000 --- a/src/main/java/freemarker/ext/dom/XalanXPathSupport.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - -import java.util.List; - -import javax.xml.transform.TransformerException; - -import org.apache.xml.utils.PrefixResolver; -import org.apache.xpath.XPath; -import org.apache.xpath.XPathContext; -import org.apache.xpath.objects.XBoolean; -import org.apache.xpath.objects.XNodeSet; -import org.apache.xpath.objects.XNull; -import org.apache.xpath.objects.XNumber; -import org.apache.xpath.objects.XObject; -import org.apache.xpath.objects.XString; -import org.w3c.dom.Node; -import org.w3c.dom.traversal.NodeIterator; - -import freemarker.core.Environment; -import freemarker.template.SimpleNumber; -import freemarker.template.SimpleScalar; -import freemarker.template.Template; -import freemarker.template.TemplateBooleanModel; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; - -/** - * Some glue code that bridges the Xalan XPath stuff (that is built into the JDK 1.4.x) - * with FreeMarker TemplateModel semantics - */ - -class XalanXPathSupport implements XPathSupport { - - private XPathContext xpathContext = new XPathContext(); - - /* I don't recommend Jaxen... - private static final String ERRMSG_RECOMMEND_JAXEN - = "(Note that there is no such restriction if you " - + "configure FreeMarker to use Jaxen instead of Xalan.)"; - */ - private static final String ERRMSG_EMPTY_NODE_SET - = "Cannot perform an XPath query against an empty node set."; /* " + ERRMSG_RECOMMEND_JAXEN;*/ - - synchronized public TemplateModel executeQuery(Object context, String xpathQuery) throws TemplateModelException { - if (!(context instanceof Node)) { - if (context != null) { - if (isNodeList(context)) { - int cnt = ((List) context).size(); - if (cnt != 0) { - throw new TemplateModelException( - "Cannot perform an XPath query against a node set of " + cnt - + " nodes. Expecting a single node."/* " + ERRMSG_RECOMMEND_JAXEN*/); - } else { - throw new TemplateModelException(ERRMSG_EMPTY_NODE_SET); - } - } else { - throw new TemplateModelException( - "Cannot perform an XPath query against a " + context.getClass().getName() - + ". Expecting a single org.w3c.dom.Node."); - } - } else { - throw new TemplateModelException(ERRMSG_EMPTY_NODE_SET); - } - } - Node node = (Node) context; - try { - XPath xpath = new XPath(xpathQuery, null, customPrefixResolver, XPath.SELECT, null); - int ctxtNode = xpathContext.getDTMHandleFromNode(node); - XObject xresult = xpath.execute(xpathContext, ctxtNode, customPrefixResolver); - if (xresult instanceof XNodeSet) { - NodeListModel result = new NodeListModel(node); - result.xpathSupport = this; - NodeIterator nodeIterator = xresult.nodeset(); - Node n; - do { - n = nodeIterator.nextNode(); - if (n != null) { - result.add(n); - } - } while (n != null); - return result.size() == 1 ? result.get(0) : result; - } - if (xresult instanceof XBoolean) { - return ((XBoolean) xresult).bool() ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; - } - if (xresult instanceof XNull) { - return null; - } - if (xresult instanceof XString) { - return new SimpleScalar(xresult.toString()); - } - if (xresult instanceof XNumber) { - return new SimpleNumber(Double.valueOf(((XNumber) xresult).num())); - } - throw new TemplateModelException("Cannot deal with type: " + xresult.getClass().getName()); - } catch (TransformerException te) { - throw new TemplateModelException(te); - } - } - - private static PrefixResolver customPrefixResolver = new PrefixResolver() { - - public String getNamespaceForPrefix(String prefix, Node node) { - return getNamespaceForPrefix(prefix); - } - - public String getNamespaceForPrefix(String prefix) { - if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) { - return Environment.getCurrentEnvironment().getDefaultNS(); - } - return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix); - } - - public String getBaseIdentifier() { - return null; - } - - public boolean handlesNullPrefixes() { - return false; - } - }; - - /** - * Used for generating more intelligent error messages. - */ - private static boolean isNodeList(Object context) { - if (context instanceof List) { - List ls = (List) context; - int ln = ls.size(); - for (int i = 0; i < ln; i++) { - if (!(ls.get(i) instanceof Node)) { - return false; - } - } - return true; - } else { - return false; - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/_ExtDomApi.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/_ExtDomApi.java b/src/main/java/freemarker/ext/dom/_ExtDomApi.java deleted file mode 100644 index 178d8a8..0000000 --- a/src/main/java/freemarker/ext/dom/_ExtDomApi.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.dom; - -import freemarker.core.Environment; - -/** - * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! - * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can - * access things inside this package that users shouldn't. - */ -public final class _ExtDomApi { - - private _ExtDomApi() { - // Not meant to be called - } - - static public boolean isXMLNameLike(String name) { - return DomStringUtil.isXMLNameLike(name); - } - - static public boolean matchesName(String qname, String nodeName, String nsURI, Environment env) { - return DomStringUtil.matchesName(qname, nodeName, nsURI, env); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/dom/package.html ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/dom/package.html b/src/main/java/freemarker/ext/dom/package.html deleted file mode 100644 index a3518ff..0000000 --- a/src/main/java/freemarker/ext/dom/package.html +++ /dev/null @@ -1,31 +0,0 @@ -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. ---> -<html> -<head> -<title></title> -</head> -<body> - -<p>Exposes DOM XML nodes to templates as easily traversable trees; -see <a href="http://freemarker.org/docs/xgui.html" target="_blank">in the Manual</a>. -The {@link freemarker.template.DefaultObjectWrapper default object wrapper} of FreeMarker -automatically wraps W3C nodes with this. - -</body> -</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/jsp/CustomTagAndELFunctionCombiner.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/jsp/CustomTagAndELFunctionCombiner.java b/src/main/java/freemarker/ext/jsp/CustomTagAndELFunctionCombiner.java deleted file mode 100644 index ba771e3..0000000 --- a/src/main/java/freemarker/ext/jsp/CustomTagAndELFunctionCombiner.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -package freemarker.ext.jsp; - -import java.io.IOException; -import java.io.Writer; -import java.util.List; -import java.util.Map; - -import freemarker.core.BugException; -import freemarker.core.Environment; -import freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; -import freemarker.ext.beans.SimpleMethodModel; -import freemarker.template.TemplateDirectiveBody; -import freemarker.template.TemplateDirectiveModel; -import freemarker.template.TemplateException; -import freemarker.template.TemplateMethodModelEx; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateSequenceModel; -import freemarker.template.TemplateTransformModel; -import freemarker.template.utility.ClassUtil; - -/** - * Used when a custom JSP tag and an EL function uses the same name in a tag library, to create a single FTL value from - * the two. As FTL as no separate namespace for "tags" and functions, both aspect has to be implemented by the same - * value. - * - * @sine 2.3.25 - */ -@SuppressWarnings("rawtypes") -class CustomTagAndELFunctionCombiner { - - /** - * @param customTag - * Either a {@link TemplateDirectiveModel} or a {@link TemplateTransformModel}. - */ - static TemplateModel combine(TemplateModel customTag, TemplateMethodModelEx elFunction) { - if (customTag instanceof TemplateDirectiveModel) { - return elFunction instanceof SimpleMethodModel // - ? new TemplateDirectiveModelAndSimpleMethodModel( // - (TemplateDirectiveModel) customTag, (SimpleMethodModel) elFunction) // - : new TemplateDirectiveModelAndTemplateMethodModelEx( // - (TemplateDirectiveModel) customTag, elFunction); - } else if (customTag instanceof TemplateTransformModel) { - return (elFunction instanceof SimpleMethodModel) - ? new TemplateTransformModelAndSimpleMethodModel( // - (TemplateTransformModel) customTag, (SimpleMethodModel) elFunction) // - : new TemplateTransformModelAndTemplateMethodModelEx( // - (TemplateTransformModel) customTag, elFunction); - } else { - throw new BugException( - "Unexpected custom JSP tag class: " + ClassUtil.getShortClassNameOfObject(customTag)); - } - } - - /** - * Tells if the value can be used as the "custom tag" parameter to - * {@link #combine(TemplateModel, TemplateMethodModelEx)}. - */ - static boolean canBeCombinedAsCustomTag(TemplateModel tm) { - return (tm instanceof TemplateDirectiveModel || tm instanceof TemplateTransformModel) - && !(tm instanceof CombinedTemplateModel); - } - - /** - * Tells if the value can be used as the "EL function" parameter to - * {@link #combine(TemplateModel, TemplateMethodModelEx)}. - */ - static boolean canBeCombinedAsELFunction(TemplateModel tm) { - return tm instanceof TemplateMethodModelEx && !(tm instanceof CombinedTemplateModel); - } - - private static class CombinedTemplateModel { - // Marker only - }; - - private static class TemplateDirectiveModelAndSimpleMethodModel extends CombinedTemplateModel - implements TemplateDirectiveModel, TemplateMethodModelEx, TemplateSequenceModel, - _UnexpectedTypeErrorExplainerTemplateModel { - - private final TemplateDirectiveModel templateDirectiveModel; - private final SimpleMethodModel simpleMethodModel; - - public TemplateDirectiveModelAndSimpleMethodModel( // - TemplateDirectiveModel templateDirectiveModel, SimpleMethodModel simpleMethodModel) { - this.templateDirectiveModel = templateDirectiveModel; - this.simpleMethodModel = simpleMethodModel; - } - - public Object exec(List arguments) throws TemplateModelException { - return simpleMethodModel.exec(arguments); - } - - public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) - throws TemplateException, IOException { - templateDirectiveModel.execute(env, params, loopVars, body); - } - - public Object[] explainTypeError(Class[] expectedClasses) { - return simpleMethodModel.explainTypeError(expectedClasses); - } - - public TemplateModel get(int index) throws TemplateModelException { - return simpleMethodModel.get(index); - } - - public int size() throws TemplateModelException { - return simpleMethodModel.size(); - } - - } - - private static class TemplateDirectiveModelAndTemplateMethodModelEx extends CombinedTemplateModel - implements TemplateDirectiveModel, TemplateMethodModelEx { - - private final TemplateDirectiveModel templateDirectiveModel; - private final TemplateMethodModelEx templateMethodModelEx; - - public TemplateDirectiveModelAndTemplateMethodModelEx( // - TemplateDirectiveModel templateDirectiveModel, TemplateMethodModelEx templateMethodModelEx) { - this.templateDirectiveModel = templateDirectiveModel; - this.templateMethodModelEx = templateMethodModelEx; - } - - public Object exec(List arguments) throws TemplateModelException { - return templateMethodModelEx.exec(arguments); - } - - public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) - throws TemplateException, IOException { - templateDirectiveModel.execute(env, params, loopVars, body); - } - - } - - private static class TemplateTransformModelAndTemplateMethodModelEx extends CombinedTemplateModel - implements TemplateTransformModel, TemplateMethodModelEx { - - private final TemplateTransformModel templateTransformModel; - private final TemplateMethodModelEx templateMethodModelEx; - - public TemplateTransformModelAndTemplateMethodModelEx( // - TemplateTransformModel templateTransformModel, TemplateMethodModelEx templateMethodModelEx) { - this.templateTransformModel = templateTransformModel; - this.templateMethodModelEx = templateMethodModelEx; - } - - public Object exec(List arguments) throws TemplateModelException { - return templateMethodModelEx.exec(arguments); - } - - public Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException { - return templateTransformModel.getWriter(out, args); - } - - } - - private static class TemplateTransformModelAndSimpleMethodModel extends CombinedTemplateModel - implements TemplateTransformModel, TemplateMethodModelEx, TemplateSequenceModel, - _UnexpectedTypeErrorExplainerTemplateModel { - - private final TemplateTransformModel templateTransformModel; - private final SimpleMethodModel simpleMethodModel; - - public TemplateTransformModelAndSimpleMethodModel( // - TemplateTransformModel templateTransformModel, SimpleMethodModel simpleMethodModel) { - this.templateTransformModel = templateTransformModel; - this.simpleMethodModel = simpleMethodModel; - } - - public Object exec(List arguments) throws TemplateModelException { - return simpleMethodModel.exec(arguments); - } - - public Object[] explainTypeError(Class[] expectedClasses) { - return simpleMethodModel.explainTypeError(expectedClasses); - } - - public TemplateModel get(int index) throws TemplateModelException { - return simpleMethodModel.get(index); - } - - public int size() throws TemplateModelException { - return simpleMethodModel.size(); - } - - public Writer getWriter(Writer out, Map args) throws TemplateModelException, IOException { - return templateTransformModel.getWriter(out, args); - } - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/jsp/EventForwarding.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/jsp/EventForwarding.java b/src/main/java/freemarker/ext/jsp/EventForwarding.java deleted file mode 100644 index d4b124f..0000000 --- a/src/main/java/freemarker/ext/jsp/EventForwarding.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.jsp; - -import java.util.ArrayList; -import java.util.EventListener; -import java.util.Iterator; -import java.util.List; - -import javax.servlet.ServletContext; -import javax.servlet.ServletContextAttributeEvent; -import javax.servlet.ServletContextAttributeListener; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.http.HttpSessionAttributeListener; -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionEvent; -import javax.servlet.http.HttpSessionListener; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * An instance of this class should be registered as a <tt><listener></tt> in - * the <tt>web.xml</tt> descriptor in order to correctly dispatch events to - * event listeners that are specified in TLD files. - */ -public class EventForwarding - implements - ServletContextAttributeListener, - ServletContextListener, - HttpSessionListener, - HttpSessionAttributeListener { - private static final Logger LOG = LoggerFactory.getLogger("freemarker.jsp"); - - private static final String ATTR_NAME = EventForwarding.class.getName(); - - private final List servletContextAttributeListeners = new ArrayList(); - private final List servletContextListeners = new ArrayList(); - private final List httpSessionAttributeListeners = new ArrayList(); - private final List httpSessionListeners = new ArrayList(); - - void addListeners(List listeners) { - for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { - addListener((EventListener) iter.next()); - } - } - - private void addListener(EventListener listener) { - boolean added = false; - if (listener instanceof ServletContextAttributeListener) { - addListener(servletContextAttributeListeners, listener); - added = true; - } - if (listener instanceof ServletContextListener) { - addListener(servletContextListeners, listener); - added = true; - } - if (listener instanceof HttpSessionAttributeListener) { - addListener(httpSessionAttributeListeners, listener); - added = true; - } - if (listener instanceof HttpSessionListener) { - addListener(httpSessionListeners, listener); - added = true; - } - if (!added) { - LOG.warn("Listener of class {} wasn't registered as it doesn't implement any of the recognized listener " - + "interfaces.", listener.getClass().getName()); - } - } - - static EventForwarding getInstance(ServletContext context) { - return (EventForwarding) context.getAttribute(ATTR_NAME); - } - private void addListener(List listeners, EventListener listener) { - synchronized (listeners) { - listeners.add(listener); - } - } - - @Override - public void attributeAdded(ServletContextAttributeEvent arg0) { - synchronized (servletContextAttributeListeners) { - int s = servletContextAttributeListeners.size(); - for (int i = 0; i < s; ++i) { - ((ServletContextAttributeListener) servletContextAttributeListeners.get(i)).attributeAdded(arg0); - } - } - } - - @Override - public void attributeRemoved(ServletContextAttributeEvent arg0) { - synchronized (servletContextAttributeListeners) { - int s = servletContextAttributeListeners.size(); - for (int i = 0; i < s; ++i) { - ((ServletContextAttributeListener) servletContextAttributeListeners.get(i)).attributeRemoved(arg0); - } - } - } - - @Override - public void attributeReplaced(ServletContextAttributeEvent arg0) { - synchronized (servletContextAttributeListeners) { - int s = servletContextAttributeListeners.size(); - for (int i = 0; i < s; ++i) { - ((ServletContextAttributeListener) servletContextAttributeListeners.get(i)).attributeReplaced(arg0); - } - } - } - - @Override - public void contextInitialized(ServletContextEvent arg0) { - arg0.getServletContext().setAttribute(ATTR_NAME, this); - - synchronized (servletContextListeners) { - int s = servletContextListeners.size(); - for (int i = 0; i < s; ++i) { - ((ServletContextListener) servletContextListeners.get(i)).contextInitialized(arg0); - } - } - } - - @Override - public void contextDestroyed(ServletContextEvent arg0) { - synchronized (servletContextListeners) { - int s = servletContextListeners.size(); - for (int i = s - 1; i >= 0; --i) { - ((ServletContextListener) servletContextListeners.get(i)).contextDestroyed(arg0); - } - } - } - - @Override - public void sessionCreated(HttpSessionEvent arg0) { - synchronized (httpSessionListeners) { - int s = httpSessionListeners.size(); - for (int i = 0; i < s; ++i) { - ((HttpSessionListener) httpSessionListeners.get(i)).sessionCreated(arg0); - } - } - } - - @Override - public void sessionDestroyed(HttpSessionEvent arg0) { - synchronized (httpSessionListeners) { - int s = httpSessionListeners.size(); - for (int i = s - 1; i >= 0; --i) { - ((HttpSessionListener) httpSessionListeners.get(i)).sessionDestroyed(arg0); - } - } - } - - @Override - public void attributeAdded(HttpSessionBindingEvent arg0) { - synchronized (httpSessionAttributeListeners) { - int s = httpSessionAttributeListeners.size(); - for (int i = 0; i < s; ++i) { - ((HttpSessionAttributeListener) httpSessionAttributeListeners.get(i)).attributeAdded(arg0); - } - } - } - - @Override - public void attributeRemoved(HttpSessionBindingEvent arg0) { - synchronized (httpSessionAttributeListeners) { - int s = httpSessionAttributeListeners.size(); - for (int i = 0; i < s; ++i) { - ((HttpSessionAttributeListener) httpSessionAttributeListeners.get(i)).attributeRemoved(arg0); - } - } - } - - @Override - public void attributeReplaced(HttpSessionBindingEvent arg0) { - synchronized (httpSessionAttributeListeners) { - int s = httpSessionAttributeListeners.size(); - for (int i = 0; i < s; ++i) { - ((HttpSessionAttributeListener) httpSessionAttributeListeners.get(i)).attributeReplaced(arg0); - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/ext/jsp/FreeMarkerJspApplicationContext.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/ext/jsp/FreeMarkerJspApplicationContext.java b/src/main/java/freemarker/ext/jsp/FreeMarkerJspApplicationContext.java deleted file mode 100644 index 5a24869..0000000 --- a/src/main/java/freemarker/ext/jsp/FreeMarkerJspApplicationContext.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ - -package freemarker.ext.jsp; - -import java.util.Iterator; -import java.util.LinkedList; - -import javax.el.ArrayELResolver; -import javax.el.BeanELResolver; -import javax.el.CompositeELResolver; -import javax.el.ELContext; -import javax.el.ELContextEvent; -import javax.el.ELContextListener; -import javax.el.ELResolver; -import javax.el.ExpressionFactory; -import javax.el.FunctionMapper; -import javax.el.ListELResolver; -import javax.el.MapELResolver; -import javax.el.ResourceBundleELResolver; -import javax.el.ValueExpression; -import javax.el.VariableMapper; -import javax.servlet.jsp.JspApplicationContext; -import javax.servlet.jsp.el.ImplicitObjectELResolver; -import javax.servlet.jsp.el.ScopedAttributeELResolver; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import freemarker.template.utility.ClassUtil; - -/** - */ -class FreeMarkerJspApplicationContext implements JspApplicationContext { - private static final Logger LOG = LoggerFactory.getLogger("freemarker.jsp"); - private static final ExpressionFactory expressionFactoryImpl = findExpressionFactoryImplementation(); - - private final LinkedList listeners = new LinkedList(); - private final CompositeELResolver elResolver = new CompositeELResolver(); - private final CompositeELResolver additionalResolvers = new CompositeELResolver(); - { - elResolver.add(new ImplicitObjectELResolver()); - elResolver.add(additionalResolvers); - elResolver.add(new MapELResolver()); - elResolver.add(new ResourceBundleELResolver()); - elResolver.add(new ListELResolver()); - elResolver.add(new ArrayELResolver()); - elResolver.add(new BeanELResolver()); - elResolver.add(new ScopedAttributeELResolver()); - } - - @Override - public void addELContextListener(ELContextListener listener) { - synchronized (listeners) { - listeners.addLast(listener); - } - } - - private static ExpressionFactory findExpressionFactoryImplementation() { - ExpressionFactory ef = tryExpressionFactoryImplementation("com.sun"); - if (ef == null) { - ef = tryExpressionFactoryImplementation("org.apache"); - if (ef == null) { - LOG.warn("Could not find any implementation for {}", ExpressionFactory.class.getName()); - } - } - return ef; - } - - private static ExpressionFactory tryExpressionFactoryImplementation(String packagePrefix) { - String className = packagePrefix + ".el.ExpressionFactoryImpl"; - try { - Class cl = ClassUtil.forName(className); - if (ExpressionFactory.class.isAssignableFrom(cl)) { - LOG.info("Using {} as implementation of {}", className, ExpressionFactory.class.getName()); - return (ExpressionFactory) cl.newInstance(); - } - LOG.warn("Class {} does not implement {}", className, ExpressionFactory.class.getName()); - } catch (ClassNotFoundException e) { - // skip - } catch (Exception e) { - LOG.error("Failed to instantiate {}", className, e); - } - return null; - } - - @Override - public void addELResolver(ELResolver resolver) { - additionalResolvers.add(resolver); - } - - @Override - public ExpressionFactory getExpressionFactory() { - return expressionFactoryImpl; - } - - ELContext createNewELContext(final FreeMarkerPageContext pageCtx) { - ELContext ctx = new FreeMarkerELContext(pageCtx); - ELContextEvent event = new ELContextEvent(ctx); - synchronized (listeners) { - for (Iterator iter = listeners.iterator(); iter.hasNext(); ) { - ELContextListener l = (ELContextListener) iter.next(); - l.contextCreated(event); - } - } - return ctx; - } - - private class FreeMarkerELContext extends ELContext { - private final FreeMarkerPageContext pageCtx; - - FreeMarkerELContext(FreeMarkerPageContext pageCtx) { - this.pageCtx = pageCtx; - } - - @Override - public ELResolver getELResolver() { - return elResolver; - } - - @Override - public FunctionMapper getFunctionMapper() { - return null; - } - - @Override - public VariableMapper getVariableMapper() { - return new VariableMapper() { - @Override - public ValueExpression resolveVariable(String name) { - Object obj = pageCtx.findAttribute(name); - if (obj == null) { - return null; - } - return expressionFactoryImpl.createValueExpression(obj, - obj.getClass()); - } - - @Override - public ValueExpression setVariable(String name, - ValueExpression value) { - ValueExpression prev = resolveVariable(name); - pageCtx.setAttribute(name, value.getValue( - FreeMarkerELContext.this)); - return prev; - } - }; - } - } -} \ No newline at end of file
