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

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

Implemented searching in 'all fields'

  • Property svn:keywords set to Rev Author Date
File size: 11.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         * Retrieves a combination of the entity and field
33         * @return
34         */
35        public String entityField() {
36                return entity.toString() + ( field ? "." + field.toString() : "" );
37        }
38       
39        /**
40         * Retrieves a human readable description of the combination of the entity and field
41         * @return
42         */
43        public String humanReadableEntityField() {
44                if( field == '*' ) {
45                        if( entity == '*' ) {
46                                return "any field in any object"
47                        } else {
48                                return "any field in " + entity.toString();
49                        }
50                } else {
51                        return entityField();
52                }
53        }
54       
55        /**
56         * Retrieves the correct value for this criterion in the given object (with template)
57         *
58         * @param entity                Entity to check for value. Should be a child of template entity
59         * @param criterion             Criterion to match on
60         * @return                              Value of the given field or null if the field doesn't exist
61         */
62        public def getFieldValue( TemplateEntity entity ) {
63                if( entity == null )
64                        return null;
65
66                try {
67                        def fieldValue
68                        if( !field ) {
69                                fieldValue = entity
70                        } else if( field == "Template" ) {
71                                fieldValue = entity.template?.name
72                        } else if( field == "*" ) {
73                                fieldValue = entity.giveFields().collect{ 
74                                        if( it && it.name ) {
75                                                Search.prepare( entity.getFieldValue( it.name ), entity.giveFieldType( it.name ) ) 
76                                        }
77                                }
78                        } else {
79                                fieldValue = Search.prepare( entity.getFieldValue( field ), entity.giveFieldType( field ) )
80                        }
81                       
82                        return fieldValue
83                } catch( Exception e ) {
84                        // An exception occurs if the given field doesn't exist. In that case, this criterion will fail.
85                        // TODO: Maybe give the user a choice whether he want's to include these studies or not
86                        return null;
87                }
88        }
89
90        /**
91         * Checks if the given object (with template) that satisfies the given criterion.
92         *
93         * @param entity                Entity to check for criterion satisfaction. Should be a child of template entity
94         * @param criterion     Criterion to match on
95         * @return                      True iff there the entity satisfies the given criterion.
96         */
97        public boolean matchOneEntity( TemplateEntity entity ) {
98                def fieldValue = this.getFieldValue( entity );
99
100                // Null is returned, the given field doesn't exist. In that case, this criterion will fail.
101                // TODO: Maybe give the user a choice whether he want's to include these studies or not
102                if( fieldValue == null )
103                        return false;
104
105                return this.match( fieldValue );
106        }
107
108        /**
109         * Checks for all entities in the given entityList, if there is any object that satisfies the given criterion.
110         *
111         * @param entityList    List with entities. The entities should be child classes of TemplateEntity
112         * @param criterion             Criterion to match on
113         * @return                              True iff there is any entity in the list that satisfies the given criterion.
114         */
115        public boolean matchAnyEntity( List<TemplateEntity> entityList ) {
116                for( entity in entityList ) {
117                        if( matchOneEntity( entity ) )
118                                return true;
119                }
120                return false;
121        }
122
123        /**
124         * Checks for all entities in the given entityList, if all objects satisfy the given criterion.
125         *
126         * @param entityList    List with entities. The entities should be child classes of TemplateEntity
127         * @param criterion             Criterion to match on
128         * @return                              True iff all entities satisfy the given criterion.
129         */
130        public boolean matchAllEntities( List<TemplateEntity> entityList ) {
131                for( entity in entityList ) {
132                        if( !matchOneEntity( entity ) )
133                                return false;
134                }
135                return true;
136        }
137
138        /**
139         * Checks for all values in the given List, if there is any value that satisfies the given criterion.
140         *
141         * @param entityList            List with values.
142         * @param criterion             Criterion to match on
143         * @return                              True iff there is any value in the list that satisfies the given criterion.
144         */
145        public boolean matchAny( List valueList ) {
146                for( value in valueList ) {
147                        if( match( value ) )
148                                return true;
149                }
150                return false;
151        }
152
153        /**
154         * Checks for all values in the given List, if all values satisfy the given criterion.
155         *
156         * @param entityList            List with values.
157         * @param criterion             Criterion to match on
158         * @return                              True iff all values satisfy the given criterion.
159         */
160        public boolean matchAll( List entityList ) {
161                for( value in valueList ) {
162                        if( !match( value ) )
163                                return false;
164                }
165                return true;
166        }
167
168        /**
169         * Tries to match a value against a criterion and returns true if it matches
170         *
171         * @param value         Value of the field to match
172         * @return                      True iff the value matches this criterion, false otherwise
173         */
174        public boolean match( def fieldValue ) {
175                if( fieldValue == null )
176                        return false;
177               
178                // in-search criteria have to be handled separately
179                if( this.operator == Operator.insearch ) {
180                        return this.value?.getResults()?.contains( fieldValue );
181                }       
182               
183                // Other criteria are handled based on the class of the value given.
184                def classname = fieldValue.class.getName();
185                classname = classname[classname.lastIndexOf( '.' ) + 1..-1].toLowerCase();
186
187                def matches = false;
188                try {
189                        switch( classname ) {
190                                case "integer":                                 matches = longCompare( new Long( fieldValue.longValue() ) ); break;
191                                case "long":                                    matches = longCompare( fieldValue ); break;
192                                case "float":                                   matches = doubleCompare( new Long( fieldValue.doubleValue() ) ); break;
193                                case "double":                                  matches = doubleCompare( fieldValue ); break;
194                                case "boolean":                                 matches = booleanCompare( fieldValue ); break;
195                                case "date":                                    matches = dateCompare( fieldValue); break;
196                                case "reltime":                                 matches = relTimeCompare( fieldValue ); break;
197                                case "assaymodule":
198                                case "template":
199                                case "term":
200                                case "templatefieldlistitem":
201                                case "string":
202                                default:                                                matches = compareValues( fieldValue.toString().trim().toLowerCase(), this.operator, value.toString().toLowerCase().trim() ); break;
203                        }
204                       
205                        return matches;
206                } catch( Exception e ) {
207                        log.error e.class.getName() + ": " + e.getMessage();
208                        return false;
209                }
210        }
211
212        /**
213         * Tries to match a value against a criterion and returns true if it matches
214         *
215         * @param fieldValue            Value of the field to match
216         * @param operator                      Operator to apply
217         * @param criterionValue        Value of the criterion
218         * @return                                      True iff the value matches this criterion value, false otherwise
219         */
220        protected boolean compareValues( def fieldValue, Operator operator, def criterionValue ) {
221                switch( operator ) {
222                        case Operator.gte:
223                                return fieldValue >= criterionValue;
224                        case Operator.gt:
225                                return fieldValue > criterionValue;
226                        case Operator.lt:
227                                return fieldValue < criterionValue;
228                        case Operator.lte:
229                                return fieldValue <= criterionValue;
230                        case Operator.contains:
231                                // Contains operator can only be used on string values
232                                return fieldValue.toString().contains( criterionValue.toString() );
233                        case Operator.equals:
234                        default:
235                                return fieldValue.equals( criterionValue );
236                }
237
238        }
239
240        /**
241         * Tries to match a date value against a criterion and returns true if it matches
242         *
243         * @param value         Date value of the field to match
244         * @return                      True iff the value matches this criterion, false otherwise
245         */
246        protected boolean dateCompare( Date fieldValue ) {
247                try {
248                        Date dateCriterion = new SimpleDateFormat( "yyyy-MM-dd" ).parse( value );
249                        Date fieldDate = new Date( fieldValue.getTime() );
250
251                        // Clear time in order to just compare dates
252                        dateCriterion.clearTime();
253                        fieldDate.clearTime();
254
255                        return compareValues( fieldDate, this.operator, dateCriterion )
256                } catch( Exception e ) {
257                        log.error e.class.getName() + ": " + e.getMessage();
258                        return false;
259                }
260        }
261
262        /**
263         * Tries to match a long value against a criterion and returns true if it matches
264         *
265         * @param value         Long value of the field to match
266         * @param criterion     Criterion to match on. Should be a map with entries 'operator' and 'value'
267         * @return                      True iff the value matches this criterion, false otherwise
268         */
269        protected boolean longCompare( Long fieldValue ) {
270                Long longCriterion;
271                try {
272                        longCriterion = Long.parseLong( value );
273                } catch( Exception e ) {
274                        try {
275                                // If converting to long doesn't work, try converting to double and rounding it
276                                Double doubleCriterion = Double.parseDouble(value);
277                                longCriterion = new Long( doubleCriterion.longValue() );
278                        } catch( Exception e2 ) {
279                                log.debug "Can't convert value to long for comparison: " + e2.class.getName() + ": " + e2.getMessage();
280                                return false;
281                        }
282                }
283                return compareValues( fieldValue, this.operator, longCriterion );
284        }
285
286        /**
287         * Tries to match a double value against a criterion and returns true if it matches
288         *
289         * @param value         Double value of the field to match
290         * @return                      True iff the value matches this criterion, false otherwise
291         */
292        protected boolean doubleCompare( Double fieldValue ) {
293                try {
294                        Double doubleCriterion = Double.parseDouble( value );
295                        return compareValues( fieldValue, this.operator, doubleCriterion );
296                } catch( Exception e ) {
297                        log.debug "Can't convert value to double for comparison: " + e.class.getName() + ": " + e.getMessage();
298                        return false;
299                }
300        }
301
302
303        /**
304         * Tries to match a boolean value against a criterion and returns true if it matches
305         *
306         * @param value         Boolean value of the field to match
307         * @return                      True iff the value matches this criterion, false otherwise
308         */
309        protected boolean booleanCompare( Boolean fieldValue ) {
310                try {
311                        // The comparison should only be performed iff the value
312                        // contains 'true' or 'false' (case insensitive)
313                        def lowerCaseValue = value.toString().toLowerCase();
314                        if( lowerCaseValue != 'true' && lowerCaseValue != 'false' )
315                                return false;
316                               
317                        Boolean booleanCriterion = Boolean.parseBoolean( value );
318                        return compareValues( fieldValue, this.operator, booleanCriterion );
319                } catch( Exception e ) {
320                        log.debug "Can't convert value to boolean for comparison: " + e.class.getName() + ": " + e.getMessage();
321                        return false;
322                }
323        }
324
325        /**
326         * Tries to match a relTime value against a criterion and returns true if it matches
327         *
328         * @param value         relTime value of the field to match
329         * @return                      True iff the value matches this criterion, false otherwise
330         */
331        protected boolean relTimeCompare( RelTime fieldValue ) {
332                try {
333                        RelTime rt
334
335                        // Numbers are taken to be seconds, if a non-numeric value is given, try to parse it
336                        if( value.toString().isLong() ) {
337                                rt = new RelTime( Long.parseLong( value.toString() ) );
338                        } else {
339                                rt = new RelTime( value.toString() );
340                        }
341
342                        return compareValues( fieldValue, this.operator, rt );
343                } catch( Exception e ) {
344                        log.debug "Can't convert value to reltime for comparison: " + e.class.getName() + ": " + e.getMessage();
345                        return false;
346                }
347        }
348       
349        public static Operator parseOperator( String name ) throws Exception {
350                switch( name.trim() ) {
351                        case "=": 
352                        case "equals":          return Operator.equals; 
353                        case "contains":        return Operator.contains; 
354                        case ">=": 
355                        case "gte":                     return Operator.gte; 
356                        case ">": 
357                        case "gt":                      return Operator.gt; 
358                        case "<=": 
359                        case "lte":                     return Operator.lte; 
360                        case "<": 
361                        case "lt":                      return Operator.lt; 
362                        case "in":                      return Operator.insearch;
363                        default:
364                                throw new Exception( "Operator not found" ); 
365                }
366        }
367
368        public String toString() {
369                return "[Criterion " + entityField() + " " + operator + " " + value + "]";
370        }
371       
372        public boolean equals( Object o ) {
373                if( o == null )
374                        return false;
375               
376                if( !( o instanceof Criterion ) ) 
377                        return false;
378                       
379                Criterion otherCriterion = (Criterion) o;
380                return  this.entity == otherCriterion.entity &&
381                                this.field == otherCriterion.field && 
382                                this.operator == otherCriterion.operator &&
383                                this.value == otherCriterion.value;
384        }
385}
Note: See TracBrowser for help on using the repository browser.