/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package freemarker.ext.xml;
import java.io.
StringWriter;
import java.util.
List;
import org.jaxen.
Context;
import org.jaxen.
NamespaceContext;
import org.jaxen.
dom.
DOMXPath;
import org.w3c.dom.
Attr;
import org.w3c.dom.
Document;
import org.w3c.dom.
DocumentType;
import org.w3c.dom.
Element;
import org.w3c.dom.
NamedNodeMap;
import org.w3c.dom.
Node;
import org.w3c.dom.
NodeList;
import org.w3c.dom.
ProcessingInstruction;
import org.w3c.dom.
Text;
import freemarker.template.
TemplateModelException;
import freemarker.template.utility.
StringUtil;
/**
* Don't use this class; it's only public to work around Google App Engine Java
* compliance issues. FreeMarker developers only: treat this class as package-visible.
*/
public class
_DomNavigator extends
Navigator {
public
_DomNavigator() {
}
@
Override
void
getAsString(
Object node,
StringWriter sw) {
outputContent((
Node)
node,
sw);
}
private void
outputContent(
Node n,
StringWriter buf) {
switch(
n.
getNodeType()) {
case
Node.
ATTRIBUTE_NODE: {
buf.
append(' ')
.
append(
getQualifiedName(
n))
.
append("=\"")
.
append(
StringUtil.
XMLEncNA(
n.
getNodeValue())) // XmlEncNA for HTML compatibility
.
append('"');
break;
}
case
Node.
CDATA_SECTION_NODE: {
buf.
append("<![CDATA[").
append(
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('<').
append(
getQualifiedName(
n));
outputContent(
n.
getAttributes(),
buf);
buf.
append('>');
outputContent(
n.
getChildNodes(),
buf);
buf.
append("</").
append(
getQualifiedName(
n)).
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.
TEXT_NODE: {
buf.
append(
StringUtil.
XMLEncNQG(
n.
getNodeValue()));
break;
}
}
}
private void
outputContent(
NodeList nodes,
StringWriter buf) {
for (int
i = 0;
i <
nodes.
getLength(); ++
i) {
outputContent(
nodes.
item(
i),
buf);
}
}
private void
outputContent(
NamedNodeMap nodes,
StringWriter buf) {
for (int
i = 0;
i <
nodes.
getLength(); ++
i) {
outputContent(
nodes.
item(
i),
buf);
}
}
@
Override
void
getChildren(
Object node,
String localName,
String namespaceUri,
List result) {
if ("".
equals(
namespaceUri)) {
namespaceUri = null;
}
NodeList children = ((
Node)
node).
getChildNodes();
for (int
i = 0;
i <
children.
getLength(); ++
i) {
Node subnode =
children.
item(
i);
// IMO, we should get the text nodes as well -- will discuss.
if (
subnode.
getNodeType() ==
Node.
ELEMENT_NODE ||
subnode.
getNodeType() ==
Node.
TEXT_NODE) {
if (
localName == null || (
equal(
subnode.
getNodeName(),
localName) &&
equal(
subnode.
getNamespaceURI(),
namespaceUri))) {
result.
add(
subnode);
}
}
}
}
@
Override
void
getAttributes(
Object node,
String localName,
String namespaceUri,
List result) {
if (
node instanceof
Element) {
Element e = (
Element)
node;
if (
localName == null) {
NamedNodeMap atts =
e.
getAttributes();
for (int
i = 0;
i <
atts.
getLength(); ++
i) {
result.
add(
atts.
item(
i));
}
} else {
if ("".
equals(
namespaceUri)) {
namespaceUri = null;
}
Attr attr =
e.
getAttributeNodeNS(
namespaceUri,
localName);
if (
attr != null) {
result.
add(
attr);
}
}
} else if (
node instanceof
ProcessingInstruction) {
ProcessingInstruction pi = (
ProcessingInstruction)
node;
if ("target".
equals(
localName)) {
result.
add(
createAttribute(
pi, "target",
pi.
getTarget()));
} else if ("data".
equals(
localName)) {
result.
add(
createAttribute(
pi, "data",
pi.
getData()));
} else {
// TODO: DOM has no facility for parsing data into
// name-value pairs...
;
}
} else if (
node instanceof
DocumentType) {
DocumentType doctype = (
DocumentType)
node;
if ("publicId".
equals(
localName)) {
result.
add(
createAttribute(
doctype, "publicId",
doctype.
getPublicId()));
} else if ("systemId".
equals(
localName)) {
result.
add(
createAttribute(
doctype, "systemId",
doctype.
getSystemId()));
} else if ("elementName".
equals(
localName)) {
result.
add(
createAttribute(
doctype, "elementName",
doctype.
getNodeName()));
}
}
}
private
Attr createAttribute(
Node node,
String name,
String value) {
Attr attr =
node.
getOwnerDocument().
createAttribute(
name);
attr.
setNodeValue(
value);
return
attr;
}
@
Override
void
getDescendants(
Object node,
List result) {
NodeList children = ((
Node)
node).
getChildNodes();
for (int
i = 0;
i <
children.
getLength(); ++
i) {
Node subnode =
children.
item(
i);
if (
subnode.
getNodeType() ==
Node.
ELEMENT_NODE) {
result.
add(
subnode);
getDescendants(
subnode,
result);
}
}
}
@
Override
Object getParent(
Object node) {
return ((
Node)
node).
getParentNode();
}
@
Override
Object getDocument(
Object node) {
return ((
Node)
node).
getOwnerDocument();
}
@
Override
Object getDocumentType(
Object node) {
return
node instanceof
Document
? ((
Document)
node).
getDoctype()
: null;
}
@
Override
void
getContent(
Object node,
List result) {
NodeList children = ((
Node)
node).
getChildNodes();
for (int
i = 0;
i <
children.
getLength(); ++
i) {
result.
add(
children.
item(
i));
}
}
@
Override
String getText(
Object node) {
StringBuilder buf = new
StringBuilder();
if (
node instanceof
Element) {
NodeList children = ((
Node)
node).
getChildNodes();
for (int
i = 0;
i <
children.
getLength(); ++
i) {
Node child =
children.
item(
i);
if (
child instanceof
Text) {
buf.
append(
child.
getNodeValue());
}
}
return
buf.
toString();
} else {
return ((
Node)
node).
getNodeValue();
}
}
@
Override
String getLocalName(
Object node) {
return ((
Node)
node).
getNodeName();
}
@
Override
String getNamespacePrefix(
Object node) {
return ((
Node)
node).
getPrefix();
}
@
Override
String getNamespaceUri(
Object node) {
return ((
Node)
node).
getNamespaceURI();
}
@
Override
String getType(
Object node) {
switch(((
Node)
node).
getNodeType()) {
case
Node.
ATTRIBUTE_NODE: {
return "attribute";
}
case
Node.
CDATA_SECTION_NODE: {
return "cdata";
}
case
Node.
COMMENT_NODE: {
return "comment";
}
case
Node.
DOCUMENT_NODE: {
return "document";
}
case
Node.
DOCUMENT_TYPE_NODE: {
return "documentType";
}
case
Node.
ELEMENT_NODE: {
return "element";
}
case
Node.
ENTITY_NODE: {
return "entity";
}
case
Node.
ENTITY_REFERENCE_NODE: {
return "entityReference";
}
case
Node.
PROCESSING_INSTRUCTION_NODE: {
return "processingInstruction";
}
case
Node.
TEXT_NODE: {
return "text";
}
}
return "unknown";
}
@
Override
XPathEx createXPathEx(
String xpathString) throws
TemplateModelException {
try {
return new
DomXPathEx(
xpathString);
} catch (
Exception e) {
throw new
TemplateModelException(
e);
}
}
private static final class
DomXPathEx
extends
DOMXPath
implements
XPathEx {
DomXPathEx(
String path)
throws
Exception {
super(
path);
}
public
List selectNodes(
Object object,
NamespaceContext namespaces)
throws
TemplateModelException {
Context context =
getContext(
object);
context.
getContextSupport().
setNamespaceContext(
namespaces);
try {
return
selectNodesForContext(
context);
} catch (
Exception e) {
throw new
TemplateModelException(
e);
}
}
}
}