001    package org.apache.ojb.broker.query;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.Enumeration;
019    import java.util.Hashtable;
020    import java.util.Vector;
021    
022    /**
023     * Class to build selection criteria for searches
024     * Search Filter Class (Abstract)
025     * This class builds a search filter tree, specifing how names and
026     * values are to be compared when searching a database.
027     * It just builds internal structures, and needs to be extended
028     * to return a search filter string or other object that can be
029     * used by the database to perform the actual search.
030     */
031    public abstract class SearchFilter
032    {
033        // Define the operators
034        // Binary operators have bit 8 set
035        // Logical operators have bit 9 set
036        public static final int AND = 0x200;              // Logical operator
037        public static final int OR = 0x201;               // Logical operator
038        public static final int NOT = 0x2;                // Unary operator
039        public static final int IN = 0x3;
040        public static final int NOT_IN = 0x4;
041        public static final int LIKE = 0x109;             // Binary operator
042        public static final int EQUAL = 0x10A;            // Binary operator
043        public static final int NOT_EQUAL = 0x10B;        // Binary operator
044        public static final int LESS_THAN = 0x10C;        // Binary operator
045        public static final int GREATER_THAN = 0x10D;     // Binary operator
046        public static final int GREATER_EQUAL = 0x10E;    // Binary operator
047        public static final int LESS_EQUAL = 0x10F;       // Binary operator
048        // Define a mask for the binary and logical operators
049        // private static final int                                 BINARY_OPER_MASK = 0x100;
050        // private static final int                                 LOGICAL_OPER_MASK = 0x200;
051        protected static final int BINARY_OPER_MASK = 0x100;
052        protected static final int LOGICAL_OPER_MASK = 0x200;
053        // Define the current search filter
054        protected SearchBase m_filter = null;
055    
056        /**
057         * Create an empty search filter.
058         *
059         */
060        // -----------------------------------------------------------
061        public SearchFilter()
062        {
063        }
064    
065        /**
066         * Change the search filter to one that specifies an element to
067         * match or not match one of a list of values.
068         * The old search filter is deleted.
069         *
070         * @param ElementName is the name of the element to be matched
071         * @param values is a vector of possible matches
072         * @param oper is the IN or NOT_IN operator to indicate how to matche
073         */
074        public void matchList(String ElementName, Vector values, int oper)
075        {
076            // Delete the old search filter
077            m_filter = null;
078            // If not NOT_IN, assume IN
079            // (Since ints are passed by value, it is OK to change it)
080            if (oper != NOT_IN)
081            {
082                oper = IN;
083                // Convert the vector of match strings to an array of strings
084            }
085            String[] value_string_array = new String[values.size()];
086            values.copyInto(value_string_array);
087            // Create a leaf node for this list and store it as the filter
088            m_filter = new SearchBaseLeaf(ElementName, oper, value_string_array);
089        }
090    
091        /**
092         * Change the search filter to one that specifies an element to not
093         * match one of a list of values.
094         * The old search filter is deleted.
095         *
096         * @param ElementName is the name of the element to be matched
097         * @param values is an array of possible matches
098         * @param oper is the IN or NOT_IN operator to indicate how to matche
099         */
100        public void matchList(String ElementName, String[] values, int oper)
101        {
102            // Delete the old search filter
103            m_filter = null;
104            // If not NOT_IN, assume IN
105            // (Since ints are passed by value, it is OK to change it)
106            if (oper != NOT_IN)
107            {
108                oper = IN;
109                // Create a leaf node for this list and store it as the filter
110            }
111            m_filter = new SearchBaseLeaf(ElementName, oper, values);
112        }
113    
114        /**
115         * Change the search filter to one that specifies an element to not
116         * match one of a list of integer values.
117         * The old search filter is deleted.
118         *
119         * @param ElementName is the name of the element to be matched
120         * @param values is an array of possible integer matches
121         * @param oper is the IN or NOT_IN operator to indicate how to matche
122         */
123        public void matchList(String ElementName, int[] values, int oper)
124        {
125            // Delete the old search filter
126            m_filter = null;
127            // If not NOT_IN, assume IN
128            // (Since ints are passed by value, it is OK to change it)
129            if (oper != NOT_IN)
130            {
131                oper = IN;
132                // Create a leaf node for this list and store it as the filter
133            }
134            m_filter = new SearchBaseLeafInt(ElementName, oper, values);
135        }
136    
137        /**
138         * Change the search filter to one that specifies an element to not
139         * match one single value.
140         * The old search filter is deleted.
141         *
142         * @param ElementName is the name of the element to be matched
143         * @param value is the value to not be matched
144         * @param oper is the IN or NOT_IN operator to indicate how to matche
145         */
146        public void matchValue(String ElementName, String value, int oper)
147        {
148            // Delete the old search filter
149            m_filter = null;
150            // If not NOT_IN, assume IN
151            // (Since ints are passed by value, it is OK to change it)
152            if (oper != NOT_IN)
153            {
154                oper = IN;
155                // Create a String array in which to hold the one name,
156                // and put that name in the array
157            }
158            String[] ValueArray = new String[1];
159            ValueArray[0] = value;
160            // Create a leaf node for this list and store it as the filter
161            m_filter = new SearchBaseLeaf(ElementName, oper, ValueArray);
162        }
163    
164        /**
165         * -----------------------------------------------------------
166         * @param ElementName
167         * @param value
168         * @param oper
169         */
170        public void matchValue(String ElementName, int value, int oper)
171        {
172            // Delete the old search filter
173            m_filter = null;
174            // If not NOT_IN, assume IN
175            // (Since ints are passed by value, it is OK to change it)
176            if (oper != NOT_IN)
177            {
178                oper = IN;
179                // Create a leaf node for this list and store it as the filter
180            }
181            m_filter = new SearchBaseLeafInt(ElementName, oper, new int[]
182            {
183                value
184            });
185        }
186    
187        /**
188         * Change the search filter to one that compares an element name to a value.
189         * The old search filter is deleted.
190         *
191         * @param ElementName is the name of the element to be tested
192         * @param value is the value to be compared against
193         * @param oper is the binary comparison operator to be used
194         * @exception DBException
195         */
196        public void compareFilter(String ElementName, String value, int oper) throws DBException
197        {
198            // Delete the old search filter
199            m_filter = null;
200            // If this is not a binary operator, throw an exception
201            if ((oper & BINARY_OPER_MASK) == 0)
202            {
203                throw new DBException();
204                // Create a SearchBaseLeafComparison node and store it as the filter
205            }
206            m_filter = new SearchBaseLeafComparison(ElementName, oper, value);
207        }
208    
209        /**
210         * Change the search filter to one that specifies a set of elements and their values
211         * that must match, and the operator to use to combine the elements.
212         * Each key is compared for an equal match to the value, and all
213         * comparisons are combined by the specified logical operator (OR or AND).
214         * The old search filter is deleted.
215         *
216         * @param elements is a hashtable holding key-value pairs
217         * @param combine_op is the logical operator to be used to combine the comparisons
218         * @param compare_op is the binary operator to be used for the comparisons
219         * @exception DBException
220         */
221        public void matchSet(Hashtable elements, int combine_op, int compare_op) throws DBException
222        {
223            // Delete the old search filter
224            m_filter = null;
225            // If combine_op is not a logical operator, throw an exception
226            if ((combine_op & LOGICAL_OPER_MASK) == 0)
227            {
228                throw new DBException();
229                // If compare_op is not a binary operator, throw an exception
230            }
231            if ((compare_op & BINARY_OPER_MASK) == 0)
232            {
233                throw new DBException();
234                // Create a vector that will hold the comparison nodes for all elements in the hashtable
235            }
236            Vector compareVector = new Vector();
237            // For each of the elements in the hashtable, create a comparison node for the match
238            for (Enumeration e = elements.keys(); e.hasMoreElements();)
239            {
240                // Get the element name from the enumerator
241                // and its value
242                String elementName = (String) e.nextElement();
243                String elementValue = (String) elements.get(elementName);
244                // Create a comparison node for this list and store it as the filter
245                SearchBaseLeafComparison comparenode = new SearchBaseLeafComparison(elementName,
246                        compare_op, elementValue);
247                // Add this leaf node to the vector
248                compareVector.addElement(comparenode);
249            }
250            // Now return a node that holds this set of leaf nodes
251            m_filter = new SearchBaseNode(combine_op, compareVector);
252        }
253    
254        /**
255         * Change the search filter to one that specifies a set of elements and their values
256         * that must match, and the operator to use to combine the elements.
257         * Each element name is compared for an equal match to the value, and all
258         * comparisons are combined by the specified logical operator (OR or AND).
259         * The old search filter is deleted.
260         *
261         * @param ElementNames is an array of names of elements to be tested
262         * @param ElementValues is an array of values for the corresponding element
263         * @param op is the logical operator to be used to combine the comparisons
264         * @exception DBException
265         */
266        public void matchSet(String[] ElementNames, String[] ElementValues, int op) throws DBException
267        {
268            // Delete the old search filter
269            m_filter = null;
270            // If this is not a logical operator, throw an exception
271            if ((op & LOGICAL_OPER_MASK) == 0)
272            {
273                throw new DBException();
274                // Create a vector that will hold the leaf nodes for all elements in the hashtable
275            }
276            Vector leafVector = new Vector();
277            // For each of the elements in the array, create a leaf node for the match
278            int numnames = ElementNames.length;
279            for (int i = 0; i < numnames; i++)
280            {
281                // Create a leaf node for this list and store it as the filter
282                SearchBaseLeaf leafnode = new SearchBaseLeaf(ElementNames[i], IN, ElementValues[i]);
283                // Add this leaf node to the vector
284                leafVector.addElement(leafnode);
285            }
286            // Now return a node that holds this set of leaf nodes
287            m_filter = new SearchBaseNode(op, leafVector);
288        }
289    
290        /**
291         * Combine other search filters with this one, using the specific operator.
292         *
293         * @param new_filters is a vector of SearchFilter classes to be combined
294         * @param op is the logical operator to be used to combine the filters
295         * @exception DBException
296         */
297        public void combine(Vector new_filters, int op) throws DBException
298        {
299            // If this is not a logical operator, throw an exception
300            if ((op & LOGICAL_OPER_MASK) == 0)
301            {
302                throw new DBException();
303                // Create a new vector consisting of just the filters
304                // from the SearchFilter classes in new_filters
305            }
306            Vector filters = new Vector();
307            // Now add in all the nodes of the new filters
308            for (Enumeration e = new_filters.elements(); e.hasMoreElements();)
309            {
310                // Get the search filter from the vector
311                SearchFilter f = (SearchFilter) e.nextElement();
312                filters.addElement(f.getFilter());
313            }
314            // Create a node for this list and return it
315            m_filter = new SearchBaseNode(op, m_filter, filters);
316        }
317    
318        /**
319         * Combine one other search filters with this one, using the specific operator.
320         *
321         * @param new_filter is the SearchFilter class to be combined
322         * @param op is the logical operator to be used to combine the filters
323         * @exception DBException
324         */
325        public void combine(SearchFilter new_filter, int op) throws DBException
326        {
327            // If this is not a logical operator, throw an exception
328            if ((op & LOGICAL_OPER_MASK) == 0)
329            {
330                throw new DBException();
331                // Create a new vector consisting of just the filters
332                // from the SearchFilter classes in new_filters
333            }
334            Vector filters = new Vector();
335            filters.addElement(new_filter.getFilter());
336            // Create a node for this list and return it
337            m_filter = new SearchBaseNode(op, m_filter, filters);
338        }
339    
340        /**
341         * Static method to convert a binary operator into a string.
342         *
343         * @param oper is the binary comparison operator to be converted
344         */
345        protected static String ConvertBinaryOperator(int oper)
346        {
347            // Convert the operator into the proper string
348            String oper_string;
349            switch (oper)
350            {
351                default:
352                case EQUAL:
353                    oper_string = "=";
354                    break;
355                case LIKE:
356                    oper_string = "LIKE";
357                    break;
358                case NOT_EQUAL:
359                    oper_string = "!=";
360                    break;
361                case LESS_THAN:
362                    oper_string = "<";
363                    break;
364                case GREATER_THAN:
365                    oper_string = ">";
366                    break;
367                case GREATER_EQUAL:
368                    oper_string = ">=";
369                    break;
370                case LESS_EQUAL:
371                    oper_string = "<=";
372                    break;
373            }
374            return oper_string;
375        }
376    
377        /**
378         * Get the actual filter out of the class.
379         * This is only needed when combining filters together
380         * and one instantiation has to get the filter from another.
381         * However, I do not know how to protect it from outside use.
382         * @return
383         *
384         */
385        protected SearchBase getFilter()
386        {
387            return m_filter;
388        }
389    
390        /**
391         * Converts this search filter into a search string for use by a database.
392         *
393         */
394    
395        /**
396         * -----------------------------------------------------------
397         * @return
398         */
399        public abstract String toString();
400    
401        /**
402         * SearchBase is the base node class for the parse tree
403         * This class holds the binary operator
404         *
405         */
406        // ===============================================================
407        protected class SearchBase
408        {
409            // Define the operator to be used for this group
410            public int oper;
411    
412            /**
413             * Constructor.
414             * This is protected so only the subclasses can instantiate it
415             *
416             */
417            // ------------------------------------------------------------
418            protected SearchBase()
419            {
420            }
421    
422        }
423    
424        /**
425         * SearchBaseLeafComparison holds a leaf of the search tree
426         * This class holds an element name, a binary operator, and a value to be compared
427         */
428        // ===============================================================
429        protected class SearchBaseLeafComparison extends SearchBase
430        {
431            // Define the element name
432            public String elementName;
433            // Only operators allowed are binary operators
434            // Define the comparison value
435            public String value;
436    
437            /**
438             * Constructor.
439             *
440             * @param ElementName is the name of the element to be tested
441             * @param oper is the binary operator to be used for the comparison
442             * @param value is the value to be used for the comparison
443             */
444            SearchBaseLeafComparison(String ElementName, int oper, String value)
445            {
446                this.elementName = ElementName;
447                this.oper = oper;
448                this.value = value;
449            }
450    
451        }
452    
453        /**
454         * SearchBaseLeaf holds a leaf of the search tree
455         * This class holds an element name, and a vector of possible matches.
456         * It searches for an element of the given name that matches at least
457         * one of the strings in the array (IN), or does not match any (NOT_IN)
458         *
459         */
460        // ===============================================================
461        protected class SearchBaseLeaf extends SearchBase
462        {
463            // Define the element name
464            public String elementName;
465            // Only operators allowed are IN and NOT_IN
466            // Define the vector of possible matches
467            public String[] matches;
468    
469            /**
470             * Constructor.
471             *
472             * @param ElementName is the name of the element to be tested
473             * @param oper is the operator (IN or NOT_IN) to be used for the comparison
474             * @param matches is an array of String values to be matched
475             */
476            SearchBaseLeaf(String ElementName, int oper, String[] matches)
477            {
478                this.elementName = ElementName;
479                this.oper = oper;
480                this.matches = matches;
481            }
482    
483            /**
484             * Constructor for only one value.
485             *
486             * @param ElementName is the name of the element to be tested
487             * @param oper is the operator (IN or NOT_IN) to be used for the comparison
488             * @param match is a string value to be matched
489             */
490            SearchBaseLeaf(String ElementName, int oper, String match)
491            {
492                this.elementName = ElementName;
493                this.oper = oper;
494                this.matches = new String[1];
495                this.matches[0] = match;
496            }
497    
498        }
499    
500        /**
501         * SearchBaseLeafInt holds a leaf of the search tree with integers
502         * This class holds an element name, and a vector of possible matches.
503         * It searches for an element of the given name that matches at least
504         * one of the integers in the array (IN), or does not match any (NOT_IN)
505         */
506        // ===============================================================
507        protected class SearchBaseLeafInt extends SearchBase
508        {
509            // Define the element name
510            public String elementName;
511            // Only operators allowed are IN and NOT_IN
512            // Define the vector of possible matches
513            public int[] matches;
514    
515            /**
516             * Constructor.
517             *
518             * @param ElementName is the name of the element to be tested
519             * @param oper is the operator (IN or NOT_IN) to be used for the comparison
520             * @param matches is an array of integer values to be matched
521             */
522            SearchBaseLeafInt(String ElementName, int oper, int[] matches)
523            {
524                this.elementName = ElementName;
525                this.oper = oper;
526                this.matches = matches;
527            }
528    
529            /**
530             * Constructor for only one value.
531             *
532             * @param ElementName is the name of the element to be tested
533             * @param oper is the operator (IN or NOT_IN) to be used for the comparison
534             * @param match is an int value to be matched
535             */
536            SearchBaseLeafInt(String ElementName, int oper, int match)
537            {
538                this.elementName = ElementName;
539                this.oper = oper;
540                this.matches = new int[1];
541                this.matches[0] = match;
542            }
543    
544        }
545    
546        /**
547         * Define the class to represent a node of the search tree
548         * This class holds an operator and a vector other nodes.
549         */
550        // ===============================================================
551        protected class SearchBaseNode extends SearchBase
552        {
553            // Define the vector of other nodes
554            public Vector nodes;
555    
556            /**
557             * Constructor.
558             * Store a list of filters and an operator for combining them.
559             *
560             * @param oper is the operator (IN or NOT_IN) to be used for the comparison
561             * @param new_filters is a vector of filters to be combined
562             */
563            SearchBaseNode(int oper, Vector new_filters)
564            {
565                this.oper = oper;
566                this.nodes = new_filters;
567            }
568    
569            /**
570             * Constructor for a specific filter and a vector of new ones.
571             * Store a list of filters and an operator for combining them.
572             *
573             * @param oper is the operator (IN or NOT_IN) to be used for the comparison
574             * @param filter is the first filter to be combined
575             * @param new_filters is a vector of filters to be combined
576             */
577            SearchBaseNode(int oper, Object filter, Vector new_filters)
578            {
579                // Store the operator
580                this.oper = oper;
581                // Create a vector and add in the first filter as the initial node (if present)
582                nodes = new Vector();
583                if (filter != null)
584                {
585                    nodes.addElement(filter);
586                    // Now add in all the nodes of the new filters
587                }
588                for (Enumeration e = new_filters.elements(); e.hasMoreElements();)
589                {
590                    nodes.addElement(e.nextElement());
591                }
592            }
593    
594        }
595    }