source: trunk/src/groovy/dbnp/query/Criterion.groovy @ 1850

Last change on this file since 1850 was 1850, checked in by robert@…, 9 years ago

Made searching in text case insensitive

  • Property svn:keywords set to Rev Author Date
File size: 23.9 KB
Line 
1package dbnp.query
2
3import java.text.SimpleDateFormat
4import org.dbnp.gdt.*
5import org.apache.commons.logging.LogFactory;
6
7/**
8 * Available operators for criteria
9 * @author robert
10 *
11 */
12enum Operator {
13        equals( "=" ), contains( "contains" ), gte( ">="), gt( ">" ), lte( "<=" ), lt( "<" ), insearch( "in" )
14        Operator(String name) { this.name = name }
15        private final String name;
16        public String toString() { return name }
17}
18
19/**
20 * Represents a criterion to search on
21 * @author robert
22 *
23 */
24class Criterion {
25        private static final log = LogFactory.getLog(this);
26        public String entity
27        public String field
28        public Operator operator
29        public def value
30
31        /**
32         * Returns the class for the entity of this criterion
33         * @return     
34         */
35        public Class entityClass() {
36                if( this.entity == '*' )
37                        return null;
38               
39                       
40                try {
41                        return TemplateEntity.parseEntity( 'dbnp.studycapturing.' + this.entity)
42                } catch( Exception e ) {
43                        throw new Exception( "Unknown entity for criterion " + this, e );
44                }
45        }
46       
47        /**
48         * Retrieves a combination of the entity and field
49         * @return
50         */
51        public String entityField() {
52                return entity.toString() + ( field ? "." + field.toString() : "" );
53        }
54
55        /**
56         * Retrieves a human readable description of the combination of the entity and field
57         * @return
58         */
59        public String humanReadableEntityField() {
60                if( field == '*' ) {
61                        if( entity == '*' ) {
62                                return "any field in any object"
63                        } else {
64                                return "any field in " + entity.toString();
65                        }
66                } else {
67                        return entityField();
68                }
69        }
70
71        /**
72         * Returns the type of criterion when searching. Multiple types can be returned, since fields
73         * with the same name might have different types.
74         * 
75         * @return      List of strings determining the type of this criterion. Possibilities are:
76         *              [STRING,BOOLEAN,..]:The criterion references a template field that contains a 'simple'
77         *                                                      value (boolean, double, long, string, reltime, date)
78         *              [STRINGLIST,...]:       The criterion references a template field that contains a 'complex'
79         *                                                      value (listitem, ontologyterm, template, module) referencing another
80         *                                                      database table
81         *              Wildcard:                       The criterion references all fields
82         */
83        protected List<String> criterionType() {
84                if( this.entity == '*' || this.field == '*' ) {
85                        return [
86                                 'String',
87                                 'Text',
88                                 'File',
89                                 'Date',
90                                 'RelTime',
91                                 'Double',
92                                 'Long',
93                                 'Boolean',
94                                 'StringList',
95                                 'ExtendableStringList',
96                                 'Term',
97                                 'Template',
98                                 'Module'
99                        ]
100                }
101
102                // Determine domain fields of the entity
103                def domainFields = entityClass().giveDomainFields();
104                def domainField = domainFields.find { it.name == this.field };
105                if( domainField )
106                        return [domainField.type?.casedName];
107
108                // If this field is not a domain field, search for the field in the database
109                def entityClass = entityClass()
110
111                if( !entityClass || !this.field )
112                        return null;
113
114                // Find all fields with this name and entity
115                def fields = TemplateField.findAllByName( this.field ).findAll { it.entity == entityClass };
116
117                // If the field is not found, return null
118                if( !fields )
119                        return null
120
121                // Return the (unique) String value of the types
122                return fields*.type.unique()*.casedName;
123        }
124
125        /**
126         * Determines whether the field in this criterion is a domain field
127         *
128         * @return      True iff the field is a domain field, false otherwise
129         */
130        protected boolean isDomainCriterion() {
131                def entityClass = entityClass()
132               
133                if( !entityClass )
134                        return false;
135                       
136                // Determine domain fields of the entity
137                def domainFields = entityClass.giveDomainFields();
138                def domainField = domainFields.find { it.name == this.field };
139
140                return (domainField ? true : false)
141        }
142       
143        /**
144         * Determines whether this criterion references a 'complex' field (i.e. a field that
145         * contains a complex type like Term, ListItem etc.)
146         *
147         * @return
148         */
149        public boolean isComplexCriterion() {
150                if( this.field == '*' )
151                        return false;
152
153                if( isDomainCriterion() )
154                        return false;
155                       
156                def types = criterionType();
157               
158                return types.any { type -> 
159                        switch( type ) {
160                                case 'StringList':
161                                case 'ExtendableStringList':
162                                case 'Term':
163                                case 'Template':
164                                case 'Module':
165                                        return true;
166                        }
167                       
168                        return false;
169                }
170        }
171
172        /**
173         * Case the field value to search on to the given type
174         * @param fieldType     Name of the template field type
175         * @return                      Value casted to the right value
176         */
177        protected def castValue( String fieldType ) {
178                switch( fieldType ) {
179
180                        case 'String':
181                        case 'Text':
182                        case 'StringList':
183                        case 'ExtendableStringList':
184                        case 'Term':
185                        case 'Template':
186                        case 'Module':
187                                return value?.toString();
188                        case 'File':
189                                return null; // Never search in filenames, since they are not very descriptive
190                        case 'Date':
191                                // The comparison with date values should only be performed iff the value
192                                // contains a parsable date
193                                // and the operator is equals, gte, gt, lt or lte
194                                if( operator == Operator.insearch || operator == Operator.contains )
195                                        return null
196
197                                try {
198                                        Date dateCriterion = new SimpleDateFormat( "yyyy-MM-dd" ).parse( value );
199                                        return dateCriterion
200                                } catch( Exception e ) {
201                                        return null;
202                                }
203
204                        case 'RelTime':
205                                // The comparison with date values should only be performed iff the value
206                                // contains a long number
207                                // and the operator is equals, gte, gt, lt or lte
208                                if( operator == Operator.insearch || operator == Operator.contains )
209                                        return null
210
211                                try {
212                                        RelTime rt
213
214                                        // Numbers are taken to be seconds, if a non-numeric value is given, try to parse it
215                                        if( value.toString().isLong() ) {
216                                                rt = new RelTime( Long.parseLong( value.toString() ) );
217                                        } else {
218                                                rt = new RelTime( value.toString() );
219                                        }
220
221                                        return rt.getValue()
222                                } catch( Exception e ) {
223                                        return null;
224                                }
225                        case 'Double':
226                                // The comparison with date values should only be performed iff the value
227                                // contains a double number
228                                // and the operator is equals, gte, gt, lt or lte
229                                if( operator == Operator.insearch || operator == Operator.contains )
230                                        return null
231
232                                if( value.isDouble() ) {
233                                        return Double.parseDouble( value )
234                                } else {
235                                        return null;
236                                }
237                        case 'Long':
238                                // The comparison with date values should only be performed iff the value
239                                // contains a long number
240                                // and the operator is equals, gte, gt, lt or lte
241                                if( operator == Operator.insearch || operator == Operator.contains )
242                                        return null
243
244                                if( value.isLong() ) {
245                                        return Long.parseLong( value )
246                                } else {
247                                        return null;
248                                }
249                        case 'Boolean':
250                                // The comparison with boolean values should only be performed iff the value
251                                // contains 'true' or 'false' (case insensitive)
252                                // and the operator is equals
253                                if( operator != Operator.equals )
254                                        return null
255
256                                def lowerCaseValue = value.toString().toLowerCase();
257                                if( lowerCaseValue == 'true' || lowerCaseValue == 'false' ) {
258                                        return Boolean.parseBoolean( this.value )
259                                } else {
260                                        return null
261                                }
262                }
263        }
264
265        /**
266         * Create a HQL where clause from this criterion, in order to be used within a larger HQL statement
267         *
268         * @param       objectToSearchIn        HQL name of the object to search in
269         * @return      Map with 3 keys:   'join' and'where' with the HQL join and where clause for this criterion and 'parameters' for the query named parameters
270         */
271        public Map toHQL( String prefix, String objectToSearchIn = "object" ) {
272                List whereClause = []
273                String joinClause = "";
274                Map parameters = [:];
275                def emptyCriterion = [ "join": null, "where": null, "parameters": null ];
276
277                // If this criterion is used to search within another search result, we use a special piece of HQL
278                if( this.operator == Operator.insearch ) {
279                        if( this.value?.results ) {
280                                parameters[ prefix + "SearchResults" ] = this.value?.results
281
282                                return [ "join": "", "where": "( " + objectToSearchIn + " in (:" + prefix + "SearchResults) )" , "parameters": parameters ];
283                        } else {
284                                return emptyCriterion;
285                        }
286                }
287               
288                // If no value is given, don't do anything
289                if( !value )
290                        return emptyCriterion;
291               
292                // Check whether the field is a domain field
293                if( isDomainCriterion() ) {
294                        // Determine the types of this criterion, but there will be only 1 for a domain field
295                        def criterionType = criterionType()[0];
296                       
297                        // Some domain fields don't contain a value, but a reference to another table
298                        // These should be handled differently
299                        def fieldName = this.field
300                       
301                        if( 
302                                ( objectToSearchIn == "subject" && fieldName == "species" ) || 
303                                ( objectToSearchIn == "sample" && fieldName == "material" ) ||
304                                ( objectToSearchIn == "assay" && fieldName == "module" ) ||
305                                ( objectToSearchIn == "samplingEvent" && fieldName == "sampleTemplate" ) ) {
306                                fieldName += ".name"
307                        }
308                               
309                        def query = extendWhereClause( "( %s )", objectToSearchIn + "." + fieldName, prefix, criterionType, castValue( criterionType ) );
310                        return [ "join": "", "where": query.where, "parameters": query.parameters  ]
311                }
312
313                // Determine the type of this criterion
314                def criterionTypes = criterionType();
315               
316                if( !criterionTypes )
317                        return emptyCriterion;                 
318
319               
320                // Several types of criteria are handled differently.
321                // The 'wildcard' is handled by searching for all types.
322                // The 'simple' types (string, double) are handled by searching in the associated table
323                // The 'complex' types (stringlist, template etc., referencing another
324                // database table) can't be handled correctly, since the HQL INDEX() function doesn't work on those relations.
325                // We do a search for these types to see whether any field with that type fits this criterion, in order to
326                // filter out false positives later on.
327                criterionTypes.findAll { it }.each { criterionType ->
328                        // Cast criterion value to the right type
329                        def currentValue = castValue( criterionType );
330
331                        // Determine field name
332                        def fieldName = "template" + criterionType + 'Fields'
333                       
334                        switch( criterionType ) {
335                                case "Wildcard":
336                                        // Wildcard search is handled by
337                                        break;
338
339                                case 'String':
340                                case 'Text':
341                                case 'File':
342                                case 'Date':
343                                case 'RelTime':
344                                case 'Double':
345                                case 'Long':
346                                case 'Boolean':
347                                        // 'Simple' field types
348                                        if( currentValue != null ) {
349                                                joinClause += " left join " + objectToSearchIn + "." + fieldName + " as " + prefix + "_" + fieldName + " ";
350       
351                                                def condition = this.oneToManyWhereCondition( prefix + "_" + fieldName, prefix, criterionType, currentValue )
352                                                whereClause += condition[ "where" ];
353       
354                                                condition[ "parameters" ].each {
355                                                        parameters[ it.key ] = it.value;
356                                                }
357                                        }
358                                        break;
359                                       
360                                case 'StringList':
361                                case 'ExtendableStringList':
362                                case 'Term':
363                                case 'Template':
364                                case 'Module':
365                                        // 'Complex' field types
366                                        def condition = this.manyToManyWhereCondition( objectToSearchIn, fieldName, prefix, "name", currentValue )
367                                        whereClause += condition[ "where" ];
368       
369                                        condition[ "parameters" ].each {
370                                                parameters[ it.key ] = it.value;
371                                        }
372                                default:
373                                        break;
374                        }
375                }
376
377                def where = whereClause?.findAll { it } ? "( " + whereClause.join( " OR " ) + " )" : ""
378               
379                return [ "join": joinClause, "where": where , "parameters": parameters ];
380        }
381
382        /**
383         * Extends a given condition with a where clause of this criterion. If you supply "select * from Study where %s", %s will
384         * be replaced by the where clause for the given field. Also, the parameters map will be extended (if needed)
385         *
386         * @param hql                   Initial HQL string where the clause will be put into
387         * @param fieldName             Name of the field that should be referenced
388         * @param uniquePrefix  Unique prefix for this criterion
389         * @param fieldType             Type of field value to search for
390         * @param fieldValue    Field value to search for
391         * @return                              Map with 'where' key referencing the extended where clause and 'parameters' key referencing a map with parameters.
392         */
393        protected Map extendWhereClause( String hql, String fieldName, String uniquePrefix, String fieldType, def fieldValue ) {
394                def parameters = [:]
395                def textFieldTypes = [ 'String', 'Text', 'File', 'StringList', 'ExtendableStringList', 'Term', 'Template', 'Module' ];
396               
397                switch( this.operator ) {
398                        case Operator.contains:
399                                // Text fields should be handled case insensitive
400                                if( textFieldTypes.contains( fieldType ) ) {
401                                        hql = sprintf( hql, "lower( " + fieldName + ") like lower( :" + uniquePrefix + "ValueLike )" );
402                                } else {
403                                        hql = sprintf( hql, fieldName + " like :" + uniquePrefix + "ValueLike" );
404                                }
405                                parameters[ uniquePrefix + "ValueLike" ] = "%" + fieldValue + "%"
406                                break;
407                        case Operator.equals:
408                        case Operator.gte:
409                        case Operator.gt:
410                        case Operator.lte:
411                        case Operator.lt:
412                                if( textFieldTypes.contains( fieldType ) ) {
413                                        hql = sprintf( hql, "lower( " + fieldName + " ) "  + this.operator.name + " lower( :" + uniquePrefix + "Value" + fieldType + ")" );
414                                } else {
415                                        hql = sprintf( hql, fieldName + " "  + this.operator.name + " :" + uniquePrefix + "Value" + fieldType );
416                                }
417                                parameters[ uniquePrefix + "Value" + fieldType ] = fieldValue
418                                break;
419                }
420
421                return [ "where": hql, "parameters": parameters]
422        }
423
424        /**
425         * Creates a condition for this criterion, for a given fieldName and value. The fieldName should reference a collection that has a one-to-many
426         * relation with the object being sought
427         * 
428         * @param fieldName             Name to search in
429         * @param uniquePrefix  Unique prefix for this criterion
430         * @param currentValue  Map with 'value' referencing the value being sought and 'type' referencing
431         *                                              the type of the value as string. The value should be be casted to the right class for this field.
432         * @return                              Map with 'where' key referencing the where clause and 'parameters' key referencing a map with parameters.
433         */
434        protected Map oneToManyWhereCondition( String fieldName, String uniquePrefix, String fieldType, def fieldValue ) {
435                // Create the where condition for checking the value
436                // First check the name of the field, if needed
437                def condition
438                def parameters = [:]
439
440                if( this.field != '*' ) {
441                        condition = "( %s AND index(" + fieldName + ") = :" + uniquePrefix + "Field )"
442                        parameters[ uniquePrefix + "Field" ] = this.field
443                } else {
444                        condition = "%s";
445                }
446
447                def whereClause = extendWhereClause( condition, fieldName, uniquePrefix, fieldType, fieldValue );
448                parameters.each {
449                        whereClause.parameters[ it.key ] = it.value;
450                }
451
452                return whereClause;
453        }
454
455        /**
456         * Creates a condition for this criterion, for a given fieldName and value. The fieldName should
457         * reference a collection that has a many-to-many relation with the object being sought (e.g. templateTermFields).
458         *
459         * Unfortunately, there is no way to determine the name of the field in HQL for this many-to-many collections, since the
460         * INDEX() function in HQL doesn't work for many-to-many collections.
461         * @see http://opensource.atlassian.com/projects/hibernate/browse/HHH-4879
462         * @see http://opensource.atlassian.com/projects/hibernate/browse/HHH-4615
463         * 
464         * @param fieldName             Name to search in
465         * @param uniquePrefix  Unique prefix for this criterion
466         * @param currentValue  Map with 'value' referencing the value being sought and 'type' referencing
467         *                                              the type of the value as string. The value should be be casted to the right class for this field.
468         * @return                              Map with 'where' key referencing the where clause and 'parameters' key referencing a map with parameters.
469         */
470        protected Map manyToManyWhereCondition( String objectToSearchIn, String collection, String uniquePrefix, String searchField, def value ) {
471                // exists( FROM [objectToSearchIn].[collection] as [uniquePrefix][collection] WHERE [searchField] LIKE [value] )
472                // Create the where condition for checking the value
473                def condition = "exists ( FROM " + objectToSearchIn + "." + collection + " as " + uniquePrefix + "_" + collection + " WHERE %s )";
474
475                return extendWhereClause( condition, uniquePrefix + "_" + collection + "." + searchField, uniquePrefix, "STRING", value );
476        }
477
478        /**
479         * Retrieves the correct value for this criterion in the given object (with template)
480         *
481         * @param entity                Entity to check for value. Should be a child of template entity
482         * @param criterion             Criterion to match on
483         * @return                              Value of the given field or null if the field doesn't exist
484         */
485        public def getFieldValue( TemplateEntity entity ) {
486                if( entity == null )
487                        return null;
488
489                try {
490                        def fieldValue
491                        if( !field ) {
492                                fieldValue = entity
493                        } else if( field == "Template" ) {
494                                fieldValue = entity.template?.name
495                        } else if( field == "*" ) {
496                                fieldValue = entity.giveFields().collect{
497                                        if( it && it.name ) {
498                                                Search.prepare( entity.getFieldValue( it.name ), entity.giveFieldType( it.name ) )
499                                        }
500                                }
501                        } else {
502                                fieldValue = Search.prepare( entity.getFieldValue( field ), entity.giveFieldType( field ) )
503                        }
504
505                        return fieldValue
506                } catch( Exception e ) {
507                        // An exception occurs if the given field doesn't exist. In that case, this criterion will fail.
508                        // TODO: Maybe give the user a choice whether he want's to include these studies or not
509                        return null;
510                }
511        }
512
513        /**
514         * Tries to match a value against a criterion and returns true if it matches
515         *
516         * @param value         Value of the field to match
517         * @return                      True iff the value matches this criterion, false otherwise
518         */
519        public boolean match( def fieldValue ) {
520                if( fieldValue == null )
521                        return false;
522
523                // in-search criteria have to be handled separately
524                if( this.operator == Operator.insearch ) {
525                        return this.value?.getResults()?.contains( fieldValue );
526                }
527
528                // Other criteria are handled based on the class of the value given.
529                def classname = fieldValue.class.getName();
530                classname = classname[classname.lastIndexOf( '.' ) + 1..-1].toLowerCase();
531
532                def matches = false;
533                try {
534                        switch( classname ) {
535                                case "integer":                                 matches = longCompare( new Long( fieldValue.longValue() ) ); break;
536                                case "long":                                    matches = longCompare( fieldValue ); break;
537                                case "float":                                   matches = doubleCompare( new Long( fieldValue.doubleValue() ) ); break;
538                                case "double":                                  matches = doubleCompare( fieldValue ); break;
539                                case "boolean":                                 matches = booleanCompare( fieldValue ); break;
540                                case "date":                                    matches = dateCompare( fieldValue); break;
541                                case "reltime":                                 matches = relTimeCompare( fieldValue ); break;
542                                case "assaymodule":
543                                case "template":
544                                case "term":
545                                case "templatefieldlistitem":
546                                case "string":
547                                default:                                                matches = compareValues( fieldValue.toString().trim().toLowerCase(), this.operator, value.toString().toLowerCase().trim() ); break;
548                        }
549
550                        return matches;
551                } catch( Exception e ) {
552                        log.error e.class.getName() + ": " + e.getMessage();
553                        return false;
554                }
555        }
556
557        /**
558         * Tries to match a value against a criterion and returns true if it matches
559         *
560         * @param fieldValue            Value of the field to match
561         * @param operator                      Operator to apply
562         * @param criterionValue        Value of the criterion
563         * @return                                      True iff the value matches this criterion value, false otherwise
564         */
565        protected boolean compareValues( def fieldValue, Operator operator, def criterionValue ) {
566                switch( operator ) {
567                        case Operator.gte:
568                                return fieldValue >= criterionValue;
569                        case Operator.gt:
570                                return fieldValue > criterionValue;
571                        case Operator.lt:
572                                return fieldValue < criterionValue;
573                        case Operator.lte:
574                                return fieldValue <= criterionValue;
575                        case Operator.contains:
576                        // Contains operator can only be used on string values
577                                return fieldValue.toString().contains( criterionValue.toString() );
578                        case Operator.equals:
579                        default:
580                                return fieldValue.equals( criterionValue );
581                }
582
583        }
584
585        /**
586         * Tries to match a date value against a criterion and returns true if it matches
587         *
588         * @param value         Date value of the field to match
589         * @return                      True iff the value matches this criterion, false otherwise
590         */
591        protected boolean dateCompare( Date fieldValue ) {
592                try {
593                        Date dateCriterion = new SimpleDateFormat( "yyyy-MM-dd" ).parse( value );
594                        Date fieldDate = new Date( fieldValue.getTime() );
595
596                        // Clear time in order to just compare dates
597                        dateCriterion.clearTime();
598                        fieldDate.clearTime();
599
600                        return compareValues( fieldDate, this.operator, dateCriterion )
601                } catch( Exception e ) {
602                        log.error e.class.getName() + ": " + e.getMessage();
603                        return false;
604                }
605        }
606
607        /**
608         * Tries to match a long value against a criterion and returns true if it matches
609         *
610         * @param value         Long value of the field to match
611         * @param criterion     Criterion to match on. Should be a map with entries 'operator' and 'value'
612         * @return                      True iff the value matches this criterion, false otherwise
613         */
614        protected boolean longCompare( Long fieldValue ) {
615                Long longCriterion;
616                try {
617                        longCriterion = Long.parseLong( value );
618                } catch( Exception e ) {
619                        try {
620                                // If converting to long doesn't work, try converting to double and rounding it
621                                Double doubleCriterion = Double.parseDouble(value);
622                                longCriterion = new Long( doubleCriterion.longValue() );
623                        } catch( Exception e2 ) {
624                                log.debug "Can't convert value to long for comparison: " + e2.class.getName() + ": " + e2.getMessage();
625                                return false;
626                        }
627                }
628                return compareValues( fieldValue, this.operator, longCriterion );
629        }
630
631        /**
632         * Tries to match a double value against a criterion and returns true if it matches
633         *
634         * @param value         Double value of the field to match
635         * @return                      True iff the value matches this criterion, false otherwise
636         */
637        protected boolean doubleCompare( Double fieldValue ) {
638                try {
639                        Double doubleCriterion = Double.parseDouble( value );
640                        return compareValues( fieldValue, this.operator, doubleCriterion );
641                } catch( Exception e ) {
642                        log.debug "Can't convert value to double for comparison: " + e.class.getName() + ": " + e.getMessage();
643                        return false;
644                }
645        }
646
647
648        /**
649         * Tries to match a boolean value against a criterion and returns true if it matches
650         *
651         * @param value         Boolean value of the field to match
652         * @return                      True iff the value matches this criterion, false otherwise
653         */
654        protected boolean booleanCompare( Boolean fieldValue ) {
655                try {
656                        // The comparison should only be performed iff the value
657                        // contains 'true' or 'false' (case insensitive)
658                        def lowerCaseValue = value.toString().toLowerCase();
659                        if( lowerCaseValue != 'true' && lowerCaseValue != 'false' )
660                                return false;
661
662                        Boolean booleanCriterion = Boolean.parseBoolean( value );
663                        return compareValues( fieldValue, this.operator, booleanCriterion );
664                } catch( Exception e ) {
665                        log.debug "Can't convert value to boolean for comparison: " + e.class.getName() + ": " + e.getMessage();
666                        return false;
667                }
668        }
669
670        /**
671         * Tries to match a relTime value against a criterion and returns true if it matches
672         *
673         * @param value         relTime value of the field to match
674         * @return                      True iff the value matches this criterion, false otherwise
675         */
676        protected boolean relTimeCompare( RelTime fieldValue ) {
677                try {
678                        RelTime rt
679
680                        // Numbers are taken to be seconds, if a non-numeric value is given, try to parse it
681                        if( value.toString().isLong() ) {
682                                rt = new RelTime( Long.parseLong( value.toString() ) );
683                        } else {
684                                rt = new RelTime( value.toString() );
685                        }
686
687                        return compareValues( fieldValue, this.operator, rt );
688                } catch( Exception e ) {
689                        log.debug "Can't convert value to reltime for comparison: " + e.class.getName() + ": " + e.getMessage();
690                        return false;
691                }
692        }
693
694        public static Operator parseOperator( String name ) throws Exception {
695                switch( name.trim() ) {
696                        case "=":
697                                case "equals":          return Operator.equals;
698                        case "contains":        return Operator.contains;
699                        case ">=":
700                                case "gte":                     return Operator.gte;
701                        case ">":
702                                case "gt":                      return Operator.gt;
703                        case "<=":
704                                case "lte":                     return Operator.lte;
705                        case "<":
706                                case "lt":                      return Operator.lt;
707                        case "in":                      return Operator.insearch;
708                        default:
709                                throw new Exception( "Operator not found" );
710                }
711        }
712
713        public String toString() {
714                return "[Criterion " + entityField() + " " + operator + " " + value + "]";
715        }
716
717        public boolean equals( Object o ) {
718                if( o == null )
719                        return false;
720
721                if( !( o instanceof Criterion ) )
722                        return false;
723
724                Criterion otherCriterion = (Criterion) o;
725                return  this.entity == otherCriterion.entity &&
726                this.field == otherCriterion.field &&
727                this.operator == otherCriterion.operator &&
728                this.value == otherCriterion.value;
729        }
730}
Note: See TracBrowser for help on using the repository browser.