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:56 UTC

[1/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.

Repository: incubator-freemarker
Updated Branches:
  refs/heads/3 053b82e7f -> be5568979


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-test-utils/build.gradle
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/build.gradle b/freemarker-test-utils/build.gradle
index 03d0087..02d5d8b 100644
--- a/freemarker-test-utils/build.gradle
+++ b/freemarker-test-utils/build.gradle
@@ -1,3 +1,22 @@
+/*
+ * 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 Test Utiltities"
 description = """\
 FreeMarker template engine, testing utilities for internal use; this is not a published module."""

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
index 9e4e7f0..932c4ae 100644
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
+++ b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/TemplateTestSuite.java
@@ -36,7 +36,6 @@ import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core.util._StringUtil;
-import org.apache.freemarker.dom.NodeModel;
 import org.junit.runner.RunWith;
 import org.junit.runners.AllTests;
 import org.w3c.dom.Attr;
@@ -108,8 +107,6 @@ public abstract class TemplateTestSuite extends TestSuite {
 
     public TemplateTestSuite() {
         try {
-            NodeModel.useJaxenXPathSupport();
-
             String filterStr = System.getProperty(TEST_FILTER_PROPERTY_NAME);
             testCaseNameFilter = filterStr != null ? Pattern.compile(filterStr) : null;
             if (testCaseNameFilter != null) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-test-utils/src/main/java/org/apache/freemarker/test/XMLLoader.java
----------------------------------------------------------------------
diff --git a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/XMLLoader.java b/freemarker-test-utils/src/main/java/org/apache/freemarker/test/XMLLoader.java
deleted file mode 100644
index 461c21e..0000000
--- a/freemarker-test-utils/src/main/java/org/apache/freemarker/test/XMLLoader.java
+++ /dev/null
@@ -1,138 +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.test;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.MalformedURLException;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.freemarker.dom.NodeModel;
-import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public final class XMLLoader {
-
-    private static final Object STATIC_LOCK = new Object();
-    
-    static private DocumentBuilderFactory docBuilderFactory;
-    
-    private XMLLoader() {
-        //
-    }
-    
-    /**
-     * Convenience method to invoke a {@link NodeModel} from a SAX {@link InputSource}.
-     */
-    static public NodeModel toModel(InputSource is, boolean simplify)
-        throws SAXException, IOException, ParserConfigurationException {
-        DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
-        final Document doc;
-        try {
-            doc = builder.parse(is);
-        } catch (MalformedURLException e) {
-            // This typical error has an error message that is hard to understand, so let's translate it:
-            if (is.getSystemId() == null && is.getCharacterStream() == null && is.getByteStream() == null) {
-                throw new MalformedURLException(
-                        "The SAX InputSource has systemId == null && characterStream == null && byteStream == null. "
-                        + "This is often because it was created with a null InputStream or Reader, which is often because "
-                        + "the XML file it should point to was not found. "
-                        + "(The original exception was: " + e + ")");
-            } else {
-                throw e;
-            }
-        }
-        if (simplify) {
-            NodeModel.simplify(doc);
-        }
-        return NodeModel.wrap(doc);
-    }
-    
-    /**
-     * Same as {@link #toModel(InputSource, boolean) parse(is, true)}.
-     */
-    static public NodeModel toModel(InputSource is) throws SAXException, IOException, ParserConfigurationException {
-        return toModel(is, true);
-    }
-    
-    /**
-     * Same as {@link #toModel(InputSource, boolean)}, but loads from a {@link File}; don't miss the security
-     * warnings documented there.
-     */
-    static public NodeModel toModel(File f, boolean simplify) 
-    throws SAXException, IOException, ParserConfigurationException {
-        DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
-        Document doc = builder.parse(f);
-        if (simplify) {
-            NodeModel.simplify(doc);
-        }
-        return NodeModel.wrap(doc);
-    }
-    
-    /**
-     * Same as {@link #toModel(InputSource, boolean) parse(source, true)}, but loads from a {@link String}.
-     */
-    static public NodeModel toModel(File f) throws SAXException, IOException, ParserConfigurationException {
-        return toModel(f, true);
-    }
-    
-    /**
-     * Same as {@link #toModel(InputSource, boolean)}, but loads from a {@link File}; don't miss the security
-     * warnings documented there.
-     */
-    static public NodeModel toModel(String content, boolean simplify) 
-    throws SAXException, IOException, ParserConfigurationException {
-        return toModel(toInputSource(content));
-    }
-    
-    /**
-     * Same as {@link #toModel(InputSource, boolean) parse(source, true)}, but loads from a {@link String}.
-     */
-    static public NodeModel toModel(String content) throws SAXException, IOException, ParserConfigurationException {
-        return toModel(content, true);
-    }
-    
-    public static Document toDOM(String content) throws SAXException, IOException, ParserConfigurationException {
-        DocumentBuilder builder =  getDocumentBuilderFactory().newDocumentBuilder();
-        return builder.parse(toInputSource(content));
-    }
-    
-    static private DocumentBuilderFactory getDocumentBuilderFactory() {
-        synchronized (STATIC_LOCK) {
-            if (docBuilderFactory == null) {
-                DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
-                newFactory.setNamespaceAware(true);
-                newFactory.setIgnoringElementContentWhitespace(true);
-                docBuilderFactory = newFactory;  // We only write it out when the initialization was full 
-            }
-            return docBuilderFactory;
-        }
-    }
-
-    private static InputSource toInputSource(String content) {
-        return new InputSource(new StringReader(content));
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/settings.gradle
----------------------------------------------------------------------
diff --git a/settings.gradle b/settings.gradle
index 3d18991..47fc644 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -25,4 +25,5 @@ include 'freemarker-core-test-java8'
 include 'freemarker-servlet'
 include 'freemarker-test-utils'
 include 'freemarker-manual'
+include 'freemarker-dom'
 


[3/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.

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
new file mode 100644
index 0000000..220f414
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/ElementModel.java
@@ -0,0 +1,234 @@
+/*
+ * 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.Collections;
+
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.util._StringUtil;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+class ElementModel extends NodeModel implements TemplateScalarModel {
+
+    public ElementModel(Element element) {
+        super(element);
+    }
+    
+    @Override
+    public boolean isEmpty() {
+        return false;
+    }
+    
+    /**
+     * An Element node supports various hash keys.
+     * Any key that corresponds to the tag name of any child elements
+     * returns a sequence of those elements. The special key "*" returns 
+     * all the element's direct children.
+     * The "**" key return all the element's descendants in the order they
+     * occur in the document.
+     * Any key starting with '@' is taken to be the name of an element attribute.
+     * The special key "@@" returns a hash of all the element's attributes.
+     * The special key "/" returns the root document node associated with this element.
+     */
+    @Override
+    public TemplateModel get(String key) throws TemplateModelException {
+        if (key.equals("*")) {
+            NodeListModel ns = new NodeListModel(this);
+            TemplateSequenceModel children = getChildNodes();
+            for (int i = 0; i < children.size(); i++) {
+                NodeModel child = (NodeModel) children.get(i);
+                if (child.node.getNodeType() == Node.ELEMENT_NODE) {
+                    ns.add(child);
+                }
+            }
+            return ns;
+        } else if (key.equals("**")) {
+            return new NodeListModel(((Element) node).getElementsByTagName("*"), this);    
+        } else if (key.startsWith("@")) {
+            if (key.startsWith("@@")) {
+                if (key.equals(AtAtKey.ATTRIBUTES.getKey())) {
+                    return new NodeListModel(node.getAttributes(), this);
+                } else if (key.equals(AtAtKey.START_TAG.getKey())) {
+                    NodeOutputter nodeOutputter = new NodeOutputter(node);
+                    return new SimpleScalar(nodeOutputter.getOpeningTag((Element) node));
+                } else if (key.equals(AtAtKey.END_TAG.getKey())) {
+                    NodeOutputter nodeOutputter = new NodeOutputter(node);
+                    return new SimpleScalar(nodeOutputter.getClosingTag((Element) node));
+                } else if (key.equals(AtAtKey.ATTRIBUTES_MARKUP.getKey())) {
+                    StringBuilder buf = new StringBuilder();
+                    NodeOutputter nu = new NodeOutputter(node);
+                    nu.outputContent(node.getAttributes(), buf);
+                    return new SimpleScalar(buf.toString().trim());
+                } else if (key.equals(AtAtKey.PREVIOUS_SIBLING_ELEMENT.getKey())) {
+                    Node previousSibling = node.getPreviousSibling();
+                    while (previousSibling != null && !isSignificantNode(previousSibling)) {
+                        previousSibling = previousSibling.getPreviousSibling();
+                    }
+                    return previousSibling != null && previousSibling.getNodeType() == Node.ELEMENT_NODE
+                            ? wrap(previousSibling) : new NodeListModel(Collections.emptyList(), null);  
+                } else if (key.equals(AtAtKey.NEXT_SIBLING_ELEMENT.getKey())) {
+                    Node nextSibling = node.getNextSibling();
+                    while (nextSibling != null && !isSignificantNode(nextSibling)) {
+                        nextSibling = nextSibling.getNextSibling();
+                    }
+                    return nextSibling != null && nextSibling.getNodeType() == Node.ELEMENT_NODE
+                            ? wrap(nextSibling) : new NodeListModel(Collections.emptyList(), null);  
+                } else {
+                    // We don't know anything like this that's element-specific; fall back 
+                    return super.get(key);
+                }
+            } else { // Starts with "@", but not with "@@"
+                if (DomStringUtil.isXMLNameLike(key, 1)) {
+                    Attr att = getAttribute(key.substring(1));
+                    if (att == null) { 
+                        return new NodeListModel(this);
+                    }
+                    return wrap(att);
+                } else if (key.equals("@*")) {
+                    return new NodeListModel(node.getAttributes(), this);
+                } else {
+                    // We don't know anything like this that's element-specific; fall back 
+                    return super.get(key);
+                }
+            }
+        } else if (DomStringUtil.isXMLNameLike(key)) {
+            // We interpret key as an element name
+            NodeListModel result = ((NodeListModel) getChildNodes()).filterByName(key);
+            return result.size() != 1 ? result : result.get(0);
+        } else {
+            // We don't anything like this that's element-specific; fall back 
+            return super.get(key);
+        }
+    }
+
+    @Override
+    public String getAsString() throws TemplateModelException {
+        NodeList nl = node.getChildNodes();
+        String result = "";
+        for (int i = 0; i < nl.getLength(); i++) {
+            Node child = nl.item(i);
+            int nodeType = child.getNodeType();
+            if (nodeType == Node.ELEMENT_NODE) {
+                String msg = "Only elements with no child elements can be processed as text."
+                             + "\nThis element with name \""
+                             + node.getNodeName()
+                             + "\" has a child element named: " + child.getNodeName();
+                throw new TemplateModelException(msg);
+            } else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
+                result += child.getNodeValue();
+            }
+        }
+        return result;
+    }
+    
+    @Override
+    public String getNodeName() {
+        String result = node.getLocalName();
+        if (result == null || result.equals("")) {
+            result = node.getNodeName();
+        }
+        return result;
+    }
+    
+    @Override
+    String getQualifiedName() {
+        String nodeName = getNodeName();
+        String nsURI = getNodeNamespace();
+        if (nsURI == null || nsURI.length() == 0) {
+            return nodeName;
+        }
+        Environment env = Environment.getCurrentEnvironment();
+        String defaultNS = env.getDefaultNS();
+        String prefix;
+        if (defaultNS != null && defaultNS.equals(nsURI)) {
+            prefix = "";
+        } else {
+            prefix = env.getPrefixForNamespace(nsURI);
+            
+        }
+        if (prefix == null) {
+            return null; // We have no qualified name, because there is no prefix mapping
+        }
+        if (prefix.length() > 0) {
+            prefix += ":";
+        }
+        return prefix + nodeName;
+    }
+    
+    private Attr getAttribute(String qname) {
+        Element element = (Element) node;
+        Attr result = element.getAttributeNode(qname);
+        if (result != null)
+            return result;
+        int colonIndex = qname.indexOf(':');
+        if (colonIndex > 0) {
+            String prefix = qname.substring(0, colonIndex);
+            String uri;
+            if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) {
+                uri = Environment.getCurrentEnvironment().getDefaultNS();
+            } else {
+                uri = Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix);
+            }
+            String localName = qname.substring(1 + colonIndex);
+            if (uri != null) {
+                result = element.getAttributeNodeNS(uri, localName);
+            }
+        }
+        return result;
+    }
+    
+    private boolean isSignificantNode(Node node) throws TemplateModelException {
+        return (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE)
+                ? !isBlankXMLText(node.getTextContent())
+                : node.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE && node.getNodeType() != Node.COMMENT_NODE;
+    }
+    
+    private boolean isBlankXMLText(String s) {
+        if (s == null) {
+            return true;
+        }
+        for (int i = 0; i < s.length(); i++) {
+            if (!isXMLWhiteSpace(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * White space according the XML spec. 
+     */
+    private boolean isXMLWhiteSpace(char c) {
+        return c == ' ' || c == '\t' || c == '\n' | c == '\r';
+    }
+
+    boolean matchesName(String name, Environment env) {
+        return _StringUtil.matchesQName(name, getNodeName(), getNodeNamespace(), env);
+    }
+}
\ 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/JaxenXPathSupport.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
new file mode 100644
index 0000000..3e52836
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
@@ -0,0 +1,243 @@
+/*
+ * 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.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.freemarker.core.CustomStateKey;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core.Template;
+import org.apache.freemarker.core.TemplateException;
+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.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateNumberModel;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+import org.apache.freemarker.core.util._ObjectHolder;
+import org.jaxen.BaseXPath;
+import org.jaxen.Function;
+import org.jaxen.FunctionCallException;
+import org.jaxen.FunctionContext;
+import org.jaxen.JaxenException;
+import org.jaxen.NamespaceContext;
+import org.jaxen.Navigator;
+import org.jaxen.UnresolvableException;
+import org.jaxen.VariableContext;
+import org.jaxen.XPathFunctionContext;
+import org.jaxen.dom.DocumentNavigator;
+import org.w3c.dom.Document;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ */
+class JaxenXPathSupport implements XPathSupport {
+
+    private static final CustomStateKey<Map<String, BaseXPath>> XPATH_CACHE_ATTR
+            = new CustomStateKey<Map<String, BaseXPath>>() {
+        @Override
+        protected Map<String, BaseXPath> create() {
+            return new HashMap<String, BaseXPath>();
+        }
+    };
+
+        // [2.4] Can't we just use Collections.emptyList()? 
+    private final static ArrayList EMPTY_ARRAYLIST = new ArrayList();
+
+    @Override
+    public TemplateModel executeQuery(Object context, String xpathQuery) throws TemplateModelException {
+        try {
+            BaseXPath xpath;
+            Map<String, BaseXPath> xpathCache = Environment.getCurrentEnvironmentNotNull().getCurrentTemplateNotNull()
+                    .getCustomState(XPATH_CACHE_ATTR);
+            synchronized (xpathCache) {
+                xpath = xpathCache.get(xpathQuery);
+                if (xpath == null) {
+                    xpath = new BaseXPath(xpathQuery, FM_DOM_NAVIGATOR);
+                    xpath.setNamespaceContext(customNamespaceContext);
+                    xpath.setFunctionContext(FM_FUNCTION_CONTEXT);
+                    xpath.setVariableContext(FM_VARIABLE_CONTEXT);
+                    xpathCache.put(xpathQuery, xpath);
+                }
+            }
+            List result = xpath.selectNodes(context != null ? context : EMPTY_ARRAYLIST);
+            if (result.size() == 1) {
+                return NodeQueryResultItemObjectWrapper.INSTANCE.wrap(result.get(0));
+            }
+            NodeListModel nlm = new NodeListModel(result, null);
+            nlm.xpathSupport = this;
+            return nlm;
+        } catch (UndeclaredThrowableException e) {
+            Throwable t  = e.getUndeclaredThrowable();
+            if (t instanceof TemplateModelException) {
+                throw (TemplateModelException) t;
+            }
+            throw e;
+        } catch (JaxenException je) {
+            throw new TemplateModelException(je);
+        }
+    }
+
+    static private final NamespaceContext customNamespaceContext = new NamespaceContext() {
+        
+        @Override
+        public String translateNamespacePrefixToUri(String prefix) {
+            if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) {
+                return Environment.getCurrentEnvironment().getDefaultNS();
+            }
+            return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix);
+        }
+    };
+
+    private static final VariableContext FM_VARIABLE_CONTEXT = new VariableContext() {
+        @Override
+        public Object getVariableValue(String namespaceURI, String prefix, String localName)
+        throws UnresolvableException {
+            try {
+                TemplateModel model = Environment.getCurrentEnvironment().getVariable(localName);
+                if (model == null) {
+                    throw new UnresolvableException("Variable \"" + localName + "\" not found.");
+                }
+                if (model instanceof TemplateScalarModel) {
+                    return ((TemplateScalarModel) model).getAsString();
+                }
+                if (model instanceof TemplateNumberModel) {
+                    return ((TemplateNumberModel) model).getAsNumber();
+                }
+                if (model instanceof TemplateDateModel) {
+                    return ((TemplateDateModel) model).getAsDate();
+                }
+                if (model instanceof TemplateBooleanModel) {
+                    return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
+                }
+            } catch (TemplateModelException e) {
+                throw new UndeclaredThrowableException(e);
+            }
+            throw new UnresolvableException(
+                    "Variable \"" + localName + "\" exists, but it's not a string, number, date, or boolean");
+        }
+    };
+     
+    private static final FunctionContext FM_FUNCTION_CONTEXT = new XPathFunctionContext() {
+        @Override
+        public Function getFunction(String namespaceURI, String prefix, String localName)
+        throws UnresolvableException {
+            try {
+                return super.getFunction(namespaceURI, prefix, localName);
+            } catch (UnresolvableException e) {
+                return super.getFunction(null, null, localName);
+            }
+        }
+    };
+    
+    /**
+     * Stores the the template parsed as {@link Document} in the template itself.
+     */
+    private static final CustomStateKey<_ObjectHolder<Document>> FM_DOM_NAVIAGOTOR_CACHED_DOM
+            = new CustomStateKey<_ObjectHolder<Document>>() {
+        @Override
+        protected _ObjectHolder<Document> create() {
+            return new _ObjectHolder<>(null);
+        }
+    };
+     
+    private static final Navigator FM_DOM_NAVIGATOR = new DocumentNavigator() {
+        @Override
+        public Object getDocument(String uri) throws FunctionCallException {
+            try {
+                Template raw = getTemplate(uri);
+                _ObjectHolder<Document> docHolder = Environment.getCurrentEnvironmentNotNull()
+                        .getCurrentTemplateNotNull().getCustomState(FM_DOM_NAVIAGOTOR_CACHED_DOM);
+                synchronized (docHolder) {
+                    Document doc = docHolder.get();
+                    if (doc == null) {
+                        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+                        factory.setNamespaceAware(true);
+                        DocumentBuilder builder = factory.newDocumentBuilder();
+                        FmEntityResolver er = new FmEntityResolver();
+                        builder.setEntityResolver(er);
+                        doc = builder.parse(createInputSource(null, raw));
+                        // If the entity resolver got called 0 times, the document
+                        // is standalone, so we can safely cache it
+                        if (er.getCallCount() == 0) {
+                            docHolder.set(doc);
+                        }
+                    }
+                    return doc;
+                }
+            } catch (Exception e) {
+                throw new FunctionCallException("Failed to parse document for URI: " + uri, e);
+            }
+        }
+    };
+
+    // [FM3] Look into this "hidden" feature
+    static Template getTemplate(String systemId) throws IOException {
+        Environment env = Environment.getCurrentEnvironment();
+        String templatePath = env.getCurrentTemplate().getLookupName();
+        int lastSlash = templatePath.lastIndexOf('/');
+        templatePath = lastSlash == -1 ? "" : templatePath.substring(0, lastSlash + 1);
+        systemId = env.toFullTemplateName(templatePath, systemId);
+        return env.getConfiguration().getTemplate(systemId, env.getLocale());
+    }
+
+    private static InputSource createInputSource(String publicId, Template raw) throws IOException, SAXException {
+        StringWriter sw = new StringWriter();
+        try {
+            raw.process(Collections.EMPTY_MAP, sw);
+        } catch (TemplateException e) {
+            throw new SAXException(e);
+        }
+        InputSource is = new InputSource();
+        is.setPublicId(publicId);
+        is.setSystemId(raw.getLookupName());
+        is.setCharacterStream(new StringReader(sw.toString()));
+        return is;
+    }
+
+    private static class FmEntityResolver implements EntityResolver {
+        private int callCount = 0;
+        
+        @Override
+        public InputSource resolveEntity(String publicId, String systemId)
+        throws SAXException, IOException {
+            ++callCount;
+            return createInputSource(publicId, getTemplate(systemId));
+        }
+        
+        int getCallCount() {
+            return callCount;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
new file mode 100644
index 0000000..333bb5c
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeListModel.java
@@ -0,0 +1,219 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.Environment;
+import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
+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.TemplateNumberModel;
+import org.apache.freemarker.core.model.TemplateScalarModel;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.impl.SimpleScalar;
+import org.apache.freemarker.core.model.impl.SimpleSequence;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Used when the result set contains 0 or multiple nodes; shouldn't be used when you have exactly 1 node. For exactly 1
+ * node, use {@link NodeModel#wrap(Node)}, because {@link NodeModel} subclasses can have extra features building on that
+ * restriction, like single elements with text content can be used as FTL string-s.
+ * <p>
+ * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as
+ * {@linkplain Configuration#getSharedVariables() shared variable}.
+ */
+class NodeListModel extends SimpleSequence implements TemplateHashModel, _UnexpectedTypeErrorExplainerTemplateModel {
+    
+    // [2.4] make these private
+    NodeModel contextNode;
+    XPathSupport xpathSupport;
+    
+    NodeListModel(Node contextNode) {
+        this(NodeModel.wrap(contextNode));
+    }
+    
+    NodeListModel(NodeModel contextNode) {
+        super(NodeQueryResultItemObjectWrapper.INSTANCE);
+        this.contextNode = contextNode;
+    }
+    
+    NodeListModel(NodeList nodeList, NodeModel contextNode) {
+        super(NodeQueryResultItemObjectWrapper.INSTANCE);
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            list.add(nodeList.item(i));
+        }
+        this.contextNode = contextNode;
+    }
+    
+    NodeListModel(NamedNodeMap nodeList, NodeModel contextNode) {
+        super(NodeQueryResultItemObjectWrapper.INSTANCE);
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            list.add(nodeList.item(i));
+        }
+        this.contextNode = contextNode;
+    }
+    
+    NodeListModel(List list, NodeModel contextNode) {
+        super(list, NodeQueryResultItemObjectWrapper.INSTANCE);
+        this.contextNode = contextNode;
+    }
+    
+    NodeListModel filterByName(String name) throws TemplateModelException {
+        NodeListModel result = new NodeListModel(contextNode);
+        int size = size();
+        if (size == 0) {
+            return result;
+        }
+        Environment env = Environment.getCurrentEnvironment();
+        for (int i = 0; i < size; i++) {
+            NodeModel nm = (NodeModel) get(i);
+            if (nm instanceof ElementModel) {
+                if (((ElementModel) nm).matchesName(name, env)) {
+                    result.add(nm);
+                }
+            }
+        }
+        return result;
+    }
+    
+    @Override
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+    
+    @Override
+    public TemplateModel get(String key) throws TemplateModelException {
+        if (size() == 1) {
+            NodeModel nm = (NodeModel) get(0);
+            return nm.get(key);
+        }
+        if (key.startsWith("@@")) {
+            if (key.equals(AtAtKey.MARKUP.getKey()) 
+                    || key.equals(AtAtKey.NESTED_MARKUP.getKey()) 
+                    || key.equals(AtAtKey.TEXT.getKey())) {
+                StringBuilder result = new StringBuilder();
+                for (int i = 0; i < size(); i++) {
+                    NodeModel nm = (NodeModel) get(i);
+                    TemplateScalarModel textModel = (TemplateScalarModel) nm.get(key);
+                    result.append(textModel.getAsString());
+                }
+                return new SimpleScalar(result.toString());
+            } else if (key.length() != 2 /* to allow "@@" to fall through */) {
+                // As @@... would cause exception in the XPath engine, we throw a nicer exception now. 
+                if (AtAtKey.containsKey(key)) {
+                    throw new TemplateModelException(
+                            "\"" + key + "\" is only applicable to a single XML node, but it was applied on "
+                            + (size() != 0
+                                    ? size() + " XML nodes (multiple matches)."
+                                    : "an empty list of XML nodes (no matches)."));
+                } else {
+                    throw new TemplateModelException("Unsupported @@ key: " + key);
+                }
+            }
+        }
+        if (DomStringUtil.isXMLNameLike(key) 
+                || ((key.startsWith("@")
+                        && (DomStringUtil.isXMLNameLike(key, 1)  || key.equals("@@") || key.equals("@*"))))
+                || key.equals("*") || key.equals("**")) {
+            NodeListModel result = new NodeListModel(contextNode);
+            for (int i = 0; i < size(); i++) {
+                NodeModel nm = (NodeModel) get(i);
+                if (nm instanceof ElementModel) {
+                    TemplateSequenceModel tsm = (TemplateSequenceModel) nm.get(key);
+                    if (tsm != null) {
+                        int size = tsm.size();
+                        for (int j = 0; j < size; j++) {
+                            result.add(tsm.get(j));
+                        }
+                    }
+                }
+            }
+            if (result.size() == 1) {
+                return result.get(0);
+            }
+            return result;
+        }
+        XPathSupport xps = getXPathSupport();
+        if (xps != null) {
+            Object context = (size() == 0) ? null : rawNodeList(); 
+            return xps.executeQuery(context, 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);
+        }
+    }
+    
+    private List rawNodeList() throws TemplateModelException {
+        int size = size();
+        ArrayList al = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            al.add(((NodeModel) get(i)).node);
+        }
+        return al;
+    }
+    
+    XPathSupport getXPathSupport() throws TemplateModelException {
+        if (xpathSupport == null) {
+            if (contextNode != null) {
+                xpathSupport = contextNode.getXPathSupport();
+            } else if (size() > 0) {
+                xpathSupport = ((NodeModel) get(0)).getXPathSupport();
+            }
+        }
+        return xpathSupport;
+    }
+
+    @Override
+    public Object[] explainTypeError(Class[] expectedClasses) {
+        for (Class expectedClass : expectedClasses) {
+            if (TemplateScalarModel.class.isAssignableFrom(expectedClass)
+                    || TemplateDateModel.class.isAssignableFrom(expectedClass)
+                    || TemplateNumberModel.class.isAssignableFrom(expectedClass)
+                    || TemplateBooleanModel.class.isAssignableFrom(expectedClass)) {
+                return newTypeErrorExplanation("string");
+            } else if (TemplateNodeModel.class.isAssignableFrom(expectedClass)) {
+                return newTypeErrorExplanation("node");
+            }
+        }
+        return null;
+    }
+
+    private Object[] newTypeErrorExplanation(String type) {
+        return new Object[] {
+                "This XML query result can't be used as ", type, " because for that it had to contain exactly "
+                + "1 XML node, but it contains ", Integer.valueOf(size()), " nodes. "
+                + "That is, the constructing XML query has found ",
+                isEmpty()
+                    ? "no matches."
+                    : "multiple matches."
+                };
+    }
+
+}
\ 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/NodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java
new file mode 100644
index 0000000..37a5c7d
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeModel.java
@@ -0,0 +1,613 @@
+/*
+ * 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-dom/src/main/java/org/apache/freemarker/dom/NodeOutputter.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeOutputter.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeOutputter.java
new file mode 100644
index 0000000..bda38ac
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeOutputter.java
@@ -0,0 +1,258 @@
+/*
+ * 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-dom/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java
new file mode 100644
index 0000000..e84e977
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/NodeQueryResultItemObjectWrapper.java
@@ -0,0 +1,92 @@
+/*
+ * 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-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
new file mode 100644
index 0000000..381d4d6
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/PINodeModel.java
@@ -0,0 +1,45 @@
+/*
+ * 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-dom/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java
new file mode 100644
index 0000000..991c93f
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/SunInternalXalanXPathSupport.java
@@ -0,0 +1,163 @@
+/*
+ * 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-dom/src/main/java/org/apache/freemarker/dom/XPathSupport.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/XPathSupport.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/XPathSupport.java
new file mode 100644
index 0000000..e94d391
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/XPathSupport.java
@@ -0,0 +1,30 @@
+/*
+ * 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-dom/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java b/freemarker-dom/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java
new file mode 100644
index 0000000..99a4249
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/XalanXPathSupport.java
@@ -0,0 +1,163 @@
+/*
+ * 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



[5/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.

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns5.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns5.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns5.ftl
deleted file mode 100644
index edc3b4a..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns5.ftl
+++ /dev/null
@@ -1,28 +0,0 @@
-<#ftl ns_prefixes = {"D": "http://y.com", "xx" : "http://x.com"}>
-<#--
-  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.
--->
-<#assign r = doc["N:root"]>
-${r["N:t1"][0]?default('-')} = No NS
-${r["xx:t2"][0]?default('-')} = x NS
-${r["t3"][0]?default('-')} = y NS
-${r["xx:t4"][0]?default('-')} = x NS
-${r["//t1"][0]?default('-')} = No NS
-${r["//t2"][0]?default('-')} = -
-${r["//t3"][0]?default('-')} = -
-${r["//t4"][0]?default('-')} = -

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
index 866da69..159266b 100644
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
+++ b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/testcases.xml
@@ -73,7 +73,6 @@ Note that for the incompatible_improvements setting you can specify a list of ve
    <testCase name="default-object-wrapper">
       <setting api_builtin_enabled="true" />
    </testCase>
-   <testCase name="default-xmlns" />
    <testCase name="encoding-builtins" />
    <testCase name="escapes" />
    <testCase name="hashliteral" />
@@ -172,13 +171,6 @@ Note that for the incompatible_improvements setting you can specify a list of ve
    <testCase name="variables"/>
    <testCase name="whitespace-trim"/>
    <testCase name="wstrip-in-header"/>
-   <testCase name="xml-fragment" />
-   <testCase name="xmlns1" />
-   <testCase name="xmlns2" template="xmlns1.ftl"  expected="xmlns1.txt" />
-   <testCase name="xmlns3" />
-   <testCase name="xmlns4" />
-   <testCase name="xmlns5" />
-   <testCase name="xml-ns_prefix-scope" template="xml-ns_prefix-scope-main.ftl" />
    <testCase name="hashconcat"/>
    <testCase name="new-defaultresolver" />
    <testCase name="new-unrestricted" template="new-defaultresolver.ftl" expected="new-defaultresolver.txt">

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml
deleted file mode 100644
index d1fe3dc..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.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.
--->
-<person>
-    <gender>male</gender>
-    <name>pradeep</name>
-    <dob>12th August</dob><address>Chennai, India</address>
-    <!--This is a comment Node -->
-    <?xml-stylesheet type="text/css" href="style.css"?>
-    <profession>Software Engineer</profession>
-    <![CDATA[    ]]>
-    <hobby>gardening</hobby>
-    <![CDATA[this is a valid cdata]]>
-    <phone>12345678</phone>
-</person>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/build.gradle
----------------------------------------------------------------------
diff --git a/freemarker-core/build.gradle b/freemarker-core/build.gradle
index 9de09c4..5adcf87 100644
--- a/freemarker-core/build.gradle
+++ b/freemarker-core/build.gradle
@@ -27,21 +27,9 @@ FreeMarker template engine, core module. This module covers all basic functional
 many applications."""
 
 dependencies {
-    // Note that commond dependencies are added in the root project.
-
-    // ------------------------------------------------------------------------
-    // For the main artifact
+    // Note that common dependencies are added in the root project.
 
     compileOnly "org.zeroturnaround:javarebel-sdk:1.2.2"
-
-    // TODO These will be moved to freemarker-dom module:
-    
-    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"
-    }
 }
 
 compileJavacc {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java
index acf43df..0fa7e46 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java
@@ -19,10 +19,8 @@
 
 package org.apache.freemarker.core.model;
 
-import org.apache.freemarker.dom.NodeModel;
-
 /**
- * A {@link NodeModel} that supports navigating to the previous and next sibling nodes.
+ * A {@link TemplateNodeModel} that supports navigating to the previous and next sibling nodes.
  * 
  * @since 2.3.26
  */

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
index 7e04776..09e1e16 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
@@ -28,6 +28,8 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -65,8 +67,7 @@ import org.apache.freemarker.core.model.WrapperTemplateModel;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util.CommonBuilder;
 import org.apache.freemarker.core.util._ClassUtil;
-import org.apache.freemarker.dom.NodeModel;
-import org.w3c.dom.Node;
+import org.apache.freemarker.core.util._NullArgumentException;
 
 /**
  * The default implementation of the {@link ObjectWrapper} interface. Usually, you don't need to invoke instances of
@@ -157,6 +158,11 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
     @Deprecated // Only exists to keep some JUnit tests working... [FM3]
     private final boolean useModelCache;
 
+    /** Extensions applicable at the beginning of wrap(Object); null if it would be 0 length otherwise. */
+    private final DefaultObjectWrapperExtension[] extensionsBeforeWrapSpecialObject;
+    /** Extensions applicable at the end of wrap(Object); null if it would be 0 length otherwise. */
+    private final DefaultObjectWrapperExtension[] extensionsAfterWrapSpecialObject;
+
     private final Version incompatibleImprovements;
 
     /**
@@ -165,7 +171,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
      * @param finalizeConstruction Decides if the construction is finalized now, or the caller will do some more
      *     adjustments on the instance and then call {@link #finalizeConstruction()} itself.
      */
-    protected DefaultObjectWrapper(ExtendableBuilder builder, boolean finalizeConstruction) {
+    protected DefaultObjectWrapper(ExtendableBuilder<?, ?> builder, boolean finalizeConstruction) {
         incompatibleImprovements = builder.getIncompatibleImprovements();  // normalized
 
         defaultDateType = builder.getDefaultDateType();
@@ -189,6 +195,33 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
         enumModels = new EnumModels(this);
         useModelCache = builder.getUseModelCache();
 
+        int extsAfterWSOCnt = 0;
+        int extsBeforeWSOCnt = 0;
+        for (DefaultObjectWrapperExtension ext : builder.getExtensions()) {
+            if (ext.getPhase() == DefaultObjectWrapperExtensionPhase.AFTER_WRAP_SPECIAL_OBJECT) {
+                extsAfterWSOCnt++;
+            } else if (ext.getPhase() == DefaultObjectWrapperExtensionPhase.BEFORE_WRAP_SPECIAL_OBJECT)  {
+                extsBeforeWSOCnt++;
+            } else {
+                throw new BugException();
+            }
+        }
+        extensionsAfterWrapSpecialObject = extsAfterWSOCnt != 0
+                ? new DefaultObjectWrapperExtension[extsAfterWSOCnt] : null;
+        extensionsBeforeWrapSpecialObject = extsBeforeWSOCnt != 0
+                ? new DefaultObjectWrapperExtension[extsBeforeWSOCnt] : null;
+        int extsAfterWSOIdx = 0;
+        int extsBeforeWSOIdx = 0;
+        for (DefaultObjectWrapperExtension ext : builder.getExtensions()) {
+            if (ext.getPhase() == DefaultObjectWrapperExtensionPhase.AFTER_WRAP_SPECIAL_OBJECT) {
+                extensionsAfterWrapSpecialObject[extsAfterWSOIdx++] = ext;
+            } else if (ext.getPhase() == DefaultObjectWrapperExtensionPhase.BEFORE_WRAP_SPECIAL_OBJECT)  {
+                extensionsBeforeWrapSpecialObject[extsBeforeWSOIdx++] = ext;
+            } else {
+                throw new BugException();
+            }
+        }
+
         finalizeConstruction();
     }
 
@@ -330,11 +363,20 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
     }
 
     /**
-     * Wraps the parameter object to {@link TemplateModel} interface(s). Simple types like numbers, strings, booleans
-     * and dates will be wrapped into the corresponding {@code SimpleXxx} classes (like {@link SimpleNumber}).
-     * {@link Map}-s, {@link List}-s, other {@link Collection}-s, arrays and {@link Iterator}-s will be wrapped into the
-     * corresponding {@code DefaultXxxAdapter} classes ({@link DefaultMapAdapter}), depending on). After that, the
-     * wrapping is handled by {@link #handleNonBasicTypes(Object)}, so see more there.
+     * Wraps the parameter object to {@link TemplateModel} interface(s). The wrapping logic uses several phases,
+     * where if a stage manages to wrap the object, this method immediately returns with the result. The stages are
+     * (executed in this order):
+     * <ol>
+     * <li>If the value is {@code null} or {@link TemplateModel} it's returned as is.</li>
+     * <li>If the value is a {@link TemplateModelAdapter}, {@link TemplateModelAdapter#getTemplateModel()} is
+     * returned.</li>
+     * <li>{@link ExtendableBuilder#extensions extensions} which subscribe to the
+     * {@link DefaultObjectWrapperExtensionPhase#BEFORE_WRAP_SPECIAL_OBJECT} phase try to wrap the object</li>
+     * <li>{@link #wrapSpecialObject(Object)} tries to wrap the object</li>
+     * <li>{@link ExtendableBuilder#extensions extensions} which subscribe to the
+     * {@link DefaultObjectWrapperExtensionPhase#AFTER_WRAP_SPECIAL_OBJECT} phase try to wrap the object</li>
+     * <li>{@link #wrapGenericObject(Object)} wraps the object (or if it can't, it must throw exception)</li>
+     * </ol>
      */
     @Override
     public TemplateModel wrap(Object obj) throws TemplateModelException {
@@ -348,6 +390,40 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
             return ((TemplateModelAdapter) obj).getTemplateModel();
         }
 
+        if (extensionsBeforeWrapSpecialObject != null) {
+            for (DefaultObjectWrapperExtension ext : extensionsBeforeWrapSpecialObject) {
+                TemplateModel tm = ext.wrap(obj);
+                if (tm != null) {
+                    return tm;
+                }
+            }
+        }
+
+        {
+            TemplateModel tm = wrapSpecialObject(obj);
+            if (tm != null) {
+                return tm;
+            }
+        }
+
+        if (extensionsAfterWrapSpecialObject != null) {
+            for (DefaultObjectWrapperExtension ext : extensionsAfterWrapSpecialObject) {
+                TemplateModel tm = ext.wrap(obj);
+                if (tm != null) {
+                    return tm;
+                }
+            }
+        }
+
+        return wrapGenericObject(obj);
+    }
+
+    /**
+     * Wraps non-generic objects; see {@link #wrap(Object)} for more.
+     *
+     * @return {@code null} if the object was not of a type that's wrapped "specially".
+     */
+    protected TemplateModel wrapSpecialObject(Object obj) {
         if (obj instanceof String) {
             return new SimpleScalar((String) obj);
         }
@@ -357,7 +433,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
         if (obj instanceof Boolean) {
             return obj.equals(Boolean.TRUE) ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
         }
-        if (obj instanceof java.util.Date) {
+        if (obj instanceof Date) {
             if (obj instanceof java.sql.Date) {
                 return new SimpleDate((java.sql.Date) obj);
             }
@@ -367,7 +443,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
             if (obj instanceof java.sql.Timestamp) {
                 return new SimpleDate((java.sql.Timestamp) obj);
             }
-            return new SimpleDate((java.util.Date) obj, getDefaultDateType());
+            return new SimpleDate((Date) obj, getDefaultDateType());
         }
         final Class<?> objClass = obj.getClass();
         if (objClass.isArray()) {
@@ -390,30 +466,21 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
         if (obj instanceof Enumeration) {
             return DefaultEnumerationAdapter.adapt((Enumeration<?>) obj, this);
         }
-
-        return handleNonBasicTypes(obj);
+        if (obj instanceof ResourceBundle) {
+            return new ResourceBundleModel((ResourceBundle) obj, this);
+        }
+        return null;
     }
 
     /**
-     * Called for an object that isn't considered to be of a "basic" Java type, like for all application specific types,
-     * but currently also for {@link Node}-s and {@link ResourceBundle}-s.
-     *
+     * Called for an object that isn't treated specially by this {@link ObjectWrapper}; see {@link #wrap(Object)} for
+     * more. The implementation in {@link DefaultObjectWrapper} wraps the object into a {@link BeanAndStringModel}.
      * <p>
-     * When you override this method, you should first decide if you want to wrap the object in a custom way (and if so
-     * then do it and return with the result), and if not, then you should call the super method (assuming the default
-     * behavior is fine with you).
+     * Note that if you want to wrap some classes in a custom way, you shouldn't override this method. Instead, either
+     * override {@link #wrapSpecialObject(Object)}}, or don't subclass the {@link ObjectWrapper} at all, and
+     * just set the {@link ExtendableBuilder#getExtensions() extensions} configuration setting of it.
      */
-    // [FM3] This is an awkward temporary solution, rework it.
-    protected TemplateModel handleNonBasicTypes(Object obj) throws TemplateModelException {
-        // [FM3] Via plugin mechanism, not by default anymore
-        if (obj instanceof Node) {
-            return NodeModel.wrap((Node) obj);
-        }
-
-        if (obj instanceof ResourceBundle) {
-            return new ResourceBundleModel((ResourceBundle) obj, this);
-        }
-
+    protected TemplateModel wrapGenericObject(Object obj) throws TemplateModelException {
         return new BeanAndStringModel(obj, this);
     }
 
@@ -1339,10 +1406,12 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
         private boolean useModelCacheSet;
         private boolean usePrivateCaches;
         private boolean usePrivateCachesSet;
+        private List<DefaultObjectWrapperExtension> extensions = Collections.emptyList();
+        private boolean extensionsSet;
         // Attention!
         // - As this object is a cache key, non-normalized field values should be avoided.
-        // - Fields with default values must be set until the end of the constructor to ensure that when the lookup happens,
-        //   there will be no unset fields.
+        // - Fields with default values must be set until the end of the constructor to ensure that when the lookup
+        //   happens, there will be no unset fields.
         // - If you add a new field, review all methods in this class
 
         /**
@@ -1415,6 +1484,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
             result = prime * result + (builder.getUseModelCache() ? 1231 : 1237);
             result = prime * result + (builder.getUsePrivateCaches() ? 1231 : 1237);
             result = prime * result + builder.classIntrospectorBuilder.hashCode();
+            result = prime * result + builder.getExtensions().hashCode();
             return result;
         }
 
@@ -1443,6 +1513,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
             if (thisBuilder.isStrict() != thatBuilder.isStrict()) return false;
             if (thisBuilder.getUseModelCache() != thatBuilder.getUseModelCache()) return false;
             if (thisBuilder.getUsePrivateCaches() != thatBuilder.getUsePrivateCaches()) return false;
+            if (!thisBuilder.getExtensions().equals(thatBuilder.getExtensions())) return false;
             return thisBuilder.classIntrospectorBuilder.equals(thatBuilder.classIntrospectorBuilder);
         }
 
@@ -1631,8 +1702,8 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
         }
 
         /**
-         * Tells if the instance cerates should try to caches with other {@link DefaultObjectWrapper} instances (where
-         * possible), or it should always invoke its own caches and not share that with anyone else.
+         * Tells if the instance creates should share caches with other {@link DefaultObjectWrapper} instances
+         * (where possible), or it should always invoke its own caches and not share that with anyone else.
          * */
         public void setUsePrivateCaches(boolean usePrivateCaches) {
             this.usePrivateCaches = usePrivateCaches;
@@ -1654,6 +1725,56 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
             return self();
         }
 
+        /**
+         * Extensions are  used for dynamically decorating the {@link #wrap(Object)} method. In effects this is very
+         * similar to extending {@link DefaultObjectWrapper} and overriding its {@link #wrap(Object)}, and adding
+         * some wrapping logic before and/or after calling {@code super.wrap(obj)} (often referred to as decorating).
+         * But with this approach instead of subclassing {@link DefaultObjectWrapper} and its builder class, you
+         * simply list the desired extensions when you build the {@link DefaultObjectWrapper}. This is usually more
+         * convenient, and more flexible (what extensions you add can be decided on runtime factors) than the
+         * subclassing approach.
+         *
+         * @return An unmodifiable {@link List}.
+         */
+        public List<? extends DefaultObjectWrapperExtension> getExtensions() {
+            return extensions;
+        }
+
+        /**
+         * Setter pair of {@link #getExtensions()}.
+         *
+         * @param extensions The list of extensions; can't be {@code null}.
+         *                   The {@link List} list is copied, so further changes to the
+         *                   {@link List} passed in won't affect the value of this setting.
+         */
+        public void setExtensions(List<? extends DefaultObjectWrapperExtension> extensions) {
+            _NullArgumentException.check("extensions", extensions);
+            this.extensions = Collections.unmodifiableList(new ArrayList(extensions));
+            this.extensionsSet = true;
+        }
+
+        /**
+         * Fluent API equivalent of {@link #setExtensions(List)}.
+         */
+        public SelfT extensions(List<? extends DefaultObjectWrapperExtension> extensions) {
+            setExtensions(extensions);
+            return self();
+        }
+
+        /**
+         * Convenience varargs overload for calling {@link #extensions(List)}.
+         */
+        public SelfT extensions(DefaultObjectWrapperExtension... extensions) {
+            return extensions(Arrays.asList(extensions));
+        }
+
+        /**
+         * Tells if the property was explicitly set, as opposed to just holding its default value.
+         */
+        public boolean isExtensionsSet() {
+            return extensionsSet;
+        }
+
         public int getExposureLevel() {
             return classIntrospectorBuilder.getExposureLevel();
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtension.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtension.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtension.java
new file mode 100644
index 0000000..2d56486
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtension.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.core.model.impl;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+
+/**
+ * See the {@link DefaultObjectWrapper.ExtendableBuilder#getExtensions() extensions} setting of
+ * {@link DefaultObjectWrapper}.
+ */
+public abstract class DefaultObjectWrapperExtension {
+
+    /**
+     * Specifies when {@link DefaultObjectWrapperExtension#wrap(Object)} is invoked inside
+     * {@link DefaultObjectWrapper#wrap(Object)}.
+     */
+    public DefaultObjectWrapperExtensionPhase getPhase() {
+        return DefaultObjectWrapperExtensionPhase.AFTER_WRAP_SPECIAL_OBJECT;
+    }
+
+    /**
+     * @param obj
+     *         The object to wrap; never {@code null} or a {@link TemplateModel} or a {@link TemplateModelAdapter}.
+     *
+     * @return {@code null} if this {@link DefaultObjectWrapperExtension} doesn't handle this object the wrapped object
+     * otherwise.
+     */
+    public abstract TemplateModel wrap(Object obj);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtensionPhase.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtensionPhase.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtensionPhase.java
new file mode 100644
index 0000000..4ef1aa4
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperExtensionPhase.java
@@ -0,0 +1,39 @@
+/*
+ * 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.core.model.impl;
+
+/**
+ * Used for the return value of {@link DefaultObjectWrapperExtension#getPhase()}.
+ */
+public enum DefaultObjectWrapperExtensionPhase {
+
+    /**
+     * Indicates that the {@link DefaultObjectWrapperExtension} should be invoked before
+     * {@link DefaultObjectWrapper#wrapSpecialObject(Object)}.
+     */
+    BEFORE_WRAP_SPECIAL_OBJECT,
+
+    /**
+     * Indicates that the {@link DefaultObjectWrapperExtension} should be invoked after
+     * {@link DefaultObjectWrapper#wrapSpecialObject(Object)}.
+     */
+    AFTER_WRAP_SPECIAL_OBJECT
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
index e456dc6..43d47c0 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
@@ -45,7 +45,7 @@ public class RestrictedObjectWrapper extends DefaultObjectWrapper {
      * In this implementation, this just throws an exception.
      */
     @Override
-    protected TemplateModel handleNonBasicTypes(Object obj) throws TemplateModelException {
+    protected TemplateModel wrapGenericObject(Object obj) throws TemplateModelException {
         throw new TemplateModelException("RestrictedObjectWrapper deliberately won't wrap 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/AtAtKey.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/AtAtKey.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/AtAtKey.java
deleted file mode 100644
index ca6ac6b..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/AtAtKey.java
+++ /dev/null
@@ -1,58 +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;
-
-/**
- * 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-core/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
deleted file mode 100644
index cc510c4..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/AttributeNodeModel.java
+++ /dev/null
@@ -1,69 +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.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-core/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
deleted file mode 100644
index 264c0db..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/CharacterDataNodeModel.java
+++ /dev/null
@@ -1,46 +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.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-core/src/main/java/org/apache/freemarker/dom/DocumentModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/DocumentModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/DocumentModel.java
deleted file mode 100644
index 876b3cf..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/DocumentModel.java
+++ /dev/null
@@ -1,76 +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.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-core/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
deleted file mode 100644
index 3448f77..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/DocumentTypeModel.java
+++ /dev/null
@@ -1,56 +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;
-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-core/src/main/java/org/apache/freemarker/dom/DomLog.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/DomLog.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/DomLog.java
deleted file mode 100644
index a1f6f0c..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/DomLog.java
+++ /dev/null
@@ -1,32 +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.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-core/src/main/java/org/apache/freemarker/dom/DomStringUtil.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/DomStringUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/DomStringUtil.java
deleted file mode 100644
index f5b58f8..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/DomStringUtil.java
+++ /dev/null
@@ -1,67 +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;
-
-/**
- * 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;
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/ElementModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/ElementModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/ElementModel.java
deleted file mode 100644
index 220f414..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/ElementModel.java
+++ /dev/null
@@ -1,234 +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.Collections;
-
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.util._StringUtil;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-class ElementModel extends NodeModel implements TemplateScalarModel {
-
-    public ElementModel(Element element) {
-        super(element);
-    }
-    
-    @Override
-    public boolean isEmpty() {
-        return false;
-    }
-    
-    /**
-     * An Element node supports various hash keys.
-     * Any key that corresponds to the tag name of any child elements
-     * returns a sequence of those elements. The special key "*" returns 
-     * all the element's direct children.
-     * The "**" key return all the element's descendants in the order they
-     * occur in the document.
-     * Any key starting with '@' is taken to be the name of an element attribute.
-     * The special key "@@" returns a hash of all the element's attributes.
-     * The special key "/" returns the root document node associated with this element.
-     */
-    @Override
-    public TemplateModel get(String key) throws TemplateModelException {
-        if (key.equals("*")) {
-            NodeListModel ns = new NodeListModel(this);
-            TemplateSequenceModel children = getChildNodes();
-            for (int i = 0; i < children.size(); i++) {
-                NodeModel child = (NodeModel) children.get(i);
-                if (child.node.getNodeType() == Node.ELEMENT_NODE) {
-                    ns.add(child);
-                }
-            }
-            return ns;
-        } else if (key.equals("**")) {
-            return new NodeListModel(((Element) node).getElementsByTagName("*"), this);    
-        } else if (key.startsWith("@")) {
-            if (key.startsWith("@@")) {
-                if (key.equals(AtAtKey.ATTRIBUTES.getKey())) {
-                    return new NodeListModel(node.getAttributes(), this);
-                } else if (key.equals(AtAtKey.START_TAG.getKey())) {
-                    NodeOutputter nodeOutputter = new NodeOutputter(node);
-                    return new SimpleScalar(nodeOutputter.getOpeningTag((Element) node));
-                } else if (key.equals(AtAtKey.END_TAG.getKey())) {
-                    NodeOutputter nodeOutputter = new NodeOutputter(node);
-                    return new SimpleScalar(nodeOutputter.getClosingTag((Element) node));
-                } else if (key.equals(AtAtKey.ATTRIBUTES_MARKUP.getKey())) {
-                    StringBuilder buf = new StringBuilder();
-                    NodeOutputter nu = new NodeOutputter(node);
-                    nu.outputContent(node.getAttributes(), buf);
-                    return new SimpleScalar(buf.toString().trim());
-                } else if (key.equals(AtAtKey.PREVIOUS_SIBLING_ELEMENT.getKey())) {
-                    Node previousSibling = node.getPreviousSibling();
-                    while (previousSibling != null && !isSignificantNode(previousSibling)) {
-                        previousSibling = previousSibling.getPreviousSibling();
-                    }
-                    return previousSibling != null && previousSibling.getNodeType() == Node.ELEMENT_NODE
-                            ? wrap(previousSibling) : new NodeListModel(Collections.emptyList(), null);  
-                } else if (key.equals(AtAtKey.NEXT_SIBLING_ELEMENT.getKey())) {
-                    Node nextSibling = node.getNextSibling();
-                    while (nextSibling != null && !isSignificantNode(nextSibling)) {
-                        nextSibling = nextSibling.getNextSibling();
-                    }
-                    return nextSibling != null && nextSibling.getNodeType() == Node.ELEMENT_NODE
-                            ? wrap(nextSibling) : new NodeListModel(Collections.emptyList(), null);  
-                } else {
-                    // We don't know anything like this that's element-specific; fall back 
-                    return super.get(key);
-                }
-            } else { // Starts with "@", but not with "@@"
-                if (DomStringUtil.isXMLNameLike(key, 1)) {
-                    Attr att = getAttribute(key.substring(1));
-                    if (att == null) { 
-                        return new NodeListModel(this);
-                    }
-                    return wrap(att);
-                } else if (key.equals("@*")) {
-                    return new NodeListModel(node.getAttributes(), this);
-                } else {
-                    // We don't know anything like this that's element-specific; fall back 
-                    return super.get(key);
-                }
-            }
-        } else if (DomStringUtil.isXMLNameLike(key)) {
-            // We interpret key as an element name
-            NodeListModel result = ((NodeListModel) getChildNodes()).filterByName(key);
-            return result.size() != 1 ? result : result.get(0);
-        } else {
-            // We don't anything like this that's element-specific; fall back 
-            return super.get(key);
-        }
-    }
-
-    @Override
-    public String getAsString() throws TemplateModelException {
-        NodeList nl = node.getChildNodes();
-        String result = "";
-        for (int i = 0; i < nl.getLength(); i++) {
-            Node child = nl.item(i);
-            int nodeType = child.getNodeType();
-            if (nodeType == Node.ELEMENT_NODE) {
-                String msg = "Only elements with no child elements can be processed as text."
-                             + "\nThis element with name \""
-                             + node.getNodeName()
-                             + "\" has a child element named: " + child.getNodeName();
-                throw new TemplateModelException(msg);
-            } else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
-                result += child.getNodeValue();
-            }
-        }
-        return result;
-    }
-    
-    @Override
-    public String getNodeName() {
-        String result = node.getLocalName();
-        if (result == null || result.equals("")) {
-            result = node.getNodeName();
-        }
-        return result;
-    }
-    
-    @Override
-    String getQualifiedName() {
-        String nodeName = getNodeName();
-        String nsURI = getNodeNamespace();
-        if (nsURI == null || nsURI.length() == 0) {
-            return nodeName;
-        }
-        Environment env = Environment.getCurrentEnvironment();
-        String defaultNS = env.getDefaultNS();
-        String prefix;
-        if (defaultNS != null && defaultNS.equals(nsURI)) {
-            prefix = "";
-        } else {
-            prefix = env.getPrefixForNamespace(nsURI);
-            
-        }
-        if (prefix == null) {
-            return null; // We have no qualified name, because there is no prefix mapping
-        }
-        if (prefix.length() > 0) {
-            prefix += ":";
-        }
-        return prefix + nodeName;
-    }
-    
-    private Attr getAttribute(String qname) {
-        Element element = (Element) node;
-        Attr result = element.getAttributeNode(qname);
-        if (result != null)
-            return result;
-        int colonIndex = qname.indexOf(':');
-        if (colonIndex > 0) {
-            String prefix = qname.substring(0, colonIndex);
-            String uri;
-            if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) {
-                uri = Environment.getCurrentEnvironment().getDefaultNS();
-            } else {
-                uri = Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix);
-            }
-            String localName = qname.substring(1 + colonIndex);
-            if (uri != null) {
-                result = element.getAttributeNodeNS(uri, localName);
-            }
-        }
-        return result;
-    }
-    
-    private boolean isSignificantNode(Node node) throws TemplateModelException {
-        return (node.getNodeType() == Node.TEXT_NODE || node.getNodeType() == Node.CDATA_SECTION_NODE)
-                ? !isBlankXMLText(node.getTextContent())
-                : node.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE && node.getNodeType() != Node.COMMENT_NODE;
-    }
-    
-    private boolean isBlankXMLText(String s) {
-        if (s == null) {
-            return true;
-        }
-        for (int i = 0; i < s.length(); i++) {
-            if (!isXMLWhiteSpace(s.charAt(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * White space according the XML spec. 
-     */
-    private boolean isXMLWhiteSpace(char c) {
-        return c == ' ' || c == '\t' || c == '\n' | c == '\r';
-    }
-
-    boolean matchesName(String name, Environment env) {
-        return _StringUtil.matchesQName(name, getNodeName(), getNodeNamespace(), env);
-    }
-}
\ 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/JaxenXPathSupport.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
deleted file mode 100644
index 3e52836..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/JaxenXPathSupport.java
+++ /dev/null
@@ -1,243 +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.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.apache.freemarker.core.CustomStateKey;
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core.Template;
-import org.apache.freemarker.core.TemplateException;
-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.TemplateModelException;
-import org.apache.freemarker.core.model.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-import org.apache.freemarker.core.util._ObjectHolder;
-import org.jaxen.BaseXPath;
-import org.jaxen.Function;
-import org.jaxen.FunctionCallException;
-import org.jaxen.FunctionContext;
-import org.jaxen.JaxenException;
-import org.jaxen.NamespaceContext;
-import org.jaxen.Navigator;
-import org.jaxen.UnresolvableException;
-import org.jaxen.VariableContext;
-import org.jaxen.XPathFunctionContext;
-import org.jaxen.dom.DocumentNavigator;
-import org.w3c.dom.Document;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-
-/**
- */
-class JaxenXPathSupport implements XPathSupport {
-
-    private static final CustomStateKey<Map<String, BaseXPath>> XPATH_CACHE_ATTR
-            = new CustomStateKey<Map<String, BaseXPath>>() {
-        @Override
-        protected Map<String, BaseXPath> create() {
-            return new HashMap<String, BaseXPath>();
-        }
-    };
-
-        // [2.4] Can't we just use Collections.emptyList()? 
-    private final static ArrayList EMPTY_ARRAYLIST = new ArrayList();
-
-    @Override
-    public TemplateModel executeQuery(Object context, String xpathQuery) throws TemplateModelException {
-        try {
-            BaseXPath xpath;
-            Map<String, BaseXPath> xpathCache = Environment.getCurrentEnvironmentNotNull().getCurrentTemplateNotNull()
-                    .getCustomState(XPATH_CACHE_ATTR);
-            synchronized (xpathCache) {
-                xpath = xpathCache.get(xpathQuery);
-                if (xpath == null) {
-                    xpath = new BaseXPath(xpathQuery, FM_DOM_NAVIGATOR);
-                    xpath.setNamespaceContext(customNamespaceContext);
-                    xpath.setFunctionContext(FM_FUNCTION_CONTEXT);
-                    xpath.setVariableContext(FM_VARIABLE_CONTEXT);
-                    xpathCache.put(xpathQuery, xpath);
-                }
-            }
-            List result = xpath.selectNodes(context != null ? context : EMPTY_ARRAYLIST);
-            if (result.size() == 1) {
-                return NodeQueryResultItemObjectWrapper.INSTANCE.wrap(result.get(0));
-            }
-            NodeListModel nlm = new NodeListModel(result, null);
-            nlm.xpathSupport = this;
-            return nlm;
-        } catch (UndeclaredThrowableException e) {
-            Throwable t  = e.getUndeclaredThrowable();
-            if (t instanceof TemplateModelException) {
-                throw (TemplateModelException) t;
-            }
-            throw e;
-        } catch (JaxenException je) {
-            throw new TemplateModelException(je);
-        }
-    }
-
-    static private final NamespaceContext customNamespaceContext = new NamespaceContext() {
-        
-        @Override
-        public String translateNamespacePrefixToUri(String prefix) {
-            if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) {
-                return Environment.getCurrentEnvironment().getDefaultNS();
-            }
-            return Environment.getCurrentEnvironment().getNamespaceForPrefix(prefix);
-        }
-    };
-
-    private static final VariableContext FM_VARIABLE_CONTEXT = new VariableContext() {
-        @Override
-        public Object getVariableValue(String namespaceURI, String prefix, String localName)
-        throws UnresolvableException {
-            try {
-                TemplateModel model = Environment.getCurrentEnvironment().getVariable(localName);
-                if (model == null) {
-                    throw new UnresolvableException("Variable \"" + localName + "\" not found.");
-                }
-                if (model instanceof TemplateScalarModel) {
-                    return ((TemplateScalarModel) model).getAsString();
-                }
-                if (model instanceof TemplateNumberModel) {
-                    return ((TemplateNumberModel) model).getAsNumber();
-                }
-                if (model instanceof TemplateDateModel) {
-                    return ((TemplateDateModel) model).getAsDate();
-                }
-                if (model instanceof TemplateBooleanModel) {
-                    return Boolean.valueOf(((TemplateBooleanModel) model).getAsBoolean());
-                }
-            } catch (TemplateModelException e) {
-                throw new UndeclaredThrowableException(e);
-            }
-            throw new UnresolvableException(
-                    "Variable \"" + localName + "\" exists, but it's not a string, number, date, or boolean");
-        }
-    };
-     
-    private static final FunctionContext FM_FUNCTION_CONTEXT = new XPathFunctionContext() {
-        @Override
-        public Function getFunction(String namespaceURI, String prefix, String localName)
-        throws UnresolvableException {
-            try {
-                return super.getFunction(namespaceURI, prefix, localName);
-            } catch (UnresolvableException e) {
-                return super.getFunction(null, null, localName);
-            }
-        }
-    };
-    
-    /**
-     * Stores the the template parsed as {@link Document} in the template itself.
-     */
-    private static final CustomStateKey<_ObjectHolder<Document>> FM_DOM_NAVIAGOTOR_CACHED_DOM
-            = new CustomStateKey<_ObjectHolder<Document>>() {
-        @Override
-        protected _ObjectHolder<Document> create() {
-            return new _ObjectHolder<>(null);
-        }
-    };
-     
-    private static final Navigator FM_DOM_NAVIGATOR = new DocumentNavigator() {
-        @Override
-        public Object getDocument(String uri) throws FunctionCallException {
-            try {
-                Template raw = getTemplate(uri);
-                _ObjectHolder<Document> docHolder = Environment.getCurrentEnvironmentNotNull()
-                        .getCurrentTemplateNotNull().getCustomState(FM_DOM_NAVIAGOTOR_CACHED_DOM);
-                synchronized (docHolder) {
-                    Document doc = docHolder.get();
-                    if (doc == null) {
-                        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-                        factory.setNamespaceAware(true);
-                        DocumentBuilder builder = factory.newDocumentBuilder();
-                        FmEntityResolver er = new FmEntityResolver();
-                        builder.setEntityResolver(er);
-                        doc = builder.parse(createInputSource(null, raw));
-                        // If the entity resolver got called 0 times, the document
-                        // is standalone, so we can safely cache it
-                        if (er.getCallCount() == 0) {
-                            docHolder.set(doc);
-                        }
-                    }
-                    return doc;
-                }
-            } catch (Exception e) {
-                throw new FunctionCallException("Failed to parse document for URI: " + uri, e);
-            }
-        }
-    };
-
-    // [FM3] Look into this "hidden" feature
-    static Template getTemplate(String systemId) throws IOException {
-        Environment env = Environment.getCurrentEnvironment();
-        String templatePath = env.getCurrentTemplate().getLookupName();
-        int lastSlash = templatePath.lastIndexOf('/');
-        templatePath = lastSlash == -1 ? "" : templatePath.substring(0, lastSlash + 1);
-        systemId = env.toFullTemplateName(templatePath, systemId);
-        return env.getConfiguration().getTemplate(systemId, env.getLocale());
-    }
-
-    private static InputSource createInputSource(String publicId, Template raw) throws IOException, SAXException {
-        StringWriter sw = new StringWriter();
-        try {
-            raw.process(Collections.EMPTY_MAP, sw);
-        } catch (TemplateException e) {
-            throw new SAXException(e);
-        }
-        InputSource is = new InputSource();
-        is.setPublicId(publicId);
-        is.setSystemId(raw.getLookupName());
-        is.setCharacterStream(new StringReader(sw.toString()));
-        return is;
-    }
-
-    private static class FmEntityResolver implements EntityResolver {
-        private int callCount = 0;
-        
-        @Override
-        public InputSource resolveEntity(String publicId, String systemId)
-        throws SAXException, IOException {
-            ++callCount;
-            return createInputSource(publicId, getTemplate(systemId));
-        }
-        
-        int getCallCount() {
-            return callCount;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeListModel.java b/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeListModel.java
deleted file mode 100644
index 333bb5c..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/dom/NodeListModel.java
+++ /dev/null
@@ -1,219 +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.ArrayList;
-import java.util.List;
-
-import org.apache.freemarker.core.Configuration;
-import org.apache.freemarker.core.Environment;
-import org.apache.freemarker.core._UnexpectedTypeErrorExplainerTemplateModel;
-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.TemplateNumberModel;
-import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.model.impl.SimpleScalar;
-import org.apache.freemarker.core.model.impl.SimpleSequence;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * Used when the result set contains 0 or multiple nodes; shouldn't be used when you have exactly 1 node. For exactly 1
- * node, use {@link NodeModel#wrap(Node)}, because {@link NodeModel} subclasses can have extra features building on that
- * restriction, like single elements with text content can be used as FTL string-s.
- * <p>
- * This class is not guaranteed to be thread safe, so instances of this shouldn't be used as
- * {@linkplain Configuration#getSharedVariables() shared variable}.
- */
-class NodeListModel extends SimpleSequence implements TemplateHashModel, _UnexpectedTypeErrorExplainerTemplateModel {
-    
-    // [2.4] make these private
-    NodeModel contextNode;
-    XPathSupport xpathSupport;
-    
-    NodeListModel(Node contextNode) {
-        this(NodeModel.wrap(contextNode));
-    }
-    
-    NodeListModel(NodeModel contextNode) {
-        super(NodeQueryResultItemObjectWrapper.INSTANCE);
-        this.contextNode = contextNode;
-    }
-    
-    NodeListModel(NodeList nodeList, NodeModel contextNode) {
-        super(NodeQueryResultItemObjectWrapper.INSTANCE);
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            list.add(nodeList.item(i));
-        }
-        this.contextNode = contextNode;
-    }
-    
-    NodeListModel(NamedNodeMap nodeList, NodeModel contextNode) {
-        super(NodeQueryResultItemObjectWrapper.INSTANCE);
-        for (int i = 0; i < nodeList.getLength(); i++) {
-            list.add(nodeList.item(i));
-        }
-        this.contextNode = contextNode;
-    }
-    
-    NodeListModel(List list, NodeModel contextNode) {
-        super(list, NodeQueryResultItemObjectWrapper.INSTANCE);
-        this.contextNode = contextNode;
-    }
-    
-    NodeListModel filterByName(String name) throws TemplateModelException {
-        NodeListModel result = new NodeListModel(contextNode);
-        int size = size();
-        if (size == 0) {
-            return result;
-        }
-        Environment env = Environment.getCurrentEnvironment();
-        for (int i = 0; i < size; i++) {
-            NodeModel nm = (NodeModel) get(i);
-            if (nm instanceof ElementModel) {
-                if (((ElementModel) nm).matchesName(name, env)) {
-                    result.add(nm);
-                }
-            }
-        }
-        return result;
-    }
-    
-    @Override
-    public boolean isEmpty() {
-        return size() == 0;
-    }
-    
-    @Override
-    public TemplateModel get(String key) throws TemplateModelException {
-        if (size() == 1) {
-            NodeModel nm = (NodeModel) get(0);
-            return nm.get(key);
-        }
-        if (key.startsWith("@@")) {
-            if (key.equals(AtAtKey.MARKUP.getKey()) 
-                    || key.equals(AtAtKey.NESTED_MARKUP.getKey()) 
-                    || key.equals(AtAtKey.TEXT.getKey())) {
-                StringBuilder result = new StringBuilder();
-                for (int i = 0; i < size(); i++) {
-                    NodeModel nm = (NodeModel) get(i);
-                    TemplateScalarModel textModel = (TemplateScalarModel) nm.get(key);
-                    result.append(textModel.getAsString());
-                }
-                return new SimpleScalar(result.toString());
-            } else if (key.length() != 2 /* to allow "@@" to fall through */) {
-                // As @@... would cause exception in the XPath engine, we throw a nicer exception now. 
-                if (AtAtKey.containsKey(key)) {
-                    throw new TemplateModelException(
-                            "\"" + key + "\" is only applicable to a single XML node, but it was applied on "
-                            + (size() != 0
-                                    ? size() + " XML nodes (multiple matches)."
-                                    : "an empty list of XML nodes (no matches)."));
-                } else {
-                    throw new TemplateModelException("Unsupported @@ key: " + key);
-                }
-            }
-        }
-        if (DomStringUtil.isXMLNameLike(key) 
-                || ((key.startsWith("@")
-                        && (DomStringUtil.isXMLNameLike(key, 1)  || key.equals("@@") || key.equals("@*"))))
-                || key.equals("*") || key.equals("**")) {
-            NodeListModel result = new NodeListModel(contextNode);
-            for (int i = 0; i < size(); i++) {
-                NodeModel nm = (NodeModel) get(i);
-                if (nm instanceof ElementModel) {
-                    TemplateSequenceModel tsm = (TemplateSequenceModel) nm.get(key);
-                    if (tsm != null) {
-                        int size = tsm.size();
-                        for (int j = 0; j < size; j++) {
-                            result.add(tsm.get(j));
-                        }
-                    }
-                }
-            }
-            if (result.size() == 1) {
-                return result.get(0);
-            }
-            return result;
-        }
-        XPathSupport xps = getXPathSupport();
-        if (xps != null) {
-            Object context = (size() == 0) ? null : rawNodeList(); 
-            return xps.executeQuery(context, 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);
-        }
-    }
-    
-    private List rawNodeList() throws TemplateModelException {
-        int size = size();
-        ArrayList al = new ArrayList(size);
-        for (int i = 0; i < size; i++) {
-            al.add(((NodeModel) get(i)).node);
-        }
-        return al;
-    }
-    
-    XPathSupport getXPathSupport() throws TemplateModelException {
-        if (xpathSupport == null) {
-            if (contextNode != null) {
-                xpathSupport = contextNode.getXPathSupport();
-            } else if (size() > 0) {
-                xpathSupport = ((NodeModel) get(0)).getXPathSupport();
-            }
-        }
-        return xpathSupport;
-    }
-
-    @Override
-    public Object[] explainTypeError(Class[] expectedClasses) {
-        for (Class expectedClass : expectedClasses) {
-            if (TemplateScalarModel.class.isAssignableFrom(expectedClass)
-                    || TemplateDateModel.class.isAssignableFrom(expectedClass)
-                    || TemplateNumberModel.class.isAssignableFrom(expectedClass)
-                    || TemplateBooleanModel.class.isAssignableFrom(expectedClass)) {
-                return newTypeErrorExplanation("string");
-            } else if (TemplateNodeModel.class.isAssignableFrom(expectedClass)) {
-                return newTypeErrorExplanation("node");
-            }
-        }
-        return null;
-    }
-
-    private Object[] newTypeErrorExplanation(String type) {
-        return new Object[] {
-                "This XML query result can't be used as ", type, " because for that it had to contain exactly "
-                + "1 XML node, but it contains ", Integer.valueOf(size()), " nodes. "
-                + "That is, the constructing XML query has found ",
-                isEmpty()
-                    ? "no matches."
-                    : "multiple matches."
-                };
-    }
-
-}
\ No newline at end of file


[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.

Posted by dd...@apache.org.
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;
+    }    
+
+}


[2/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.

Posted by dd...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/main/java/org/apache/freemarker/dom/package.html
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/main/java/org/apache/freemarker/dom/package.html b/freemarker-dom/src/main/java/org/apache/freemarker/dom/package.html
new file mode 100644
index 0000000..61b1737
--- /dev/null
+++ b/freemarker-dom/src/main/java/org/apache/freemarker/dom/package.html
@@ -0,0 +1,30 @@
+<!--
+  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/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java
new file mode 100644
index 0000000..ed6d7bb
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.dom.test.DOMLoader;
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+public class DOMSiblingTest extends TemplateTest {
+
+    @Before
+    public void setUp() throws SAXException, IOException, ParserConfigurationException {
+        addToDataModel("doc", DOMLoader.toModel(getClass(),"DOMSiblingTest.xml"));
+    }
+
+    @Test
+    public void testBlankPreviousSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.name?previousSibling}", "\n    ");
+        assertOutput("${doc.person.name?previous_sibling}", "\n    ");
+    }
+
+    @Test
+    public void testNonBlankPreviousSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.address?previousSibling}", "12th August");
+    }
+
+    @Test
+    public void testBlankNextSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.name?nextSibling}", "\n    ");
+        assertOutput("${doc.person.name?next_sibling}", "\n    ");
+    }
+
+    @Test
+    public void testNonBlankNextSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.dob?nextSibling}", "Chennai, India");
+    }
+
+    @Test
+    public void testNullPreviousSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person?previousSibling?? ?c}", "false");
+    }
+
+    @Test
+    public void testSignificantPreviousSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.name.@@previous_sibling_element}", "male");
+    }
+
+    @Test
+    public void testSignificantNextSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.name.@@next_sibling_element}", "12th August");
+    }
+
+    @Test
+    public void testNullSignificantPreviousSibling() throws IOException, TemplateException {
+        assertOutput("${doc.person.phone.@@next_sibling_element?size}", "0");
+    }
+
+    @Test
+    public void testSkippingCommentNode() throws IOException, TemplateException {
+        assertOutput("${doc.person.profession.@@previous_sibling_element}", "Chennai, India");
+    }
+
+    @Test
+    public void testSkippingEmptyCDataNode() throws IOException, TemplateException {
+        assertOutput("${doc.person.hobby.@@previous_sibling_element}", "Software Engineer");
+    }
+
+    @Test
+    public void testValidCDataNode() throws IOException, TemplateException {
+        assertOutput("${doc.person.phone.@@previous_sibling_element?size}", "0");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java
new file mode 100644
index 0000000..d8004fb
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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 static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.freemarker.dom.test.DOMLoader;
+import org.junit.Test;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.xml.sax.SAXException;
+
+public class DOMSimplifiersTest {
+
+    private static final String COMMON_TEST_XML
+            = "<!DOCTYPE a []><?p?><a>x<![CDATA[y]]><!--c--><?p?>z<?p?><b><!--c--></b><c></c>"
+              + "<d>a<e>c</e>b<!--c--><!--c--><!--c--><?p?><?p?><?p?></d>"
+              + "<f><![CDATA[1]]>2</f></a><!--c-->";
+
+    private static final String TEXT_MERGE_CONTENT =
+            "<a>"
+            + "a<!--c--><s/>"
+            + "<!--c-->a<s/>"
+            + "a<!--c-->b<s/>"
+            + "<!--c-->a<!--c-->b<!--c--><s/>"
+            + "a<b>b</b>c<s/>"
+            + "a<b>b</b><!--c-->c<s/>"
+            + "a<!--c-->1<b>b<!--c--></b>c<!--c-->1<s/>"
+            + "a<!--c-->1<b>b<!--c-->c</b>d<!--c-->1<s/>"
+            + "a<!--c-->1<b>b<!--c-->c</b>d<!--c-->1<s/>"
+            + "a<!--c-->1<b>b<!--c-->1<e>c<!--c-->1</e>d<!--c-->1</b>e<!--c-->1<s/>"
+            + "</a>";
+    private static final String TEXT_MERGE_EXPECTED =
+            "<a>"
+            + "%a<s/>"
+            + "%a<s/>"
+            + "%ab<s/>"
+            + "%ab<s/>"
+            + "%a<b>%b</b>%c<s/>"
+            + "%a<b>%b</b>%c<s/>"
+            + "%a1<b>%b</b>%c1<s/>"
+            + "%a1<b>%bc</b>%d1<s/>"
+            + "%a1<b>%bc</b>%d1<s/>"
+            + "%a1<b>%b1<e>%c1</e>%d1</b>%e1<s/>"
+            + "</a>";
+    
+    @Test
+    public void testTest() throws Exception {
+        String expected = "<!DOCTYPE ...><?p?><a>%x<![CDATA[y]]><!--c--><?p?>%z<?p?><b><!--c--></b><c/>"
+                   + "<d>%a<e>%c</e>%b<!--c--><!--c--><!--c--><?p?><?p?><?p?></d>"
+                   + "<f><![CDATA[1]]>%2</f></a><!--c-->";
+        assertEquals(expected, toString(DOMLoader.toDOM(COMMON_TEST_XML)));
+    }
+
+    @Test
+    public void testMergeAdjacentText() throws Exception {
+        Document dom = DOMLoader.toDOM(COMMON_TEST_XML);
+        NodeModel.mergeAdjacentText(dom);
+        assertEquals(
+                "<!DOCTYPE ...><?p?><a>%xy<!--c--><?p?>%z<?p?><b><!--c--></b><c/>"
+                + "<d>%a<e>%c</e>%b<!--c--><!--c--><!--c--><?p?><?p?><?p?></d>"
+                + "<f><![CDATA[12]]></f></a><!--c-->",
+                toString(dom));
+    }
+
+    @Test
+    public void testRemoveComments() throws Exception {
+        Document dom = DOMLoader.toDOM(COMMON_TEST_XML);
+        NodeModel.removeComments(dom);
+        assertEquals(
+                "<!DOCTYPE ...><?p?><a>%x<![CDATA[y]]><?p?>%z<?p?><b/><c/>"
+                + "<d>%a<e>%c</e>%b<?p?><?p?><?p?></d>"
+                + "<f><![CDATA[1]]>%2</f></a>",
+                toString(dom));
+    }
+
+    @Test
+    public void testRemovePIs() throws Exception {
+        Document dom = DOMLoader.toDOM(COMMON_TEST_XML);
+        NodeModel.removePIs(dom);
+        assertEquals(
+                "<!DOCTYPE ...><a>%x<![CDATA[y]]><!--c-->%z<b><!--c--></b><c/>"
+                + "<d>%a<e>%c</e>%b<!--c--><!--c--><!--c--></d>"
+                + "<f><![CDATA[1]]>%2</f></a><!--c-->",
+                toString(dom));
+    }
+    
+    @Test
+    public void testSimplify() throws Exception {
+        testSimplify(
+                "<!DOCTYPE ...><a>%xyz<b/><c/>"
+                + "<d>%a<e>%c</e>%b</d><f><![CDATA[12]]></f></a>",
+                COMMON_TEST_XML);
+    }
+
+    @Test
+    public void testSimplify2() throws Exception {
+        testSimplify(TEXT_MERGE_EXPECTED, TEXT_MERGE_CONTENT);
+    }
+
+    @Test
+    public void testSimplify3() throws Exception {
+        testSimplify("<a/>", "<a/>");
+    }
+    
+    private void testSimplify(String expected, String content)
+            throws SAXException, IOException, ParserConfigurationException {
+        {
+            Document dom = DOMLoader.toDOM(content);
+            NodeModel.simplify(dom);
+            assertEquals(expected, toString(dom));
+        }
+        
+        // Must be equivalent:
+        {
+            Document dom = DOMLoader.toDOM(content);
+            NodeModel.removeComments(dom);
+            NodeModel.removePIs(dom);
+            NodeModel.mergeAdjacentText(dom);
+            assertEquals(expected, toString(dom));
+        }
+        
+        // Must be equivalent:
+        {
+            Document dom = DOMLoader.toDOM(content);
+            NodeModel.removeComments(dom);
+            NodeModel.removePIs(dom);
+            NodeModel.simplify(dom);
+            assertEquals(expected, toString(dom));
+        }
+    }
+
+    private String toString(Document doc) {
+        StringBuilder sb = new StringBuilder();
+        toString(doc, sb);
+        return sb.toString();
+    }
+
+    private void toString(Node node, StringBuilder sb) {
+        if (node instanceof Document) {
+            childrenToString(node, sb);
+        } else if (node instanceof Element) {
+            if (node.hasChildNodes()) {
+                sb.append("<").append(node.getNodeName()).append(">");
+                childrenToString(node, sb);
+                sb.append("</").append(node.getNodeName()).append(">");
+            } else {
+                sb.append("<").append(node.getNodeName()).append("/>");
+            }
+        } else if (node instanceof Text) {
+            if (node instanceof CDATASection) {
+                sb.append("<![CDATA[").append(node.getNodeValue()).append("]]>");
+            } else {
+                sb.append("%").append(node.getNodeValue());
+            }
+        } else if (node instanceof Comment) {
+            sb.append("<!--").append(node.getNodeValue()).append("-->");
+        } else if (node instanceof ProcessingInstruction) {
+            sb.append("<?").append(node.getNodeName()).append("?>");
+        } else if (node instanceof DocumentType) {
+            sb.append("<!DOCTYPE ...>");
+        } else {
+            throw new IllegalStateException("Unhandled node type: " + node.getClass().getName());
+        }
+    }
+
+    private void childrenToString(Node node, StringBuilder sb) {
+        Node child = node.getFirstChild();
+        while (child != null) {
+            toString(child, sb);
+            child = child.getNextSibling();
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMTest.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMTest.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMTest.java
new file mode 100644
index 0000000..0d03dbe
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DOMTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.dom.test.DOMLoader;
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class DOMTest extends TemplateTest {
+
+    @Test
+    public void xpathDetectionBugfix() throws Exception {
+        addDocToDataModel("<root><a>A</a><b>B</b><c>C</c></root>");
+        assertOutput("${doc.root.b['following-sibling::c']}", "C");
+        assertOutput("${doc.root.b['following-sibling::*']}", "C");
+    }
+
+    @Test
+    public void xmlnsPrefixes() throws Exception {
+        addDocToDataModel("<root xmlns='http://example.com/ns1' xmlns:ns2='http://example.com/ns2'>"
+                + "<a>A</a><ns2:b>B</ns2:b><c a1='1' ns2:a2='2'/></root>");
+
+        String ftlHeader = "<#ftl ns_prefixes={'D':'http://example.com/ns1', 'n2':'http://example.com/ns2'}>";
+        
+        // @@markup:
+        assertOutput("${doc.@@markup}",
+                "<a:root xmlns:a=\"http://example.com/ns1\" xmlns:b=\"http://example.com/ns2\">"
+                + "<a:a>A</a:a><b:b>B</b:b><a:c a1=\"1\" b:a2=\"2\" />"
+                + "</a:root>");
+        assertOutput(ftlHeader
+                + "${doc.@@markup}",
+                "<root xmlns=\"http://example.com/ns1\" xmlns:n2=\"http://example.com/ns2\">"
+                + "<a>A</a><n2:b>B</n2:b><c a1=\"1\" n2:a2=\"2\" /></root>");
+        assertOutput("<#ftl ns_prefixes={'D':'http://example.com/ns1'}>"
+                + "${doc.@@markup}",
+                "<root xmlns=\"http://example.com/ns1\" xmlns:a=\"http://example.com/ns2\">"
+                + "<a>A</a><a:b>B</a:b><c a1=\"1\" a:a2=\"2\" /></root>");
+        
+        // When there's no matching prefix declared via the #ftl header, return null for qname:
+        assertOutput("${doc?children[0].@@qname!'null'}", "null");
+        assertOutput("${doc?children[0]?children[1].@@qname!'null'}", "null");
+        assertOutput("${doc?children[0]?children[2]['@*'][1].@@qname!'null'}", "null");
+        
+        // When we have prefix declared in the #ftl header:
+        assertOutput(ftlHeader + "${doc?children[0].@@qname}", "root");
+        assertOutput(ftlHeader + "${doc?children[0]?children[1].@@qname}", "n2:b");
+        assertOutput(ftlHeader + "${doc?children[0]?children[2].@@qname}", "c");
+        assertOutput(ftlHeader + "${doc?children[0]?children[2]['@*'][0].@@qname}", "a1");
+        assertOutput(ftlHeader + "${doc?children[0]?children[2]['@*'][1].@@qname}", "n2:a2");
+        // Unfortunately these include the xmlns attributes, but that would be non-BC to fix now:
+        assertThat(getOutput(ftlHeader + "${doc?children[0].@@start_tag}"), startsWith("<root"));
+        assertThat(getOutput(ftlHeader + "${doc?children[0]?children[1].@@start_tag}"), startsWith("<n2:b"));
+    }
+    
+    @Test
+    public void namespaceUnaware() throws Exception {
+        addNSUnawareDocToDataModel("<root><x:a>A</x:a><:>B</:><xyz::c>C</xyz::c></root>");
+        assertOutput("${doc.root['x:a']}", "A");
+        assertOutput("${doc.root[':']}", "B");
+        try {
+            assertOutput("${doc.root['xyz::c']}", "C");
+            fail();
+        } catch (TemplateException e) {
+            assertThat(e.getMessage(), containsString("xyz"));
+        }
+    }
+    
+    private void addDocToDataModel(String xml) throws SAXException, IOException, ParserConfigurationException {
+        addToDataModel("doc", DOMLoader.toModel(new InputSource(new StringReader(xml))));
+    }
+
+    private void addDocToDataModelNoSimplification(String xml) throws SAXException, IOException, ParserConfigurationException {
+        addToDataModel("doc", DOMLoader.toModel(new InputSource(new StringReader(xml)), false));
+    }
+    
+    private void addNSUnawareDocToDataModel(String xml) throws ParserConfigurationException, SAXException, IOException {
+        DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
+        newFactory.setNamespaceAware(false);
+        DocumentBuilder builder = newFactory.newDocumentBuilder();
+        Document doc = builder.parse(new InputSource(new StringReader(xml)));
+        addToDataModel("doc", NodeModel.wrap(doc));
+    }
+
+    @Test
+    public void testInvalidAtAtKeyErrors() throws Exception {
+        addDocToDataModel("<r><multipleMatches /><multipleMatches /></r>");
+        assertErrorContains("${doc.r.@@invalid_key}", "Unsupported @@ key", "@invalid_key");
+        assertErrorContains("${doc.@@start_tag}", "@@start_tag", "not supported", "document");
+        assertErrorContains("${doc.@@}", "\"@@\"", "not supported", "document");
+        assertErrorContains("${doc.r.noMatch.@@invalid_key}", "Unsupported @@ key", "@invalid_key");
+        assertErrorContains("${doc.r.multipleMatches.@@invalid_key}", "Unsupported @@ key", "@invalid_key");
+        assertErrorContains("${doc.r.noMatch.@@attributes_markup}", "single XML node", "@@attributes_markup");
+        assertErrorContains("${doc.r.multipleMatches.@@attributes_markup}", "single XML node", "@@attributes_markup");
+    }
+    
+    @Test
+    public void testAtAtSiblingElement() throws Exception {
+        addDocToDataModel("<r><a/><b/></r>");
+        assertOutput("${doc.r.@@previous_sibling_element?size}", "0");
+        assertOutput("${doc.r.@@next_sibling_element?size}", "0");
+        assertOutput("${doc.r.a.@@previous_sibling_element?size}", "0");
+        assertOutput("${doc.r.a.@@next_sibling_element.@@qname}", "b");
+        assertOutput("${doc.r.b.@@previous_sibling_element.@@qname}", "a");
+        assertOutput("${doc.r.b.@@next_sibling_element?size}", "0");
+        
+        addDocToDataModel("<r>\r\n\t <a/>\r\n\t <b/>\r\n\t </r>");
+        assertOutput("${doc.r.@@previous_sibling_element?size}", "0");
+        assertOutput("${doc.r.@@next_sibling_element?size}", "0");
+        assertOutput("${doc.r.a.@@previous_sibling_element?size}", "0");
+        assertOutput("${doc.r.a.@@next_sibling_element.@@qname}", "b");
+        assertOutput("${doc.r.b.@@previous_sibling_element.@@qname}", "a");
+        assertOutput("${doc.r.b.@@next_sibling_element?size}", "0");
+        
+        addDocToDataModel("<r>t<a/>t<b/>t</r>");
+        assertOutput("${doc.r.a.@@previous_sibling_element?size}", "0");
+        assertOutput("${doc.r.a.@@next_sibling_element?size}", "0");
+        assertOutput("${doc.r.b.@@previous_sibling_element?size}", "0");
+        assertOutput("${doc.r.b.@@next_sibling_element?size}", "0");
+        
+        addDocToDataModelNoSimplification("<r><a/> <!-- --><?pi?>&#x20;<b/></r>");
+        assertOutput("${doc.r.a.@@next_sibling_element.@@qname}", "b");
+        assertOutput("${doc.r.b.@@previous_sibling_element.@@qname}", "a");
+        
+        addDocToDataModelNoSimplification("<r><a/> <!-- -->t<!-- --> <b/></r>");
+        assertOutput("${doc.r.a.@@next_sibling_element?size}", "0");
+        assertOutput("${doc.r.b.@@previous_sibling_element?size}", "0");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/java/org/apache/freemarker/dom/DefaultObjectWrapperExtensionTest.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/DefaultObjectWrapperExtensionTest.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DefaultObjectWrapperExtensionTest.java
new file mode 100644
index 0000000..347980d
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/DefaultObjectWrapperExtensionTest.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 static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
+import org.apache.freemarker.dom.test.DOMLoader;
+import org.apache.freemarker.test.TemplateTest;
+import org.apache.freemarker.test.TestConfigurationBuilder;
+import org.junit.Before;
+import org.junit.Test;
+import org.xml.sax.SAXException;
+
+public class DefaultObjectWrapperExtensionTest extends TemplateTest {
+
+    @Before
+    public void setup() throws ParserConfigurationException, SAXException, IOException {
+        addToDataModel("doc", DOMLoader.toDOM("<doc><title>test</title></doc>").getDocumentElement());
+    }
+
+    @Test
+    public void testWithExtensions() throws IOException, TemplateException {
+        setConfiguration(new TestConfigurationBuilder()
+                .objectWrapper(
+                        new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0)
+                                .extensions(DOMDefaultObjectWrapperExtension.INSTANCE)
+                                .build()
+                )
+                .build());
+        assertOutput("${doc.title}", "test");
+    }
+
+    @Test
+    public void testWithoutExtensions() throws IOException, TemplateException {
+        try {
+            assertOutput("${doc.title}", "test");
+            fail();
+        } catch (TemplateException e) {
+            // Expected
+        }
+
+        assertOutput("${doc.getElementsByTagName('title').item(0).textContent}", "test");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/java/org/apache/freemarker/dom/TypeErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/TypeErrorMessagesTest.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/TypeErrorMessagesTest.java
new file mode 100644
index 0000000..b2f1304
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/TypeErrorMessagesTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.dom.test.DOMLoader;
+import org.apache.freemarker.test.TemplateTest;
+import org.junit.Test;
+
+public class TypeErrorMessagesTest extends TemplateTest {
+
+    @Test
+    public void test() throws Exception {
+        addToDataModel("doc", DOMLoader.toModel("<a><b>123</b><c a='true'>1</c><c a='false'>2</c></a>"));
+
+        assertErrorContains("${doc.a.c}",
+                "used as string", "query result", "2", "multiple matches");
+        assertErrorContains("${doc.a.c?boolean}",
+                "used as string", "query result", "2", "multiple matches");
+        assertErrorContains("${doc.a.d}",
+                "used as string", "query result", "0", "no matches");
+        assertErrorContains("${doc.a.d?boolean}",
+                "used as string", "query result", "0", "no matches");
+
+        assertErrorContains("${doc.a.c.@a}",
+                "used as string", "query result", "2", "multiple matches");
+        assertErrorContains("${doc.a.d.@b}",
+                "used as string", "query result", "x", "no matches");
+
+        assertErrorContains("${doc.a.b * 2}",
+                "used as number", "text", "explicit conversion");
+        assertErrorContains("<#if doc.a.b></#if>",
+                "used as number", "text", "explicit conversion");
+
+        assertErrorContains("${doc.a.d?nodeName}",
+                "used as node", "query result", "0", "no matches");
+        assertErrorContains("${doc.a.c?nodeName}",
+                "used as node", "query result", "2", "multiple matches");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/java/org/apache/freemarker/dom/templatesuite/DomTemplateTestSuite.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/templatesuite/DomTemplateTestSuite.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/templatesuite/DomTemplateTestSuite.java
new file mode 100644
index 0000000..e3a91e1
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/templatesuite/DomTemplateTestSuite.java
@@ -0,0 +1,62 @@
+/*
+ * 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.templatesuite;
+
+import java.util.Map;
+
+import org.apache.freemarker.core.Configuration;
+import org.apache.freemarker.dom.NodeModel;
+import org.apache.freemarker.dom.test.DOMLoader;
+import org.apache.freemarker.test.TemplateTestSuite;
+import org.w3c.dom.Document;
+
+import junit.framework.TestSuite;
+
+public class DomTemplateTestSuite extends TemplateTestSuite {
+
+    @Override
+    protected void setUpTestCase(String simpleTestName, Map<String, Object> dataModel,
+            Configuration.ExtendableBuilder<?> confB) throws Exception {
+        NodeModel.useJaxenXPathSupport();
+
+        if (simpleTestName.equals("default-xmlns")) {
+            dataModel.put("doc", DOMLoader.toModel(getClass(), "models/defaultxmlns1.xml"));
+        } else if (simpleTestName.equals("xml-fragment")) {
+            Document doc = DOMLoader.toDOM(getClass(), "models/xmlfragment.xml");
+            NodeModel.simplify(doc);
+            dataModel.put("node", NodeModel.wrap(doc.getDocumentElement().getFirstChild().getFirstChild()));
+        } else if (simpleTestName.equals("xmlns1")) {
+            dataModel.put("doc", DOMLoader.toModel(getClass(), "models/xmlns.xml"));
+        } else if (simpleTestName.equals("xmlns2")) {
+            dataModel.put("doc", DOMLoader.toModel(getClass(), "models/xmlns2.xml"));
+        } else if (simpleTestName.equals("xmlns3") || simpleTestName.equals("xmlns4")) {
+            dataModel.put("doc", DOMLoader.toModel(getClass(), "models/xmlns3.xml"));
+        } else if (simpleTestName.equals("xmlns5")) {
+            dataModel.put("doc", DOMLoader.toModel(getClass(), "models/defaultxmlns1.xml"));
+        } else if (simpleTestName.equals("xml-ns_prefix-scope")) {
+            dataModel.put("doc", DOMLoader.toModel(getClass(), "models/xml-ns_prefix-scope.xml"));
+        }
+    }
+
+    public static TestSuite suite() {
+        return new DomTemplateTestSuite();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/java/org/apache/freemarker/dom/test/DOMLoader.java
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/java/org/apache/freemarker/dom/test/DOMLoader.java b/freemarker-dom/src/test/java/org/apache/freemarker/dom/test/DOMLoader.java
new file mode 100644
index 0000000..bc957c5
--- /dev/null
+++ b/freemarker-dom/src/test/java/org/apache/freemarker/dom/test/DOMLoader.java
@@ -0,0 +1,145 @@
+/*
+ * 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.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.freemarker.core.util._StringUtil;
+import org.apache.freemarker.dom.NodeModel;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Utility to load XML resources into {@link Node}-s or {@link NodeModel}-s.
+ */
+public final class DOMLoader {
+
+    private static final Object STATIC_LOCK = new Object();
+    
+    static private DocumentBuilderFactory docBuilderFactory;
+    
+    private DOMLoader() {
+        //
+    }
+    
+    /**
+     * Convenience method to invoke a {@link NodeModel} from a SAX {@link InputSource}.
+     */
+    static public NodeModel toModel(InputSource is, boolean simplify)
+        throws SAXException, IOException, ParserConfigurationException {
+        DocumentBuilder builder = getDocumentBuilderFactory().newDocumentBuilder();
+        final Document doc;
+        try {
+            doc = builder.parse(is);
+        } catch (MalformedURLException e) {
+            // This typical error has an error message that is hard to understand, so let's translate it:
+            if (is.getSystemId() == null && is.getCharacterStream() == null && is.getByteStream() == null) {
+                throw new MalformedURLException(
+                        "The SAX InputSource has systemId == null && characterStream == null && byteStream == null. "
+                        + "This is often because it was created with a null InputStream or Reader, which is often because "
+                        + "the XML file it should point to was not found. "
+                        + "(The original exception was: " + e + ")");
+            } else {
+                throw e;
+            }
+        }
+        if (simplify) {
+            NodeModel.simplify(doc);
+        }
+        return NodeModel.wrap(doc);
+    }
+    
+    /**
+     * Same as {@link #toModel(InputSource, boolean) parse(is, true)}.
+     */
+    static public NodeModel toModel(InputSource is) throws SAXException, IOException, ParserConfigurationException {
+        return toModel(is, true);
+    }
+
+    static public NodeModel toModel(Class<?> baseClass, String resourcePath)
+            throws ParserConfigurationException, SAXException, IOException {
+        InputStream in = baseClass.getResourceAsStream(resourcePath);
+        if (in == null) {
+            throw new FileNotFoundException("Class loader resource not found: baseClass=" + baseClass.getName()
+                    + "; path=" + _StringUtil.jQuote(resourcePath));
+        }
+        return toModel(new InputSource(in));
+    }
+
+    /**
+     * Same as {@link #toModel(InputSource, boolean)}, but loads from a {@link File}; don't miss the security
+     * warnings documented there.
+     */
+    static public NodeModel toModel(String content, boolean simplify) 
+    throws SAXException, IOException, ParserConfigurationException {
+        return toModel(toInputSource(content));
+    }
+    
+    /**
+     * Same as {@link #toModel(InputSource, boolean) parse(source, true)}, but loads from a {@link String}.
+     */
+    static public NodeModel toModel(String content) throws SAXException, IOException, ParserConfigurationException {
+        return toModel(content, true);
+    }
+    
+    public static Document toDOM(String content) throws SAXException, IOException, ParserConfigurationException {
+        DocumentBuilder builder =  getDocumentBuilderFactory().newDocumentBuilder();
+        return builder.parse(toInputSource(content));
+    }
+
+    public static Document toDOM(Class<?> baseClass, String resourcePath) throws SAXException, IOException,
+            ParserConfigurationException {
+        DocumentBuilder builder =  getDocumentBuilderFactory().newDocumentBuilder();
+        InputStream in = baseClass.getResourceAsStream(resourcePath);
+        if (in == null) {
+            throw new FileNotFoundException("Class loader resource not found: baseClass="
+                    + baseClass.getName() + "; " + "path=" + _StringUtil.jQuote(resourcePath));
+        }
+        return builder.parse(new InputSource(in));
+    }
+
+    static private DocumentBuilderFactory getDocumentBuilderFactory() {
+        synchronized (STATIC_LOCK) {
+            if (docBuilderFactory == null) {
+                DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
+                newFactory.setNamespaceAware(true);
+                newFactory.setIgnoringElementContentWhitespace(true);
+                docBuilderFactory = newFactory;  // We only write it out when the initialization was full 
+            }
+            return docBuilderFactory;
+        }
+    }
+
+    private static InputSource toInputSource(String content) {
+        return new InputSource(new StringReader(content));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml
new file mode 100644
index 0000000..d1fe3dc
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/DOMSiblingTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.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.
+-->
+<person>
+    <gender>male</gender>
+    <name>pradeep</name>
+    <dob>12th August</dob><address>Chennai, India</address>
+    <!--This is a comment Node -->
+    <?xml-stylesheet type="text/css" href="style.css"?>
+    <profession>Software Engineer</profession>
+    <![CDATA[    ]]>
+    <hobby>gardening</hobby>
+    <![CDATA[this is a valid cdata]]>
+    <phone>12345678</phone>
+</person>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/default-xmlns.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/default-xmlns.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/default-xmlns.txt
new file mode 100644
index 0000000..3a52c46
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/default-xmlns.txt
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+No NS = No NS
+x NS = x NS
+y NS = y NS
+x NS = x NS
+
+true
+

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-fragment.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-fragment.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-fragment.txt
new file mode 100644
index 0000000..cb0da5f
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-fragment.txt
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+b = b
+@document = @document
+@document = @document
+
+C<>&"']]> = C<>&"']]>
+
+<root xmlns:n="http://x"><a><b><n:c>C&lt;>&amp;"']]&gt;</n:c></b></a></root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-ns_prefix-scope.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-ns_prefix-scope.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-ns_prefix-scope.txt
new file mode 100644
index 0000000..bf8e6bd
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml-ns_prefix-scope.txt
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+//e: e in NS namespace-test, e in NS namespace-test
+//n:e: e in NS foo, e in NS foo
+//bar:e: e in NS bar, e in NS bar
+
+Included:
+//n:e: e in NS foo, e in NS foo
+//n:e: e in NS foo, e in NS foo
+
+Imported:
+//n:e: e in NS bar, e in NS bar
+//n:e: e in NS bar, e in NS bar

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml.txt
new file mode 100644
index 0000000..718bba1
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xml.txt
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+  <?firstPi customKey="something"?>
+  firstPi
+  customKey="something"
+  <?secondPi secondPiData?>
+  secondPi
+  secondPiData
+2
+p
+ customKey="something"
+
+<ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text1</ns1:e11><ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text2</ns1:e11>
+<ns1:e12 xmlns:ns1="http://www.foo.com/ns1/"><![CDATA[cdata-section1]]></ns1:e12><ns1:e12 xmlns:ns1="http://www.foo.com/ns1/"><![CDATA[cdata-section2<&]]></ns1:e12>
+<ns1:e1 xmlns:ns1="http://www.foo.com/ns1/" a1="v1" a2="v2">
+    <ns1:e11>text1</ns1:e11>
+    <ns1:e12><![CDATA[cdata-section1]]></ns1:e12>
+  </ns1:e1>
+<ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text1</ns1:e11><ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text2</ns1:e11>
+a1
+v2
+rootroot
+root
+  root
+  e1
+  e11
+  e12
+  e2
+  e11
+  e12
+  root
+  root
+  e1
+  root
+  e1
+  e11
+  root
+  e1
+  e12
+  root
+  e2
+  root
+  e2
+  e11
+  root
+  e2
+  e12
+cdata-section2&lt;&amp;
+cdata-section2<&

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns1.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns1.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns1.txt
new file mode 100644
index 0000000..69af553
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns1.txt
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+<book xmlns="http://example.com/eBook">
+  <title>Test Book</title>
+  <chapter>
+    <title>Ch1</title>
+    <para>p1.1</para>
+    <para>p1.2</para>
+    <para>p1.3</para>
+  </chapter>
+  <chapter>
+    <title>Ch2</title>
+    <para>p2.1</para>
+    <para>p2.2</para>
+  </chapter>
+</book>
+
+  <html>
+    <head>
+      <title>Test Book</title>
+    </head>
+    <body>
+      <h1>Test Book</h1>
+
+  
+    <h2>Ch1</h2>
+
+    
+      <p>p1.1
+
+      <p>p1.2
+
+      <p>p1.3
+
+  
+    <h2>Ch2</h2>
+
+    
+      <p>p2.1
+
+      <p>p2.2
+
+  
+    </body>
+  </html>
+

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns3.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns3.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns3.txt
new file mode 100644
index 0000000..f028f0a
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns3.txt
@@ -0,0 +1,47 @@
+/*
+ * 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>Test Book</title>
+    </head>
+    <body>
+      <h1>Test Book</h1>
+
+  
+    <h2>Ch1</h2>
+
+    
+      <p>p1.1
+
+      <p>p1.2
+
+      <p>p1.3
+
+  
+    <h2>Ch2</h2>
+
+    
+      <p>p2.1
+
+      <p>p2.2
+
+  
+    </body>
+  </html>
+

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns4.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns4.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns4.txt
new file mode 100644
index 0000000..f028f0a
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns4.txt
@@ -0,0 +1,47 @@
+/*
+ * 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>Test Book</title>
+    </head>
+    <body>
+      <h1>Test Book</h1>
+
+  
+    <h2>Ch1</h2>
+
+    
+      <p>p1.1
+
+      <p>p1.2
+
+      <p>p1.3
+
+  
+    <h2>Ch2</h2>
+
+    
+      <p>p2.1
+
+      <p>p2.2
+
+  
+    </body>
+  </html>
+

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns5.txt
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns5.txt b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns5.txt
new file mode 100644
index 0000000..6e42b09
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/expected/xmlns5.txt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+No NS = No NS
+x NS = x NS
+y NS = y NS
+x NS = x NS
+No NS = No NS
+- = -
+- = -
+- = -

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/defaultxmlns1.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/defaultxmlns1.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/defaultxmlns1.xml
new file mode 100644
index 0000000..ed289bb
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/defaultxmlns1.xml
@@ -0,0 +1,24 @@
+<!--
+  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.
+-->
+<root xmlns:x="http://x.com" xmlns:y="http://y.com">
+  <t1>No NS</t1>
+  <x:t2>x NS</x:t2>
+  <y:t3>y NS</y:t3>
+  <t4 xmlns="http://x.com">x NS</t4>
+</root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml-ns_prefix-scope.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml-ns_prefix-scope.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml-ns_prefix-scope.xml
new file mode 100644
index 0000000..934acac
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml-ns_prefix-scope.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<root xmlns="http://freemarker.org/test/namespace-test"
+    xmlns:foo="http://freemarker.org/test/foo"
+    xmlns:bar="http://freemarker.org/test/bar">
+  <e>e in NS namespace-test</e>
+  <foo:e>e in NS foo</foo:e>
+  <bar:e>e in NS bar</bar:e>
+</root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml.xml
new file mode 100644
index 0000000..abf7e96
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xml.xml
@@ -0,0 +1,31 @@
+<?xml version="1.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.
+-->
+<?firstPi customKey="something"?>
+<?secondPi secondPiData?>
+<ns1:root xmlns:ns1="http://www.foo.com/ns1/">
+  <ns1:e1 a1="v1" a2="v2">
+    <ns1:e11>text1</ns1:e11>
+    <ns1:e12><![CDATA[cdata-section1]]></ns1:e12>
+  </ns1:e1>
+  <ns1:e2>
+    <ns1:e11>text2</ns1:e11>
+    <ns1:e12><![CDATA[cdata-section2<&]]></ns1:e12>
+  </ns1:e2>
+</ns1:root>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlfragment.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlfragment.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlfragment.xml
new file mode 100644
index 0000000..b5578b6
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlfragment.xml
@@ -0,0 +1,19 @@
+<!--
+  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.
+-->
+<root><a><b><c xmlns="http://x">C&lt;&gt;&amp;"']]&gt;</c></b></a></root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns.xml
new file mode 100644
index 0000000..6f6453e
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns.xml
@@ -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.
+-->
+<book xmlns="http://example.com/eBook">
+  <title>Test Book</title>
+  <chapter>
+    <title>Ch1</title>
+    <para>p1.1</para>
+    <para>p1.2</para>
+    <para>p1.3</para>
+  </chapter>
+  <chapter>
+    <title>Ch2</title>
+    <para>p2.1</para>
+    <para>p2.2</para>
+  </chapter>
+</book>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns2.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns2.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns2.xml
new file mode 100644
index 0000000..c8bfc9f
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns2.xml
@@ -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.
+-->
+<eb:book xmlns:eb="http://example.com/eBook">
+  <eb:title>Test Book</eb:title>
+  <eb:chapter>
+    <eb:title>Ch1</eb:title>
+    <eb:para>p1.1</eb:para>
+    <eb:para>p1.2</eb:para>
+    <eb:para>p1.3</eb:para>
+  </eb:chapter>
+  <eb:chapter>
+    <eb:title>Ch2</eb:title>
+    <eb:para>p2.1</eb:para>
+    <eb:para>p2.2</eb:para>
+  </eb:chapter>
+</eb:book>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns3.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns3.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns3.xml
new file mode 100644
index 0000000..8502ead
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/models/xmlns3.xml
@@ -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.
+-->
+<book xmlns:x="http://x" xmlns:y="http://y">
+  <x:title>Test Book</x:title>
+  <chapter>
+    <y:title>Ch1</y:title>
+    <para>p1.1</para>
+    <para>p1.2</para>
+    <para>p1.3</para>
+  </chapter>
+  <x:chapter>
+    <y:title>Ch2</y:title>
+    <x:para>p2.1</x:para>
+    <y:para>p2.2</y:para>
+  </x:chapter>
+</book>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.ftl
new file mode 100644
index 0000000..4f01835
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/default-xmlns.ftl
@@ -0,0 +1,28 @@
+<#ftl ns_prefixes={"D" : "http://x.com", "y" : "http://y.com"}>
+<#--
+  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.
+-->
+<#assign r = doc.*[0]>
+${r["N:t1"]?default('-')} = No NS
+${r["t2"]?default('-')} = x NS
+${r["y:t3"]?default('-')} = y NS
+${r["./D:t4"]?default('-')} = x NS
+
+<#assign bool = doc["true()"]>
+${bool?string}
+

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-fragment.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-fragment.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-fragment.ftl
new file mode 100644
index 0000000..226215b
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-fragment.ftl
@@ -0,0 +1,26 @@
+<#ftl ns_prefixes = {"n" : "http://x"}>
+<#--
+  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.
+-->
+${node?node_name} = b
+${node?root?node_name} = @document
+${node['/']?node_name} = @document
+
+${node['n:c']} = C<>&"']]>
+
+${node?root.@@markup}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-lib.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-lib.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-lib.ftl
new file mode 100644
index 0000000..0f0bde2
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-lib.ftl
@@ -0,0 +1,23 @@
+<#ftl ns_prefixes={ "n": "http://freemarker.org/test/bar", "D": "http://freemarker.org/test/namespace-test" }>
+<#--
+  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.
+-->
+<#global libResult>//n:e: ${doc['//n:e']}, ${doc.root['n:e']}</#global>
+<#macro m>
+//n:e: ${doc['//n:e']}, ${doc.root['n:e']}
+</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-main.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-main.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-main.ftl
new file mode 100644
index 0000000..5b7ce24
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml-ns_prefix-scope-main.ftl
@@ -0,0 +1,36 @@
+<#ftl ns_prefixes={
+    "D": "http://freemarker.org/test/namespace-test",
+    "n": "http://freemarker.org/test/foo",
+    "bar": "http://freemarker.org/test/bar"
+}>
+<#--
+  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.
+-->
+//e: ${doc['//D:e']}, ${doc.root.e}
+//n:e: ${doc['//n:e']}, ${doc.root['n:e']}
+//bar:e: ${doc['//bar:e']}, ${doc.root['bar:e']}
+
+Included:
+<#include "xml-ns_prefix-scope-lib.ftl">
+${libResult}
+<@m />
+
+Imported:
+<#import "xml-ns_prefix-scope-lib.ftl" as lib>
+${libResult}
+<@lib.m />

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml.ftl
new file mode 100644
index 0000000..b85fc03
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xml.ftl
@@ -0,0 +1,47 @@
+<#--
+  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.
+-->
+<#-- test processing instructions -->
+<#global PIs = doc._content._ftype("p")>
+<#list PIs as pi>
+  ${pi}
+  ${pi["@target"]._text}
+  ${pi["@data"]._text}
+</#list>
+${PIs?size}
+<#global firstPi = PIs[0]>
+${firstPi._type}
+${firstPi["@customKey"]}
+${doc._registerNamespace("ns", "http://www.foo.com/ns1/")}
+${doc._descendant["ns:e11"]}
+${doc._descendant["ns:e12"]}
+<#global docRoot = doc["ns:root"]>
+${docRoot["ns:e1"]}
+${doc("//ns:e11")}
+${docRoot["ns:e1"]["@a1"]._name}
+${docRoot["ns:e1"]["@a2"]._text}
+${docRoot._children._parent._name}
+${docRoot._children._parent._unique._name}
+<#list doc._descendant as d>
+  ${d._name}
+</#list>
+<#list doc._descendant._ancestorOrSelf as d>
+  ${d._name}
+</#list>
+${docRoot["ns:e2"]["ns:e12"]._text}
+${docRoot["ns:e2"]["ns:e12"]._plaintext}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns1.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns1.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns1.ftl
new file mode 100644
index 0000000..8aa893e
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns1.ftl
@@ -0,0 +1,53 @@
+<#ftl ns_prefixes = {"D" : "http://example.com/eBook"}>
+<#--
+  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.
+-->
+
+${doc.@@markup}
+
+<#recurse doc >
+
+<#macro book>
+  <html>
+    <head>
+      <title><#recurse .node.title></title>
+    </head>
+    <body>
+      <h1><#recurse .node.title></h1>
+      <#recurse>
+    </body>
+  </html>
+</#macro>
+
+<#macro chapter>
+  <h2><#recurse .node.title></h2>
+  <#recurse>
+</#macro>
+
+<#macro para>
+  <p><#recurse>
+</#macro>
+
+<#macro title>
+  <#--
+    We have handled this element imperatively,
+    so we do nothing here.
+  -->
+</#macro>
+
+<#macro @text>${.node?html}</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns3.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns3.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns3.ftl
new file mode 100644
index 0000000..c84ec69
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns3.ftl
@@ -0,0 +1,70 @@
+<#ftl ns_prefixes = {"x" : "http://x", "y" : "http://y"}>
+<#--
+  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.
+-->
+<#recurse doc >
+
+<#macro book>
+  <html>
+    <head>
+      <title><#recurse .node["x:title"]></title>
+    </head>
+    <body>
+      <h1><#recurse .node["x:title"]></h1>
+      <#recurse>
+    </body>
+  </html>
+</#macro>
+
+<#macro chapter>
+  <h2><#recurse .node["y:title"]></h2>
+  <#recurse>
+</#macro>
+
+<#macro "x:chapter">
+  <h2><#recurse .node["y:title"]></h2>
+  <#recurse>
+</#macro>
+
+<#macro para>
+  <p><#recurse>
+</#macro>
+
+<#macro "x:para">
+  <p><#recurse>
+</#macro>
+
+<#macro "y:para">
+  <p><#recurse>
+</#macro>
+
+<#macro "x:title">
+  <#--
+    We have handled this element imperatively,
+    so we do nothing here.
+  -->
+</#macro>
+
+<#macro "y:title">
+  <#--
+    We have handled this element imperatively,
+    so we do nothing here.
+  -->
+</#macro>
+
+<#macro @text>${.node?html}</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns4.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns4.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns4.ftl
new file mode 100644
index 0000000..e97bfc0
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns4.ftl
@@ -0,0 +1,70 @@
+<#ftl ns_prefixes = {"x" : "http://x", "y" : "http://y"}>
+<#--
+  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.
+-->
+<#recurse doc >
+
+<#macro book>
+  <html>
+    <head>
+      <title><#recurse .node["x:title"]></title>
+    </head>
+    <body>
+      <h1><#recurse .node["x:title"]></h1>
+      <#recurse>
+    </body>
+  </html>
+</#macro>
+
+<#macro chapter>
+  <h2><#recurse .node["y:title"]></h2>
+  <#recurse>
+</#macro>
+
+<#macro 'x:chapter'>
+  <h2><#recurse .node["y:title"]></h2>
+  <#recurse>
+</#macro>
+
+<#macro para>
+  <p><#recurse>
+</#macro>
+
+<#macro 'x:para'>
+  <p><#recurse>
+</#macro>
+
+<#macro 'y:para'>
+  <p><#recurse>
+</#macro>
+
+<#macro "x:title">
+  <#--
+    We have handled this element imperatively,
+    so we do nothing here.
+  -->
+</#macro>
+
+<#macro "y:title">
+  <#--
+    We have handled this element imperatively,
+    so we do nothing here.
+  -->
+</#macro>
+
+<#macro @text>${.node?html}</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns5.ftl
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns5.ftl b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns5.ftl
new file mode 100644
index 0000000..edc3b4a
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/templates/xmlns5.ftl
@@ -0,0 +1,28 @@
+<#ftl ns_prefixes = {"D": "http://y.com", "xx" : "http://x.com"}>
+<#--
+  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.
+-->
+<#assign r = doc["N:root"]>
+${r["N:t1"][0]?default('-')} = No NS
+${r["xx:t2"][0]?default('-')} = x NS
+${r["t3"][0]?default('-')} = y NS
+${r["xx:t4"][0]?default('-')} = x NS
+${r["//t1"][0]?default('-')} = No NS
+${r["//t2"][0]?default('-')} = -
+${r["//t3"][0]?default('-')} = -
+${r["//t4"][0]?default('-')} = -

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/testcases.xml
----------------------------------------------------------------------
diff --git a/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/testcases.xml b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/testcases.xml
new file mode 100644
index 0000000..ffb42c3
--- /dev/null
+++ b/freemarker-dom/src/test/resources/org/apache/freemarker/dom/templatesuite/testcases.xml
@@ -0,0 +1,65 @@
+<?xml version="1.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.
+-->
+
+<!DOCTYPE testCases [
+  <!ELEMENT testCases (setting?, testCase*)>
+  <!ELEMENT testCase (setting?)>
+     <!ATTLIST testCase 
+               name CDATA #REQUIRED
+               template CDATA #IMPLIED
+               expected CDATA #IMPLIED
+               noOutput CDATA #IMPLIED
+     >
+     <!-- The default of `template` is "${name?keep_before('[#endTN]')}.ftl" -->
+     <!-- The default of `expected` is "${name}.txt" -->
+     <!-- The default of `noOutput` is false -->
+     
+ <!ELEMENT setting EMPTY>
+     <!ATTLIST setting 
+            auto_import CDATA #IMPLIED
+            source_encoding CDATA #IMPLIED
+            locale CDATA #IMPLIED
+            object_wrapper CDATA #IMPLIED
+            output_encoding CDATA #IMPLIED
+            output_dir CDATA #IMPLIED
+            new_builtin_class_resolver CDATA #IMPLIED
+            url_escaping_charset CDATA #IMPLIED
+            incompatible_improvements CDATA #IMPLIED
+            time_zone CDATA #IMPLIED
+            api_builtin_enabled CDATA #IMPLIED
+      >
+]>
+<!--
+Note that for the incompatible_improvements setting you can specify a list of versions, for example:
+<setting incompatible_improvements="min, 3.0.5, max" /> 
+-->
+
+<testCases>
+   <setting source_encoding="UTF-8" output_encoding="UTF-8" />
+
+   <testCase name="default-xmlns" />
+   <testCase name="xml-fragment" />
+   <testCase name="xmlns1" />
+   <testCase name="xmlns2" template="xmlns1.ftl"  expected="xmlns1.txt" />
+   <testCase name="xmlns3" />
+   <testCase name="xmlns4" />
+   <testCase name="xmlns5" />
+   <testCase name="xml-ns_prefix-scope" template="xml-ns_prefix-scope-main.ftl" />
+</testCases>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-servlet/build.gradle
----------------------------------------------------------------------
diff --git a/freemarker-servlet/build.gradle b/freemarker-servlet/build.gradle
index 41a8302..c0710b9 100644
--- a/freemarker-servlet/build.gradle
+++ b/freemarker-servlet/build.gradle
@@ -1,3 +1,22 @@
+/*
+ * 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 Servlet and JSP support"
 description = """\
 FreeMarker template engine, Servlet and JSP support. \



[6/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.

Posted by dd...@apache.org.
Factored out freemarker-dom from freemarker-core. Also added mechanism to "inject" DOM wrapping capability into DefaultObjectWrapper on configuration time. See details below.

- `DefaultObjectWrapper` now has a configuration setting, `extensions`, to add `DefaultObjectWrapperExtension`-s, which
  meant to be used for wrapping application-specific objects specially. Along with this, `DefaultObjectWrapper` now has
  two protected methods to customze the wrapping logic, `wrapSpecialObject(Object)` and `wrapGenericObject(Object)`; the
  last replaces the `handleUnknownType(Object)` method from FM2. `DefaultObjectWrapperExtension`-s are applied before or
  after `wrapSpecialObject`, depending on what `DefaultObjectWrapperExtension.getPhase()` returns. See the JavaDoc of
  `DefaultObjectWrapper.wrap(Object)` for more on wrapping phases.

- `DefaultObjectWrapper` doesn't wrap W3C DOM nodes (XML) specially anymore, as DOM wrapping was factored out to a
  separate jar (freemarker-dom) as part of modularizing FreeMarker. To ensure that DOM nodes are wrapped specially as
  in FM2, the `extensions` setting of the `DefaultObjectWrapper` has to be so that it contains
  `DOMDefaultObjectWrapperExtension.INSTANCE`. For example:

    cfgBuilder.objectWrapper(
            new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0)
                    .extensions(DOMDefaultObjectWrapperExtension.INSTANCE)
                    .build())


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/be556897
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/be556897
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/be556897

Branch: refs/heads/3
Commit: be556897911e91e32aaa8f989a20591fe0f71d8c
Parents: 053b82e
Author: ddekany <dd...@apache.org>
Authored: Sun May 21 23:30:44 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun May 21 23:34:40 2017 +0200

----------------------------------------------------------------------
 FM3-CHANGE-LOG.txt                              |  21 +-
 freemarker-core-test/build.gradle               |  26 +-
 .../freemarker/core/TypeErrorMessagesTest.java  |  51 +-
 .../model/impl/DefaultObjectWrapperTest.java    |  23 +-
 .../templatesuite/CoreTemplateTestSuite.java    |  37 --
 .../apache/freemarker/dom/DOMSiblingTest.java   |  99 ---
 .../freemarker/dom/DOMSimplifiersTest.java      | 201 ------
 .../java/org/apache/freemarker/dom/DOMTest.java | 159 -----
 .../templatesuite/expected/default-xmlns.txt    |  25 -
 .../templatesuite/expected/xml-fragment.txt     |  25 -
 .../expected/xml-ns_prefix-scope.txt            |  29 -
 .../core/templatesuite/expected/xml.txt         |  65 --
 .../core/templatesuite/expected/xmlns1.txt      |  63 --
 .../core/templatesuite/expected/xmlns3.txt      |  47 --
 .../core/templatesuite/expected/xmlns4.txt      |  47 --
 .../core/templatesuite/expected/xmlns5.txt      |  26 -
 .../core/templatesuite/models/defaultxmlns1.xml |  24 -
 .../models/xml-ns_prefix-scope.xml              |  26 -
 .../core/templatesuite/models/xml.xml           |  31 -
 .../core/templatesuite/models/xmlfragment.xml   |  19 -
 .../core/templatesuite/models/xmlns.xml         |  32 -
 .../core/templatesuite/models/xmlns2.xml        |  32 -
 .../core/templatesuite/models/xmlns3.xml        |  32 -
 .../templatesuite/templates/default-xmlns.ftl   |  28 -
 .../templatesuite/templates/xml-fragment.ftl    |  26 -
 .../templates/xml-ns_prefix-scope-lib.ftl       |  23 -
 .../templates/xml-ns_prefix-scope-main.ftl      |  36 --
 .../core/templatesuite/templates/xml.ftl        |  47 --
 .../core/templatesuite/templates/xmlns1.ftl     |  53 --
 .../core/templatesuite/templates/xmlns3.ftl     |  70 ---
 .../core/templatesuite/templates/xmlns4.ftl     |  70 ---
 .../core/templatesuite/templates/xmlns5.ftl     |  28 -
 .../freemarker/core/templatesuite/testcases.xml |   8 -
 .../apache/freemarker/dom/DOMSiblingTest.xml    |  31 -
 freemarker-core/build.gradle                    |  14 +-
 .../core/model/TemplateNodeModelEx.java         |   4 +-
 .../core/model/impl/DefaultObjectWrapper.java   | 187 +++++-
 .../impl/DefaultObjectWrapperExtension.java     |  48 ++
 .../DefaultObjectWrapperExtensionPhase.java     |  39 ++
 .../model/impl/RestrictedObjectWrapper.java     |   2 +-
 .../java/org/apache/freemarker/dom/AtAtKey.java |  58 --
 .../freemarker/dom/AttributeNodeModel.java      |  69 ---
 .../freemarker/dom/CharacterDataNodeModel.java  |  46 --
 .../apache/freemarker/dom/DocumentModel.java    |  76 ---
 .../freemarker/dom/DocumentTypeModel.java       |  56 --
 .../java/org/apache/freemarker/dom/DomLog.java  |  32 -
 .../apache/freemarker/dom/DomStringUtil.java    |  67 --
 .../org/apache/freemarker/dom/ElementModel.java | 234 -------
 .../freemarker/dom/JaxenXPathSupport.java       | 243 --------
 .../apache/freemarker/dom/NodeListModel.java    | 219 -------
 .../org/apache/freemarker/dom/NodeModel.java    | 613 -------------------
 .../apache/freemarker/dom/NodeOutputter.java    | 258 --------
 .../dom/NodeQueryResultItemObjectWrapper.java   |  92 ---
 .../org/apache/freemarker/dom/PINodeModel.java  |  45 --
 .../dom/SunInternalXalanXPathSupport.java       | 163 -----
 .../org/apache/freemarker/dom/XPathSupport.java |  30 -
 .../freemarker/dom/XalanXPathSupport.java       | 163 -----
 .../java/org/apache/freemarker/dom/package.html |  30 -
 freemarker-dom/build.gradle                     |  83 +++
 .../java/org/apache/freemarker/dom/AtAtKey.java |  58 ++
 .../freemarker/dom/AttributeNodeModel.java      |  69 +++
 .../freemarker/dom/CharacterDataNodeModel.java  |  46 ++
 .../dom/DOMDefaultObjectWrapperExtension.java   |  48 ++
 .../apache/freemarker/dom/DocumentModel.java    |  76 +++
 .../freemarker/dom/DocumentTypeModel.java       |  56 ++
 .../java/org/apache/freemarker/dom/DomLog.java  |  32 +
 .../apache/freemarker/dom/DomStringUtil.java    |  67 ++
 .../org/apache/freemarker/dom/ElementModel.java | 234 +++++++
 .../freemarker/dom/JaxenXPathSupport.java       | 243 ++++++++
 .../apache/freemarker/dom/NodeListModel.java    | 219 +++++++
 .../org/apache/freemarker/dom/NodeModel.java    | 613 +++++++++++++++++++
 .../apache/freemarker/dom/NodeOutputter.java    | 258 ++++++++
 .../dom/NodeQueryResultItemObjectWrapper.java   |  92 +++
 .../org/apache/freemarker/dom/PINodeModel.java  |  45 ++
 .../dom/SunInternalXalanXPathSupport.java       | 163 +++++
 .../org/apache/freemarker/dom/XPathSupport.java |  30 +
 .../freemarker/dom/XalanXPathSupport.java       | 163 +++++
 .../java/org/apache/freemarker/dom/package.html |  30 +
 .../apache/freemarker/dom/DOMSiblingTest.java   |  97 +++
 .../freemarker/dom/DOMSimplifiersTest.java      | 201 ++++++
 .../java/org/apache/freemarker/dom/DOMTest.java | 159 +++++
 .../dom/DefaultObjectWrapperExtensionTest.java  |  69 +++
 .../freemarker/dom/TypeErrorMessagesTest.java   |  57 ++
 .../dom/templatesuite/DomTemplateTestSuite.java |  62 ++
 .../apache/freemarker/dom/test/DOMLoader.java   | 145 +++++
 .../apache/freemarker/dom/DOMSiblingTest.xml    |  31 +
 .../templatesuite/expected/default-xmlns.txt    |  25 +
 .../dom/templatesuite/expected/xml-fragment.txt |  25 +
 .../expected/xml-ns_prefix-scope.txt            |  29 +
 .../dom/templatesuite/expected/xml.txt          |  65 ++
 .../dom/templatesuite/expected/xmlns1.txt       |  63 ++
 .../dom/templatesuite/expected/xmlns3.txt       |  47 ++
 .../dom/templatesuite/expected/xmlns4.txt       |  47 ++
 .../dom/templatesuite/expected/xmlns5.txt       |  26 +
 .../dom/templatesuite/models/defaultxmlns1.xml  |  24 +
 .../models/xml-ns_prefix-scope.xml              |  26 +
 .../freemarker/dom/templatesuite/models/xml.xml |  31 +
 .../dom/templatesuite/models/xmlfragment.xml    |  19 +
 .../dom/templatesuite/models/xmlns.xml          |  32 +
 .../dom/templatesuite/models/xmlns2.xml         |  32 +
 .../dom/templatesuite/models/xmlns3.xml         |  32 +
 .../templatesuite/templates/default-xmlns.ftl   |  28 +
 .../templatesuite/templates/xml-fragment.ftl    |  26 +
 .../templates/xml-ns_prefix-scope-lib.ftl       |  23 +
 .../templates/xml-ns_prefix-scope-main.ftl      |  36 ++
 .../dom/templatesuite/templates/xml.ftl         |  47 ++
 .../dom/templatesuite/templates/xmlns1.ftl      |  53 ++
 .../dom/templatesuite/templates/xmlns3.ftl      |  70 +++
 .../dom/templatesuite/templates/xmlns4.ftl      |  70 +++
 .../dom/templatesuite/templates/xmlns5.ftl      |  28 +
 .../freemarker/dom/templatesuite/testcases.xml  |  65 ++
 freemarker-servlet/build.gradle                 |  19 +
 freemarker-test-utils/build.gradle              |  19 +
 .../freemarker/test/TemplateTestSuite.java      |   3 -
 .../org/apache/freemarker/test/XMLLoader.java   | 138 -----
 settings.gradle                                 |   1 +
 116 files changed, 4738 insertions(+), 4205 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/FM3-CHANGE-LOG.txt
----------------------------------------------------------------------
diff --git a/FM3-CHANGE-LOG.txt b/FM3-CHANGE-LOG.txt
index 64a2a38..9600281 100644
--- a/FM3-CHANGE-LOG.txt
+++ b/FM3-CHANGE-LOG.txt
@@ -17,7 +17,7 @@
  * under the License.
  */
  
-Because the Manual won't be updated for a good while, I will lead
+Because the Manual won't be updated for a good while, we will lead
 the FreeMarer 3 changelog here:
 
 - Increased version number to 3.0.0 (nightly aka. SNAPSHOT)
@@ -227,6 +227,21 @@ the FreeMarer 3 changelog here:
 - Modularized the project. Now we have these published jar-s:
   - org.apache.freemarker:freemarker-core
   - org.apache.freemarker:freemarker-servlet
-  - [TODO] org.apache.freemarker:freemarker-dom
+  - org.apache.freemarker:freemarker-dom
   There are several other internal modules related to testing; these aren't published.
-- Migrated buiding from Ant to Gradle. [TODO: This is only partially done; jar building, testing and javadoc works.]
\ No newline at end of file
+- Migrated buiding from Ant to Gradle. [TODO: This is only partially done; jar building, testing and javadoc works.]
+- `DefaultObjectWrapper` now has a configuration setting, `extensions`, to add `DefaultObjectWrapperExtension`-s, which
+  meant to be used for wrapping application-specific objects specially. Along with this, `DefaultObjectWrapper` now has
+  two protected methods to customze the wrapping logic, `wrapSpecialObject(Object)` and `wrapGenericObject(Object)`; the
+  last replaces the `handleUnknownType(Object)` method from FM2. `DefaultObjectWrapperExtension`-s are applied before or
+  after `wrapSpecialObject`, depending on what `DefaultObjectWrapperExtension.getPhase()` returns. See the JavaDoc of
+  `DefaultObjectWrapper.wrap(Object)` for more on wrapping phases.
+- `DefaultObjectWrapper` doesn't wrap W3C DOM nodes (XML) specially anymore, as DOM wrapping was factored out to a
+  separate jar (freemarker-dom) as part of modularizing FreeMarker. To ensure that DOM nodes are wrapped specially as
+  in FM2, the `extensions` setting of the `DefaultObjectWrapper` has to be so that it contains
+  `DOMDefaultObjectWrapperExtension.INSTANCE`. For example:
+  
+    cfgBuilder.objectWrapper(
+            new DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0)
+                    .extensions(DOMDefaultObjectWrapperExtension.INSTANCE)
+                    .build())

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/build.gradle
----------------------------------------------------------------------
diff --git a/freemarker-core-test/build.gradle b/freemarker-core-test/build.gradle
index 1edfe8f..6ba033c 100644
--- a/freemarker-core-test/build.gradle
+++ b/freemarker-core-test/build.gradle
@@ -1,3 +1,22 @@
+/*
+ * 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.
+ */
+
 description = """\
 The unit tests of freemarker-core. These tests used to be in freemarker-core, but to avoid depenency loop \
 through freemarker-test-utils, they had to be moved into a separate project."""
@@ -5,13 +24,6 @@ through freemarker-test-utils, they had to be moved into a separate project."""
 dependencies {
     compile project(":freemarker-core")
     compile 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"
-    }
 }
 
 // We have nothing to put into the jar, as we have test classes only

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java
index 749d057..96c671d 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TypeErrorMessagesTest.java
@@ -19,31 +19,11 @@
 
 package org.apache.freemarker.core;
 
-import java.io.StringReader;
-import java.util.Map;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
 import org.apache.freemarker.test.TemplateTest;
 import org.junit.Test;
-import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
 
 public class TypeErrorMessagesTest extends TemplateTest {
 
-    static final Document doc;
-    static {
-        try {
-            DocumentBuilder docBuilder;
-            docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-            doc = docBuilder.parse(new InputSource(new StringReader(
-                    "<a><b>123</b><c a='true'>1</c><c a='false'>2</c></a>")));
-        } catch (Exception e) {
-            throw new RuntimeException("Failed to build data-model", e);
-        }
-    }
-
     @Test
     public void testNumericalBinaryOperator() {
         assertErrorContains("${n - s}", "\"-\"", "right-hand", "number", "string");
@@ -68,38 +48,9 @@ public class TypeErrorMessagesTest extends TemplateTest {
                 "string", "method", "obj.something(params)");
     }
 
-    @Test
-    public void testXMLTypeMismarches() throws Exception {
-        assertErrorContains("${doc.a.c}",
-                "used as string", "query result", "2", "multiple matches");
-        assertErrorContains("${doc.a.c?boolean}",
-                "used as string", "query result", "2", "multiple matches");
-        assertErrorContains("${doc.a.d}",
-                "used as string", "query result", "0", "no matches");
-        assertErrorContains("${doc.a.d?boolean}",
-                "used as string", "query result", "0", "no matches");
-        
-        assertErrorContains("${doc.a.c.@a}",
-                "used as string", "query result", "2", "multiple matches");
-        assertErrorContains("${doc.a.d.@b}",
-                "used as string", "query result", "x", "no matches");
-        
-        assertErrorContains("${doc.a.b * 2}",
-                "used as number", "text", "explicit conversion");
-        assertErrorContains("<#if doc.a.b></#if>",
-                "used as number", "text", "explicit conversion");
-
-        assertErrorContains("${doc.a.d?nodeName}",
-                "used as node", "query result", "0", "no matches");
-        assertErrorContains("${doc.a.c?nodeName}",
-                "used as node", "query result", "2", "multiple matches");
-    }
-
     @Override
     protected Object createDataModel() {
-        Map<String, Object> dataModel = createCommonTestValuesDataModel();
-        dataModel.put("doc", doc);
-        return dataModel;
+        return createCommonTestValuesDataModel();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
index 6e9ae25..221b7b3 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapperTest.java
@@ -24,7 +24,6 @@ import static org.hamcrest.Matchers.*;
 import static org.junit.Assert.*;
 
 import java.io.IOException;
-import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -41,10 +40,6 @@ import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.Vector;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.Template;
 import org.apache.freemarker.core.TemplateException;
@@ -62,7 +57,6 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateModelWithAPISupport;
-import org.apache.freemarker.core.model.TemplateNodeModel;
 import org.apache.freemarker.core.model.TemplateNumberModel;
 import org.apache.freemarker.core.model.TemplateScalarModel;
 import org.apache.freemarker.core.model.TemplateSequenceModel;
@@ -70,9 +64,6 @@ import org.apache.freemarker.core.model.WrapperTemplateModel;
 import org.apache.freemarker.core.model.WrappingTemplateModel;
 import org.apache.freemarker.test.TestConfigurationBuilder;
 import org.junit.Test;
-import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -689,16 +680,6 @@ public class DefaultObjectWrapperTest {
     }
 
     @Test
-    public void assertCanWrapDOM() throws SAXException, IOException, ParserConfigurationException,
-            TemplateModelException {
-        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
-        InputSource is = new InputSource();
-        is.setCharacterStream(new StringReader("<doc><sub a='1' /></doc>"));
-        Document doc = db.parse(is);        
-        assertTrue(OW.wrap(doc) instanceof TemplateNodeModel);
-    }
-
-    @Test
     public void testExposureLevel() throws Exception {
         TestBean bean = new TestBean();
 
@@ -857,12 +838,12 @@ public class DefaultObjectWrapperTest {
         }
         
         @Override
-        protected TemplateModel handleNonBasicTypes(final Object obj) throws TemplateModelException {
+        protected TemplateModel wrapGenericObject(final Object obj) throws TemplateModelException {
             if (obj instanceof Tupple) {
                 return new TuppleAdapter((Tupple<?, ?>) obj, this);
             }
             
-            return super.handleNonBasicTypes(obj);
+            return super.wrapGenericObject(obj);
         }
         
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
index b49c213..dde5c2f 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/templatesuite/CoreTemplateTestSuite.java
@@ -15,9 +15,6 @@ import java.util.Set;
 import java.util.TimeZone;
 import java.util.TreeSet;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-
 import org.apache.freemarker.core.ASTPrinter;
 import org.apache.freemarker.core.Configuration;
 import org.apache.freemarker.core.Template;
@@ -44,10 +41,7 @@ import org.apache.freemarker.core.templatesuite.models.TestBoolean;
 import org.apache.freemarker.core.templatesuite.models.TestMethod;
 import org.apache.freemarker.core.templatesuite.models.TestNode;
 import org.apache.freemarker.core.templatesuite.models.VarArgTestModel;
-import org.apache.freemarker.dom.NodeModel;
 import org.apache.freemarker.test.TemplateTestSuite;
-import org.apache.freemarker.test.XMLLoader;
-import org.xml.sax.InputSource;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -155,10 +149,6 @@ public class CoreTemplateTestSuite extends TemplateTestSuite {
             mMixed.put("s2", "string2");
             mMixed.put("s2n", null);
             dataModel.put("mMixed", mMixed);
-        } else if (simpleTestName.equals("default-xmlns")) {
-            InputSource is = new InputSource(getClass().getResourceAsStream("models/defaultxmlns1.xml"));
-            NodeModel nm = XMLLoader.toModel(is);
-            dataModel.put("doc", nm);
         } else if (simpleTestName.equals("multimodels")) {
             dataModel.put("test", "selftest");
             dataModel.put("self", "self");
@@ -184,33 +174,6 @@ public class CoreTemplateTestSuite extends TemplateTestSuite {
             dataModel.put("x", Integer.valueOf(4));
             dataModel.put("z", Integer.valueOf(4));
             confB.setSharedVariable("y", Integer.valueOf(7));
-        } else if (simpleTestName.equals("xml-fragment")) {
-            DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
-            f.setNamespaceAware(true);
-            DocumentBuilder db = f.newDocumentBuilder();
-            org.w3c.dom.Document doc = db.parse(new InputSource(getClass().getResourceAsStream("models/xmlfragment.xml")));
-            NodeModel.simplify(doc);
-            dataModel.put("node", NodeModel.wrap(doc.getDocumentElement().getFirstChild().getFirstChild()));
-        } else if (simpleTestName.equals("xmlns1")) {
-            InputSource is = new InputSource(getClass().getResourceAsStream("models/xmlns.xml"));
-            NodeModel nm = XMLLoader.toModel(is);
-            dataModel.put("doc", nm);
-        } else if (simpleTestName.equals("xmlns2")) {
-            InputSource is = new InputSource(getClass().getResourceAsStream("models/xmlns2.xml"));
-            NodeModel nm = XMLLoader.toModel(is);
-            dataModel.put("doc", nm);
-        } else if (simpleTestName.equals("xmlns3") || simpleTestName.equals("xmlns4")) {
-            InputSource is = new InputSource(getClass().getResourceAsStream("models/xmlns3.xml"));
-            NodeModel nm = XMLLoader.toModel(is);
-            dataModel.put("doc", nm);
-        } else if (simpleTestName.equals("xmlns5")) {
-            InputSource is = new InputSource(getClass().getResourceAsStream("models/defaultxmlns1.xml"));
-            NodeModel nm = XMLLoader.toModel(is);
-            dataModel.put("doc", nm);
-        } else if (simpleTestName.equals("xml-ns_prefix-scope")) {
-            InputSource is = new InputSource(getClass().getResourceAsStream("models/xml-ns_prefix-scope.xml"));
-            NodeModel nm = XMLLoader.toModel(is);
-            dataModel.put("doc", nm);
         } else if (simpleTestName.startsWith("sequence-builtins")) {
             Set<String> abcSet = new TreeSet<>();
             abcSet.add("a");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java
deleted file mode 100644
index 56a15eb..0000000
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSiblingTest.java
+++ /dev/null
@@ -1,99 +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.io.IOException;
-
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.test.TemplateTest;
-import org.apache.freemarker.test.XMLLoader;
-import org.junit.Before;
-import org.junit.Test;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class DOMSiblingTest extends TemplateTest {
-
-    @Before
-    public void setUp() throws SAXException, IOException, ParserConfigurationException {
-        InputSource is = new InputSource(getClass().getResourceAsStream("DOMSiblingTest.xml"));
-        addToDataModel("doc", XMLLoader.toModel(is));
-    }
-
-    @Test
-    public void testBlankPreviousSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.name?previousSibling}", "\n    ");
-        assertOutput("${doc.person.name?previous_sibling}", "\n    ");
-    }
-
-    @Test
-    public void testNonBlankPreviousSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.address?previousSibling}", "12th August");
-    }
-
-    @Test
-    public void testBlankNextSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.name?nextSibling}", "\n    ");
-        assertOutput("${doc.person.name?next_sibling}", "\n    ");
-    }
-
-    @Test
-    public void testNonBlankNextSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.dob?nextSibling}", "Chennai, India");
-    }
-
-    @Test
-    public void testNullPreviousSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person?previousSibling?? ?c}", "false");
-    }
-
-    @Test
-    public void testSignificantPreviousSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.name.@@previous_sibling_element}", "male");
-    }
-
-    @Test
-    public void testSignificantNextSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.name.@@next_sibling_element}", "12th August");
-    }
-
-    @Test
-    public void testNullSignificantPreviousSibling() throws IOException, TemplateException {
-        assertOutput("${doc.person.phone.@@next_sibling_element?size}", "0");
-    }
-
-    @Test
-    public void testSkippingCommentNode() throws IOException, TemplateException {
-        assertOutput("${doc.person.profession.@@previous_sibling_element}", "Chennai, India");
-    }
-
-    @Test
-    public void testSkippingEmptyCDataNode() throws IOException, TemplateException {
-        assertOutput("${doc.person.hobby.@@previous_sibling_element}", "Software Engineer");
-    }
-
-    @Test
-    public void testValidCDataNode() throws IOException, TemplateException {
-        assertOutput("${doc.person.phone.@@previous_sibling_element?size}", "0");
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java
deleted file mode 100644
index f135873..0000000
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMSimplifiersTest.java
+++ /dev/null
@@ -1,201 +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 static org.junit.Assert.*;
-
-import java.io.IOException;
-
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.freemarker.test.XMLLoader;
-import org.junit.Test;
-import org.w3c.dom.CDATASection;
-import org.w3c.dom.Comment;
-import org.w3c.dom.Document;
-import org.w3c.dom.DocumentType;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.ProcessingInstruction;
-import org.w3c.dom.Text;
-import org.xml.sax.SAXException;
-
-public class DOMSimplifiersTest {
-
-    private static final String COMMON_TEST_XML
-            = "<!DOCTYPE a []><?p?><a>x<![CDATA[y]]><!--c--><?p?>z<?p?><b><!--c--></b><c></c>"
-              + "<d>a<e>c</e>b<!--c--><!--c--><!--c--><?p?><?p?><?p?></d>"
-              + "<f><![CDATA[1]]>2</f></a><!--c-->";
-
-    private static final String TEXT_MERGE_CONTENT =
-            "<a>"
-            + "a<!--c--><s/>"
-            + "<!--c-->a<s/>"
-            + "a<!--c-->b<s/>"
-            + "<!--c-->a<!--c-->b<!--c--><s/>"
-            + "a<b>b</b>c<s/>"
-            + "a<b>b</b><!--c-->c<s/>"
-            + "a<!--c-->1<b>b<!--c--></b>c<!--c-->1<s/>"
-            + "a<!--c-->1<b>b<!--c-->c</b>d<!--c-->1<s/>"
-            + "a<!--c-->1<b>b<!--c-->c</b>d<!--c-->1<s/>"
-            + "a<!--c-->1<b>b<!--c-->1<e>c<!--c-->1</e>d<!--c-->1</b>e<!--c-->1<s/>"
-            + "</a>";
-    private static final String TEXT_MERGE_EXPECTED =
-            "<a>"
-            + "%a<s/>"
-            + "%a<s/>"
-            + "%ab<s/>"
-            + "%ab<s/>"
-            + "%a<b>%b</b>%c<s/>"
-            + "%a<b>%b</b>%c<s/>"
-            + "%a1<b>%b</b>%c1<s/>"
-            + "%a1<b>%bc</b>%d1<s/>"
-            + "%a1<b>%bc</b>%d1<s/>"
-            + "%a1<b>%b1<e>%c1</e>%d1</b>%e1<s/>"
-            + "</a>";
-    
-    @Test
-    public void testTest() throws Exception {
-        String expected = "<!DOCTYPE ...><?p?><a>%x<![CDATA[y]]><!--c--><?p?>%z<?p?><b><!--c--></b><c/>"
-                   + "<d>%a<e>%c</e>%b<!--c--><!--c--><!--c--><?p?><?p?><?p?></d>"
-                   + "<f><![CDATA[1]]>%2</f></a><!--c-->";
-        assertEquals(expected, toString(XMLLoader.toDOM(COMMON_TEST_XML)));
-    }
-
-    @Test
-    public void testMergeAdjacentText() throws Exception {
-        Document dom = XMLLoader.toDOM(COMMON_TEST_XML);
-        NodeModel.mergeAdjacentText(dom);
-        assertEquals(
-                "<!DOCTYPE ...><?p?><a>%xy<!--c--><?p?>%z<?p?><b><!--c--></b><c/>"
-                + "<d>%a<e>%c</e>%b<!--c--><!--c--><!--c--><?p?><?p?><?p?></d>"
-                + "<f><![CDATA[12]]></f></a><!--c-->",
-                toString(dom));
-    }
-
-    @Test
-    public void testRemoveComments() throws Exception {
-        Document dom = XMLLoader.toDOM(COMMON_TEST_XML);
-        NodeModel.removeComments(dom);
-        assertEquals(
-                "<!DOCTYPE ...><?p?><a>%x<![CDATA[y]]><?p?>%z<?p?><b/><c/>"
-                + "<d>%a<e>%c</e>%b<?p?><?p?><?p?></d>"
-                + "<f><![CDATA[1]]>%2</f></a>",
-                toString(dom));
-    }
-
-    @Test
-    public void testRemovePIs() throws Exception {
-        Document dom = XMLLoader.toDOM(COMMON_TEST_XML);
-        NodeModel.removePIs(dom);
-        assertEquals(
-                "<!DOCTYPE ...><a>%x<![CDATA[y]]><!--c-->%z<b><!--c--></b><c/>"
-                + "<d>%a<e>%c</e>%b<!--c--><!--c--><!--c--></d>"
-                + "<f><![CDATA[1]]>%2</f></a><!--c-->",
-                toString(dom));
-    }
-    
-    @Test
-    public void testSimplify() throws Exception {
-        testSimplify(
-                "<!DOCTYPE ...><a>%xyz<b/><c/>"
-                + "<d>%a<e>%c</e>%b</d><f><![CDATA[12]]></f></a>",
-                COMMON_TEST_XML);
-    }
-
-    @Test
-    public void testSimplify2() throws Exception {
-        testSimplify(TEXT_MERGE_EXPECTED, TEXT_MERGE_CONTENT);
-    }
-
-    @Test
-    public void testSimplify3() throws Exception {
-        testSimplify("<a/>", "<a/>");
-    }
-    
-    private void testSimplify(String expected, String content)
-            throws SAXException, IOException, ParserConfigurationException {
-        {
-            Document dom = XMLLoader.toDOM(content);
-            NodeModel.simplify(dom);
-            assertEquals(expected, toString(dom));
-        }
-        
-        // Must be equivalent:
-        {
-            Document dom = XMLLoader.toDOM(content);
-            NodeModel.removeComments(dom);
-            NodeModel.removePIs(dom);
-            NodeModel.mergeAdjacentText(dom);
-            assertEquals(expected, toString(dom));
-        }
-        
-        // Must be equivalent:
-        {
-            Document dom = XMLLoader.toDOM(content);
-            NodeModel.removeComments(dom);
-            NodeModel.removePIs(dom);
-            NodeModel.simplify(dom);
-            assertEquals(expected, toString(dom));
-        }
-    }
-
-    private String toString(Document doc) {
-        StringBuilder sb = new StringBuilder();
-        toString(doc, sb);
-        return sb.toString();
-    }
-
-    private void toString(Node node, StringBuilder sb) {
-        if (node instanceof Document) {
-            childrenToString(node, sb);
-        } else if (node instanceof Element) {
-            if (node.hasChildNodes()) {
-                sb.append("<").append(node.getNodeName()).append(">");
-                childrenToString(node, sb);
-                sb.append("</").append(node.getNodeName()).append(">");
-            } else {
-                sb.append("<").append(node.getNodeName()).append("/>");
-            }
-        } else if (node instanceof Text) {
-            if (node instanceof CDATASection) {
-                sb.append("<![CDATA[").append(node.getNodeValue()).append("]]>");
-            } else {
-                sb.append("%").append(node.getNodeValue());
-            }
-        } else if (node instanceof Comment) {
-            sb.append("<!--").append(node.getNodeValue()).append("-->");
-        } else if (node instanceof ProcessingInstruction) {
-            sb.append("<?").append(node.getNodeName()).append("?>");
-        } else if (node instanceof DocumentType) {
-            sb.append("<!DOCTYPE ...>");
-        } else {
-            throw new IllegalStateException("Unhandled node type: " + node.getClass().getName());
-        }
-    }
-
-    private void childrenToString(Node node, StringBuilder sb) {
-        Node child = node.getFirstChild();
-        while (child != null) {
-            toString(child, sb);
-            child = child.getNextSibling();
-        }
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMTest.java
deleted file mode 100644
index 847f503..0000000
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/dom/DOMTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.freemarker.dom;
-
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.IOException;
-import java.io.StringReader;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.test.TemplateTest;
-import org.apache.freemarker.test.XMLLoader;
-import org.junit.Test;
-import org.w3c.dom.Document;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class DOMTest extends TemplateTest {
-
-    @Test
-    public void xpathDetectionBugfix() throws Exception {
-        addDocToDataModel("<root><a>A</a><b>B</b><c>C</c></root>");
-        assertOutput("${doc.root.b['following-sibling::c']}", "C");
-        assertOutput("${doc.root.b['following-sibling::*']}", "C");
-    }
-
-    @Test
-    public void xmlnsPrefixes() throws Exception {
-        addDocToDataModel("<root xmlns='http://example.com/ns1' xmlns:ns2='http://example.com/ns2'>"
-                + "<a>A</a><ns2:b>B</ns2:b><c a1='1' ns2:a2='2'/></root>");
-
-        String ftlHeader = "<#ftl ns_prefixes={'D':'http://example.com/ns1', 'n2':'http://example.com/ns2'}>";
-        
-        // @@markup:
-        assertOutput("${doc.@@markup}",
-                "<a:root xmlns:a=\"http://example.com/ns1\" xmlns:b=\"http://example.com/ns2\">"
-                + "<a:a>A</a:a><b:b>B</b:b><a:c a1=\"1\" b:a2=\"2\" />"
-                + "</a:root>");
-        assertOutput(ftlHeader
-                + "${doc.@@markup}",
-                "<root xmlns=\"http://example.com/ns1\" xmlns:n2=\"http://example.com/ns2\">"
-                + "<a>A</a><n2:b>B</n2:b><c a1=\"1\" n2:a2=\"2\" /></root>");
-        assertOutput("<#ftl ns_prefixes={'D':'http://example.com/ns1'}>"
-                + "${doc.@@markup}",
-                "<root xmlns=\"http://example.com/ns1\" xmlns:a=\"http://example.com/ns2\">"
-                + "<a>A</a><a:b>B</a:b><c a1=\"1\" a:a2=\"2\" /></root>");
-        
-        // When there's no matching prefix declared via the #ftl header, return null for qname:
-        assertOutput("${doc?children[0].@@qname!'null'}", "null");
-        assertOutput("${doc?children[0]?children[1].@@qname!'null'}", "null");
-        assertOutput("${doc?children[0]?children[2]['@*'][1].@@qname!'null'}", "null");
-        
-        // When we have prefix declared in the #ftl header:
-        assertOutput(ftlHeader + "${doc?children[0].@@qname}", "root");
-        assertOutput(ftlHeader + "${doc?children[0]?children[1].@@qname}", "n2:b");
-        assertOutput(ftlHeader + "${doc?children[0]?children[2].@@qname}", "c");
-        assertOutput(ftlHeader + "${doc?children[0]?children[2]['@*'][0].@@qname}", "a1");
-        assertOutput(ftlHeader + "${doc?children[0]?children[2]['@*'][1].@@qname}", "n2:a2");
-        // Unfortunately these include the xmlns attributes, but that would be non-BC to fix now:
-        assertThat(getOutput(ftlHeader + "${doc?children[0].@@start_tag}"), startsWith("<root"));
-        assertThat(getOutput(ftlHeader + "${doc?children[0]?children[1].@@start_tag}"), startsWith("<n2:b"));
-    }
-    
-    @Test
-    public void namespaceUnaware() throws Exception {
-        addNSUnawareDocToDataModel("<root><x:a>A</x:a><:>B</:><xyz::c>C</xyz::c></root>");
-        assertOutput("${doc.root['x:a']}", "A");
-        assertOutput("${doc.root[':']}", "B");
-        try {
-            assertOutput("${doc.root['xyz::c']}", "C");
-            fail();
-        } catch (TemplateException e) {
-            assertThat(e.getMessage(), containsString("xyz"));
-        }
-    }
-    
-    private void addDocToDataModel(String xml) throws SAXException, IOException, ParserConfigurationException {
-        addToDataModel("doc", XMLLoader.toModel(new InputSource(new StringReader(xml))));
-    }
-
-    private void addDocToDataModelNoSimplification(String xml) throws SAXException, IOException, ParserConfigurationException {
-        addToDataModel("doc", XMLLoader.toModel(new InputSource(new StringReader(xml)), false));
-    }
-    
-    private void addNSUnawareDocToDataModel(String xml) throws ParserConfigurationException, SAXException, IOException {
-        DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
-        newFactory.setNamespaceAware(false);
-        DocumentBuilder builder = newFactory.newDocumentBuilder();
-        Document doc = builder.parse(new InputSource(new StringReader(xml)));
-        addToDataModel("doc", doc);
-    }
-
-    @Test
-    public void testInvalidAtAtKeyErrors() throws Exception {
-        addDocToDataModel("<r><multipleMatches /><multipleMatches /></r>");
-        assertErrorContains("${doc.r.@@invalid_key}", "Unsupported @@ key", "@invalid_key");
-        assertErrorContains("${doc.@@start_tag}", "@@start_tag", "not supported", "document");
-        assertErrorContains("${doc.@@}", "\"@@\"", "not supported", "document");
-        assertErrorContains("${doc.r.noMatch.@@invalid_key}", "Unsupported @@ key", "@invalid_key");
-        assertErrorContains("${doc.r.multipleMatches.@@invalid_key}", "Unsupported @@ key", "@invalid_key");
-        assertErrorContains("${doc.r.noMatch.@@attributes_markup}", "single XML node", "@@attributes_markup");
-        assertErrorContains("${doc.r.multipleMatches.@@attributes_markup}", "single XML node", "@@attributes_markup");
-    }
-    
-    @Test
-    public void testAtAtSiblingElement() throws Exception {
-        addDocToDataModel("<r><a/><b/></r>");
-        assertOutput("${doc.r.@@previous_sibling_element?size}", "0");
-        assertOutput("${doc.r.@@next_sibling_element?size}", "0");
-        assertOutput("${doc.r.a.@@previous_sibling_element?size}", "0");
-        assertOutput("${doc.r.a.@@next_sibling_element.@@qname}", "b");
-        assertOutput("${doc.r.b.@@previous_sibling_element.@@qname}", "a");
-        assertOutput("${doc.r.b.@@next_sibling_element?size}", "0");
-        
-        addDocToDataModel("<r>\r\n\t <a/>\r\n\t <b/>\r\n\t </r>");
-        assertOutput("${doc.r.@@previous_sibling_element?size}", "0");
-        assertOutput("${doc.r.@@next_sibling_element?size}", "0");
-        assertOutput("${doc.r.a.@@previous_sibling_element?size}", "0");
-        assertOutput("${doc.r.a.@@next_sibling_element.@@qname}", "b");
-        assertOutput("${doc.r.b.@@previous_sibling_element.@@qname}", "a");
-        assertOutput("${doc.r.b.@@next_sibling_element?size}", "0");
-        
-        addDocToDataModel("<r>t<a/>t<b/>t</r>");
-        assertOutput("${doc.r.a.@@previous_sibling_element?size}", "0");
-        assertOutput("${doc.r.a.@@next_sibling_element?size}", "0");
-        assertOutput("${doc.r.b.@@previous_sibling_element?size}", "0");
-        assertOutput("${doc.r.b.@@next_sibling_element?size}", "0");
-        
-        addDocToDataModelNoSimplification("<r><a/> <!-- --><?pi?>&#x20;<b/></r>");
-        assertOutput("${doc.r.a.@@next_sibling_element.@@qname}", "b");
-        assertOutput("${doc.r.b.@@previous_sibling_element.@@qname}", "a");
-        
-        addDocToDataModelNoSimplification("<r><a/> <!-- -->t<!-- --> <b/></r>");
-        assertOutput("${doc.r.a.@@next_sibling_element?size}", "0");
-        assertOutput("${doc.r.b.@@previous_sibling_element?size}", "0");
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/default-xmlns.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/default-xmlns.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/default-xmlns.txt
deleted file mode 100644
index 3a52c46..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/default-xmlns.txt
+++ /dev/null
@@ -1,25 +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.
- */
-No NS = No NS
-x NS = x NS
-y NS = y NS
-x NS = x NS
-
-true
-

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-fragment.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-fragment.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-fragment.txt
deleted file mode 100644
index cb0da5f..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-fragment.txt
+++ /dev/null
@@ -1,25 +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.
- */
-b = b
-@document = @document
-@document = @document
-
-C<>&"']]> = C<>&"']]>
-
-<root xmlns:n="http://x"><a><b><n:c>C&lt;>&amp;"']]&gt;</n:c></b></a></root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-ns_prefix-scope.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-ns_prefix-scope.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-ns_prefix-scope.txt
deleted file mode 100644
index bf8e6bd..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml-ns_prefix-scope.txt
+++ /dev/null
@@ -1,29 +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.
- */
-//e: e in NS namespace-test, e in NS namespace-test
-//n:e: e in NS foo, e in NS foo
-//bar:e: e in NS bar, e in NS bar
-
-Included:
-//n:e: e in NS foo, e in NS foo
-//n:e: e in NS foo, e in NS foo
-
-Imported:
-//n:e: e in NS bar, e in NS bar
-//n:e: e in NS bar, e in NS bar

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml.txt
deleted file mode 100644
index 718bba1..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xml.txt
+++ /dev/null
@@ -1,65 +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.
- */
-  <?firstPi customKey="something"?>
-  firstPi
-  customKey="something"
-  <?secondPi secondPiData?>
-  secondPi
-  secondPiData
-2
-p
- customKey="something"
-
-<ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text1</ns1:e11><ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text2</ns1:e11>
-<ns1:e12 xmlns:ns1="http://www.foo.com/ns1/"><![CDATA[cdata-section1]]></ns1:e12><ns1:e12 xmlns:ns1="http://www.foo.com/ns1/"><![CDATA[cdata-section2<&]]></ns1:e12>
-<ns1:e1 xmlns:ns1="http://www.foo.com/ns1/" a1="v1" a2="v2">
-    <ns1:e11>text1</ns1:e11>
-    <ns1:e12><![CDATA[cdata-section1]]></ns1:e12>
-  </ns1:e1>
-<ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text1</ns1:e11><ns1:e11 xmlns:ns1="http://www.foo.com/ns1/">text2</ns1:e11>
-a1
-v2
-rootroot
-root
-  root
-  e1
-  e11
-  e12
-  e2
-  e11
-  e12
-  root
-  root
-  e1
-  root
-  e1
-  e11
-  root
-  e1
-  e12
-  root
-  e2
-  root
-  e2
-  e11
-  root
-  e2
-  e12
-cdata-section2&lt;&amp;
-cdata-section2<&

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns1.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns1.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns1.txt
deleted file mode 100644
index 69af553..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns1.txt
+++ /dev/null
@@ -1,63 +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.
- */
-
-<book xmlns="http://example.com/eBook">
-  <title>Test Book</title>
-  <chapter>
-    <title>Ch1</title>
-    <para>p1.1</para>
-    <para>p1.2</para>
-    <para>p1.3</para>
-  </chapter>
-  <chapter>
-    <title>Ch2</title>
-    <para>p2.1</para>
-    <para>p2.2</para>
-  </chapter>
-</book>
-
-  <html>
-    <head>
-      <title>Test Book</title>
-    </head>
-    <body>
-      <h1>Test Book</h1>
-
-  
-    <h2>Ch1</h2>
-
-    
-      <p>p1.1
-
-      <p>p1.2
-
-      <p>p1.3
-
-  
-    <h2>Ch2</h2>
-
-    
-      <p>p2.1
-
-      <p>p2.2
-
-  
-    </body>
-  </html>
-

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns3.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns3.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns3.txt
deleted file mode 100644
index f028f0a..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns3.txt
+++ /dev/null
@@ -1,47 +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>Test Book</title>
-    </head>
-    <body>
-      <h1>Test Book</h1>
-
-  
-    <h2>Ch1</h2>
-
-    
-      <p>p1.1
-
-      <p>p1.2
-
-      <p>p1.3
-
-  
-    <h2>Ch2</h2>
-
-    
-      <p>p2.1
-
-      <p>p2.2
-
-  
-    </body>
-  </html>
-

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns4.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns4.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns4.txt
deleted file mode 100644
index f028f0a..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns4.txt
+++ /dev/null
@@ -1,47 +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>Test Book</title>
-    </head>
-    <body>
-      <h1>Test Book</h1>
-
-  
-    <h2>Ch1</h2>
-
-    
-      <p>p1.1
-
-      <p>p1.2
-
-      <p>p1.3
-
-  
-    <h2>Ch2</h2>
-
-    
-      <p>p2.1
-
-      <p>p2.2
-
-  
-    </body>
-  </html>
-

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns5.txt
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns5.txt b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns5.txt
deleted file mode 100644
index 6e42b09..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/expected/xmlns5.txt
+++ /dev/null
@@ -1,26 +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.
- */
-No NS = No NS
-x NS = x NS
-y NS = y NS
-x NS = x NS
-No NS = No NS
-- = -
-- = -
-- = -

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/defaultxmlns1.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/defaultxmlns1.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/defaultxmlns1.xml
deleted file mode 100644
index ed289bb..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/defaultxmlns1.xml
+++ /dev/null
@@ -1,24 +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.
--->
-<root xmlns:x="http://x.com" xmlns:y="http://y.com">
-  <t1>No NS</t1>
-  <x:t2>x NS</x:t2>
-  <y:t3>y NS</y:t3>
-  <t4 xmlns="http://x.com">x NS</t4>
-</root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml-ns_prefix-scope.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml-ns_prefix-scope.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml-ns_prefix-scope.xml
deleted file mode 100644
index 934acac..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml-ns_prefix-scope.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  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.
--->
-<root xmlns="http://freemarker.org/test/namespace-test"
-    xmlns:foo="http://freemarker.org/test/foo"
-    xmlns:bar="http://freemarker.org/test/bar">
-  <e>e in NS namespace-test</e>
-  <foo:e>e in NS foo</foo:e>
-  <bar:e>e in NS bar</bar:e>
-</root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml.xml
deleted file mode 100644
index abf7e96..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xml.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.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.
--->
-<?firstPi customKey="something"?>
-<?secondPi secondPiData?>
-<ns1:root xmlns:ns1="http://www.foo.com/ns1/">
-  <ns1:e1 a1="v1" a2="v2">
-    <ns1:e11>text1</ns1:e11>
-    <ns1:e12><![CDATA[cdata-section1]]></ns1:e12>
-  </ns1:e1>
-  <ns1:e2>
-    <ns1:e11>text2</ns1:e11>
-    <ns1:e12><![CDATA[cdata-section2<&]]></ns1:e12>
-  </ns1:e2>
-</ns1:root>

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlfragment.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlfragment.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlfragment.xml
deleted file mode 100644
index b5578b6..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlfragment.xml
+++ /dev/null
@@ -1,19 +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.
--->
-<root><a><b><c xmlns="http://x">C&lt;&gt;&amp;"']]&gt;</c></b></a></root>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns.xml
deleted file mode 100644
index 6f6453e..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns.xml
+++ /dev/null
@@ -1,32 +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.
--->
-<book xmlns="http://example.com/eBook">
-  <title>Test Book</title>
-  <chapter>
-    <title>Ch1</title>
-    <para>p1.1</para>
-    <para>p1.2</para>
-    <para>p1.3</para>
-  </chapter>
-  <chapter>
-    <title>Ch2</title>
-    <para>p2.1</para>
-    <para>p2.2</para>
-  </chapter>
-</book>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns2.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns2.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns2.xml
deleted file mode 100644
index c8bfc9f..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns2.xml
+++ /dev/null
@@ -1,32 +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.
--->
-<eb:book xmlns:eb="http://example.com/eBook">
-  <eb:title>Test Book</eb:title>
-  <eb:chapter>
-    <eb:title>Ch1</eb:title>
-    <eb:para>p1.1</eb:para>
-    <eb:para>p1.2</eb:para>
-    <eb:para>p1.3</eb:para>
-  </eb:chapter>
-  <eb:chapter>
-    <eb:title>Ch2</eb:title>
-    <eb:para>p2.1</eb:para>
-    <eb:para>p2.2</eb:para>
-  </eb:chapter>
-</eb:book>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns3.xml
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns3.xml b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns3.xml
deleted file mode 100644
index 8502ead..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/models/xmlns3.xml
+++ /dev/null
@@ -1,32 +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.
--->
-<book xmlns:x="http://x" xmlns:y="http://y">
-  <x:title>Test Book</x:title>
-  <chapter>
-    <y:title>Ch1</y:title>
-    <para>p1.1</para>
-    <para>p1.2</para>
-    <para>p1.3</para>
-  </chapter>
-  <x:chapter>
-    <y:title>Ch2</y:title>
-    <x:para>p2.1</x:para>
-    <y:para>p2.2</y:para>
-  </x:chapter>
-</book>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/default-xmlns.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/default-xmlns.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/default-xmlns.ftl
deleted file mode 100644
index 4f01835..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/default-xmlns.ftl
+++ /dev/null
@@ -1,28 +0,0 @@
-<#ftl ns_prefixes={"D" : "http://x.com", "y" : "http://y.com"}>
-<#--
-  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.
--->
-<#assign r = doc.*[0]>
-${r["N:t1"]?default('-')} = No NS
-${r["t2"]?default('-')} = x NS
-${r["y:t3"]?default('-')} = y NS
-${r["./D:t4"]?default('-')} = x NS
-
-<#assign bool = doc["true()"]>
-${bool?string}
-

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-fragment.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-fragment.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-fragment.ftl
deleted file mode 100644
index 226215b..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-fragment.ftl
+++ /dev/null
@@ -1,26 +0,0 @@
-<#ftl ns_prefixes = {"n" : "http://x"}>
-<#--
-  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.
--->
-${node?node_name} = b
-${node?root?node_name} = @document
-${node['/']?node_name} = @document
-
-${node['n:c']} = C<>&"']]>
-
-${node?root.@@markup}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-lib.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-lib.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-lib.ftl
deleted file mode 100644
index 0f0bde2..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-lib.ftl
+++ /dev/null
@@ -1,23 +0,0 @@
-<#ftl ns_prefixes={ "n": "http://freemarker.org/test/bar", "D": "http://freemarker.org/test/namespace-test" }>
-<#--
-  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.
--->
-<#global libResult>//n:e: ${doc['//n:e']}, ${doc.root['n:e']}</#global>
-<#macro m>
-//n:e: ${doc['//n:e']}, ${doc.root['n:e']}
-</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-main.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-main.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-main.ftl
deleted file mode 100644
index 5b7ce24..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml-ns_prefix-scope-main.ftl
+++ /dev/null
@@ -1,36 +0,0 @@
-<#ftl ns_prefixes={
-    "D": "http://freemarker.org/test/namespace-test",
-    "n": "http://freemarker.org/test/foo",
-    "bar": "http://freemarker.org/test/bar"
-}>
-<#--
-  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.
--->
-//e: ${doc['//D:e']}, ${doc.root.e}
-//n:e: ${doc['//n:e']}, ${doc.root['n:e']}
-//bar:e: ${doc['//bar:e']}, ${doc.root['bar:e']}
-
-Included:
-<#include "xml-ns_prefix-scope-lib.ftl">
-${libResult}
-<@m />
-
-Imported:
-<#import "xml-ns_prefix-scope-lib.ftl" as lib>
-${libResult}
-<@lib.m />

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml.ftl
deleted file mode 100644
index b85fc03..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xml.ftl
+++ /dev/null
@@ -1,47 +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.
--->
-<#-- test processing instructions -->
-<#global PIs = doc._content._ftype("p")>
-<#list PIs as pi>
-  ${pi}
-  ${pi["@target"]._text}
-  ${pi["@data"]._text}
-</#list>
-${PIs?size}
-<#global firstPi = PIs[0]>
-${firstPi._type}
-${firstPi["@customKey"]}
-${doc._registerNamespace("ns", "http://www.foo.com/ns1/")}
-${doc._descendant["ns:e11"]}
-${doc._descendant["ns:e12"]}
-<#global docRoot = doc["ns:root"]>
-${docRoot["ns:e1"]}
-${doc("//ns:e11")}
-${docRoot["ns:e1"]["@a1"]._name}
-${docRoot["ns:e1"]["@a2"]._text}
-${docRoot._children._parent._name}
-${docRoot._children._parent._unique._name}
-<#list doc._descendant as d>
-  ${d._name}
-</#list>
-<#list doc._descendant._ancestorOrSelf as d>
-  ${d._name}
-</#list>
-${docRoot["ns:e2"]["ns:e12"]._text}
-${docRoot["ns:e2"]["ns:e12"]._plaintext}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns1.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns1.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns1.ftl
deleted file mode 100644
index 8aa893e..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns1.ftl
+++ /dev/null
@@ -1,53 +0,0 @@
-<#ftl ns_prefixes = {"D" : "http://example.com/eBook"}>
-<#--
-  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.
--->
-
-${doc.@@markup}
-
-<#recurse doc >
-
-<#macro book>
-  <html>
-    <head>
-      <title><#recurse .node.title></title>
-    </head>
-    <body>
-      <h1><#recurse .node.title></h1>
-      <#recurse>
-    </body>
-  </html>
-</#macro>
-
-<#macro chapter>
-  <h2><#recurse .node.title></h2>
-  <#recurse>
-</#macro>
-
-<#macro para>
-  <p><#recurse>
-</#macro>
-
-<#macro title>
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro @text>${.node?html}</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns3.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns3.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns3.ftl
deleted file mode 100644
index c84ec69..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns3.ftl
+++ /dev/null
@@ -1,70 +0,0 @@
-<#ftl ns_prefixes = {"x" : "http://x", "y" : "http://y"}>
-<#--
-  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.
--->
-<#recurse doc >
-
-<#macro book>
-  <html>
-    <head>
-      <title><#recurse .node["x:title"]></title>
-    </head>
-    <body>
-      <h1><#recurse .node["x:title"]></h1>
-      <#recurse>
-    </body>
-  </html>
-</#macro>
-
-<#macro chapter>
-  <h2><#recurse .node["y:title"]></h2>
-  <#recurse>
-</#macro>
-
-<#macro "x:chapter">
-  <h2><#recurse .node["y:title"]></h2>
-  <#recurse>
-</#macro>
-
-<#macro para>
-  <p><#recurse>
-</#macro>
-
-<#macro "x:para">
-  <p><#recurse>
-</#macro>
-
-<#macro "y:para">
-  <p><#recurse>
-</#macro>
-
-<#macro "x:title">
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro "y:title">
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro @text>${.node?html}</#macro>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/be556897/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns4.ftl
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns4.ftl b/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns4.ftl
deleted file mode 100644
index e97bfc0..0000000
--- a/freemarker-core-test/src/test/resources/org/apache/freemarker/core/templatesuite/templates/xmlns4.ftl
+++ /dev/null
@@ -1,70 +0,0 @@
-<#ftl ns_prefixes = {"x" : "http://x", "y" : "http://y"}>
-<#--
-  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.
--->
-<#recurse doc >
-
-<#macro book>
-  <html>
-    <head>
-      <title><#recurse .node["x:title"]></title>
-    </head>
-    <body>
-      <h1><#recurse .node["x:title"]></h1>
-      <#recurse>
-    </body>
-  </html>
-</#macro>
-
-<#macro chapter>
-  <h2><#recurse .node["y:title"]></h2>
-  <#recurse>
-</#macro>
-
-<#macro 'x:chapter'>
-  <h2><#recurse .node["y:title"]></h2>
-  <#recurse>
-</#macro>
-
-<#macro para>
-  <p><#recurse>
-</#macro>
-
-<#macro 'x:para'>
-  <p><#recurse>
-</#macro>
-
-<#macro 'y:para'>
-  <p><#recurse>
-</#macro>
-
-<#macro "x:title">
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro "y:title">
-  <#--
-    We have handled this element imperatively,
-    so we do nothing here.
-  -->
-</#macro>
-
-<#macro @text>${.node?html}</#macro>
\ No newline at end of file