001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.ext.dom;
054:
055: import freemarker.template.*;
056: import freemarker.template.utility.StringUtil;
057: import freemarker.core.Environment;
058: import org.w3c.dom.*;
059: import java.util.List;
060: import java.util.ArrayList;
061:
062: class NodeListModel extends SimpleSequence implements TemplateHashModel {
063:
064: NodeModel contextNode;
065: XPathSupport xpathSupport;
066:
067: private static ObjectWrapper nodeWrapper = new ObjectWrapper() {
068: public TemplateModel wrap(Object obj) {
069: if (obj instanceof NodeModel) {
070: return (NodeModel) obj;
071: }
072: return NodeModel.wrap((Node) obj);
073: }
074: };
075:
076: NodeListModel(Node node) {
077: this (NodeModel.wrap(node));
078: }
079:
080: NodeListModel(NodeModel contextNode) {
081: super (nodeWrapper);
082: this .contextNode = contextNode;
083: }
084:
085: NodeListModel(NodeList nodeList, NodeModel contextNode) {
086: super (nodeWrapper);
087: for (int i = 0; i < nodeList.getLength(); i++) {
088: list.add(nodeList.item(i));
089: }
090: this .contextNode = contextNode;
091: }
092:
093: NodeListModel(NamedNodeMap nodeList, NodeModel contextNode) {
094: super (nodeWrapper);
095: for (int i = 0; i < nodeList.getLength(); i++) {
096: list.add(nodeList.item(i));
097: }
098: this .contextNode = contextNode;
099: }
100:
101: NodeListModel(List list, NodeModel contextNode) {
102: super (list, nodeWrapper);
103: this .contextNode = contextNode;
104: }
105:
106: NodeListModel filterByName(String name)
107: throws TemplateModelException {
108: NodeListModel result = new NodeListModel(contextNode);
109: int size = size();
110: if (size == 0) {
111: return result;
112: }
113: Environment env = Environment.getCurrentEnvironment();
114: for (int i = 0; i < size; i++) {
115: NodeModel nm = (NodeModel) get(i);
116: if (nm instanceof ElementModel) {
117: if (((ElementModel) nm).matchesName(name, env)) {
118: result.add(nm);
119: }
120: }
121: }
122: return result;
123: }
124:
125: public boolean isEmpty() {
126: return size() == 0;
127: }
128:
129: public TemplateModel get(String key) throws TemplateModelException {
130: if (size() == 1) {
131: NodeModel nm = (NodeModel) get(0);
132: return nm.get(key);
133: }
134: if (key.equals("@@markup") || key.equals("@@nested_markup")
135: || key.equals("@@text")) {
136: StringBuffer result = new StringBuffer();
137: for (int i = 0; i < size(); i++) {
138: NodeModel nm = (NodeModel) get(i);
139: TemplateScalarModel textModel = (TemplateScalarModel) nm
140: .get(key);
141: result.append(textModel.getAsString());
142: }
143: return new SimpleScalar(result.toString());
144: }
145: if (StringUtil.isXMLID(key)
146: || ((key.startsWith("@") && StringUtil.isXMLID(key
147: .substring(1)))) || key.equals("*")
148: || key.equals("**") || key.equals("@@")
149: || key.equals("@*")) {
150: NodeListModel result = new NodeListModel(contextNode);
151: for (int i = 0; i < size(); i++) {
152: NodeModel nm = (NodeModel) get(i);
153: if (nm instanceof ElementModel) {
154: TemplateSequenceModel tsm = (TemplateSequenceModel) ((ElementModel) nm)
155: .get(key);
156: int size = tsm == null ? 0 : tsm.size();
157: for (int j = 0; j < size; j++) {
158: result.add(tsm.get(j));
159: }
160: }
161: }
162: if (result.size() == 1) {
163: return result.get(0);
164: }
165: return result;
166: }
167: XPathSupport xps = getXPathSupport();
168: if (xps != null) {
169: Object context = (size() == 0) ? null : rawNodeList();
170: return xps.executeQuery(context, key);
171: }
172: throw new TemplateModelException(
173: "Key: '"
174: + key
175: + "' is not legal for a node sequence ("
176: + this .getClass().getName()
177: + "). This node sequence contains "
178: + size()
179: + " node(s). "
180: + "Some keys are valid only for node sequences of size 1. "
181: + "If you use Xalan (instead of Jaxen), XPath expression keys work only with "
182: + "node lists of size 1.");
183: }
184:
185: private List rawNodeList() throws TemplateModelException {
186: int size = size();
187: ArrayList al = new ArrayList(size);
188: for (int i = 0; i < size; i++) {
189: al.add(((NodeModel) get(i)).node);
190: }
191: return al;
192: }
193:
194: XPathSupport getXPathSupport() throws TemplateModelException {
195: if (xpathSupport == null) {
196: if (contextNode != null) {
197: xpathSupport = contextNode.getXPathSupport();
198: } else if (size() > 0) {
199: xpathSupport = ((NodeModel) get(0)).getXPathSupport();
200: }
201: }
202: return xpathSupport;
203: }
204: }
|