blob: 1a0dd57fb2574c2fe457069da20de4c2c7a904a9 [file] [log] [blame]
/*
* 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.template.utility;
import java.util.HashMap;
import java.util.List;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import freemarker.template.SimpleHash;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateMethodModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateSequenceModel;
/**
* A convenient wrapper class for wrapping a Node in the W3C DOM API.
*/
public class DOMNodeModel implements TemplateHashModel {
static private HashMap equivalenceTable = new HashMap();
static {
equivalenceTable.put("*", "children");
equivalenceTable.put("@*", "attributes");
}
private Node node;
private HashMap cache = new HashMap();
public DOMNodeModel(Node node) {
this.node = node;
}
public TemplateModel get(String key) throws TemplateModelException {
TemplateModel result = null;
if (equivalenceTable.containsKey(key)) {
key = (String) equivalenceTable.get(key);
}
if (cache.containsKey(key)) {
result = (TemplateModel) cache.get(key);
}
if (result == null) {
if ("attributes".equals(key)) {
NamedNodeMap attributes = node.getAttributes();
if (attributes != null) {
SimpleHash hash = new SimpleHash();
for (int i = 0; i < attributes.getLength(); i++) {
Attr att = (Attr) attributes.item(i);
hash.put(att.getName(), att.getValue());
}
result = hash;
}
} else if (key.charAt(0) == '@') {
if (node instanceof Element) {
String attValue = ((Element) node).getAttribute(key.substring(1));
result = new SimpleScalar(attValue);
} else {
throw new TemplateModelException("Trying to get an attribute value for a non-element node");
}
} else if ("is_element".equals(key)) {
result = (node instanceof Element) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
} else if ("is_text".equals(key)) {
result = (node instanceof Text) ?
TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE;
} else if ("name".equals(key)) {
result = new SimpleScalar(node.getNodeName());
} else if ("children".equals(key)) {
result = new NodeListTM(node.getChildNodes());
} else if ("parent".equals(key)) {
Node parent = node.getParentNode();
result = (parent == null) ? null : new DOMNodeModel(parent);
} else if ("ancestorByName".equals(key)) {
result = new AncestorByName();
} else if ("nextSibling".equals(key)) {
Node next = node.getNextSibling();
result = (next == null) ? null : new DOMNodeModel(next);
} else if ("previousSibling".equals(key)) {
Node previous = node.getPreviousSibling();
result = (previous == null) ? null : new DOMNodeModel(previous);
} else if ("nextSiblingElement".equals(key)) {
Node next = nextSiblingElement(node);
result = (next == null) ? null : new DOMNodeModel(next);
} else if ("previousSiblingElement".equals(key)) {
Node previous = previousSiblingElement(node);
result = (previous == null) ? null : new DOMNodeModel(previous);
} else if ("nextElement".equals(key)) {
Node next = nextElement(node);
result = (next == null) ? null : new DOMNodeModel(next);
} else if ("previousElement".equals(key)) {
Node previous = previousElement(node);
result = (previous == null) ? null : new DOMNodeModel(previous);
} else if ("text".equals(key)) {
result = new SimpleScalar(getText(node));
}
cache.put(key, result);
}
return result;
}
public boolean isEmpty() {
return false;
}
static private String getText(Node node) {
String result = "";
if (node instanceof Text) {
result = ((Text) node).getData();
} else if (node instanceof Element) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
result += getText(children.item(i));
}
}
return result;
}
static private Element nextSiblingElement(Node node) {
Node next = node;
while (next != null) {
next = next.getNextSibling();
if (next instanceof Element) {
return (Element) next;
}
}
return null;
}
static private Element previousSiblingElement(Node node) {
Node previous = node;
while (previous != null) {
previous = previous.getPreviousSibling();
if (previous instanceof Element) {
return (Element) previous;
}
}
return null;
}
static private Element nextElement(Node node) {
if (node.hasChildNodes()) {
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child instanceof Element) {
return (Element) child;
}
}
}
Element nextSiblingElement = nextSiblingElement(node);
if (nextSiblingElement != null) {
return nextSiblingElement;
}
Node parent = node.getParentNode();
while (parent instanceof Element) {
Element next = nextSiblingElement(parent);
if (next != null) {
return next;
}
parent = parent.getParentNode();
}
return null;
}
static private Element previousElement(Node node) {
Element result = previousSiblingElement(node);
if (result != null) {
return result;
}
Node parent = node.getParentNode();
if (parent instanceof Element) {
return (Element) parent;
}
return null;
}
void setParent(DOMNodeModel parent) {
if (parent != null) {
cache.put("parent", parent);
}
}
String getNodeName() {
return node.getNodeName();
}
class AncestorByName implements TemplateMethodModel {
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 1) {
throw new TemplateModelException("Expecting exactly one string argument here");
}
String nodeName = (String) arguments.get(0);
DOMNodeModel ancestor = (DOMNodeModel) DOMNodeModel.this.get("parent");
while (ancestor != null) {
if (nodeName.equals(ancestor.getNodeName())) {
return ancestor;
}
ancestor = (DOMNodeModel) ancestor.get("parent");
}
return null;
}
}
class NodeListTM implements TemplateSequenceModel, TemplateMethodModel {
private NodeList nodeList;
private TemplateModel[] nodes;
NodeListTM(NodeList nodeList) {
this.nodeList = nodeList;
nodes = new TemplateModel[nodeList.getLength()];
}
public TemplateModel get(int index) {
DOMNodeModel result = (DOMNodeModel) nodes[index];
if (result == null) {
result = new DOMNodeModel(nodeList.item(index));
nodes[index] = result;
result.setParent(DOMNodeModel.this);
}
return result;
}
public int size() {
return nodes.length;
}
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 1) {
throw new TemplateModelException("Expecting exactly one string argument here");
}
if (!(node instanceof Element)) {
throw new TemplateModelException("Expecting element here.");
}
Element elem = (Element) node;
return new NodeListTM(elem.getElementsByTagName((String) arguments.get(0)));
}
}
}