1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: NodeTest.java 468655 2006-10-28 07:12:06Z minchau $
20 */
21package org.apache.xpath.patterns;
22
23import org.apache.xml.dtm.DTM;
24import org.apache.xml.dtm.DTMFilter;
25import org.apache.xpath.Expression;
26import org.apache.xpath.ExpressionOwner;
27import org.apache.xpath.XPath;
28import org.apache.xpath.XPathContext;
29import org.apache.xpath.XPathVisitor;
30import org.apache.xpath.objects.XNumber;
31import org.apache.xpath.objects.XObject;
32
33/**
34 * This is the basic node test class for both match patterns and location path
35 * steps.
36 * @xsl.usage advanced
37 */
38public class NodeTest extends Expression
39{
40    static final long serialVersionUID = -5736721866747906182L;
41
42  /**
43   * The namespace or local name for node tests with a wildcard.
44   *  @see <a href="http://www.w3.org/TR/xpath#NT-NameTest">the XPath NameTest production.</a>
45   */
46  public static final String WILD = "*";
47
48  /**
49   * The URL to pass to the Node#supports method, to see if the
50   * DOM has already been stripped of whitespace nodes.
51   */
52  public static final String SUPPORTS_PRE_STRIPPING =
53    "http://xml.apache.org/xpath/features/whitespace-pre-stripping";
54
55  /**
56   * This attribute determines which node types are accepted.
57   * @serial
58   */
59  protected int m_whatToShow;
60
61  /**
62   * Special bitmap for match patterns starting with a function.
63   * Make sure this does not conflict with {@link org.w3c.dom.traversal.NodeFilter}.
64   */
65  public static final int SHOW_BYFUNCTION = 0x00010000;
66
67  /**
68   * This attribute determines which node types are accepted.
69   * These constants are defined in the {@link org.w3c.dom.traversal.NodeFilter}
70   * interface.
71   *
72   * @return bitset mainly defined in {@link org.w3c.dom.traversal.NodeFilter}.
73   */
74  public int getWhatToShow()
75  {
76    return m_whatToShow;
77  }
78
79  /**
80   * This attribute determines which node types are accepted.
81   * These constants are defined in the {@link org.w3c.dom.traversal.NodeFilter}
82   * interface.
83   *
84   * @param what bitset mainly defined in {@link org.w3c.dom.traversal.NodeFilter}.
85   */
86  public void setWhatToShow(int what)
87  {
88    m_whatToShow = what;
89  }
90
91  /**
92   * The namespace to be tested for, which may be null.
93   *  @serial
94   */
95  String m_namespace;
96
97  /**
98   * Return the namespace to be tested.
99   *
100   * @return The namespace to be tested for, or {@link #WILD}, or null.
101   */
102  public String getNamespace()
103  {
104    return m_namespace;
105  }
106
107  /**
108   * Set the namespace to be tested.
109   *
110   * @param ns The namespace to be tested for, or {@link #WILD}, or null.
111   */
112  public void setNamespace(String ns)
113  {
114    m_namespace = ns;
115  }
116
117  /**
118   * The local name to be tested for.
119   *  @serial
120   */
121  protected String m_name;
122
123  /**
124   * Return the local name to be tested.
125   *
126   * @return the local name to be tested, or {@link #WILD}, or an empty string.
127   */
128  public String getLocalName()
129  {
130    return (null == m_name) ? "" : m_name;
131  }
132
133  /**
134   * Set the local name to be tested.
135   *
136   * @param name the local name to be tested, or {@link #WILD}, or an empty string.
137   */
138  public void setLocalName(String name)
139  {
140    m_name = name;
141  }
142
143  /**
144   * Statically calculated score for this test.  One of
145   *  {@link #SCORE_NODETEST},
146   *  {@link #SCORE_NONE},
147   *  {@link #SCORE_NSWILD},
148   *  {@link #SCORE_QNAME}, or
149   *  {@link #SCORE_OTHER}.
150   *  @serial
151   */
152  XNumber m_score;
153
154  /**
155   * The match score if the pattern consists of just a NodeTest.
156   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a>
157   */
158  public static final XNumber SCORE_NODETEST =
159    new XNumber(XPath.MATCH_SCORE_NODETEST);
160
161  /**
162   * The match score if the pattern pattern has the form NCName:*.
163   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a>
164   */
165  public static final XNumber SCORE_NSWILD =
166    new XNumber(XPath.MATCH_SCORE_NSWILD);
167
168  /**
169   * The match score if the pattern has the form
170   * of a QName optionally preceded by an @ character.
171   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a>
172   */
173  public static final XNumber SCORE_QNAME =
174    new XNumber(XPath.MATCH_SCORE_QNAME);
175
176  /**
177   * The match score if the pattern consists of something
178   * other than just a NodeTest or just a qname.
179   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a>
180   */
181  public static final XNumber SCORE_OTHER =
182    new XNumber(XPath.MATCH_SCORE_OTHER);
183
184  /**
185   * The match score if no match is made.
186   *  @see <a href="http://www.w3.org/TR/xslt#conflict">XSLT Specification - 5.5 Conflict Resolution for Template Rules</a>
187   */
188  public static final XNumber SCORE_NONE =
189    new XNumber(XPath.MATCH_SCORE_NONE);
190
191  /**
192   * Construct an NodeTest that tests for namespaces and node names.
193   *
194   *
195   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
196   * @param namespace The namespace to be tested.
197   * @param name The local name to be tested.
198   */
199  public NodeTest(int whatToShow, String namespace, String name)
200  {
201    initNodeTest(whatToShow, namespace, name);
202  }
203
204  /**
205   * Construct an NodeTest that doesn't test for node names.
206   *
207   *
208   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
209   */
210  public NodeTest(int whatToShow)
211  {
212    initNodeTest(whatToShow);
213  }
214
215  /**
216   * @see Expression#deepEquals(Expression)
217   */
218  public boolean deepEquals(Expression expr)
219  {
220  	if(!isSameClass(expr))
221  		return false;
222
223  	NodeTest nt = (NodeTest)expr;
224
225  	if(null != nt.m_name)
226  	{
227  		if(null == m_name)
228  			return false;
229  		else if(!nt.m_name.equals(m_name))
230  			return false;
231  	}
232  	else if(null != m_name)
233  		return false;
234
235  	if(null != nt.m_namespace)
236  	{
237  		if(null == m_namespace)
238  			return false;
239  		else if(!nt.m_namespace.equals(m_namespace))
240  			return false;
241  	}
242  	else if(null != m_namespace)
243  		return false;
244
245  	if(m_whatToShow != nt.m_whatToShow)
246  		return false;
247
248  	if(m_isTotallyWild != nt.m_isTotallyWild)
249  		return false;
250
251	return true;
252  }
253
254  /**
255   * Null argument constructor.
256   */
257  public NodeTest(){}
258
259  /**
260   * Initialize this node test by setting the whatToShow property, and
261   * calculating the score that this test will return if a test succeeds.
262   *
263   *
264   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
265   */
266  public void initNodeTest(int whatToShow)
267  {
268
269    m_whatToShow = whatToShow;
270
271    calcScore();
272  }
273
274  /**
275   * Initialize this node test by setting the whatToShow property and the
276   * namespace and local name, and
277   * calculating the score that this test will return if a test succeeds.
278   *
279   *
280   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
281   * @param namespace The namespace to be tested.
282   * @param name The local name to be tested.
283   */
284  public void initNodeTest(int whatToShow, String namespace, String name)
285  {
286
287    m_whatToShow = whatToShow;
288    m_namespace = namespace;
289    m_name = name;
290
291    calcScore();
292  }
293
294  /**
295   * True if this test has a null namespace and a local name of {@link #WILD}.
296   *  @serial
297   */
298  private boolean m_isTotallyWild;
299
300  /**
301   * Get the static score for this node test.
302   * @return Should be one of the SCORE_XXX constants.
303   */
304  public XNumber getStaticScore()
305  {
306    return m_score;
307  }
308
309  /**
310   * Set the static score for this node test.
311   * @param score Should be one of the SCORE_XXX constants.
312   */
313  public void setStaticScore(XNumber score)
314  {
315    m_score = score;
316  }
317
318  /**
319   * Static calc of match score.
320   */
321  protected void calcScore()
322  {
323
324    if ((m_namespace == null) && (m_name == null))
325      m_score = SCORE_NODETEST;
326    else if (((m_namespace == WILD) || (m_namespace == null))
327             && (m_name == WILD))
328      m_score = SCORE_NODETEST;
329    else if ((m_namespace != WILD) && (m_name == WILD))
330      m_score = SCORE_NSWILD;
331    else
332      m_score = SCORE_QNAME;
333
334    m_isTotallyWild = (m_namespace == null && m_name == WILD);
335  }
336
337  /**
338   * Get the score that this test will return if a test succeeds.
339   *
340   *
341   * @return the score that this test will return if a test succeeds.
342   */
343  public double getDefaultScore()
344  {
345    return m_score.num();
346  }
347
348  /**
349   * Tell what node type to test, if not DTMFilter.SHOW_ALL.
350   *
351   * @param whatToShow Bit set defined mainly by
352   *        {@link org.apache.xml.dtm.DTMFilter}.
353   * @return the node type for the whatToShow.  Since whatToShow can specify
354   *         multiple types, it will return the first bit tested that is on,
355   *         so the caller of this function should take care that this is
356   *         the function they really want to call.  If none of the known bits
357   *         are set, this function will return zero.
358   */
359  public static int getNodeTypeTest(int whatToShow)
360  {
361    // %REVIEW% Is there a better way?
362    if (0 != (whatToShow & DTMFilter.SHOW_ELEMENT))
363      return DTM.ELEMENT_NODE;
364
365    if (0 != (whatToShow & DTMFilter.SHOW_ATTRIBUTE))
366      return DTM.ATTRIBUTE_NODE;
367
368    if (0 != (whatToShow & DTMFilter.SHOW_TEXT))
369      return DTM.TEXT_NODE;
370
371    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT))
372      return DTM.DOCUMENT_NODE;
373
374    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_FRAGMENT))
375      return DTM.DOCUMENT_FRAGMENT_NODE;
376
377    if (0 != (whatToShow & DTMFilter.SHOW_NAMESPACE))
378      return DTM.NAMESPACE_NODE;
379
380    if (0 != (whatToShow & DTMFilter.SHOW_COMMENT))
381      return DTM.COMMENT_NODE;
382
383    if (0 != (whatToShow & DTMFilter.SHOW_PROCESSING_INSTRUCTION))
384      return DTM.PROCESSING_INSTRUCTION_NODE;
385
386    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_TYPE))
387      return DTM.DOCUMENT_TYPE_NODE;
388
389    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY))
390      return DTM.ENTITY_NODE;
391
392    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY_REFERENCE))
393      return DTM.ENTITY_REFERENCE_NODE;
394
395    if (0 != (whatToShow & DTMFilter.SHOW_NOTATION))
396      return DTM.NOTATION_NODE;
397
398    if (0 != (whatToShow & DTMFilter.SHOW_CDATA_SECTION))
399      return DTM.CDATA_SECTION_NODE;
400
401
402    return 0;
403  }
404
405
406  /**
407   * Do a diagnostics dump of a whatToShow bit set.
408   *
409   *
410   * @param whatToShow Bit set defined mainly by
411   *        {@link org.apache.xml.dtm.DTMFilter}.
412   */
413  public static void debugWhatToShow(int whatToShow)
414  {
415
416    java.util.Vector v = new java.util.Vector();
417
418    if (0 != (whatToShow & DTMFilter.SHOW_ATTRIBUTE))
419      v.addElement("SHOW_ATTRIBUTE");
420
421    if (0 != (whatToShow & DTMFilter.SHOW_NAMESPACE))
422      v.addElement("SHOW_NAMESPACE");
423
424    if (0 != (whatToShow & DTMFilter.SHOW_CDATA_SECTION))
425      v.addElement("SHOW_CDATA_SECTION");
426
427    if (0 != (whatToShow & DTMFilter.SHOW_COMMENT))
428      v.addElement("SHOW_COMMENT");
429
430    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT))
431      v.addElement("SHOW_DOCUMENT");
432
433    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_FRAGMENT))
434      v.addElement("SHOW_DOCUMENT_FRAGMENT");
435
436    if (0 != (whatToShow & DTMFilter.SHOW_DOCUMENT_TYPE))
437      v.addElement("SHOW_DOCUMENT_TYPE");
438
439    if (0 != (whatToShow & DTMFilter.SHOW_ELEMENT))
440      v.addElement("SHOW_ELEMENT");
441
442    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY))
443      v.addElement("SHOW_ENTITY");
444
445    if (0 != (whatToShow & DTMFilter.SHOW_ENTITY_REFERENCE))
446      v.addElement("SHOW_ENTITY_REFERENCE");
447
448    if (0 != (whatToShow & DTMFilter.SHOW_NOTATION))
449      v.addElement("SHOW_NOTATION");
450
451    if (0 != (whatToShow & DTMFilter.SHOW_PROCESSING_INSTRUCTION))
452      v.addElement("SHOW_PROCESSING_INSTRUCTION");
453
454    if (0 != (whatToShow & DTMFilter.SHOW_TEXT))
455      v.addElement("SHOW_TEXT");
456
457    int n = v.size();
458
459    for (int i = 0; i < n; i++)
460    {
461      if (i > 0)
462        System.out.print(" | ");
463
464      System.out.print(v.elementAt(i));
465    }
466
467    if (0 == n)
468      System.out.print("empty whatToShow: " + whatToShow);
469
470    System.out.println();
471  }
472
473  /**
474   * Two names are equal if they and either both are null or
475   * the name t is wild and the name p is non-null, or the two
476   * strings are equal.
477   *
478   * @param p part string from the node.
479   * @param t target string, which may be {@link #WILD}.
480   *
481   * @return true if the strings match according to the rules of this method.
482   */
483  private static final boolean subPartMatch(String p, String t)
484  {
485
486    // boolean b = (p == t) || ((null != p) && ((t == WILD) || p.equals(t)));
487    // System.out.println("subPartMatch - p: "+p+", t: "+t+", result: "+b);
488    return (p == t) || ((null != p) && ((t == WILD) || p.equals(t)));
489  }
490
491  /**
492   * This is temporary to patch over Xerces issue with representing DOM
493   * namespaces as "".
494   *
495   * @param p part string from the node, which may represent the null namespace
496   *        as null or as "".
497   * @param t target string, which may be {@link #WILD}.
498   *
499   * @return true if the strings match according to the rules of this method.
500   */
501  private static final boolean subPartMatchNS(String p, String t)
502  {
503
504    return (p == t)
505           || ((null != p)
506               && ((p.length() > 0)
507                   ? ((t == WILD) || p.equals(t)) : null == t));
508  }
509
510  /**
511   * Tell what the test score is for the given node.
512   *
513   *
514   * @param xctxt XPath runtime context.
515   * @param context The node being tested.
516   *
517   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
518   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
519   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
520   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
521   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
522   *
523   * @throws javax.xml.transform.TransformerException
524   */
525  public XObject execute(XPathContext xctxt, int context)
526          throws javax.xml.transform.TransformerException
527  {
528
529    DTM dtm = xctxt.getDTM(context);
530    short nodeType = dtm.getNodeType(context);
531
532    if (m_whatToShow == DTMFilter.SHOW_ALL)
533      return m_score;
534
535    int nodeBit = (m_whatToShow & (0x00000001 << (nodeType - 1)));
536
537    switch (nodeBit)
538    {
539    case DTMFilter.SHOW_DOCUMENT_FRAGMENT :
540    case DTMFilter.SHOW_DOCUMENT :
541      return SCORE_OTHER;
542    case DTMFilter.SHOW_COMMENT :
543      return m_score;
544    case DTMFilter.SHOW_CDATA_SECTION :
545    case DTMFilter.SHOW_TEXT :
546
547      // was:
548      // return (!xctxt.getDOMHelper().shouldStripSourceNode(context))
549      //       ? m_score : SCORE_NONE;
550      return m_score;
551    case DTMFilter.SHOW_PROCESSING_INSTRUCTION :
552      return subPartMatch(dtm.getNodeName(context), m_name)
553             ? m_score : SCORE_NONE;
554
555    // From the draft: "Two expanded names are equal if they
556    // have the same local part, and either both have no URI or
557    // both have the same URI."
558    // "A node test * is true for any node of the principal node type.
559    // For example, child::* will select all element children of the
560    // context node, and attribute::* will select all attributes of
561    // the context node."
562    // "A node test can have the form NCName:*. In this case, the prefix
563    // is expanded in the same way as with a QName using the context
564    // namespace declarations. The node test will be true for any node
565    // of the principal type whose expanded name has the URI to which
566    // the prefix expands, regardless of the local part of the name."
567    case DTMFilter.SHOW_NAMESPACE :
568    {
569      String ns = dtm.getLocalName(context);
570
571      return (subPartMatch(ns, m_name)) ? m_score : SCORE_NONE;
572    }
573    case DTMFilter.SHOW_ATTRIBUTE :
574    case DTMFilter.SHOW_ELEMENT :
575    {
576      return (m_isTotallyWild || (subPartMatchNS(dtm.getNamespaceURI(context), m_namespace) && subPartMatch(dtm.getLocalName(context), m_name)))
577             ? m_score : SCORE_NONE;
578    }
579    default :
580      return SCORE_NONE;
581    }  // end switch(testType)
582  }
583
584  /**
585   * Tell what the test score is for the given node.
586   *
587   *
588   * @param xctxt XPath runtime context.
589   * @param context The node being tested.
590   *
591   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
592   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
593   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
594   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
595   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
596   *
597   * @throws javax.xml.transform.TransformerException
598   */
599  public XObject execute(XPathContext xctxt, int context,
600                         DTM dtm, int expType)
601          throws javax.xml.transform.TransformerException
602  {
603
604    if (m_whatToShow == DTMFilter.SHOW_ALL)
605      return m_score;
606
607    int nodeBit = (m_whatToShow & (0x00000001
608                   << ((dtm.getNodeType(context)) - 1)));
609
610    switch (nodeBit)
611    {
612    case DTMFilter.SHOW_DOCUMENT_FRAGMENT :
613    case DTMFilter.SHOW_DOCUMENT :
614      return SCORE_OTHER;
615    case DTMFilter.SHOW_COMMENT :
616      return m_score;
617    case DTMFilter.SHOW_CDATA_SECTION :
618    case DTMFilter.SHOW_TEXT :
619
620      // was:
621      // return (!xctxt.getDOMHelper().shouldStripSourceNode(context))
622      //       ? m_score : SCORE_NONE;
623      return m_score;
624    case DTMFilter.SHOW_PROCESSING_INSTRUCTION :
625      return subPartMatch(dtm.getNodeName(context), m_name)
626             ? m_score : SCORE_NONE;
627
628    // From the draft: "Two expanded names are equal if they
629    // have the same local part, and either both have no URI or
630    // both have the same URI."
631    // "A node test * is true for any node of the principal node type.
632    // For example, child::* will select all element children of the
633    // context node, and attribute::* will select all attributes of
634    // the context node."
635    // "A node test can have the form NCName:*. In this case, the prefix
636    // is expanded in the same way as with a QName using the context
637    // namespace declarations. The node test will be true for any node
638    // of the principal type whose expanded name has the URI to which
639    // the prefix expands, regardless of the local part of the name."
640    case DTMFilter.SHOW_NAMESPACE :
641    {
642      String ns = dtm.getLocalName(context);
643
644      return (subPartMatch(ns, m_name)) ? m_score : SCORE_NONE;
645    }
646    case DTMFilter.SHOW_ATTRIBUTE :
647    case DTMFilter.SHOW_ELEMENT :
648    {
649      return (m_isTotallyWild || (subPartMatchNS(dtm.getNamespaceURI(context), m_namespace) && subPartMatch(dtm.getLocalName(context), m_name)))
650             ? m_score : SCORE_NONE;
651    }
652    default :
653      return SCORE_NONE;
654    }  // end switch(testType)
655  }
656
657  /**
658   * Test the current node to see if it matches the given node test.
659   *
660   * @param xctxt XPath runtime context.
661   *
662   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
663   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
664   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
665   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
666   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
667   *
668   * @throws javax.xml.transform.TransformerException
669   */
670  public XObject execute(XPathContext xctxt)
671          throws javax.xml.transform.TransformerException
672  {
673    return execute(xctxt, xctxt.getCurrentNode());
674  }
675
676  /**
677   * Node tests by themselves do not need to fix up variables.
678   */
679  public void fixupVariables(java.util.Vector vars, int globalsSize)
680  {
681    // no-op
682  }
683
684  /**
685   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
686   */
687  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
688  {
689  	assertion(false, "callVisitors should not be called for this object!!!");
690  }
691
692}
693