Moved o.a.f.core.model.impl.dom to org.apache.freemarker.dom, so that later it can be a separate module (separate jar)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/59412b29 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/59412b29 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/59412b29 Branch: refs/heads/3 Commit: 59412b293d5beb364c2ddb35a6251efb4a065277 Parents: 4e60f9e Author: ddekany <[email protected]> Authored: Sun Feb 26 02:53:31 2017 +0100 Committer: ddekany <[email protected]> Committed: Sun Feb 26 02:56:54 2017 +0100 ---------------------------------------------------------------------- .../freemarker/core/BuiltInsForNodes.java | 6 +- .../org/apache/freemarker/core/_CoreLogs.java | 1 - .../core/model/TemplateNodeModelEx.java | 2 +- .../core/model/impl/DefaultObjectWrapper.java | 2 +- .../freemarker/core/model/impl/dom/AtAtKey.java | 58 -- .../core/model/impl/dom/AttributeNodeModel.java | 69 --- .../model/impl/dom/CharacterDataNodeModel.java | 46 -- .../core/model/impl/dom/DocumentModel.java | 76 --- .../core/model/impl/dom/DocumentTypeModel.java | 56 -- .../core/model/impl/dom/DomStringUtil.java | 93 --- .../core/model/impl/dom/ElementModel.java | 233 ------- .../core/model/impl/dom/JaxenXPathSupport.java | 238 ------- .../core/model/impl/dom/NodeListModel.java | 231 ------- .../core/model/impl/dom/NodeModel.java | 618 ------------------- .../core/model/impl/dom/NodeOutputter.java | 258 -------- .../core/model/impl/dom/PINodeModel.java | 45 -- .../impl/dom/SunInternalXalanXPathSupport.java | 163 ----- .../core/model/impl/dom/XPathSupport.java | 30 - .../core/model/impl/dom/XalanXPathSupport.java | 163 ----- .../core/model/impl/dom/_ExtDomApi.java | 43 -- .../freemarker/core/model/impl/dom/package.html | 31 - .../freemarker/core/util/_StringUtil.java | 22 +- .../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 | 70 +++ .../org/apache/freemarker/dom/ElementModel.java | 234 +++++++ .../freemarker/dom/JaxenXPathSupport.java | 238 +++++++ .../apache/freemarker/dom/NodeListModel.java | 231 +++++++ .../org/apache/freemarker/dom/NodeModel.java | 618 +++++++++++++++++++ .../apache/freemarker/dom/NodeOutputter.java | 258 ++++++++ .../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 | 31 + src/manual/en_US/FM3-CHANGE-LOG.txt | 2 +- .../core/model/impl/dom/DOMSiblingTest.java | 99 --- .../core/model/impl/dom/DOMSimplifiersTest.java | 201 ------ .../freemarker/core/model/impl/dom/DOMTest.java | 161 ----- .../apache/freemarker/dom/DOMSiblingTest.java | 99 +++ .../freemarker/dom/DOMSimplifiersTest.java | 201 ++++++ .../java/org/apache/freemarker/dom/DOMTest.java | 161 +++++ .../test/templatesuite/TemplateTestCase.java | 2 +- .../test/templatesuite/TemplateTestSuite.java | 2 +- .../apache/freemarker/test/util/XMLLoader.java | 2 +- .../core/model/impl/dom/DOMSiblingTest.xml | 31 - .../apache/freemarker/dom/DOMSiblingTest.xml | 31 + 51 files changed, 2938 insertions(+), 2956 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java b/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java index 4d808b3..3894c5b 100644 --- a/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java +++ b/src/main/java/org/apache/freemarker/core/BuiltInsForNodes.java @@ -28,7 +28,7 @@ import org.apache.freemarker.core.model.TemplateNodeModel; import org.apache.freemarker.core.model.TemplateNodeModelEx; import org.apache.freemarker.core.model.impl.SimpleScalar; import org.apache.freemarker.core.model.impl.SimpleSequence; -import org.apache.freemarker.core.model.impl.dom._ExtDomApi; +import org.apache.freemarker.core.util._StringUtil; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -120,7 +120,7 @@ class BuiltInsForNodes { static class AncestorSequence extends SimpleSequence implements TemplateMethodModel { @SuppressFBWarnings(value="SE_BAD_FIELD", - justification="Can't make this Serializable, and not extneding SimpleSequence would be non-BC.") + justification="Can't make this Serializable, and not extending SimpleSequence would be non-BC.") private Environment env; AncestorSequence(Environment env) { @@ -143,7 +143,7 @@ class BuiltInsForNodes { } } else { for (int j = 0; j < names.size(); j++) { - if (_ExtDomApi.matchesName((String) names.get(j), nodeName, nsURI, env)) { + if (_StringUtil.matchesQName((String) names.get(j), nodeName, nsURI, env)) { result.add(tnm); break; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/_CoreLogs.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_CoreLogs.java b/src/main/java/org/apache/freemarker/core/_CoreLogs.java index 0e16f6c..1ccb915 100644 --- a/src/main/java/org/apache/freemarker/core/_CoreLogs.java +++ b/src/main/java/org/apache/freemarker/core/_CoreLogs.java @@ -32,7 +32,6 @@ public final class _CoreLogs { public static final Logger ATTEMPT = LoggerFactory.getLogger("org.apache.freemarker.core.runtime.attempt"); public static final Logger SECURITY = LoggerFactory.getLogger("org.apache.freemarker.core.security"); public static final Logger BEANS_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model.impl.beans"); - public static final Logger DOM_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model.impl.dom"); public static final Logger TEMPLATE_RESOLVER = LoggerFactory.getLogger( "org.apache.freemarker.core.templateresolver"); public static final Logger DEBUG_SERVER = LoggerFactory.getLogger("org.apache.freemarker.core.debug.server"); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java b/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java index 5c31d9a..27adb97 100644 --- a/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java +++ b/src/main/java/org/apache/freemarker/core/model/TemplateNodeModelEx.java @@ -19,7 +19,7 @@ package org.apache.freemarker.core.model; -import org.apache.freemarker.core.model.impl.dom.NodeModel; +import org.apache.freemarker.dom.NodeModel; /** * A {@link NodeModel} that supports navigating to the previous and next sibling nodes. http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java index 224836d..ba61a2c 100644 --- a/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java +++ b/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java @@ -37,7 +37,7 @@ import org.apache.freemarker.core.model.TemplateModelException; import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.impl.beans.BeansWrapper; import org.apache.freemarker.core.model.impl.beans.BeansWrapperConfiguration; -import org.apache.freemarker.core.model.impl.dom.NodeModel; +import org.apache.freemarker.dom.NodeModel; import org.w3c.dom.Node; /** http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/AtAtKey.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/AtAtKey.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/AtAtKey.java deleted file mode 100644 index a21ebcc..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/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.core.model.impl.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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/AttributeNodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/AttributeNodeModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/AttributeNodeModel.java deleted file mode 100644 index 97915e6..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/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.core.model.impl.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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/CharacterDataNodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/CharacterDataNodeModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/CharacterDataNodeModel.java deleted file mode 100644 index a488fee..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/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.core.model.impl.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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/DocumentModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/DocumentModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/DocumentModel.java deleted file mode 100644 index 0d15c71..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/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.core.model.impl.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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/DocumentTypeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/DocumentTypeModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/DocumentTypeModel.java deleted file mode 100644 index 768b1d6..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/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.core.model.impl.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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/DomStringUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/DomStringUtil.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/DomStringUtil.java deleted file mode 100644 index 23a9bd6..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/dom/DomStringUtil.java +++ /dev/null @@ -1,93 +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.core.model.impl.dom; - -import org.apache.freemarker.core.Environment; -import org.apache.freemarker.core.Template; - -/** - * 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; - } - - /** - * @return whether the qname matches the combination of nodeName, nsURI, and environment prefix settings. - */ - static boolean matchesName(String qname, String nodeName, String nsURI, Environment env) { - String defaultNS = env.getDefaultNS(); - if ((defaultNS != null) && defaultNS.equals(nsURI)) { - return qname.equals(nodeName) - || qname.equals(Template.DEFAULT_NAMESPACE_PREFIX + ":" + nodeName); - } - if ("".equals(nsURI)) { - if (defaultNS != null) { - return qname.equals(Template.NO_NS_PREFIX + ":" + nodeName); - } else { - return qname.equals(nodeName) || qname.equals(Template.NO_NS_PREFIX + ":" + nodeName); - } - } - String prefix = env.getPrefixForNamespace(nsURI); - if (prefix == null) { - return false; // Is this the right thing here??? - } - return qname.equals(prefix + ":" + nodeName); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/ElementModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/ElementModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/ElementModel.java deleted file mode 100644 index 6bfe08f..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/dom/ElementModel.java +++ /dev/null @@ -1,233 +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.core.model.impl.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.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 DomStringUtil.matchesName(name, getNodeName(), getNodeNamespace(), env); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/JaxenXPathSupport.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/JaxenXPathSupport.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/JaxenXPathSupport.java deleted file mode 100644 index 76d818f..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/dom/JaxenXPathSupport.java +++ /dev/null @@ -1,238 +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.core.model.impl.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.CustomAttribute; -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.model.impl._StaticObjectWrappers; -import org.apache.freemarker.core.util.UndeclaredThrowableException; -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 CustomAttribute XPATH_CACHE_ATTR = - new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE) { - @Override - protected Object 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 = (Map<String, BaseXPath>) XPATH_CACHE_ATTR.get(); - 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) { - // [2.4] Use the proper object wrapper (argument in 2.4) - return _StaticObjectWrappers.DEFAULT_OBJECT_WRAPPER.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 CustomAttribute FM_DOM_NAVIAGOTOR_CACHED_DOM - = new CustomAttribute(CustomAttribute.SCOPE_TEMPLATE); - - private static final Navigator FM_DOM_NAVIGATOR = new DocumentNavigator() { - @Override - public Object getDocument(String uri) throws FunctionCallException { - try { - Template raw = getTemplate(uri); - Document doc = (Document) FM_DOM_NAVIAGOTOR_CACHED_DOM.get(raw); - 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) { - FM_DOM_NAVIAGOTOR_CACHED_DOM.set(doc, raw); - } - } - 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 encoding = env.getCurrentTemplate().getEncoding(); // [FM3] Encoding shouldn't be inherited anymore - if (encoding == null) { - encoding = env.getConfiguration().getEncoding(env.getLocale()); - } - String templatePath = env.getCurrentTemplate().getName(); - int lastSlash = templatePath.lastIndexOf('/'); - templatePath = lastSlash == -1 ? "" : templatePath.substring(0, lastSlash + 1); - systemId = env.toFullTemplateName(templatePath, systemId); - return env.getConfiguration().getTemplate(systemId, env.getLocale(), encoding, false); - } - - 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.getName()); - 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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeListModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeListModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeListModel.java deleted file mode 100644 index 1a70524..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeListModel.java +++ /dev/null @@ -1,231 +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.core.model.impl.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.ObjectWrapper; -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 shared variable ( - * {@link Configuration#setSharedVariable(String, Object)}). - */ -class NodeListModel extends SimpleSequence implements TemplateHashModel, _UnexpectedTypeErrorExplainerTemplateModel { - - // [2.4] make these private - NodeModel contextNode; - XPathSupport xpathSupport; - - private static ObjectWrapper nodeWrapper = new ObjectWrapper() { - @Override - public TemplateModel wrap(Object obj) { - if (obj instanceof NodeModel) { - return (NodeModel) obj; - } - return NodeModel.wrap((Node) obj); - } - }; - - NodeListModel(Node contextNode) { - this(NodeModel.wrap(contextNode)); - } - - NodeListModel(NodeModel contextNode) { - super(nodeWrapper); - this.contextNode = contextNode; - } - - NodeListModel(NodeList nodeList, NodeModel contextNode) { - super(nodeWrapper); - for (int i = 0; i < nodeList.getLength(); i++) { - list.add(nodeList.item(i)); - } - this.contextNode = contextNode; - } - - NodeListModel(NamedNodeMap nodeList, NodeModel contextNode) { - super(nodeWrapper); - for (int i = 0; i < nodeList.getLength(); i++) { - list.add(nodeList.item(i)); - } - this.contextNode = contextNode; - } - - NodeListModel(List list, NodeModel contextNode) { - super(list, nodeWrapper); - 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/59412b29/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeModel.java b/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeModel.java deleted file mode 100644 index ca04d53..0000000 --- a/src/main/java/org/apache/freemarker/core/model/impl/dom/NodeModel.java +++ /dev/null @@ -1,618 +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.core.model.impl.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._CoreLogs; -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 shared variable ( - * {@link Configuration#setSharedVariable(String, Object)}). - * - * <p> - * To represent a node sequence (such as a query result) of exactly 1 nodes, this class should be used instead of - * {@link NodeListModel}, as it adds extra capabilities by utilizing that we have exactly 1 node. If you need to wrap a - * node sequence of 0 or multiple nodes, you must use {@link NodeListModel}. - */ -abstract public class NodeModel -implements TemplateNodeModelEx, TemplateHashModel, TemplateSequenceModel, - AdapterTemplateModel, WrapperTemplateModel, _UnexpectedTypeErrorExplainerTemplateModel { - - static private final Logger LOG = _CoreLogs.DOM_WRAPPER; - - 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.core.model.impl.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.core.model.impl.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.core.model.impl.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.core.model.impl.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.core.model.impl.dom.XPathSupport"); - } - synchronized (STATIC_LOCK) { - xpathSupportClass = cl; - } - } - - /** - * Get the currently used org.apache.freemarker.core.model.impl.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; - } - -}
