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.core.Environment;
057: import org.w3c.dom.*;
058: import org.w3c.dom.traversal.NodeIterator;
059: import org.apache.xpath.*;
060: import org.apache.xpath.objects.*;
061: import org.apache.xml.utils.PrefixResolver;
062: import java.util.List;
063: import javax.xml.transform.TransformerException;
064:
065: /**
066: * Some glue code that bridges the Xalan XPath stuff (that is built into the JDK 1.4.x)
067: * with FreeMarker TemplateModel semantics
068: * @author Jonathan Revusky
069: * @version $Id: XalanXPathSupport.java,v 1.20 2004/01/02 20:20:23 ddekany Exp $
070: */
071:
072: class XalanXPathSupport implements XPathSupport {
073:
074: private XPathContext xpathContext = new XPathContext();
075:
076: /* I don't recommend Jaxen...
077: private static final String ERRMSG_RECOMMEND_JAXEN
078: = "(Note that there is no such restriction if you "
079: + "configure FreeMarker to use Jaxen instead of Xalan.)";
080: */
081: private static final String ERRMSG_EMPTY_NODE_SET = "Cannot perform an XPath query against an empty node set."; /* " + ERRMSG_RECOMMEND_JAXEN;*/
082:
083: synchronized public TemplateModel executeQuery(Object context,
084: String xpathQuery) throws TemplateModelException {
085: if (!(context instanceof Node)) {
086: if (context != null) {
087: if (isNodeList(context)) {
088: int cnt = ((List) context).size();
089: if (cnt != 0) {
090: throw new TemplateModelException(
091: "Cannot perform an XPath query against a node set of "
092: + cnt
093: + " nodes. Expecting a single node."/* " + ERRMSG_RECOMMEND_JAXEN*/);
094: } else {
095: throw new TemplateModelException(
096: ERRMSG_EMPTY_NODE_SET);
097: }
098: } else {
099: throw new TemplateModelException(
100: "Cannot perform an XPath query against a "
101: + context.getClass().getName()
102: + ". Expecting a single org.w3c.dom.Node.");
103: }
104: } else {
105: throw new TemplateModelException(ERRMSG_EMPTY_NODE_SET);
106: }
107: }
108: Node node = (Node) context;
109: try {
110: XPath xpath = new XPath(xpathQuery, null,
111: customPrefixResolver, XPath.SELECT, null);
112: int ctxtNode = xpathContext.getDTMHandleFromNode(node);
113: XObject xresult = xpath.execute(xpathContext, ctxtNode,
114: customPrefixResolver);
115: if (xresult instanceof XNodeSet) {
116: NodeListModel result = new NodeListModel(node);
117: result.xpathSupport = this ;
118: NodeIterator nodeIterator = xresult.nodeset();
119: Node n;
120: do {
121: n = nodeIterator.nextNode();
122: if (n != null) {
123: result.add(n);
124: }
125: } while (n != null);
126: return result.size() == 1 ? result.get(0) : result;
127: }
128: if (xresult instanceof XBoolean) {
129: return ((XBoolean) xresult).bool() ? TemplateBooleanModel.TRUE
130: : TemplateBooleanModel.FALSE;
131: }
132: if (xresult instanceof XNull) {
133: return null;
134: }
135: if (xresult instanceof XString) {
136: return new SimpleScalar(xresult.toString());
137: }
138: if (xresult instanceof XNumber) {
139: return new SimpleNumber(new Double(((XNumber) xresult)
140: .num()));
141: }
142: throw new TemplateModelException("Cannot deal with type: "
143: + xresult.getClass().getName());
144: } catch (TransformerException te) {
145: throw new TemplateModelException(te);
146: }
147: }
148:
149: private static PrefixResolver customPrefixResolver = new PrefixResolver() {
150:
151: public String getNamespaceForPrefix(String prefix, Node node) {
152: return getNamespaceForPrefix(prefix);
153: }
154:
155: public String getNamespaceForPrefix(String prefix) {
156: if (prefix.equals(Template.DEFAULT_NAMESPACE_PREFIX)) {
157: return Environment.getCurrentEnvironment()
158: .getDefaultNS();
159: }
160: return Environment.getCurrentEnvironment()
161: .getNamespaceForPrefix(prefix);
162: }
163:
164: public String getBaseIdentifier() {
165: return null;
166: }
167:
168: public boolean handlesNullPrefixes() {
169: return false;
170: }
171: };
172:
173: /**
174: * Used for generating more intelligent error messages.
175: */
176: private static boolean isNodeList(Object context) {
177: if (context instanceof List) {
178: List ls = (List) context;
179: int ln = ls.size();
180: for (int i = 0; i < ln; i++) {
181: if (!(ls.get(i) instanceof Node)) {
182: return false;
183: }
184: }
185: return true;
186: } else {
187: return false;
188: }
189: }
190: }
|