You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/05/21 21:34:59 UTC
[4/6] incubator-freemarker git commit: Factored out freemarker-dom
from freemarker-core. Also added mechanism to "inject" DOM wrapping
capability into DefaultObjectWrapper on configuration time. See details
below.
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;
+ }
+
+}