Lines 1-896
Link Here
|
1 |
/* |
|
|
2 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
3 |
* contributor license agreements. See the NOTICE file distributed with |
4 |
* this work for additional information regarding copyright ownership. |
5 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
6 |
* (the "License"); you may not use this file except in compliance with |
7 |
* the License. You may obtain a copy of the License at |
8 |
* |
9 |
* http://www.apache.org/licenses/LICENSE-2.0 |
10 |
* |
11 |
* Unless required by applicable law or agreed to in writing, software |
12 |
* distributed under the License is distributed on an "AS IS" BASIS, |
13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
* See the License for the specific language governing permissions and |
15 |
* limitations under the License. |
16 |
*/ |
17 |
|
18 |
package org.apache.taglibs.standard.tag.common.xml; |
19 |
|
20 |
import java.util.Enumeration; |
21 |
import java.util.HashMap; |
22 |
import java.util.List; |
23 |
import java.util.Vector; |
24 |
|
25 |
import javax.servlet.http.Cookie; |
26 |
import javax.servlet.http.HttpServletRequest; |
27 |
import javax.servlet.jsp.JspTagException; |
28 |
import javax.servlet.jsp.PageContext; |
29 |
import javax.servlet.jsp.tagext.Tag; |
30 |
import javax.servlet.jsp.tagext.TagSupport; |
31 |
import javax.xml.parsers.DocumentBuilder; |
32 |
import javax.xml.parsers.DocumentBuilderFactory; |
33 |
import javax.xml.transform.TransformerException; |
34 |
|
35 |
import org.apache.taglibs.standard.resources.Resources; |
36 |
import org.apache.xml.utils.QName; |
37 |
import org.apache.xpath.VariableStack; |
38 |
import org.apache.xpath.XPathContext; |
39 |
import org.apache.xpath.objects.XBoolean; |
40 |
import org.apache.xpath.objects.XNodeSetForDOM; |
41 |
import org.apache.xpath.objects.XNumber; |
42 |
import org.apache.xpath.objects.XObject; |
43 |
import org.apache.xpath.objects.XString; |
44 |
import org.w3c.dom.DOMImplementation; |
45 |
import org.w3c.dom.Document; |
46 |
import org.w3c.dom.Element; |
47 |
import org.w3c.dom.Node; |
48 |
import org.w3c.dom.NodeList; |
49 |
|
50 |
/** |
51 |
* <p>Support for tag handlers that evaluate XPath expressions.</p> |
52 |
* |
53 |
* @author Shawn Bayern |
54 |
* @author Ramesh Mandava ( ramesh.mandava@sun.com ) |
55 |
* @author Pierre Delisle ( pierre.delisle@sun.com ) |
56 |
*/ |
57 |
// would ideally be a base class, but some of our user handlers already |
58 |
// have their own parents |
59 |
public class XPathUtil { |
60 |
|
61 |
//********************************************************************* |
62 |
// Constructor |
63 |
|
64 |
/** |
65 |
* Constructs a new XPathUtil object associated with the given |
66 |
* PageContext. |
67 |
*/ |
68 |
public XPathUtil(PageContext pc) { |
69 |
pageContext = pc; |
70 |
} |
71 |
|
72 |
int globalVarSize = 0; |
73 |
|
74 |
public Vector getVariableQNames() { |
75 |
|
76 |
globalVarSize = 0; |
77 |
Vector variableVector = new Vector(); |
78 |
// Now construct attributes in different scopes |
79 |
Enumeration enum_ = pageContext.getAttributeNamesInScope( |
80 |
PageContext.PAGE_SCOPE); |
81 |
while (enum_.hasMoreElements()) { |
82 |
String varName = (String) enum_.nextElement(); |
83 |
QName varQName = new QName(PAGE_NS_URL, PAGE_P, varName); |
84 |
//Adding both namespace qualified QName and just localName |
85 |
variableVector.addElement(varQName); |
86 |
globalVarSize++; |
87 |
|
88 |
variableVector.addElement(new QName(null, varName)); |
89 |
globalVarSize++; |
90 |
} |
91 |
enum_ = pageContext.getAttributeNamesInScope( |
92 |
PageContext.REQUEST_SCOPE); |
93 |
while (enum_.hasMoreElements()) { |
94 |
String varName = (String) enum_.nextElement(); |
95 |
QName varQName = new QName(REQUEST_NS_URL, REQUEST_P, varName); |
96 |
//Adding both namespace qualified QName and just localName |
97 |
variableVector.addElement(varQName); |
98 |
globalVarSize++; |
99 |
variableVector.addElement(new QName(null, varName)); |
100 |
globalVarSize++; |
101 |
} |
102 |
|
103 |
if (pageContext.getSession() != null) { |
104 |
// we may have a page directive preventing session creation/access |
105 |
// do not attempt to retrieve attribute names in session scope |
106 |
// @see [ http://issues.apache.org/bugzilla/show_bug.cgi?id=35216 ] |
107 |
enum_ = pageContext.getAttributeNamesInScope( |
108 |
PageContext.SESSION_SCOPE); |
109 |
while (enum_.hasMoreElements()) { |
110 |
String varName = (String) enum_.nextElement(); |
111 |
QName varQName = new QName(SESSION_NS_URL, SESSION_P, varName); |
112 |
//Adding both namespace qualified QName and just localName |
113 |
variableVector.addElement(varQName); |
114 |
globalVarSize++; |
115 |
variableVector.addElement(new QName(null, varName)); |
116 |
globalVarSize++; |
117 |
} |
118 |
} |
119 |
|
120 |
enum_ = pageContext.getAttributeNamesInScope( |
121 |
PageContext.APPLICATION_SCOPE); |
122 |
while (enum_.hasMoreElements()) { |
123 |
String varName = (String) enum_.nextElement(); |
124 |
QName varQName = new QName(APP_NS_URL, APP_P, varName); |
125 |
//Adding both namespace qualified QName and just localName |
126 |
variableVector.addElement(varQName); |
127 |
globalVarSize++; |
128 |
variableVector.addElement(new QName(null, varName)); |
129 |
globalVarSize++; |
130 |
} |
131 |
enum_ = pageContext.getRequest().getParameterNames(); |
132 |
while (enum_.hasMoreElements()) { |
133 |
String varName = (String) enum_.nextElement(); |
134 |
QName varQName = new QName(PARAM_NS_URL, PARAM_P, varName); |
135 |
//Adding both namespace qualified QName and just localName |
136 |
variableVector.addElement(varQName); |
137 |
globalVarSize++; |
138 |
} |
139 |
enum_ = pageContext.getServletContext().getInitParameterNames(); |
140 |
while (enum_.hasMoreElements()) { |
141 |
String varName = (String) enum_.nextElement(); |
142 |
QName varQName = new QName(INITPARAM_NS_URL, INITPARAM_P, varName); |
143 |
//Adding both namespace qualified QName and just localName |
144 |
variableVector.addElement(varQName); |
145 |
globalVarSize++; |
146 |
} |
147 |
enum_ = ((HttpServletRequest) pageContext.getRequest()).getHeaderNames(); |
148 |
while (enum_.hasMoreElements()) { |
149 |
String varName = (String) enum_.nextElement(); |
150 |
QName varQName = new QName(HEADER_NS_URL, HEADER_P, varName); |
151 |
//Adding namespace qualified QName |
152 |
variableVector.addElement(varQName); |
153 |
globalVarSize++; |
154 |
} |
155 |
Cookie[] c = ((HttpServletRequest) pageContext.getRequest()).getCookies(); |
156 |
if (c != null) { |
157 |
for (int i = 0; i < c.length; i++) { |
158 |
String varName = c[i].getName(); |
159 |
QName varQName = new QName(COOKIE_NS_URL, COOKIE_P, varName); |
160 |
//Adding namespace qualified QName |
161 |
variableVector.addElement(varQName); |
162 |
globalVarSize++; |
163 |
} |
164 |
} |
165 |
|
166 |
return variableVector; |
167 |
|
168 |
} |
169 |
|
170 |
//********************************************************************* |
171 |
// Support for JSTL variable resolution |
172 |
|
173 |
// The URLs |
174 |
private static final String PAGE_NS_URL |
175 |
= "http://java.sun.com/jstl/xpath/page"; |
176 |
private static final String REQUEST_NS_URL |
177 |
= "http://java.sun.com/jstl/xpath/request"; |
178 |
private static final String SESSION_NS_URL |
179 |
= "http://java.sun.com/jstl/xpath/session"; |
180 |
private static final String APP_NS_URL |
181 |
= "http://java.sun.com/jstl/xpath/app"; |
182 |
private static final String PARAM_NS_URL |
183 |
= "http://java.sun.com/jstl/xpath/param"; |
184 |
private static final String INITPARAM_NS_URL |
185 |
= "http://java.sun.com/jstl/xpath/initParam"; |
186 |
private static final String COOKIE_NS_URL |
187 |
= "http://java.sun.com/jstl/xpath/cookie"; |
188 |
private static final String HEADER_NS_URL |
189 |
= "http://java.sun.com/jstl/xpath/header"; |
190 |
|
191 |
// The prefixes |
192 |
private static final String PAGE_P = "pageScope"; |
193 |
private static final String REQUEST_P = "requestScope"; |
194 |
private static final String SESSION_P = "sessionScope"; |
195 |
private static final String APP_P = "applicationScope"; |
196 |
private static final String PARAM_P = "param"; |
197 |
private static final String INITPARAM_P = "initParam"; |
198 |
private static final String COOKIE_P = "cookie"; |
199 |
private static final String HEADER_P = "header"; |
200 |
|
201 |
/** |
202 |
* org.apache.xpath.VariableStack defines a class to keep track of a stack |
203 |
* for template arguments and variables. |
204 |
* JstlVariableContext customizes it so it handles JSTL custom |
205 |
* variable-mapping rules. |
206 |
*/ |
207 |
protected class JstlVariableContext extends org.apache.xpath.VariableStack { |
208 |
|
209 |
public JstlVariableContext() { |
210 |
super(); |
211 |
} |
212 |
|
213 |
/** |
214 |
* Get a variable as an XPath object based on it's qualified name. |
215 |
* We override the base class method so JSTL's custom variable-mapping |
216 |
* rules can be applied. |
217 |
* |
218 |
* @param xctxt The XPath context. @@@ we don't use it... |
219 |
* (from xalan: which must be passed in order to lazy evaluate variables.) |
220 |
* @param qname The qualified name of the variable. |
221 |
*/ |
222 |
@Override |
223 |
public XObject getVariableOrParam( |
224 |
XPathContext xctxt, |
225 |
org.apache.xml.utils.QName qname) |
226 |
throws javax.xml.transform.TransformerException, UnresolvableException { |
227 |
//p( "***********************************getVariableOrParam begin****"); |
228 |
String namespace = qname.getNamespaceURI(); |
229 |
String prefix = qname.getPrefix(); |
230 |
String localName = qname.getLocalName(); |
231 |
|
232 |
//p("namespace:prefix:localname=>"+ namespace |
233 |
// + ":" + prefix +":" + localName ); |
234 |
|
235 |
try { |
236 |
Object varObject = getVariableValue(namespace, prefix, localName); |
237 |
|
238 |
|
239 |
//XObject varObject = myvs.getVariableOrParam( xpathSupport, varQName); |
240 |
XObject newXObject = new XObject(varObject); |
241 |
|
242 |
if (Class.forName("org.w3c.dom.Document").isInstance(varObject)) { |
243 |
|
244 |
NodeList nl = ((Document) varObject).getChildNodes(); |
245 |
// To allow non-welformed document |
246 |
Vector nodeVector = new Vector(); |
247 |
for (int i = 0; i < nl.getLength(); i++) { |
248 |
Node currNode = nl.item(i); |
249 |
if (currNode.getNodeType() == Node.ELEMENT_NODE) { |
250 |
nodeVector.addElement(currNode); |
251 |
} |
252 |
} |
253 |
JSTLNodeList jstlNodeList = new JSTLNodeList(nodeVector); |
254 |
newXObject = new XNodeSetForDOM(jstlNodeList, xctxt); |
255 |
|
256 |
return newXObject; |
257 |
|
258 |
} |
259 |
if (Class.forName( |
260 |
"org.apache.taglibs.standard.tag.common.xml.JSTLNodeList").isInstance( |
261 |
varObject)) { |
262 |
JSTLNodeList jstlNodeList = (JSTLNodeList) varObject; |
263 |
if ((jstlNodeList.getLength() == 1) && |
264 |
(!Class.forName("org.w3c.dom.Node").isInstance(jstlNodeList.elementAt(0)))) { |
265 |
varObject = jstlNodeList.elementAt(0); |
266 |
//Now we need to allow this primitive type to be coverted |
267 |
// to type which Xalan XPath understands |
268 |
} else { |
269 |
return new XNodeSetForDOM(jstlNodeList, xctxt); |
270 |
} |
271 |
} |
272 |
if (Class.forName("org.w3c.dom.Node").isInstance(varObject)) { |
273 |
newXObject = new XNodeSetForDOM(new JSTLNodeList((Node) varObject), xctxt); |
274 |
} else if (Class.forName("java.lang.String").isInstance(varObject)) { |
275 |
newXObject = new XString((String) varObject); |
276 |
} else if (Class.forName("java.lang.Boolean").isInstance(varObject)) { |
277 |
newXObject = new XBoolean((Boolean) varObject); |
278 |
} else if (Class.forName("java.lang.Number").isInstance(varObject)) { |
279 |
newXObject = new XNumber((Number) varObject); |
280 |
} |
281 |
|
282 |
return newXObject; |
283 |
// myvs.setGlobalVariable( i, newXObject ); |
284 |
} catch (ClassNotFoundException cnfe) { |
285 |
// This shouldn't happen (TODO: LOG) |
286 |
System.out.println("CLASS NOT FOUND EXCEPTION :" + cnfe); |
287 |
} |
288 |
//System.out.println("*****getVariableOrParam returning *null*" ); |
289 |
return null; |
290 |
} |
291 |
|
292 |
/** |
293 |
* Retrieve an XPath's variable value using JSTL's custom |
294 |
* variable-mapping rules |
295 |
*/ |
296 |
public Object getVariableValue( |
297 |
String namespace, |
298 |
String prefix, |
299 |
String localName) |
300 |
throws UnresolvableException { |
301 |
// p("resolving: ns=" + namespace + " prefix=" + prefix + " localName=" + localName); |
302 |
// We can match on namespace with Xalan but leaving as is |
303 |
// [ I 'd prefer to match on namespace, but this doesn't appear |
304 |
// to work in Jaxen] |
305 |
if (prefix == null || prefix.equals("")) { |
306 |
return notNull( |
307 |
pageContext.findAttribute(localName), |
308 |
prefix, |
309 |
localName); |
310 |
} else if (prefix.equals(PAGE_P)) { |
311 |
return notNull( |
312 |
pageContext.getAttribute(localName, PageContext.PAGE_SCOPE), |
313 |
prefix, |
314 |
localName); |
315 |
} else if (prefix.equals(REQUEST_P)) { |
316 |
return notNull( |
317 |
pageContext.getAttribute(localName, |
318 |
PageContext.REQUEST_SCOPE), |
319 |
prefix, |
320 |
localName); |
321 |
} else if (prefix.equals(SESSION_P)) { |
322 |
return notNull( |
323 |
pageContext.getAttribute(localName, |
324 |
PageContext.SESSION_SCOPE), |
325 |
prefix, |
326 |
localName); |
327 |
} else if (prefix.equals(APP_P)) { |
328 |
return notNull( |
329 |
pageContext.getAttribute(localName, |
330 |
PageContext.APPLICATION_SCOPE), |
331 |
prefix, |
332 |
localName); |
333 |
} else if (prefix.equals(PARAM_P)) { |
334 |
return notNull( |
335 |
pageContext.getRequest().getParameter(localName), |
336 |
prefix, |
337 |
localName); |
338 |
} else if (prefix.equals(INITPARAM_P)) { |
339 |
return notNull( |
340 |
pageContext.getServletContext(). |
341 |
getInitParameter(localName), |
342 |
prefix, |
343 |
localName); |
344 |
} else if (prefix.equals(HEADER_P)) { |
345 |
HttpServletRequest hsr = |
346 |
(HttpServletRequest) pageContext.getRequest(); |
347 |
return notNull( |
348 |
hsr.getHeader(localName), |
349 |
prefix, |
350 |
localName); |
351 |
} else if (prefix.equals(COOKIE_P)) { |
352 |
HttpServletRequest hsr = |
353 |
(HttpServletRequest) pageContext.getRequest(); |
354 |
Cookie[] c = hsr.getCookies(); |
355 |
for (int i = 0; i < c.length; i++) { |
356 |
if (c[i].getName().equals(localName)) { |
357 |
return c[i].getValue(); |
358 |
} |
359 |
} |
360 |
throw new UnresolvableException("$" + prefix + ":" + localName); |
361 |
} else { |
362 |
throw new UnresolvableException("$" + prefix + ":" + localName); |
363 |
} |
364 |
} |
365 |
|
366 |
/** |
367 |
* Validate that the Object returned is not null. If it is |
368 |
* null, throw an exception. |
369 |
*/ |
370 |
private Object notNull(Object o, String prefix, String localName) |
371 |
throws UnresolvableException { |
372 |
if (o == null) { |
373 |
throw new UnresolvableException("$" + (prefix == null ? "" : prefix + ":") + localName); |
374 |
} |
375 |
//p("resolved to: " + o); |
376 |
return o; |
377 |
} |
378 |
} |
379 |
|
380 |
//********************************************************************* |
381 |
// Support for XPath evaluation |
382 |
|
383 |
private PageContext pageContext; |
384 |
private static HashMap exprCache; |
385 |
private static JSTLPrefixResolver jstlPrefixResolver = null; |
386 |
|
387 |
/** |
388 |
* Initialize globally useful data. |
389 |
*/ |
390 |
private synchronized static void staticInit() { |
391 |
if (jstlPrefixResolver == null) { |
392 |
// register supported namespaces |
393 |
jstlPrefixResolver = new JSTLPrefixResolver(); |
394 |
jstlPrefixResolver.addNamespace("pageScope", PAGE_NS_URL); |
395 |
jstlPrefixResolver.addNamespace("requestScope", REQUEST_NS_URL); |
396 |
jstlPrefixResolver.addNamespace("sessionScope", SESSION_NS_URL); |
397 |
jstlPrefixResolver.addNamespace("applicationScope", APP_NS_URL); |
398 |
jstlPrefixResolver.addNamespace("param", PARAM_NS_URL); |
399 |
jstlPrefixResolver.addNamespace("initParam", INITPARAM_NS_URL); |
400 |
jstlPrefixResolver.addNamespace("header", HEADER_NS_URL); |
401 |
jstlPrefixResolver.addNamespace("cookie", COOKIE_NS_URL); |
402 |
|
403 |
|
404 |
// create a HashMap to cache the expressions |
405 |
exprCache = new HashMap(); |
406 |
} |
407 |
} |
408 |
|
409 |
static DocumentBuilderFactory dbf = null; |
410 |
static DocumentBuilder db = null; |
411 |
static Document d = null; |
412 |
|
413 |
static Document getDummyDocument() { |
414 |
try { |
415 |
if (dbf == null) { |
416 |
dbf = DocumentBuilderFactory.newInstance(); |
417 |
dbf.setNamespaceAware(true); |
418 |
dbf.setValidating(false); |
419 |
} |
420 |
db = dbf.newDocumentBuilder(); |
421 |
|
422 |
DOMImplementation dim = db.getDOMImplementation(); |
423 |
d = dim.createDocument("http://java.sun.com/jstl", "dummyroot", null); |
424 |
//d = db.newDocument(); |
425 |
return d; |
426 |
} catch (Exception e) { |
427 |
e.printStackTrace(); |
428 |
} |
429 |
return null; |
430 |
} |
431 |
|
432 |
static Document getDummyDocumentWithoutRoot() { |
433 |
try { |
434 |
if (dbf == null) { |
435 |
dbf = DocumentBuilderFactory.newInstance(); |
436 |
dbf.setNamespaceAware(true); |
437 |
dbf.setValidating(false); |
438 |
} |
439 |
db = dbf.newDocumentBuilder(); |
440 |
|
441 |
d = db.newDocument(); |
442 |
return d; |
443 |
} catch (Exception e) { |
444 |
e.printStackTrace(); |
445 |
} |
446 |
return null; |
447 |
} |
448 |
|
449 |
private static Document getDocumentForNode(Node node) { |
450 |
Document doc = getDummyDocumentWithoutRoot(); |
451 |
Node importedNode = doc.importNode(node, true); |
452 |
doc.appendChild(importedNode); |
453 |
return doc; |
454 |
} |
455 |
|
456 |
// The following variable is used for holding the modified xpath string |
457 |
// when adapting parameter for Xalan XPath engine, where we need to have |
458 |
// a Non null context node. |
459 |
String modifiedXPath = null; |
460 |
|
461 |
|
462 |
/** |
463 |
* Evaluate an XPath expression to a String value. |
464 |
*/ |
465 |
public String valueOf(Node n, String xpath) throws JspTagException { |
466 |
//p("******** valueOf(" + n + ", " + xpath + ")"); |
467 |
staticInit(); |
468 |
// @@@ but where do we set the Pag4eContext for the varaiblecontext? |
469 |
JstlVariableContext vs = new JstlVariableContext(); |
470 |
XPathContext xpathSupport = new XPathContext(); |
471 |
xpathSupport.setVarStack(vs); |
472 |
|
473 |
Vector varVector = fillVarStack(vs, xpathSupport); |
474 |
|
475 |
Node contextNode = adaptParamsForXalan(vs, n, xpath.trim()); |
476 |
|
477 |
xpath = modifiedXPath; |
478 |
|
479 |
//p("******** valueOf: modified xpath: " + xpath); |
480 |
|
481 |
XObject result = JSTLXPathAPI.eval(contextNode, xpath, |
482 |
jstlPrefixResolver, xpathSupport, varVector); |
483 |
|
484 |
|
485 |
//p("******Result TYPE => " + result.getTypeString() ); |
486 |
|
487 |
String resultString = result.str(); |
488 |
//p("******** valueOf: after eval: " + resultString); |
489 |
|
490 |
return resultString; |
491 |
|
492 |
} |
493 |
|
494 |
/** |
495 |
* Evaluate an XPath expression to a boolean value. |
496 |
*/ |
497 |
public boolean booleanValueOf(Node n, String xpath) |
498 |
throws JspTagException { |
499 |
|
500 |
staticInit(); |
501 |
JstlVariableContext vs = new JstlVariableContext(); |
502 |
XPathContext xpathSupport = new XPathContext(); |
503 |
xpathSupport.setVarStack(vs); |
504 |
|
505 |
Vector varVector = fillVarStack(vs, xpathSupport); |
506 |
|
507 |
Node contextNode = adaptParamsForXalan(vs, n, xpath.trim()); |
508 |
xpath = modifiedXPath; |
509 |
|
510 |
XObject result = JSTLXPathAPI.eval(contextNode, xpath, |
511 |
jstlPrefixResolver, xpathSupport, varVector); |
512 |
|
513 |
try { |
514 |
return result.bool(); |
515 |
} catch (TransformerException ex) { |
516 |
throw new JspTagException( |
517 |
Resources.getMessage("XPATH_ERROR_XOBJECT", ex.toString()), ex); |
518 |
} |
519 |
} |
520 |
|
521 |
/** |
522 |
* Evaluate an XPath expression to a List of nodes. |
523 |
*/ |
524 |
public List selectNodes(Node n, String xpath) throws JspTagException { |
525 |
|
526 |
staticInit(); |
527 |
JstlVariableContext vs = new JstlVariableContext(); |
528 |
XPathContext xpathSupport = new XPathContext(); |
529 |
xpathSupport.setVarStack(vs); |
530 |
|
531 |
Vector varVector = fillVarStack(vs, xpathSupport); |
532 |
|
533 |
Node contextNode = adaptParamsForXalan(vs, n, xpath.trim()); |
534 |
xpath = modifiedXPath; |
535 |
|
536 |
XObject result = JSTLXPathAPI.eval(contextNode, xpath, |
537 |
jstlPrefixResolver, xpathSupport, varVector); |
538 |
try { |
539 |
NodeList nl = JSTLXPathAPI.getNodeList(result); |
540 |
return new JSTLNodeList(nl); |
541 |
} catch (JspTagException e) { |
542 |
try { |
543 |
//If result can't be converted to NodeList we receive exception |
544 |
// In this case we may have single primitive value as the result |
545 |
// Populating List with this value ( String, Boolean or Number ) |
546 |
|
547 |
//System.out.println("JSTLXPathAPI.getNodeList thrown exception:"+ e); |
548 |
Vector vector = new Vector(); |
549 |
Object resultObject = null; |
550 |
if (result.getType() == XObject.CLASS_BOOLEAN) { |
551 |
resultObject = result.bool(); |
552 |
} else if (result.getType() == XObject.CLASS_NUMBER) { |
553 |
resultObject = result.num(); |
554 |
} else if (result.getType() == XObject.CLASS_STRING) { |
555 |
resultObject = result.str(); |
556 |
} |
557 |
|
558 |
vector.add(resultObject); |
559 |
return new JSTLNodeList(vector); |
560 |
} catch (TransformerException te) { |
561 |
throw new JspTagException(te.toString(), te); |
562 |
} |
563 |
} |
564 |
|
565 |
|
566 |
} |
567 |
|
568 |
/** |
569 |
* Evaluate an XPath expression to a single node. |
570 |
*/ |
571 |
public Node selectSingleNode(Node n, String xpath) |
572 |
throws JspTagException { |
573 |
//p("selectSingleNode of XPathUtil = passed node:" + |
574 |
// "xpath => " + n + " : " + xpath ); |
575 |
|
576 |
staticInit(); |
577 |
JstlVariableContext vs = new JstlVariableContext(); |
578 |
XPathContext xpathSupport = new XPathContext(); |
579 |
xpathSupport.setVarStack(vs); |
580 |
|
581 |
Vector varVector = fillVarStack(vs, xpathSupport); |
582 |
|
583 |
Node contextNode = adaptParamsForXalan(vs, n, xpath.trim()); |
584 |
xpath = modifiedXPath; |
585 |
|
586 |
return (Node) JSTLXPathAPI.selectSingleNode(contextNode, xpath, |
587 |
jstlPrefixResolver, xpathSupport); |
588 |
} |
589 |
|
590 |
/** |
591 |
* Returns a locally appropriate context given a node. |
592 |
*/ |
593 |
private VariableStack getLocalContext() { |
594 |
// set up instance-specific contexts |
595 |
VariableStack vc = new JstlVariableContext(); |
596 |
return vc; |
597 |
} |
598 |
|
599 |
//********************************************************************* |
600 |
// Adapt XPath expression for integration with Xalan |
601 |
|
602 |
/** |
603 |
* To evaluate an XPath expression using Xalan, we need |
604 |
* to create an XPath object, which wraps an expression object and provides |
605 |
* general services for execution of that expression. |
606 |
* <p>An XPath object can be instantiated with the following information: |
607 |
* - XPath expression to evaluate |
608 |
* - SourceLocator |
609 |
* (reports where an error occurred in the XML source or |
610 |
* transformation instructions) |
611 |
* - PrefixResolver |
612 |
* (resolve prefixes to namespace URIs) |
613 |
* - type |
614 |
* (one of SELECT or MATCH) |
615 |
* - ErrorListener |
616 |
* (customized error handling) |
617 |
* <p>Execution of the XPath expression represented by an XPath object |
618 |
* is done via method execute which takes the following parameters: |
619 |
* - XPathContext |
620 |
* The execution context |
621 |
* - Node contextNode |
622 |
* The node that "." expresses |
623 |
* - PrefixResolver namespaceContext |
624 |
* The context in which namespaces in the XPath are supposed to be |
625 |
* expanded. |
626 |
* <p>Given all of this, if no context node is set for the evaluation |
627 |
* of the XPath expression, one must be set so Xalan |
628 |
* can successfully evaluate a JSTL XPath expression. |
629 |
* (it will not work if the context node is given as a varialbe |
630 |
* at the beginning of the expression) |
631 |
* |
632 |
* @@@ Provide more details... |
633 |
*/ |
634 |
protected Node adaptParamsForXalan(JstlVariableContext jvc, Node n, |
635 |
String xpath) { |
636 |
Node boundDocument = null; |
637 |
|
638 |
modifiedXPath = xpath; |
639 |
String origXPath = xpath; |
640 |
boolean whetherOrigXPath = true; |
641 |
|
642 |
// If contextNode is not null then just pass the values to Xalan XPath |
643 |
// unless this is an expression that starts off with an xml document |
644 |
if (n != null && !xpath.startsWith("$")) { |
645 |
return n; |
646 |
} |
647 |
|
648 |
if (xpath.startsWith("$")) { |
649 |
// JSTL uses $scopePrefix:varLocalName/xpath expression |
650 |
|
651 |
String varQName = xpath.substring(xpath.indexOf("$") + 1); |
652 |
if (varQName.indexOf("/") > 0) { |
653 |
varQName = varQName.substring(0, varQName.indexOf("/")); |
654 |
} |
655 |
String varPrefix = null; |
656 |
String varLocalName = varQName; |
657 |
if (varQName.indexOf(":") >= 0) { |
658 |
varPrefix = varQName.substring(0, varQName.indexOf(":")); |
659 |
varLocalName = varQName.substring(varQName.indexOf(":") + 1); |
660 |
} |
661 |
|
662 |
if (xpath.indexOf("/") > 0) { |
663 |
xpath = xpath.substring(xpath.indexOf("/")); |
664 |
} else { |
665 |
xpath = "/*"; |
666 |
whetherOrigXPath = false; |
667 |
} |
668 |
|
669 |
|
670 |
try { |
671 |
Object varObject = jvc.getVariableValue(null, varPrefix, |
672 |
varLocalName); |
673 |
//System.out.println( "varObject => : its Class " +varObject + |
674 |
// ":" + varObject.getClass() ); |
675 |
|
676 |
if (Class.forName("org.w3c.dom.Document").isInstance( |
677 |
varObject)) { |
678 |
//boundDocument = ((Document)varObject).getDocumentElement(); |
679 |
boundDocument = ((Document) varObject); |
680 |
} else { |
681 |
|
682 |
//System.out.println("Creating a Dummy document to pass " + |
683 |
// " onto as context node " ); |
684 |
|
685 |
if (Class.forName("org.apache.taglibs.standard.tag.common.xml.JSTLNodeList").isInstance(varObject)) { |
686 |
Document newDocument = getDummyDocument(); |
687 |
|
688 |
JSTLNodeList jstlNodeList = (JSTLNodeList) varObject; |
689 |
if (jstlNodeList.getLength() == 1) { |
690 |
if (Class.forName("org.w3c.dom.Node").isInstance( |
691 |
jstlNodeList.elementAt(0))) { |
692 |
Node node = (Node) jstlNodeList.elementAt(0); |
693 |
boundDocument = getDocumentForNode(node); |
694 |
if (whetherOrigXPath) { |
695 |
xpath = "/*" + xpath; |
696 |
} |
697 |
|
698 |
} else { |
699 |
|
700 |
//Nodelist with primitive type |
701 |
Object myObject = jstlNodeList.elementAt(0); |
702 |
|
703 |
//p("Single Element of primitive type"); |
704 |
//p("Type => " + myObject.getClass()); |
705 |
|
706 |
xpath = myObject.toString(); |
707 |
|
708 |
//p("String value ( xpathwould be this) => " + xpath); |
709 |
boundDocument = newDocument; |
710 |
} |
711 |
|
712 |
} else { |
713 |
|
714 |
Element dummyroot = newDocument.getDocumentElement(); |
715 |
for (int i = 0; i < jstlNodeList.getLength(); i++) { |
716 |
Node currNode = (Node) jstlNodeList.item(i); |
717 |
|
718 |
Node importedNode = newDocument.importNode( |
719 |
currNode, true); |
720 |
|
721 |
//printDetails ( newDocument); |
722 |
|
723 |
dummyroot.appendChild(importedNode); |
724 |
|
725 |
//p( "Details of the document After importing"); |
726 |
//printDetails ( newDocument); |
727 |
} |
728 |
boundDocument = newDocument; |
729 |
// printDetails ( boundDocument ); |
730 |
//Verify :As we are adding Document element we need |
731 |
// to change the xpath expression.Hopefully this |
732 |
// won't change the result |
733 |
|
734 |
xpath = "/*" + xpath; |
735 |
} |
736 |
} else if (Class.forName("org.w3c.dom.Node").isInstance( |
737 |
varObject)) { |
738 |
boundDocument = getDocumentForNode((Node) varObject); |
739 |
if (whetherOrigXPath) { |
740 |
xpath = "/*" + xpath; |
741 |
} |
742 |
} else { |
743 |
boundDocument = getDummyDocument(); |
744 |
xpath = origXPath; |
745 |
} |
746 |
|
747 |
|
748 |
} |
749 |
} catch (UnresolvableException ue) { |
750 |
// TODO: LOG |
751 |
System.out.println("Variable Unresolvable :" + ue.getMessage()); |
752 |
ue.printStackTrace(); |
753 |
} catch (ClassNotFoundException cnf) { |
754 |
// Will never happen |
755 |
} |
756 |
} else { |
757 |
//System.out.println("Not encountered $ Creating a Dummydocument 2 "+ |
758 |
// "pass onto as context node " ); |
759 |
boundDocument = getDummyDocument(); |
760 |
} |
761 |
|
762 |
modifiedXPath = xpath; |
763 |
//System.out.println("Modified XPath::boundDocument =>" + modifiedXPath + |
764 |
// "::" + boundDocument ); |
765 |
|
766 |
return boundDocument; |
767 |
} |
768 |
|
769 |
|
770 |
//********************************************************************* |
771 |
// |
772 |
|
773 |
/** |
774 |
* * @@@ why do we have to pass varVector in the varStack first, and then |
775 |
* to XPath object? |
776 |
*/ |
777 |
private Vector fillVarStack(JstlVariableContext vs, XPathContext xpathSupport) |
778 |
throws JspTagException { |
779 |
org.apache.xpath.VariableStack myvs = xpathSupport.getVarStack(); |
780 |
Vector varVector = getVariableQNames(); |
781 |
for (int i = 0; i < varVector.size(); i++) { |
782 |
|
783 |
QName varQName = (QName) varVector.elementAt(i); |
784 |
|
785 |
try { |
786 |
XObject variableValue = vs.getVariableOrParam(xpathSupport, varQName); |
787 |
//p("&&&&Variable set to => " + variableValue.toString() ); |
788 |
//p("&&&&Variable type => " + variableValue.getTypeString() ); |
789 |
myvs.setGlobalVariable(i, variableValue); |
790 |
|
791 |
} catch (TransformerException te) { |
792 |
throw new JspTagException(te.toString(), te); |
793 |
} |
794 |
|
795 |
} |
796 |
return varVector; |
797 |
} |
798 |
|
799 |
|
800 |
//********************************************************************* |
801 |
// Static support for context retrieval from parent <forEach> tag |
802 |
|
803 |
public static Node getContext(Tag t) throws JspTagException { |
804 |
ForEachTag xt = |
805 |
(ForEachTag) TagSupport.findAncestorWithClass( |
806 |
t, ForEachTag.class); |
807 |
if (xt == null) { |
808 |
return null; |
809 |
} else { |
810 |
return (xt.getContext()); |
811 |
} |
812 |
} |
813 |
|
814 |
//********************************************************************* |
815 |
// Utility methods |
816 |
|
817 |
private static void p(String s) { |
818 |
System.out.println("[XPathUtil] " + s); |
819 |
} |
820 |
|
821 |
public static void printDetails(Node n) { |
822 |
System.out.println("\n\nDetails of Node = > " + n); |
823 |
System.out.println("Name:Type:Node Value = > " + n.getNodeName() + |
824 |
":" + n.getNodeType() + ":" + n.getNodeValue()); |
825 |
System.out.println("Namespace URI : Prefix : localName = > " + |
826 |
n.getNamespaceURI() + ":" + n.getPrefix() + ":" + n.getLocalName()); |
827 |
System.out.println("\n Node has children => " + n.hasChildNodes()); |
828 |
if (n.hasChildNodes()) { |
829 |
NodeList nl = n.getChildNodes(); |
830 |
System.out.println("Number of Children => " + nl.getLength()); |
831 |
for (int i = 0; i < nl.getLength(); i++) { |
832 |
Node childNode = nl.item(i); |
833 |
printDetails(childNode); |
834 |
} |
835 |
} |
836 |
} |
837 |
} |
838 |
|
839 |
class JSTLNodeList extends Vector implements NodeList { |
840 |
|
841 |
Vector nodeVector; |
842 |
|
843 |
public JSTLNodeList(Vector nodeVector) { |
844 |
this.nodeVector = nodeVector; |
845 |
} |
846 |
|
847 |
public JSTLNodeList(NodeList nl) { |
848 |
nodeVector = new Vector(); |
849 |
//System.out.println("[JSTLNodeList] nodelist details"); |
850 |
for (int i = 0; i < nl.getLength(); i++) { |
851 |
Node currNode = nl.item(i); |
852 |
//XPathUtil.printDetails ( currNode ); |
853 |
nodeVector.add(i, nl.item(i)); |
854 |
} |
855 |
} |
856 |
|
857 |
public JSTLNodeList(Node n) { |
858 |
nodeVector = new Vector(); |
859 |
nodeVector.addElement(n); |
860 |
} |
861 |
|
862 |
|
863 |
public Node item(int index) { |
864 |
return (Node) nodeVector.elementAt(index); |
865 |
} |
866 |
|
867 |
@Override |
868 |
public Object elementAt(int index) { |
869 |
return nodeVector.elementAt(index); |
870 |
} |
871 |
|
872 |
@Override |
873 |
public Object get(int index) { |
874 |
return nodeVector.get(index); |
875 |
} |
876 |
|
877 |
public int getLength() { |
878 |
return nodeVector.size(); |
879 |
} |
880 |
|
881 |
@Override |
882 |
public int size() { |
883 |
//System.out.println("JSTL node list size => " + nodeVector.size() ); |
884 |
return nodeVector.size(); |
885 |
} |
886 |
|
887 |
// Can implement other Vector methods to redirect those methods to |
888 |
// the vector in the variable param. As we are not using them as part |
889 |
// of this implementation we are not doing that here. If this changes |
890 |
// then we need to override those methods accordingly |
891 |
|
892 |
} |
893 |
|
894 |
|
895 |
|
896 |
|