KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ws > jaxme > xs > XPathMatcher


1 /*
2  * Copyright 2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15
16  */

17 package org.apache.ws.jaxme.xs;
18
19 import java.util.ArrayList JavaDoc;
20 import java.util.HashSet JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.Set JavaDoc;
24
25 import org.apache.ws.jaxme.xs.parser.impl.LocSAXException;
26 import org.apache.ws.jaxme.xs.xml.XsEField;
27 import org.apache.ws.jaxme.xs.xml.XsESelector;
28 import org.apache.ws.jaxme.xs.xml.XsQName;
29 import org.apache.ws.jaxme.xs.xml.XsTKeybase;
30 import org.xml.sax.Locator JavaDoc;
31 import org.xml.sax.SAXException JavaDoc;
32
33 /**
34  * An XPath matcher. Implements the restricted subset of XPath as defined
35  * by the XML Schema.
36  *
37  * @author <a HREF="mailto:mrck1996@yahoo.co.uk">Chris Kirk</a>
38  */

39 public final class XPathMatcher {
40   private static final XSElementOrAttrRef[] NO_MATCHES
41     = new XSElementOrAttrRef[] {};
42
43   /**
44    * The matching of elements and attributes works by walking a finite
45    * state automata produced by parsing the xpath. There is one more
46    * automata than |'s in the xpath, the first node of each automata is
47    * stored in this array.
48    */

49   private final InternalNode[] _stateMachineRoots;
50   private final Locator JavaDoc _saxLocator;
51
52   private XPathMatcher( Locator JavaDoc locator, InternalNode[] roots ) {
53     _saxLocator = locator;
54     _stateMachineRoots = roots;
55   }
56
57   /**
58    * Matches every element and attribute referenced by the specified
59    * keybase.<p>
60    *
61    * The result is a two dimensional array, the first dimension corresponds to
62    * each xs:field used to declare the constraint. The second dimension is
63    * for each 'or' used within the fields xpath query. <p>
64    *
65    * Only tags and attributes that were matched by the xpath will be in the
66    * result, any xpath that fails to match anything will not be stored
67    * in this array.<p>
68    */

69   public static XSElementOrAttrRef[][] match(
70     XsTKeybase keybase,
71     XSElement startingNode
72   )
73     throws SAXException
74   {
75     XsESelector selector = keybase.getSelector();
76     XsEField[] fields = keybase.getFields();
77
78     String JavaDoc selectorXPath = selector.getXpath().getToken();
79     XPathMatcher baseMatcher = XPathMatcher.parse(
80       selector.getLocator(),
81       selectorXPath,
82       true
83     );
84
85     XSElementOrAttrRef[] baseElements = baseMatcher.match( startingNode );
86
87     final int numBaseElements = baseElements.length;
88     final int numFields = fields.length;
89     XSElementOrAttrRef[][] results = new XSElementOrAttrRef[numFields][];
90
91
92     // populate the results, the first dimension is indexed by 'field'
93
for ( int i=0; i<numFields; i++ ) {
94       XsEField field = fields[i];
95       String JavaDoc fieldXPath = field.getXpath().getToken();
96
97       XPathMatcher fieldMatcher = XPathMatcher.parse(
98         field.getLocator(),
99         fieldXPath,
100         false
101       );
102
103       Set JavaDoc matches = new HashSet JavaDoc(3);
104
105       // run the xpath for each of the elements identified by the selector,
106
// referred to as the base elements. The values saved in matches then
107
// form the second dimension of the result.
108
for (
109         int baseElementIndex=0;
110         baseElementIndex<numBaseElements;
111         baseElementIndex++
112       ) {
113         fieldMatcher.match(
114           baseElements[baseElementIndex].getElement(),
115           matches
116         );
117       }
118
119       int numResults = matches.size();
120       results[i] = (XSElementOrAttrRef[]) matches.toArray(
121         new XSElementOrAttrRef[numResults]
122       );
123     }
124
125     return results;
126   }
127
128
129
130
131   /**
132    * Create an XPathMatcher. Parses a string holding a restricted subset
133    * of XPath and returns an object that knows how to walk XSElement
134    * objects based on that XPath.
135    *
136    * @param elementsOnly True if the xpath is not allowed to match attributes.
137    */

138   public static XPathMatcher parse(
139     Locator JavaDoc locator,
140     String JavaDoc xpath,
141     boolean elementsOnly
142   )
143     throws SAXException
144   {
145     Tokenizer tokenizer = new Tokenizer( locator, xpath );
146     List JavaDoc automatasList = new ArrayList JavaDoc(3);
147
148     InternalNode automata = createNextAutomata( tokenizer, elementsOnly );
149     while ( automata != null ) {
150       automatasList.add( automata );
151       automata = createNextAutomata( tokenizer, elementsOnly );
152     }
153
154     int numAutomatas = automatasList.size();
155     InternalNode[] automataRoots = new InternalNode[numAutomatas];
156     automataRoots = (InternalNode[]) automatasList.toArray( automataRoots );
157
158     return new XPathMatcher( locator, automataRoots );
159   }
160
161   /**
162    * Return the elements and attributes matched by this xpath when applied
163    * from the specified starting node.
164    */

165   public XSElementOrAttrRef[] match( XSElement startingNode )
166     throws SAXException
167   {
168     Set JavaDoc matches = new HashSet JavaDoc(5);
169
170     match( startingNode, matches );
171
172     int numMatches = matches.size();
173     if ( numMatches == 0 ) {
174       return NO_MATCHES;
175     }
176
177     XSElementOrAttrRef[] matchesArray = new XSElementOrAttrRef[numMatches];
178
179     return (XSElementOrAttrRef[]) matches.toArray( matchesArray );
180   }
181
182   /**
183    * Return the elements and attributes matched by this xpath when applied
184    * from the specified starting node.
185    */

186   public void match(XSElement startingNode, Set JavaDoc matches) throws SAXException {
187     InternalNode[] roots = _stateMachineRoots;
188     int numRoots = roots.length;
189
190     for ( int i=0; i<numRoots; i++ ) {
191       roots[i].match( startingNode, matches );
192     }
193   }
194
195
196
197 //
198
// Below is the parser used to create the finate state automatas.
199
//
200

201
202   /**
203    * Called by #parse to create a new automata. If there are n |'s in an
204    * xpath then there will be 1+n automatas generated. An automata is
205    * used to walk over the XSElement objects, with the goal of creating
206    * and XSElementOrAttrRef object for each match defined by the original
207    * xpath.
208    */

209   private static InternalNode createNextAutomata(
210     Tokenizer tokenizer,
211     boolean elementsOnly
212   )
213     throws SAXException
214   {
215     InternalNode rootNode = createAnEntityNode( tokenizer, elementsOnly );
216
217     if ( rootNode != null ) {
218       appendASeperatorNode( tokenizer, rootNode, elementsOnly );
219     }
220
221     return rootNode;
222   }
223
224   /**
225    * Called when the next token in the xpath <b>must</b> refer to either
226    * an attribute or an element. Called by #createNextAutomata and
227    * #appendAnEntityNode.
228    */

229   private static InternalNode createAnEntityNode(
230     Tokenizer tokenizer,
231     boolean elementsOnly
232   ) throws SAXException {
233     Token token = tokenizer.next();
234   
235     switch ( token.getTokenCode() ) {
236       case Tokenizer.ELEMENT_TOKEN:
237         return new NamedChildElementNode(
238           token.getNameSpace(),
239           token.getLabel()
240         );
241       case Tokenizer.ATTR_TOKEN:
242         if ( elementsOnly ) {
243           tokenizer.throwException(
244             "No references to an attribute are allowed here."
245           );
246         }
247
248         return new StaticAttributeNode(token.getNameSpace(), token.getLabel());
249       case Tokenizer.THIS_TOKEN:
250         return new ThisNode();
251       case Tokenizer.ALL_CHILDREN_TOKEN:
252         return new AllChildrenNode( token.getNameSpace() );
253       case Tokenizer.END_TOKEN:
254         return null;
255       default:
256         tokenizer.throwException(
257           "Read '" + token.getImage() + "' when a reference to either an "
258           + "attribute or an element was expected."
259         );
260     }
261
262
263     //assert false : "unreachable code reached";
264
//This return is only here to keep the compiler happy..
265
return null;
266   }
267
268   /**
269    * Given the current state of the automata (previousNode), append a
270    * new node that will match either an attribute or an element.
271    */

272   private static void appendAnEntityNode(
273     Tokenizer tokenizer,
274     InternalNode previousNode,
275     boolean elementsOnly
276   ) throws SAXException {
277     InternalNode newNode = createAnEntityNode( tokenizer, elementsOnly );
278
279     // has the end of the xpath been reached?
280
if ( newNode != null ) {
281       previousNode.setNextNode( newNode );
282       appendASeperatorNode( tokenizer, newNode, elementsOnly );
283     }
284   }
285
286   /**
287    * The automata is made up only of nodes that match either 1 attribute
288    * or 1+ elements. However the xpath string separates these references in
289    * a number of ways, this function will part those separators and
290    * choose between ending the automata or parsing the next attribute/element
291    * reference.
292    */

293   private static void appendASeperatorNode(
294     Tokenizer tokenizer,
295     InternalNode previousNode,
296     boolean elementsOnly
297   ) throws SAXException {
298     Token token = tokenizer.next();
299
300     switch ( token.getTokenCode() ) {
301       case Tokenizer.SEPARATOR_TOKEN:
302         appendAnEntityNode( tokenizer, previousNode, elementsOnly );
303         return;
304       case Tokenizer.OR_TOKEN:
305         return;
306       case Tokenizer.END_TOKEN:
307         return;
308       case Tokenizer.ALL_DESCENDANTS_TOKEN:
309         InternalNode newNode = new AllDescendantsNode( token.getNameSpace() );
310         previousNode.setNextNode( newNode );
311         appendAnEntityNode( tokenizer, newNode, elementsOnly );
312         return;
313       default:
314         tokenizer.throwException(
315           "Unexpected string '" + token.getImage() + "' encountered. Expected"
316           + " either | / or nothing."
317         );
318     }
319   }
320
321
322
323
324 //
325
// Below is the lexical code. Breaks the xpath down into its component parts.
326
// Used by #parse to create the matcher (or finate state automata).
327
//
328

329
330   /**
331    * Breaks the input xpath down into its basic parts.
332    */

333   private static final class Tokenizer {
334     public static final int ELEMENT_TOKEN = 0;
335     public static final int THIS_TOKEN = 1;
336     public static final int ATTR_TOKEN = 2;
337     public static final int ALL_CHILDREN_TOKEN = 3;
338     public static final int ALL_DESCENDANTS_TOKEN = 4;
339     public static final int SEPARATOR_TOKEN = 5;
340     public static final int OR_TOKEN = 6;
341     public static final int END_TOKEN = 7;
342
343     public static final CharHandler STARTING_HANDLER =new InitialCharHandler();
344     public static final CharHandler ATTRIBUTE_HANDLER
345       = new AttributeCharHandler();
346     public static final CharHandler ELEMENT_HANDLER = new ElementCharHandler();
347
348     private final String JavaDoc _xpath;
349     private final Locator JavaDoc _saxLocator;
350
351     private int _pos;
352
353     public Tokenizer( Locator JavaDoc locator, String JavaDoc xpath ) {
354       _xpath = xpath;
355       _saxLocator = locator;
356     }
357
358     public boolean hasNext() {
359       return _pos < _xpath.length();
360     }
361
362     /**
363      * Fetches the next token from the input xpath.
364      */

365     public Token next() throws SAXException {
366       TokenizerState context = new TokenizerState();
367       while ( context.hasNext() ) {
368         CharHandler ch = context.getCharHandler();
369
370         ch.process( context );
371       }
372
373       _pos = context.getPos();
374
375       return context.createToken();
376     }
377
378     public void throwException( String JavaDoc msg ) throws SAXException {
379       throw new LocSAXException( msg, _saxLocator );
380     }
381
382     private class TokenizerState {
383       private final int _startPos;
384
385       private int _pos;
386       private int _markedPos;
387       private int _tokenCode = -1;
388       private String JavaDoc _nameSpace;
389       private String JavaDoc _label;
390
391       private CharHandler _charHandler = STARTING_HANDLER;
392
393       public TokenizerState() {
394         _startPos = Tokenizer.this._pos;
395         _pos = Tokenizer.this._pos;
396         _markedPos = Tokenizer.this._pos;
397       }
398
399       public int getPos() {
400         return _pos;
401       }
402
403       public CharHandler getCharHandler() {
404         return _charHandler;
405       }
406
407       public void setCharHandler( CharHandler ch ) {
408         _charHandler = ch;
409       }
410
411       public boolean hasMatch() {
412         return _tokenCode >= 0;
413       }
414
415       public boolean hasNext() {
416         boolean boo = !hasMatch() && this._pos < _xpath.length();
417
418         return boo;
419       }
420
421       public char peekAhead() {
422         return Tokenizer.this._xpath.charAt( _pos );
423       }
424
425       public char scrollAhead() {
426         return Tokenizer.this._xpath.charAt( _pos++ );
427       }
428
429       // TODO skipWhiteSpace and skipOverIdentifier are almost identical,
430
// consider pulling the internals of them out into a reusable utility.
431

432       public void skipWhiteSpace() {
433         final String JavaDoc xpath = Tokenizer.this._xpath;
434         final int maxPos = xpath.length();
435
436         int pos = _pos;
437         while ( pos < maxPos ) {
438           char ch = xpath.charAt( pos );
439
440           if ( Character.isWhitespace(ch) ) {
441             pos++;
442           } else {
443             break;
444           }
445         }
446
447         _pos = pos;
448       }
449
450       public void skipOverIdentifier() {
451         final String JavaDoc xpath = Tokenizer.this._xpath;
452         final int maxPos = xpath.length();
453
454         int pos = _pos;
455         while ( pos < maxPos ) {
456           char ch = xpath.charAt( pos );
457
458           if ( Character.isLetterOrDigit(ch) || ch == '_' || ch == '-' ) {
459             pos++;
460           } else {
461             break;
462           }
463         }
464
465         _pos = pos;
466       }
467
468       public void setTokenCode( int tokenCode ) {
469         _tokenCode = tokenCode;
470       }
471
472       public void markPos() {
473         _markedPos = _pos;
474       }
475
476       public void saveNameSpace() {
477         _nameSpace = Tokenizer.this._xpath.substring( _markedPos, _pos );
478         _markedPos = _pos;
479       }
480
481       public void saveLabel() {
482         _label = Tokenizer.this._xpath.substring( _markedPos, _pos );
483         _markedPos = _pos;
484       }
485
486       public Token createToken() {
487         if ( !hasMatch() && !hasNext() ) {
488           _tokenCode = END_TOKEN;
489         }
490
491         return new Token(
492           _tokenCode,
493           _nameSpace,
494           _label,
495           _xpath.substring( _startPos, _pos )
496         );
497       }
498
499       public void throwException( String JavaDoc msg ) throws SAXException {
500         Tokenizer.this.throwException( msg );
501       }
502     }
503
504     /**
505      * Base class for the 'state' classes that process the xpath. The main
506      * loop in Tokenizer#next keeps asking the current char handler to
507      * process the context (current state of the tokenizer) until either
508      * the char handler decides to hand over to another char handler, the
509      * end of the xpath string is reached or a match is reached inwhich case
510      * a token will be generated by Tokenizer#next.
511      */

512     private static abstract class CharHandler {
513       public abstract void process( TokenizerState context )
514         throws SAXException;
515     }
516
517     /**
518      * The first char handler invoked for each new token. Does not know what
519      * token to expect, so does its best to handle the simple one or two char
520      * constant tokens (eg . * / // |) but will delegate to another char
521      * handler for the more complicated composite tokens such as @ns:foo,
522      * foo, ns:foo, ns:* etc.
523      */

524     private static final class InitialCharHandler extends CharHandler {
525       public void process( TokenizerState context )
526         throws SAXException
527       {
528         // assert context.hasNext();
529
context.skipWhiteSpace();
530
531         if ( !context.hasNext() ) {
532           return;
533         }
534
535         char ch = context.peekAhead();
536         switch ( ch ) {
537           case '*':
538             context.scrollAhead();
539             context.setTokenCode( Tokenizer.ALL_CHILDREN_TOKEN );
540             break;
541           case '.':
542             context.scrollAhead();
543             context.setTokenCode( Tokenizer.THIS_TOKEN );
544             break;
545           case '/':
546             context.scrollAhead();
547             if ( context.peekAhead() == '/' ) {
548               context.scrollAhead();
549               context.setTokenCode( Tokenizer.ALL_DESCENDANTS_TOKEN );
550             } else {
551               context.setTokenCode( Tokenizer.SEPARATOR_TOKEN );
552             }
553
554             break;
555           case '|':
556             context.scrollAhead();
557             context.setTokenCode( Tokenizer.OR_TOKEN );
558             break;
559           case '@':
560             context.scrollAhead();
561             context.markPos();
562             context.setCharHandler( ATTRIBUTE_HANDLER );
563             break;
564           default:
565             context.setCharHandler( ELEMENT_HANDLER );
566         }
567       }
568     }
569
570     /**
571      * Accepts elementName, ns:*, ns:elementName
572      */

573     private static final class ElementCharHandler extends CharHandler {
574       public void process( TokenizerState context )
575         throws SAXException
576       {
577         context.skipOverIdentifier();
578
579         if ( context.hasNext() ) {
580           if ( context.peekAhead() == ':' ) {
581             context.saveNameSpace();
582             context.scrollAhead();
583
584             if ( context.hasNext() ) {
585               if ( context.peekAhead() == '*' ) {
586                 context.scrollAhead();
587                 context.setTokenCode( Tokenizer.ALL_CHILDREN_TOKEN );
588
589                 return;
590               }
591             }
592
593             context.markPos();
594             context.skipOverIdentifier();
595           }
596         }
597
598         context.saveLabel();
599         context.setTokenCode( Tokenizer.ELEMENT_TOKEN );
600       }
601     }
602
603     /**
604      * Accepts fieldName, or ns:fieldName.
605      */

606     private static final class AttributeCharHandler extends CharHandler {
607       public void process( TokenizerState context )
608         throws SAXException
609       {
610         context.skipOverIdentifier();
611
612         if ( context.hasNext() && context.peekAhead() == ':' ) {
613           context.saveNameSpace();
614           context.scrollAhead();
615           context.markPos();
616           context.skipOverIdentifier();
617         }
618
619         context.saveLabel();
620         context.setTokenCode( Tokenizer.ATTR_TOKEN );
621       }
622     }
623   }
624
625   /**
626    * Represents a basic part of the xpath input. These tokens are
627    * created by the Tokenizer.
628    */

629   private static final class Token {
630     private final int _tokenCode;
631
632     /**
633      * The name of the name space, or null if the match does not belong to
634      * a name space.
635      */

636     private final String JavaDoc _nameSpace;
637
638     /**
639      * The local name of the element/field that was matched.
640      */

641     private final String JavaDoc _label;
642
643     /**
644      * The entire matched string in its raw form.
645      */

646     private final String JavaDoc _image;
647
648     public Token(int tokenCode, String JavaDoc nameSpace, String JavaDoc label, String JavaDoc image) {
649       _tokenCode = tokenCode;
650       _nameSpace = nameSpace;
651       _label = label;
652       _image = image;
653     }
654
655     public int getTokenCode() {
656       return _tokenCode;
657     }
658
659     public String JavaDoc getNameSpace() {
660       return _nameSpace;
661     }
662
663     public String JavaDoc getLabel() {
664       return _label;
665     }
666
667     public String JavaDoc getImage() {
668       return _image;
669     }
670   }
671
672
673
674 //
675
// Below is the finate state automata used to match the xpath with
676
// XSElements and XSAttributes. Generated by the #parse method and executed
677
// by the #match method.
678
//
679

680
681   /**
682    * The xpath is converted into a small finite state machine. This interface
683    * is the base type of each node within the generated graph.
684    */

685   private abstract static class InternalNode {
686     private InternalNode _next;
687
688     /**
689      * Applies the action associated with this node to the current element.
690      * If the node decides that the currentElement (or attribute or the
691      * element) is a match then a XSElementOrAttrRef will be added to the
692      * matches list. If the match is incomplete then this node will
693      * invoke match on other nodes within the automata. If there is no
694      * match at all then this node will exit immediately.
695      *
696      * @param currentElement The element that is the focus of the match.
697      * @param matches The list that collects all of the elements/attributes
698      * that reach the end of the finite state machine.
699      */

700     public abstract void match( XSElement currentElement, Set JavaDoc matches )
701       throws SAXException;
702
703     /**
704      * Link this node with another node. Due to the restrictions made to
705      * the valid XPath usable within the XML Schema have very simple graphs
706      * where each node links to at most one other node.
707      */

708     public final void setNextNode( InternalNode next ) {
709       _next = next;
710     }
711
712     /**
713      * Carry on to the next node in the graph. If there is no other
714      * node then it means that this element is a match and should be
715      * added to the matches list before returning.
716      */

717     protected final void continueSearchFor(
718       XSElement currentElement,
719       Set JavaDoc matches
720     ) throws SAXException {
721       InternalNode next = _next;
722
723       if ( next == null ) {
724         matches.add( new XSElementOrAttrRef(currentElement) );
725       } else {
726         next.match( currentElement, matches );
727       }
728     }
729
730     /**
731      * A utility method that checks whether the specified namespace/name
732      * matches the XsQName.
733      */

734     protected boolean doesMatch(String JavaDoc nameSpace, String JavaDoc name, XsQName qName) {
735       boolean boo = doesNSMatch(nameSpace, qName)
736         && name.equals( qName.getLocalName() );
737
738       return boo;
739     }
740
741     /**
742      * A utility method that checks whether the specified namespace/name
743      * matches the XsQName.
744      */

745     protected boolean doesNSMatch(String JavaDoc nameSpace, XsQName qName) {
746       if ( nameSpace == null ) {
747         return qName.getPrefix() == null;
748       } else {
749         return nameSpace.equals( qName.getPrefix() );
750       }
751     }
752
753     // TODO consider moving this functionality into the XSElement object
754
protected Iterator JavaDoc getChildrenIteratorFor( XSElement element )
755       throws SAXException
756     {
757       List JavaDoc children = new ArrayList JavaDoc(5);
758
759       XSType type = element.getType();
760
761       if ( !type.isSimple() ) {
762         XSComplexType complexType = type.getComplexType();
763
764         if ( !complexType.isEmpty() ) {
765           XSParticle particle = complexType.getParticle();
766
767           if ( particle.isElement() ) {
768             children.add( particle.getElement() );
769           } else if ( particle.isGroup() ) {
770             XSGroup group = particle.getGroup();
771
772             XSParticle[] particles = group.getParticles();
773             int numParticles = particles.length;
774
775             for ( int i=0; i<numParticles; i++ ) {
776               XSParticle groupedParticle = particles[i];
777
778               if ( groupedParticle.isElement() ) {
779                 children.add( groupedParticle.getElement() );
780               }
781             }
782           }
783         }
784       }
785
786       return children.iterator();
787     }
788   }
789
790   /**
791    * This state represents a . within the xpath.
792    */

793   private static final class ThisNode extends InternalNode {
794     /**
795      * Use this singleton when this node appears as the last of the matching
796      * criteria.
797      */

798     public static final ThisNode LEAF_INSTANCE = new ThisNode();
799
800     public void match( XSElement currentElement, Set JavaDoc matches )
801       throws SAXException
802     {
803       continueSearchFor( currentElement, matches );
804     }
805   }
806
807   /**
808    * Represents a constant within the xpath that matches an elements name.
809    */

810   private static final class NamedChildElementNode extends InternalNode {
811     private final String JavaDoc _nameSpace;
812     private final String JavaDoc _name;
813
814     /**
815      * @param nameSpace The XML name space required for the matching element.
816      * Null means no name space.
817      * @param name The name of the XSElement that will be matched. The name
818      * is not nullable.
819      */

820     public NamedChildElementNode( String JavaDoc nameSpace, String JavaDoc name ) {
821       _nameSpace = nameSpace;
822       _name = name;
823     }
824
825     public void match( XSElement currentElement, Set JavaDoc matches )
826       throws SAXException
827     {
828       Iterator JavaDoc iterator = getChildrenIteratorFor( currentElement );
829
830       while ( iterator.hasNext() ) {
831         XSElement element = (XSElement) iterator.next();
832
833         if ( doesMatch(_nameSpace, _name, element.getName()) ) {
834           continueSearchFor( element, matches );
835
836           // there can only be one match..
837
break;
838         }
839       }
840     }
841   }
842
843   /**
844    * Represents a constant within the xpath that matches an attributes name.
845    */

846   private static final class StaticAttributeNode extends InternalNode {
847     private final String JavaDoc _nameSpace;
848     private final String JavaDoc _name;
849
850     /**
851      * @param nameSpace The XML name space required for the matching element.
852      * Null means no name space.
853      * @param name The name of the XSElement that will be matched. The name
854      * is not nullable.
855      */

856     public StaticAttributeNode( String JavaDoc nameSpace, String JavaDoc name ) {
857       _nameSpace = nameSpace;
858       _name = name;
859     }
860
861     public void match( XSElement currentElement, Set JavaDoc matches )
862       throws SAXException
863     {
864       //assert getNextNode() == null;
865
XSType type = currentElement.getType();
866
867       if ( !type.isSimple() ) {
868         XSComplexType complexType = type.getComplexType();
869         XSAttributable[] attributables = complexType.getAttributes();
870         int numAttribables = attributables.length;
871
872         // TODO consider moving this functionality onto the XSElement object
873
for ( int i=0; i<numAttribables; i++ ) {
874           XSAttributable attributable = attributables[i];
875
876           if ( attributable instanceof XSAttribute ) {
877             XSAttribute attribute = (XSAttribute) attributable;
878
879             if ( doesMatch(_nameSpace, _name, attribute.getName()) ) {
880               matches.add( new XSElementOrAttrRef(attribute) );
881
882               return;
883             }
884           }
885         }
886       }
887     }
888   }
889
890   /**
891    * This node matches all elements that are children of the current element.
892    */

893   private static final class AllChildrenNode extends InternalNode {
894     private final String JavaDoc _nameSpace;
895
896     public AllChildrenNode( String JavaDoc nameSpace ) {
897       _nameSpace = nameSpace;
898     }
899
900     public void match( XSElement currentElement, Set JavaDoc matches )
901       throws SAXException
902     {
903       String JavaDoc nameSpace = _nameSpace;
904       Iterator JavaDoc iterator = getChildrenIteratorFor( currentElement );
905
906       while ( iterator.hasNext() ) {
907         XSElement element = (XSElement) iterator.next();
908
909         if ( doesNSMatch(nameSpace, element.getName()) ) {
910           continueSearchFor( element, matches );
911         }
912       }
913     }
914   }
915
916   /**
917    * This node matches every element that is below the current element.
918    */

919   private static final class AllDescendantsNode extends InternalNode {
920     private final String JavaDoc _nameSpace;
921
922     public AllDescendantsNode( String JavaDoc nameSpace ) {
923       _nameSpace = nameSpace;
924     }
925
926     public void match( XSElement currentElement, Set JavaDoc matches )
927       throws SAXException
928     {
929       String JavaDoc nameSpace = _nameSpace;
930       Iterator JavaDoc iterator = getChildrenIteratorFor( currentElement );
931
932       while ( iterator.hasNext() ) {
933         XSElement element = (XSElement) iterator.next();
934
935         if ( doesNSMatch(nameSpace, element.getName()) ) {
936           continueSearchFor( element, matches );
937
938           // go recursive
939
this.match( element, matches );
940         }
941       }
942     }
943   }
944 }
945
Popular Tags