http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeModel.java deleted file mode 100644 index 37a5c7d..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeModel.java +++ /dev/null @@ -1,613 +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 org.apache.freemarker.dom; - - -import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; - -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel; -import org.apache.freemarker.core.model.AdapterTemplateModel; -import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateDateModel; -import org.apache.freemarker.core.model.TemplateHashModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.TemplateNodeModel; -import org.apache.freemarker.core.model.TemplateNodeModelEx; -import org.apache.freemarker.core.model.TemplateNumberModel; -import org.apache.freemarker.core.model.TemplateSequenceModel; -import org.apache.freemarker.core.model.WrapperTemplateModel; -import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; -import org.apache.freemarker.core.model.impl.SimpleScalar; -import org.slf4j.Logger; -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; - -/** - * A base class for wrapping a single W3C DOM_WRAPPER Node as a FreeMarker template model. - * <p> - * Note that {@link DefaultObjectWrapper} automatically wraps W3C DOM_WRAPPER {@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 - * {@linkplain Configuration#getSharedVariables() shared variable}. - * <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 = DomLog.LOG; - - private static final Object STATIC_LOCK = new Object(); - - static private final Map xpathSupportMap = Collections.synchronizedMap(new WeakHashMap()); - - static private XPathSupport jaxenXPathSupport; - - 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_WRAPPER Node being wrapped. - */ - final Node node; - private TemplateSequenceModel children; - private NodeModel parent; - - protected NodeModel(Node node) { - this.node = node; - } - - /** - * @return the underling W3C DOM_WRAPPER 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() == getClass() - && ((NodeModel) other).node.equals(node); - } - - /** - * Creates a {@link NodeModel} from a DOM {@link Node}. It's strongly recommended modify the {@link Node} with - * {@link #simplify(Node)}, so the DOM will be easier to process in templates. - * - * @param node - * The DOM node to wrap. This is typically an {@link Element} or a {@link Document}, but all kind of node - * types are supported. If {@code null}, {@code null} will be returned. - */ - 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; - default: throw new IllegalArgumentException( - "Unsupported node type: " + node.getNodeType() + " (" - + node.getClass().getName() + ")"); - } - 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("org.apache.freemarker.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("org.apache.freemarker.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("org.apache.freemarker.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 org.apache.freemarker.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 org.apache.freemarker.dom.XPathSupport"); - } - synchronized (STATIC_LOCK) { - xpathSupportClass = cl; - } - } - - /** - * Get the currently used org.apache.freemarker.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 (Class expectedClass : expectedClasses) { - 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/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeOutputter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeOutputter.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeOutputter.java deleted file mode 100644 index bda38ac..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeOutputter.java +++ /dev/null @@ -1,258 +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 org.apache.freemarker.dom; - -import java.util.Iterator; -import java.util.LinkedHashMap; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.util.BugException; -import org.apache.freemarker.core.util._StringUtil; -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; - -class NodeOutputter { - - private Element contextNode; - private Environment env; - private String defaultNS; - private boolean hasDefaultNS; - private boolean explicitDefaultNSPrefix; - private LinkedHashMap<String, String> namespacesToPrefixLookup = new LinkedHashMap<>(); - 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; - env = Environment.getCurrentEnvironment(); - defaultNS = env.getDefaultNS(); - 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("\""); - } - 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/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java deleted file mode 100644 index e84e977..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java +++ /dev/null @@ -1,92 +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 org.apache.freemarker.dom; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.model.ObjectWrapper; -import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateDateModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelAdapter; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.WrappingTemplateModel; -import org.apache.freemarker.core.model.impl.SimpleDate; -import org.apache.freemarker.core.model.impl.SimpleNumber; -import org.apache.freemarker.core.model.impl.SimpleScalar; -import org.w3c.dom.Node; - -/** - * Used for wrapping query result items (such as XPath query result items). Because {@link NodeModel} and such aren't - * {@link WrappingTemplateModel}-s, we can't use the actual {@link ObjectWrapper} from the {@link Environment}, also, - * even if we could, it might not be the right thing to do, because that {@link ObjectWrapper} might not even wrap - * {@link Node}-s via {@link NodeModel}. - */ -class NodeQueryResultItemObjectWrapper implements ObjectWrapper { - - static final NodeQueryResultItemObjectWrapper INSTANCE = new NodeQueryResultItemObjectWrapper(); - - private NodeQueryResultItemObjectWrapper() { - // - } - - @Override - public TemplateModel wrap(Object obj) throws TemplateModelException { - if (obj instanceof NodeModel) { - return (NodeModel) obj; - } - if (obj instanceof Node) { - return NodeModel.wrap((Node) obj); - } else { - if (obj == null) { - return null; - } - if (obj instanceof TemplateModel) { - return (TemplateModel) obj; - } - if (obj instanceof TemplateModelAdapter) { - return ((TemplateModelAdapter) obj).getTemplateModel(); - } - - if (obj instanceof String) { - return new SimpleScalar((String) obj); - } - if (obj instanceof Number) { - return new SimpleNumber((Number) obj); - } - if (obj instanceof Boolean) { - return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; - } - if (obj instanceof java.util.Date) { - if (obj instanceof java.sql.Date) { - return new SimpleDate((java.sql.Date) obj); - } - if (obj instanceof java.sql.Time) { - return new SimpleDate((java.sql.Time) obj); - } - if (obj instanceof java.sql.Timestamp) { - return new SimpleDate((java.sql.Timestamp) obj); - } - return new SimpleDate((java.util.Date) obj, TemplateDateModel.UNKNOWN); - } - throw new TemplateModelException("Don't know how to wrap a W3C DOM query result item of this type: " - + obj.getClass().getName()); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/PINodeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/PINodeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/PINodeModel.java deleted file mode 100644 index 381d4d6..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/PINodeModel.java +++ /dev/null @@ -1,45 +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 org.apache.freemarker.dom; - -import org.apache.freemarker.core.model.TemplateScalarModel; -import org.w3c.dom.ProcessingInstruction; - -class PINodeModel extends NodeModel implements TemplateScalarModel { - - public PINodeModel(ProcessingInstruction pi) { - super(pi); - } - - @Override - public String getAsString() { - return ((ProcessingInstruction) node).getData(); - } - - @Override - public String getNodeName() { - return "@pi$" + ((ProcessingInstruction) node).getTarget(); - } - - @Override - public boolean isEmpty() { - return true; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java deleted file mode 100644 index 991c93f..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java +++ /dev/null @@ -1,163 +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 org.apache.freemarker.dom; - -import java.util.List; - -import javax.xml.transform.TransformerException; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.impl.SimpleNumber; -import org.apache.freemarker.core.model.impl.SimpleScalar; -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; - -/** - * 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; - - @Override - 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() { - - @Override - public String getNamespaceForPrefix(String prefix, Node node) { - return getNamespaceForPrefix(prefix); - } - - @Override - public String getNamespaceForPrefix(String prefix) { - if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) { - return Environment.getCurrentEnvironment().getDefaultNS(); - } - return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix); - } - - @Override - public String getBaseIdentifier() { - return null; - } - - @Override - 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/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/XPathSupport.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/XPathSupport.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/XPathSupport.java deleted file mode 100644 index e94d391..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/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 org.apache.freemarker.dom; - -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.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/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java deleted file mode 100644 index 99a4249..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java +++ /dev/null @@ -1,163 +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 org.apache.freemarker.dom; - -import java.util.List; - -import javax.xml.transform.TransformerException; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelException; -import org.apache.freemarker.core.model.impl.SimpleNumber; -import org.apache.freemarker.core.model.impl.SimpleScalar; -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; - -/** - * 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;*/ - - @Override - 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() { - - @Override - public String getNamespaceForPrefix(String prefix, Node node) { - return getNamespaceForPrefix(prefix); - } - - @Override - public String getNamespaceForPrefix(String prefix) { - if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) { - return Environment.getCurrentEnvironment().getDefaultNS(); - } - return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix); - } - - @Override - public String getBaseIdentifier() { - return null; - } - - @Override - 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/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/package.html ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/package.html b/freemarker-core/src/main/java/org/apache/freemarker/dom/package.html deleted file mode 100644 index 61b1737..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/dom/package.html +++ /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. ---> -<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 default object wrapper of FreeMarker can automatically wraps W3C nodes with this. - -</body> -</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/build.gradle ---------------------------------------------------------------------- diff --git a/freemarker-dom/build.gradle b/freemarker-dom/build.gradle new file mode 100644 index 0000000..0fe800f --- /dev/null +++ b/freemarker-dom/build.gradle @@ -0,0 +1,83 @@ +/* + * 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. + */ + +title = "Apache FreeMarker DOM support" +description = """\ +FreeMarker template engine, W3C DOM (XML) wrapping support. \ +This is an optional module, useful when the data-model can contain variables that are XML nodes.""" + +dependencies { + compile project(":freemarker-core") + + compileOnly "jaxen:jaxen:1.0-FCS" + compileOnly "saxpath:saxpath:1.0-FCS" + compileOnly("xalan:xalan:2.7.0") { + // xml-apis is part of Java SE since version 1.4: + exclude group: "xml-apis", module: "xml-apis" + } + + testCompile project(":freemarker-test-utils") + testRuntime "jaxen:jaxen:1.0-FCS" + testRuntime "saxpath:saxpath:1.0-FCS" + testRuntime("xalan:xalan:2.7.0") { + // xml-apis is part of Java SE since version 1.4: + exclude group: "xml-apis", module: "xml-apis" + } +} + +jar { + manifest { + instructionReplace 'Bundle-RequiredExecutionEnvironment', 'JavaSE-1.7' + + attributes( + "Extension-name": "${project.group}:${project.name}", + "Specification-Title": project.title, + "Implementation-Title": project.title + ) + } +} + +javadoc { + title "${project.title} ${versionCanonical} API" +} + +// The identical parts of Maven "deployer" and "installer" configurations: +def mavenCommons = { callerDelegate -> + delegate = callerDelegate + + pom.project { + description project.description + } +} + +uploadArchives { + repositories { + mavenDeployer { + mavenCommons(delegate) + } + } +} + +install { + repositories { + mavenInstaller { + mavenCommons(delegate) + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/AtAtKey.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/AtAtKey.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/AtAtKey.java new file mode 100644 index 0000000..ca6ac6b --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/AtAtKey.java @@ -0,0 +1,58 @@ +/* + * 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 org.apache.freemarker.dom; + +/** + * The special hash keys that start with "@@". + */ +enum AtAtKey { + + MARKUP("@@markup"), + NESTED_MARKUP("@@nested_markup"), + ATTRIBUTES_MARKUP("@@attributes_markup"), + TEXT("@@text"), + START_TAG("@@start_tag"), + END_TAG("@@end_tag"), + QNAME("@@qname"), + NAMESPACE("@@namespace"), + LOCAL_NAME("@@local_name"), + ATTRIBUTES("@@"), + PREVIOUS_SIBLING_ELEMENT("@@previous_sibling_element"), + NEXT_SIBLING_ELEMENT("@@next_sibling_element"); + + private final String key; + + public String getKey() { + return key; + } + + AtAtKey(String key) { + this.key = key; + } + + public static boolean containsKey(String key) { + for (AtAtKey item : AtAtKey.values()) { + if (item.getKey().equals(key)) { + return true; + } + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java new file mode 100644 index 0000000..cc510c4 --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java @@ -0,0 +1,69 @@ +/* + * 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 org.apache.freemarker.dom; + +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.model.TemplateScalarModel; +import org.w3c.dom.Attr; + +class AttributeNodeModel extends NodeModel implements TemplateScalarModel { + + public AttributeNodeModel(Attr att) { + super(att); + } + + @Override + public String getAsString() { + return ((Attr) node).getValue(); + } + + @Override + public String getNodeName() { + String result = node.getLocalName(); + if (result == null || result.equals("")) { + result = node.getNodeName(); + } + return result; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + String getQualifiedName() { + String nsURI = node.getNamespaceURI(); + if (nsURI == null || nsURI.equals("")) + return node.getNodeName(); + Environment env = Environment.getCurrentEnvironment(); + String defaultNS = env.getDefaultNS(); + String prefix = null; + if (nsURI.equals(defaultNS)) { + prefix = "D"; + } else { + prefix = env.getPrefixForNamespace(nsURI); + } + if (prefix == null) { + return null; + } + return prefix + ":" + node.getLocalName(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java new file mode 100644 index 0000000..264c0db --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java @@ -0,0 +1,46 @@ +/* + * 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 org.apache.freemarker.dom; + +import org.apache.freemarker.core.model.TemplateScalarModel; +import org.w3c.dom.CharacterData; +import org.w3c.dom.Comment; + +class CharacterDataNodeModel extends NodeModel implements TemplateScalarModel { + + public CharacterDataNodeModel(CharacterData text) { + super(text); + } + + @Override + public String getAsString() { + return ((org.w3c.dom.CharacterData) node).getData(); + } + + @Override + public String getNodeName() { + return (node instanceof Comment) ? "@comment" : "@text"; + } + + @Override + public boolean isEmpty() { + return true; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/DOMDefaultObjectWrapperExtension.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DOMDefaultObjectWrapperExtension.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DOMDefaultObjectWrapperExtension.java new file mode 100644 index 0000000..90c50b7 --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DOMDefaultObjectWrapperExtension.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.freemarker.dom; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; +import org.apache.freemarker.core.model.impl.DefaultObjectWrapperExtension; +import org.w3c.dom.Node; + +/** + * Add this extension to {@link DefaultObjectWrapper} if you want {@link Node}-s to be wrapped into {@link NodeModel}-s. + */ +public class DOMDefaultObjectWrapperExtension extends DefaultObjectWrapperExtension { + + /** + * The singleton instance of this class. + */ + public static final DOMDefaultObjectWrapperExtension INSTANCE = new DOMDefaultObjectWrapperExtension(); + + private DOMDefaultObjectWrapperExtension() { + // private to hide it from outside + } + + @Override + public TemplateModel wrap(Object obj) { + if (obj instanceof Node) { + return NodeModel.wrap((Node) obj); + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java new file mode 100644 index 0000000..876b3cf --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentModel.java @@ -0,0 +1,76 @@ +/* + * 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 org.apache.freemarker.dom; + +import org.apache.freemarker.core.Environment; +import org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +/** + * A class that wraps the root node of a parsed XML document, using + * the W3C DOM_WRAPPER API. + */ + +class DocumentModel extends NodeModel implements TemplateHashModel { + + private ElementModel rootElement; + + DocumentModel(Document doc) { + super(doc); + } + + @Override + public String getNodeName() { + return "@document"; + } + + @Override + public TemplateModel get(String key) throws TemplateModelException { + if (key.equals("*")) { + return getRootElement(); + } else if (key.equals("**")) { + NodeList nl = ((Document) node).getElementsByTagName("*"); + return new NodeListModel(nl, this); + } else if (DomStringUtil.isXMLNameLike(key)) { + ElementModel em = (ElementModel) NodeModel.wrap(((Document) node).getDocumentElement()); + if (em.matchesName(key, Environment.getCurrentEnvironment())) { + return em; + } else { + return new NodeListModel(this); + } + } + return super.get(key); + } + + ElementModel getRootElement() { + if (rootElement == null) { + rootElement = (ElementModel) wrap(((Document) node).getDocumentElement()); + } + return rootElement; + } + + @Override + public boolean isEmpty() { + return false; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java new file mode 100644 index 0000000..3448f77 --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java @@ -0,0 +1,56 @@ +/* + * 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 org.apache.freemarker.dom; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.w3c.dom.DocumentType; +import org.w3c.dom.ProcessingInstruction; + +class DocumentTypeModel extends NodeModel { + + public DocumentTypeModel(DocumentType docType) { + super(docType); + } + + public String getAsString() { + return ((ProcessingInstruction) node).getData(); + } + + public TemplateSequenceModel getChildren() throws TemplateModelException { + throw new TemplateModelException("entering the child nodes of a DTD node is not currently supported"); + } + + @Override + public TemplateModel get(String key) throws TemplateModelException { + throw new TemplateModelException("accessing properties of a DTD is not currently supported"); + } + + @Override + public String getNodeName() { + return "@document_type$" + node.getNodeName(); + } + + @Override + public boolean isEmpty() { + return true; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomLog.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomLog.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomLog.java new file mode 100644 index 0000000..a1f6f0c --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomLog.java @@ -0,0 +1,32 @@ +/* + * 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 org.apache.freemarker.dom; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class DomLog { + + private DomLog() { + // + } + + public static final Logger LOG = LoggerFactory.getLogger("org.apache.freemarker.dom"); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomStringUtil.java ---------------------------------------------------------------------- diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomStringUtil.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomStringUtil.java new file mode 100644 index 0000000..f5b58f8 --- /dev/null +++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/DomStringUtil.java @@ -0,0 +1,67 @@ +/* + * 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 org.apache.freemarker.dom; + +/** + * 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. + */ +final class DomStringUtil { + + private DomStringUtil() { + // Not meant to be instantiated + } + + static boolean isXMLNameLike(String name) { + return isXMLNameLike(name, 0); + } + + /** + * Check if the name looks like an XML element name. + * + * @param firstCharIdx The index of the character in the string parameter that we treat as the beginning of the + * string to check. This is to spare substringing that has become more expensive in Java 7. + * + * @return whether the name is a valid XML element name. (This routine might only be 99% accurate. REVISIT) + */ + static boolean isXMLNameLike(String name, int firstCharIdx) { + int ln = name.length(); + for (int i = firstCharIdx; i < ln; i++) { + char c = name.charAt(i); + if (i == firstCharIdx && (c == '-' || c == '.' || Character.isDigit(c))) { + return false; + } + if (!Character.isLetterOrDigit(c) && c != '_' && c != '-' && c != '.') { + if (c == ':') { + if (i + 1 < ln && name.charAt(i + 1) == ':') { + // "::" is used in XPath + return false; + } + // We don't return here, as a lonely ":" is allowed. + } else { + return false; + } + } + } + return true; + } + +}
