Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/aendvari/common/model/ModelQueryPath.java


1   /*
2    * ModelQueryPath.java
3    *
4    * Copyright (c) 2001, 2002 Aendvari, Ltd. All Rights Reserved.
5    *
6    * See the file LICENSE for terms of use.
7    *
8    */
9   
10  package com.aendvari.common.model;
11  
12  import java.util.ArrayList;
13  import java.util.Collection;
14  import java.util.Iterator;
15  import java.util.List;
16  import java.util.StringTokenizer;
17  
18  /**
19   * <p>Provides the ability to access nodes within an Model Tree using
20   * an expression.</p>
21   *
22   * <p>This class allows the selection of single and multiple nodes.</p>
23   *
24   * @author  Trevor Milne
25   *
26   */
27  
28  public class ModelQueryPath
29  {
30    /* Attributes. */
31  
32  
33    /** Contains the compiled query expression. */
34    private List operations;
35  
36  
37    /* Constructors. */
38  
39  
40    /**
41     * Constructs an <code>ModelQueryPath</code> instance.
42     *
43     * @param    expression          Query expression for node selection.
44     *
45     */
46  
47    public ModelQueryPath(String expression)
48    {
49      // compile the query expression
50      compile(expression);
51    }
52  
53  
54    /* Expressions. */
55  
56  
57    /**
58     * Selects a single node using the compiled expression.
59     *
60     * @param    context            The context node specifying the root of the expression.
61     *
62     * @return                  The node found using the expression, null if a node cannot be found.
63     *
64     */
65  
66    public ModelNode selectNode(ModelNode context)
67    {
68      // select node using expression
69      return selectNode(context, 0);
70    }
71  
72    /**
73     * Selects multiple nodes using the compiled expression.
74     *
75     * @param    context            The context node specifying the root of the expression.
76     *
77     * @return                  A <code>Collection</code> of the selected {@link ModelNode ModelNodes}. Will not be null.
78     *
79     */
80  
81    public Collection selectNodes(ModelNode context)
82    {
83      ArrayList nodes = new ArrayList();
84  
85      // select nodes
86      selectNodes(context, 0, nodes);
87  
88      return nodes;
89    }
90  
91    /**
92     * Uses the expression to select a single node.
93     *
94     * @param    context            The context node specifying the root of the expression.
95     * @param    expression          Query expression to select the node.
96     *
97     * @return                  The node found using the expression, null if a node cannot be found.
98     *
99     */
100 
101   public static ModelNode selectNode(ModelNode context, String expression)
102   {
103     ModelQueryPath query = new ModelQueryPath(expression);
104 
105     return query.selectNode(context);
106   }
107 
108   /**
109    * Uses the expression to select multiple nodes.
110    *
111    * @param    context            The context node specifying the root of the expression.
112    * @param    expression          Query expression to select the nodes.
113    *
114    * @return                  A <code>Collection</code> of the selected {@link ModelNode ModelNodes}. Can not be null.
115    *
116    */
117 
118   public static Collection selectNodes(ModelNode context, String expression)
119   {
120     ModelQueryPath query = new ModelQueryPath(expression);
121 
122     return query.selectNodes(context);
123   }
124 
125 
126   /* Single selection */
127 
128 
129   /**
130    * Selects a child node based on its name.
131    *
132    * @param    operator          The current operation.
133    * @param    context            The node being searched.
134    * @param    operationIndex        The current operation.
135    *
136    */
137 
138   protected ModelNode selectNodeByName(Operator operator, ModelNode context, int operationIndex)
139   {
140     String segment = operator.stringParameter;
141     boolean any = segment.equals("*");
142 
143     // get array of children
144     Collection childNodes = context.getChildNodes();
145     ModelNode children[] = new ModelNode[childNodes.size()];
146     childNodes.toArray(children);
147 
148     // search children for name
149     int childIndex;
150     for (childIndex = 0; childIndex < children.length; childIndex++)
151     {
152       ModelNode child = children[childIndex];
153 
154       // pick either the first in the group, or the first name match
155       if (any || child.getNodeName().equals(segment))
156       {
157         // continue evaluating expression on the selected node
158         return selectNode(child, (operationIndex + 1));
159       }
160     }
161 
162     // no match found
163     return null;
164   }
165 
166   /**
167    * Selects a child node based on its position.
168    *
169    * @param    operator          The current operation.
170    * @param    context            The node being searched.
171    * @param    operationIndex        The current operation.
172    *
173    */
174 
175   protected ModelNode selectNodeByIndex(Operator operator, ModelNode context, int operationIndex)
176   {
177     String segment = operator.stringParameter;
178 
179     // find the n'th node
180     int nodeIndex = 1;
181 
182     // get array of children
183     Collection childNodes = context.getChildNodes();
184     ModelNode children[] = new ModelNode[childNodes.size()];
185     childNodes.toArray(children);
186 
187     int childIndex;
188     for (childIndex = 0; childIndex < children.length; childIndex++)
189     {
190       ModelNode child = children[childIndex];
191 
192       // check each node matching the segment
193       if (child.getNodeName().equals(segment))
194       {
195         // continue until the n'th named node is found
196         if (nodeIndex == operator.intParameter)
197         {
198           // continue evaluating expression on the selected node
199           return selectNode(child, (operationIndex + 1));
200         }
201 
202         nodeIndex++;
203       }
204     }
205 
206     // no match found
207     return null;
208   }
209 
210   /**
211    * Performs the query expression operations for a single node selection.
212    *
213    * @param    context            The node being searched.
214    * @param    operationIndex        The current operation.
215    *
216    */
217 
218   protected ModelNode selectNode(ModelNode context, int operationIndex)
219   {
220     // if at the end of the expression, select the context node
221     if (operationIndex == operations.size())
222     {
223       return context;
224     }
225 
226     // get the next operator
227     Operator operator = (Operator)operations.get(operationIndex);
228 
229     // select by name
230     if (operator.type == Operator.Type.SelectNode)
231     {
232       return selectNodeByName(operator, context, operationIndex);
233     }
234     else
235     // select by index
236     if (operator.type == Operator.Type.SelectIndex)
237     {
238       return selectNodeByIndex(operator, context, operationIndex);
239     }
240 
241     // bad operation
242     return null;
243   }
244 
245 
246   /* Multiple selection */
247 
248 
249   /**
250    * Selects child nodes based on their name.
251    *
252    * @param    operator          The current operation.
253    * @param    context            The node being searched.
254    * @param    operationIndex        The current operation.
255    * @param    nodes            The collection to store selected nodes.
256    *
257    */
258 
259   protected void selectNodesByName(Operator operator, ModelNode context, int operationIndex, Collection nodes)
260   {
261     String segment = operator.stringParameter;
262     boolean any = segment.equals("*");
263 
264     // get array of children
265     Collection childNodes = context.getChildNodes();
266     ModelNode children[] = new ModelNode[childNodes.size()];
267     childNodes.toArray(children);
268 
269     // search children for name
270     int childIndex;
271     for (childIndex = 0; childIndex < children.length; childIndex++)
272     {
273       ModelNode child = children[childIndex];
274 
275       // traverse down each node matching the segment
276       if (any || child.getNodeName().equals(segment))
277       {
278         // continue evaluating expression on the selected node
279         selectNodes(child, (operationIndex + 1), nodes);
280       }
281     }
282   }
283 
284   /**
285    * Selects a child node based on its position.
286    *
287    * @param    operator          The current operation.
288    * @param    context            The node being searched.
289    * @param    operationIndex        The current operation.
290    * @param    nodes            The collection to store selected nodes.
291    *
292    */
293 
294   protected void selectNodesByIndex(Operator operator, ModelNode context, int operationIndex, Collection nodes)
295   {
296     String segment = operator.stringParameter;
297 
298     // find the n'th node
299     int nodeIndex = 1;
300 
301     // get array of children
302     Collection childNodes = context.getChildNodes();
303     ModelNode children[] = new ModelNode[childNodes.size()];
304     childNodes.toArray(children);
305 
306     // search children for name
307     int childIndex;
308     for (childIndex = 0; childIndex < children.length; childIndex++)
309     {
310       ModelNode child = children[childIndex];
311 
312       // check each node matching the segment
313       if (child.getNodeName().equals(segment))
314       {
315         // continue until the n'th named node is found
316         if (nodeIndex == operator.intParameter)
317         {
318           // continue evaluating expression on the selected node
319           selectNodes(child, (operationIndex + 1), nodes);
320 
321           break;
322         }
323 
324         nodeIndex++;
325       }
326     }
327   }
328 
329   /**
330    * Performs the query expression operations for multiple node selection.
331    *
332    * @param    context            The node being searched.
333    * @param    operationIndex        The current operation.
334    * @param    nodes            The collection to store selected nodes.
335    *
336    */
337 
338   protected void selectNodes(ModelNode context, int operationIndex, Collection nodes)
339   {
340     // if at the end of the expression, select the context node
341     if (operationIndex == operations.size())
342     {
343       nodes.add(context);
344       return;
345     }
346 
347     // get the next operator
348     Operator operator = (Operator)operations.get(operationIndex);
349 
350     // select by name
351     if (operator.type == Operator.Type.SelectNode)
352     {
353       selectNodesByName(operator, context, operationIndex, nodes);
354     }
355     else
356     // select by index
357     if (operator.type == Operator.Type.SelectIndex)
358     {
359       selectNodesByIndex(operator, context, operationIndex, nodes);
360     }
361   }
362 
363 
364   /* Expression compiling. */
365 
366 
367   /**
368    * Represents a single operation to perform during expression evaluation.
369    *
370    */
371 
372   protected static class Operator
373   {
374     /** The operation to perform. */
375     public int type;
376 
377     /** A string parameter for the operation. */
378     public String stringParameter;
379 
380     /** An integer parameter for the operation. */
381     public int intParameter;
382 
383     /* Constants. */
384 
385 
386     /** Defines the constants for the operation <code>type</code>. */
387     public interface Type
388     {
389       /** Select a node by its name. */
390       public static final int SelectNode = 1;
391 
392       /** Select a node by its position. */
393       public static final int SelectIndex = 2;
394     }
395 
396 
397     /* Constructors. */
398 
399 
400     /**
401      * Creates an <code>Operator</code> instance.
402      *
403      */
404 
405     public Operator(int setType, String setStringParameter, int setIntParameter)
406     {
407       type = setType;
408       stringParameter = setStringParameter;
409       intParameter = setIntParameter;
410     }
411   }
412 
413   /**
414    * Parses a query expressions.
415    *
416    */
417 
418   protected static class Parser
419   {
420     /** The current position in the string. */
421     protected int position;
422 
423     /** The string to parse. */
424     protected String expression;
425 
426 
427     /* Constants. */
428 
429 
430     /** Constants for operator tokens. */
431     public interface Operators
432     {
433       /** Null operation. */
434       final static char Null = 'n';
435 
436       /** Node select operation. */
437       final static char SelectNodeDot = '.';
438 
439       /** Wilcard select operation. */
440       final static char SelectNodeWild = '*';
441 
442       /** Node select operation. */
443       final static char SelectNodeSlash = '/';
444 
445       /** Node index operation, start. */
446       final static char SelectIndexStart = '[';
447 
448       /** Node index operation, end. */
449       final static char SelectIndexEnd = ']';
450     }
451 
452 
453     /* Construction */
454 
455 
456     /**
457      * Creates a <code>Tokenizer</code> to parse the supplied string.
458      *
459      * @param    setExpression        The expression to parse.
460      *
461      */
462 
463     Parser(String setExpression)
464     {
465       expression = setExpression;
466       position = 0;
467     }
468 
469 
470     /* Parsing */
471 
472     /**
473      * Returns true if more tokens are available.
474      *
475      * @return                  True if more tokens, false otherwise.
476      *
477      */
478 
479     boolean hasMoreTokens()
480     {
481       // check for end of value
482       return (position < expression.length());
483     }
484 
485     /**
486      * Returns the next available string.
487      *
488      * @return                  The next available string token, null if no tokens remain.
489      *
490      */
491 
492     String getStringToken()
493     {
494       // check for end of value
495       if (position >= expression.length()) return null;
496 
497       // search for end of next token
498       int nextPosition = position;
499 
500       while (nextPosition < expression.length())
501       {
502         char letter = expression.charAt(nextPosition);
503 
504         // check for operator
505         if ((letter == Operators.SelectNodeDot) ||
506           (letter == Operators.SelectNodeSlash) ||
507           (letter == Operators.SelectIndexStart) ||
508           (letter == Operators.SelectIndexEnd))
509         {
510           break;
511         }
512 
513         nextPosition++;
514       }
515 
516       // extract token
517       String token = expression.substring(position, nextPosition);
518 
519       // start next token search at this delimiter
520       position = nextPosition;
521 
522       return token;
523     }
524 
525     /**
526      * Returns the next available operator.
527      *
528      * @return                  The next available operator token, null if no tokens remain.
529      *
530      */
531 
532     char getOperatorToken()
533     {
534       // check for end of value
535       if (position >= expression.length()) return Operators.Null;
536 
537       // retrieve operator
538       char token = expression.charAt(position);
539       position++;
540 
541       return token;
542     }
543   }
544 
545   /**
546    * Parses an index selection operator.
547    *
548    * @param    parser            The {@link Parser} being used for the expression.
549    * @param    segment            The segment that the index belongs to.
550    *
551    */
552 
553   protected void parseIndexOperator(Parser parser, String segment)
554   {
555     // set to index operator
556     int type = Operator.Type.SelectIndex;
557 
558     // get index, [n]
559     int index = Integer.parseInt(parser.getStringToken());
560 
561     // get closing ']'
562     char op = parser.getOperatorToken();
563 
564     // get next operator, if any
565     parser.getOperatorToken();
566 
567     // create operator
568     operations.add(new Operator(type, segment, index));
569   }
570 
571   /**
572    * Compiles the supplied query expression.
573    *
574    * @param    expression          Query expression to select the nodes.
575    *
576    */
577 
578   protected void compile(String expression)
579   {
580     // create storage for compiled expression
581     operations = new ArrayList();
582 
583     // initialize parser
584     Parser parser = new Parser(expression);
585 
586     // check for initial operator
587     char start = expression.charAt(0);
588 
589     if ((start == '.') || (start == '/'))
590     {
591       parser.getOperatorToken();
592     }
593 
594     // parse expression
595     while (parser.hasMoreTokens())
596     {
597       // get node name
598       String segment = parser.getStringToken();
599 
600       int type = Operator.Type.SelectNode;
601       int index = 0;
602 
603       // read next operator
604       if (parser.hasMoreTokens())
605       {
606         char operator = parser.getOperatorToken();
607 
608         // check if index operator follows
609         if (operator == Parser.Operators.SelectIndexStart)
610         {
611           parseIndexOperator(parser, segment);
612         }
613         else
614         {
615           // create "node" operator for previous operator
616           operations.add(new Operator(type, segment, index));
617         }
618       }
619       else
620       {
621         // create "node" operator for last operator
622         operations.add(new Operator(type, segment, index));
623       }
624     }
625   }
626 }
627