blob: 44a9f6d05292f7218f91f3dde0a8f971e8709b5d [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Copyright 1999-2004 The Apache Software Foundation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 */
21package com.sun.org.apache.xml.internal.security.utils;
22
23
24
25import com.sun.org.apache.xml.internal.dtm.DTMManager;
26import com.sun.org.apache.xml.internal.security.transforms.implementations.FuncHere;
27import com.sun.org.apache.xml.internal.security.transforms.implementations.FuncHereContext;
28import com.sun.org.apache.xml.internal.utils.PrefixResolver;
29import com.sun.org.apache.xml.internal.utils.PrefixResolverDefault;
30import com.sun.org.apache.xpath.internal.CachedXPathAPI;
31import com.sun.org.apache.xpath.internal.Expression;
32import com.sun.org.apache.xpath.internal.XPath;
33import com.sun.org.apache.xpath.internal.XPathContext;
34import com.sun.org.apache.xpath.internal.compiler.FunctionTable;
35import com.sun.org.apache.xpath.internal.objects.XObject;
36import org.w3c.dom.*;
37import org.w3c.dom.traversal.NodeIterator;
38
39import javax.xml.transform.ErrorListener;
40import javax.xml.transform.SourceLocator;
41import javax.xml.transform.TransformerException;
42import java.lang.reflect.Constructor;
43import java.lang.reflect.Method;
44import java.lang.reflect.Modifier;
45
46/**
47 *
48 * @author $Author: dims $
49 */
50public class CachedXPathFuncHereAPI {
51
52 static java.util.logging.Logger log =
53 java.util.logging.Logger.getLogger(CachedXPathFuncHereAPI.class.getName());
54 /**
55 * XPathContext, and thus DTMManager and DTMs, persists through multiple
56 * calls to this object.
57 */
58 FuncHereContext _funcHereContext = null;
59
60 /** Field _dtmManager */
61 DTMManager _dtmManager = null;
62
63 XPathContext _context = null;
64
65 String xpathStr=null;
66
67 XPath xpath=null;
68
69 static FunctionTable _funcTable = null;
70
71 static {
72 fixupFunctionTable();
73 }
74
75 /**
76 * Method getFuncHereContext
77 * @return the context for this object
78 *
79 */
80 public FuncHereContext getFuncHereContext() {
81 return this._funcHereContext;
82 }
83
84 /**
85 * Constructor CachedXPathFuncHereAPI
86 *
87 */
88 private CachedXPathFuncHereAPI() {}
89
90 /**
91 * Constructor CachedXPathFuncHereAPI
92 *
93 * @param existingXPathContext
94 */
95 public CachedXPathFuncHereAPI(XPathContext existingXPathContext) {
96 this._dtmManager = existingXPathContext.getDTMManager();
97 this._context=existingXPathContext;
98 }
99
100 /**
101 * Constructor CachedXPathFuncHereAPI
102 *
103 * @param previouslyUsed
104 */
105 public CachedXPathFuncHereAPI(CachedXPathAPI previouslyUsed) {
106 this._dtmManager = previouslyUsed.getXPathContext().getDTMManager();
107 this._context=previouslyUsed.getXPathContext();
108 }
109
110 /**
111 * Use an XPath string to select a single node. XPath namespace
112 * prefixes are resolved from the context node, which may not
113 * be what you want (see the next method).
114 *
115 * @param contextNode The node to start searching from.
116 * @param xpathnode A Node containing a valid XPath string.
117 * @return The first node found that matches the XPath, or null.
118 *
119 * @throws TransformerException
120 */
121 public Node selectSingleNode(Node contextNode, Node xpathnode)
122 throws TransformerException {
123 return selectSingleNode(contextNode, xpathnode, contextNode);
124 }
125
126 /**
127 * Use an XPath string to select a single node.
128 * XPath namespace prefixes are resolved from the namespaceNode.
129 *
130 * @param contextNode The node to start searching from.
131 * @param xpathnode
132 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
133 * @return The first node found that matches the XPath, or null.
134 *
135 * @throws TransformerException
136 */
137 public Node selectSingleNode(
138 Node contextNode, Node xpathnode, Node namespaceNode)
139 throws TransformerException {
140
141 // Have the XObject return its result as a NodeSetDTM.
142 NodeIterator nl = selectNodeIterator(contextNode, xpathnode,
143 namespaceNode);
144
145 // Return the first node, or null
146 return nl.nextNode();
147 }
148
149 /**
150 * Use an XPath string to select a nodelist.
151 * XPath namespace prefixes are resolved from the contextNode.
152 *
153 * @param contextNode The node to start searching from.
154 * @param xpathnode
155 * @return A NodeIterator, should never be null.
156 *
157 * @throws TransformerException
158 */
159 public NodeIterator selectNodeIterator(Node contextNode, Node xpathnode)
160 throws TransformerException {
161 return selectNodeIterator(contextNode, xpathnode, contextNode);
162 }
163
164 /**
165 * Use an XPath string to select a nodelist.
166 * XPath namespace prefixes are resolved from the namespaceNode.
167 *
168 * @param contextNode The node to start searching from.
169 * @param xpathnode
170 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
171 * @return A NodeIterator, should never be null.
172 *
173 * @throws TransformerException
174 * @deprecated
175 */
176 public NodeIterator selectNodeIterator(
177 Node contextNode, Node xpathnode, Node namespaceNode)
178 throws TransformerException {
179
180 // Execute the XPath, and have it return the result
181 XObject list = eval(contextNode, xpathnode, getStrFromNode(xpathnode), namespaceNode);
182
183 // Have the XObject return its result as a NodeSetDTM.
184 return list.nodeset();
185 }
186
187 /**
188 * Use an XPath string to select a nodelist.
189 * XPath namespace prefixes are resolved from the contextNode.
190 *
191 * @param contextNode The node to start searching from.
192 * @param xpathnode
193 * @return A NodeIterator, should never be null.
194 *
195 * @throws TransformerException
196 * @deprecated
197 */
198 public NodeList selectNodeList(Node contextNode, Node xpathnode)
199 throws TransformerException {
200 return selectNodeList(contextNode, xpathnode, getStrFromNode(xpathnode), contextNode);
201 }
202
203 /**
204 * Use an XPath string to select a nodelist.
205 * XPath namespace prefixes are resolved from the namespaceNode.
206 *
207 * @param contextNode The node to start searching from.
208 * @param xpathnode
209 * @param str
210 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
211 * @return A NodeIterator, should never be null.
212 *
213 * @throws TransformerException
214 */
215 public NodeList selectNodeList(
216 Node contextNode, Node xpathnode, String str, Node namespaceNode)
217 throws TransformerException {
218
219 // Execute the XPath, and have it return the result
220 XObject list = eval(contextNode, xpathnode, str, namespaceNode);
221
222 // Return a NodeList.
223 return list.nodelist();
224 }
225
226 /**
227 * Evaluate XPath string to an XObject. Using this method,
228 * XPath namespace prefixes will be resolved from the namespaceNode.
229 * @param contextNode The node to start searching from.
230 * @param xpathnode
231 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
232 * @see com.sun.org.apache.xpath.internal.objects.XObject
233 * @see com.sun.org.apache.xpath.internal.objects.XNull
234 * @see com.sun.org.apache.xpath.internal.objects.XBoolean
235 * @see com.sun.org.apache.xpath.internal.objects.XNumber
236 * @see com.sun.org.apache.xpath.internal.objects.XString
237 * @see com.sun.org.apache.xpath.internal.objects.XRTreeFrag
238 *
239 * @throws TransformerException
240 * @deprecated
241 */
242 public XObject eval(Node contextNode, Node xpathnode)
243 throws TransformerException {
244 return eval(contextNode, xpathnode, getStrFromNode(xpathnode),contextNode);
245 }
246
247 /**
248 * Evaluate XPath string to an XObject.
249 * XPath namespace prefixes are resolved from the namespaceNode.
250 * The implementation of this is a little slow, since it creates
251 * a number of objects each time it is called. This could be optimized
252 * to keep the same objects around, but then thread-safety issues would arise.
253 *
254 * @param contextNode The node to start searching from.
255 * @param xpathnode
256 * @param str
257 * @param namespaceNode The node from which prefixes in the XPath will be resolved to namespaces.
258 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
259 * @see com.sun.org.apache.xpath.internal.objects.XObject
260 * @see com.sun.org.apache.xpath.internal.objects.XNull
261 * @see com.sun.org.apache.xpath.internal.objects.XBoolean
262 * @see com.sun.org.apache.xpath.internal.objects.XNumber
263 * @see com.sun.org.apache.xpath.internal.objects.XString
264 * @see com.sun.org.apache.xpath.internal.objects.XRTreeFrag
265 *
266 * @throws TransformerException
267 */
268 public XObject eval(Node contextNode, Node xpathnode, String str, Node namespaceNode)
269 throws TransformerException {
270 // Create the XPath object.
271 //String str = CachedXPathFuncHereAPI.getStrFromNode(xpathnode);
272
273 // Since we don't have a XML Parser involved here, install some default support
274 // for things like namespaces, etc.
275 // (Changed from: XPathContext xpathSupport = new XPathContext();
276 // because XPathContext is weak in a number of areas... perhaps
277 // XPathContext should be done away with.)
278 if (this._funcHereContext == null) {
279 this._funcHereContext = new FuncHereContext(xpathnode,
280 this._dtmManager);
281 }
282
283 // Create an object to resolve namespace prefixes.
284 // XPath namespaces are resolved from the input context node's document element
285 // if it is a root node, or else the current context node (for lack of a better
286 // resolution space, given the simplicity of this sample code).
287 PrefixResolverDefault prefixResolver =
288 new PrefixResolverDefault((namespaceNode.getNodeType()
289 == Node.DOCUMENT_NODE)
290 ? ((Document) namespaceNode)
291 .getDocumentElement()
292 : namespaceNode);
293
294 if (str!=xpathStr) {
295 if (str.indexOf("here()")>0) {
296 _context.reset();
297 _dtmManager=_context.getDTMManager();
298 }
299 xpath = createXPath(str, prefixResolver);
300 xpathStr=str;
301 }
302
303 // Execute the XPath, and have it return the result
304 // return xpath.execute(xpathSupport, contextNode, prefixResolver);
305 int ctxtNode = this._funcHereContext.getDTMHandleFromNode(contextNode);
306
307 return xpath.execute(this._funcHereContext, ctxtNode, prefixResolver);
308 }
309
310 /**
311 * Evaluate XPath string to an XObject.
312 * XPath namespace prefixes are resolved from the namespaceNode.
313 * The implementation of this is a little slow, since it creates
314 * a number of objects each time it is called. This could be optimized
315 * to keep the same objects around, but then thread-safety issues would arise.
316 *
317 * @param contextNode The node to start searching from.
318 * @param xpathnode
319 * @param str
320 * @param prefixResolver Will be called if the parser encounters namespace
321 * prefixes, to resolve the prefixes to URLs.
322 * @return An XObject, which can be used to obtain a string, number, nodelist, etc, should never be null.
323 * @see com.sun.org.apache.xpath.internal.objects.XObject
324 * @see com.sun.org.apache.xpath.internal.objects.XNull
325 * @see com.sun.org.apache.xpath.internal.objects.XBoolean
326 * @see com.sun.org.apache.xpath.internal.objects.XNumber
327 * @see com.sun.org.apache.xpath.internal.objects.XString
328 * @see com.sun.org.apache.xpath.internal.objects.XRTreeFrag
329 *
330 * @throws TransformerException
331 */
332 public XObject eval(
333 Node contextNode, Node xpathnode, String str, PrefixResolver prefixResolver)
334 throws TransformerException {
335
336 // Since we don't have a XML Parser involved here, install some default support
337 // for things like namespaces, etc.
338 // (Changed from: XPathContext xpathSupport = new XPathContext();
339 // because XPathContext is weak in a number of areas... perhaps
340 // XPathContext should be done away with.)
341 // Create the XPath object.
342 //String str = CachedXPathFuncHereAPI.getStrFromNode(xpathnode);
343 if (str!=xpathStr) {
344 if (str.indexOf("here()")>0) {
345 _context.reset();
346 _dtmManager=_context.getDTMManager();
347 }
348 try {
349 xpath = createXPath(str, prefixResolver);
350 } catch (TransformerException ex) {
351 //Try to see if it is a problem with the classloader.
352 Throwable th= ex.getCause();
353 if (th instanceof ClassNotFoundException) {
354 if (th.getMessage().indexOf("FuncHere")>0) {
355 throw new RuntimeException(I18n.translate("endorsed.jdk1.4.0")/*,*/+ex);
356 }
357 }
358 throw ex;
359 }
360 xpathStr=str;
361 }
362
363 // Execute the XPath, and have it return the result
364 if (this._funcHereContext == null) {
365 this._funcHereContext = new FuncHereContext(xpathnode,
366 this._dtmManager);
367 }
368
369 int ctxtNode = this._funcHereContext.getDTMHandleFromNode(contextNode);
370
371 return xpath.execute(this._funcHereContext, ctxtNode, prefixResolver);
372 }
373
374 private XPath createXPath(String str, PrefixResolver prefixResolver) throws TransformerException {
375 XPath xpath = null;
376 Class[] classes = new Class[]{String.class, SourceLocator.class, PrefixResolver.class, int.class,
377 ErrorListener.class, FunctionTable.class};
378 Object[] objects = new Object[]{str, null, prefixResolver, new Integer(XPath.SELECT), null, _funcTable};
379 try {
380 Constructor constructor = XPath.class.getConstructor(classes);
381 xpath = (XPath) constructor.newInstance(objects);
382 } catch (Throwable t) {
383 }
384 if (xpath == null) {
385 xpath = new XPath(str, null, prefixResolver, XPath.SELECT, null);
386 }
387 return xpath;
388 }
389
390 /**
391 * Method getStrFromNode
392 *
393 * @param xpathnode
394 * @return the string for the node.
395 */
396 public static String getStrFromNode(Node xpathnode) {
397
398 if (xpathnode.getNodeType() == Node.TEXT_NODE) {
399
400 // we iterate over all siblings of the context node because eventually,
401 // the text is "polluted" with pi's or comments
402 StringBuffer sb = new StringBuffer();
403
404 for (Node currentSibling = xpathnode.getParentNode().getFirstChild();
405 currentSibling != null;
406 currentSibling = currentSibling.getNextSibling()) {
407 if (currentSibling.getNodeType() == Node.TEXT_NODE) {
408 sb.append(((Text) currentSibling).getData());
409 }
410 }
411
412 return sb.toString();
413 } else if (xpathnode.getNodeType() == Node.ATTRIBUTE_NODE) {
414 return ((Attr) xpathnode).getNodeValue();
415 } else if (xpathnode.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
416 return ((ProcessingInstruction) xpathnode).getNodeValue();
417 }
418
419 return null;
420 }
421
422 private static void fixupFunctionTable() {
423 boolean installed = false;
424 if (log.isLoggable(java.util.logging.Level.INFO)) log.log(java.util.logging.Level.INFO, "Registering Here function");
425 /**
426 * Try to register our here() implementation as internal function.
427 */
428 try {
429 Class []args = {String.class, Expression.class};
430 Method installFunction = FunctionTable.class.getMethod("installFunction", args);
431 if ((installFunction.getModifiers() & Modifier.STATIC) != 0) {
432 Object []params = {"here", new FuncHere()};
433 installFunction.invoke(null, params);
434 installed = true;
435 }
436 } catch (Throwable t) {
437 if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Error installing function using the static installFunction method", t);
438 }
439 if(!installed) {
440 try {
441 _funcTable = new FunctionTable();
442 Class []args = {String.class, Class.class};
443 Method installFunction = FunctionTable.class.getMethod("installFunction", args);
444 Object []params = {"here", FuncHere.class};
445 installFunction.invoke(_funcTable, params);
446 installed = true;
447 } catch (Throwable t) {
448 if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Error installing function using the static installFunction method", t);
449 }
450 }
451 if (true) {
452 if (installed) {
453 if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Registered class " + FuncHere.class.getName()
454 + " for XPath function 'here()' function in internal table");
455 } else {
456 if (log.isLoggable(java.util.logging.Level.FINE)) log.log(java.util.logging.Level.FINE, "Unable to register class " + FuncHere.class.getName()
457 + " for XPath function 'here()' function in internal table");
458 }
459 }
460 }
461}