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

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

Implemented searching for 'any field' in advanced query

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