1 /* DocumentFunction.java --
2    Copyright (C) 2004 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.xml.transform;
39 
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.Collections;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.TreeSet;
46 import javax.xml.namespace.QName;
47 import javax.xml.transform.TransformerException;
48 import javax.xml.transform.dom.DOMSource;
49 import javax.xml.xpath.XPathFunction;
50 import javax.xml.xpath.XPathFunctionException;
51 import org.w3c.dom.Node;
52 import gnu.xml.xpath.Constant;
53 import gnu.xml.xpath.Expr;
54 import gnu.xml.xpath.Function;
55 import gnu.xml.xpath.IdFunction;
56 
57 /**
58  * The XSLT <code>document()</code>function.
59  *
60  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
61  */
62 final class DocumentFunction
63   extends Expr
64   implements Function, XPathFunction
65 {
66 
67   final Stylesheet stylesheet;
68   final Node base;
69   List args;
70   List values;
71 
DocumentFunction(Stylesheet stylesheet, Node base)72   DocumentFunction(Stylesheet stylesheet, Node base)
73   {
74     this.stylesheet = stylesheet;
75     this.base = base;
76   }
77 
evaluate(List args)78   public Object evaluate(List args)
79     throws XPathFunctionException
80   {
81     values = args;
82     return evaluate(null, 1, 1);
83   }
84 
setArguments(List args)85   public void setArguments(List args)
86   {
87     this.args = args;
88   }
89 
evaluate(Node context, int pos, int len)90   public Object evaluate(Node context, int pos, int len)
91   {
92     int arity = args.size();
93     if (values == null)
94       {
95         values = new ArrayList(arity);
96         for (int i = 0; i < arity; i++)
97           {
98             Expr arg = (Expr) args.get(i);
99             values.add(arg.evaluate(context, pos, len));
100           }
101       }
102     Object ret;
103     switch (arity)
104       {
105       case 1:
106         Object arg = values.get(0);
107         if (arg instanceof Collection)
108           {
109             Collection ns = (Collection) arg;
110             Collection acc = new TreeSet();
111             for (Iterator i = ns.iterator(); i.hasNext(); )
112               {
113                 Node node = (Node) i.next();
114                 String s = Expr.stringValue(node);
115                 acc.addAll(document(s, node.getBaseURI()));
116               }
117             ret = acc;
118           }
119         else
120           {
121             String s = Expr._string(context, arg);
122             ret = document(s, base.getBaseURI());
123           }
124         break;
125       case 2:
126         Object arg1 = values.get(0);
127         Object arg2 = values.get(1);
128         if (!(arg2 instanceof Collection))
129           {
130             throw new RuntimeException("second argument is not a node-set");
131           }
132         Collection arg2ns = (Collection) arg2;
133         String base2 = arg2ns.isEmpty() ? null :
134           ((Node) arg2ns.iterator().next()).getBaseURI();
135         if (arg1 instanceof Collection)
136           {
137             Collection arg1ns = (Collection) arg1;
138             Collection acc = new TreeSet();
139             for (Iterator i = arg1ns.iterator(); i.hasNext(); )
140               {
141                 Node node = (Node) i.next();
142                 String s = Expr.stringValue(node);
143                 acc.addAll(document(s, base2));
144               }
145             ret = acc;
146           }
147         else
148           {
149             String s = Expr._string(context, arg1);
150             ret = document(s, base2);
151           }
152         break;
153       default:
154         throw new RuntimeException("invalid arity");
155       }
156     values = null;
157     return ret;
158   }
159 
160   /**
161    * The XSL <code>document</code> function.
162    * @see XSLT 12.1
163    * @param uri the URI from which to retrieve nodes
164    * @param base the base URI for relative URIs
165    */
document(String uri, String base)166   Collection document(String uri, String base)
167   {
168     if ("".equals(uri) || uri == null)
169       {
170         uri = this.base.getBaseURI();
171       }
172 
173     // Get fragment
174     Expr fragment = null;
175     int hi = uri.indexOf('#');
176     if (hi != -1)
177       {
178         String f = uri.substring(hi + 1);
179         uri = uri.substring(0, hi);
180         // TODO handle xpointer() here
181         // this only handles IDs
182         fragment = new IdFunction(new Constant(f));
183       }
184 
185     // Get document source
186     try
187       {
188         DOMSource source;
189         XSLURIResolver resolver = stylesheet.factory.resolver;
190         synchronized (resolver)
191           {
192             if (stylesheet.transformer != null)
193               {
194                 resolver.setUserResolver(stylesheet.transformer.uriResolver);
195                 resolver.setUserListener(stylesheet.transformer.errorListener);
196               }
197             source = resolver.resolveDOM(null, base, uri);
198           }
199         Node node = source.getNode();
200         if (fragment == null)
201           {
202             return Collections.singleton(node);
203           }
204         else
205           {
206             Object ret = fragment.evaluate(node, 1, 1);
207             if (!(ret instanceof Collection))
208               {
209                 // XXX Report error?
210                 return Collections.EMPTY_SET;
211               }
212             return (Collection) ret;
213           }
214       }
215     catch (TransformerException e)
216       {
217         String msg = "can't open " + uri;
218         if (base != null)
219           {
220             msg += " with base " + base;
221           }
222         throw new RuntimeException(msg);
223       }
224   }
225 
clone(Object context)226   public Expr clone(Object context)
227   {
228     Stylesheet s = stylesheet;
229     if (context instanceof Stylesheet)
230       {
231         s = (Stylesheet) context;
232       }
233     DocumentFunction f = new DocumentFunction(s, base);
234     int len = args.size();
235     List args2 = new ArrayList(len);
236     for (int i = 0; i < len; i++)
237       {
238         args2.add(((Expr) args.get(i)).clone(context));
239       }
240     f.setArguments(args2);
241     return f;
242   }
243 
references(QName var)244   public boolean references(QName var)
245   {
246     for (Iterator i = args.iterator(); i.hasNext(); )
247       {
248         if (((Expr) i.next()).references(var))
249           {
250             return true;
251           }
252       }
253     return false;
254   }
255 
256 }
257