source: trunk/grails-app/domain/dbnp/studycapturing/TemplateEntity.groovy @ 406

Last change on this file since 406 was 406, checked in by keesvb, 12 years ago

added study tests, skip study adds when in test environment in bootstrap, refactored TemplateEntity?

  • Property svn:keywords set to Date Author Rev
File size: 12.7 KB
Line 
1package dbnp.studycapturing
2
3import dbnp.data.Term
4import org.springframework.validation.FieldError
5
6/**
7 * TemplateEntity Domain Class
8 *
9 * Revision information:
10 * $Rev: 406 $
11 * $Author: keesvb $
12 * $Date: 2010-05-11 13:35:21 +0000 (di, 11 mei 2010) $
13 */
14abstract class TemplateEntity implements Serializable {
15        Template template
16        Map templateStringFields = [:]
17        Map templateTextFields = [:]
18        Map templateStringListFields = [:]
19        Map templateIntegerFields = [:]
20        Map templateFloatFields = [:]
21        Map templateDoubleFields = [:]
22        Map templateDateFields = [:]
23        Map templateTermFields = [:]
24
25        static hasMany = [
26                templateStringFields: String,
27                templateTextFields: String,
28                templateStringListFields: TemplateFieldListItem,
29                templateIntegerFields: int,
30                templateFloatFields: float,
31                templateDoubleFields: double,
32                templateDateFields: Date,
33                templateTermFields: Term,
34                systemFields: TemplateField
35        ]
36
37        static mapping = {
38                tablePerHierarchy false
39
40                templateTextFields type: 'text'
41        }       
42
43        /**
44         * Constraints
45         *
46         * All template fields have their own custom validator. Note that there
47         * currently is a lot of code repetition. Ideally we don't want this, but
48         * unfortunately due to scope issues we cannot re-use the code. So make
49         * sure to replicate any changes to all pieces of logic! Only commented
50         * the first occurrence of the logic, please refer to the templateStringFields
51         * validator if you require information about the validation logic...
52         */
53        static constraints = {
54                template(nullable: true, blank: true)
55                templateStringFields(validator: { fields, obj, errors ->
56                        // note that we only use 'fields' and 'errors', 'obj' is
57                        // merely here because it's the way the closure is called
58                        // by the validator...
59
60                        // define a boolean
61                        def error = false
62
63                        // iterate through fields
64                        fields.each { key, value ->
65                                // check if the value is of proper type
66                                if ( value && value.class != String ) {
67                                        // it's of some other type
68                                        try {
69                                                // try to cast it to the proper type
70                                                fields[key] = (value as String)
71                                        } catch (Exception e) {
72                                                // could not typecast properly, value is of improper type
73                                                // add error message
74                                                error = true
75                                                errors.rejectValue(
76                                                        'templateStringFields',
77                                                        'templateEntity.typeMismatch.string',
78                                                        [key, value.class] as Object[],
79                                                        'Property {0} must be of type String and is currently of type {1}'
80                                                )
81                                        }
82                                }
83                        }
84
85                        // got an error, or not?
86                        return (!error)
87                })
88                templateTextFields(validator: { fields, obj, errors ->
89                        def error = false
90                        fields.each { key, value ->
91                                if ( value && value.class != String ) {
92                                        try {
93                                                fields[key] = (value as String)
94                                        } catch (Exception e) {
95                                                error = true
96                                                errors.rejectValue(
97                                                        'templateTextFields',
98                                                        'templateEntity.typeMismatch.string',
99                                                        [key, value.class] as Object[],
100                                                        'Property {0} must be of type String and is currently of type {1}'
101                                                )
102                                        }
103                                }
104                        }
105                        return (!error)
106                })
107                templateStringListFields(validator: { fields, obj, errors ->
108                        def error = false
109                        fields.each { key, value ->
110                                if ( value && value.class != TemplateFieldListItem ) {
111                                        try {
112                                                fields[key] = (value as TemplateFieldListItem)
113                                        } catch (Exception e) {
114                                                error = true
115                                                errors.rejectValue(
116                                                        'templateIntegerFields',
117                                                        'templateEntity.typeMismatch.templateFieldListItem',
118                                                        [key, value.class] as Object[],
119                                                        'Property {0} must be of type TemplateFieldListItem and is currently of type {1}'
120                                                )
121                                        }
122                                }
123                        }
124                        return (!error)
125                })
126                templateIntegerFields(validator: { fields, obj, errors ->
127                        def error = false
128                        fields.each { key, value ->
129                                if (value && value.class != Integer ) {
130                                        try {
131                                                fields[key] = (value as Integer)
132                                        } catch (Exception e) {
133                                                error = true
134                                                errors.rejectValue(
135                                                        'templateIntegerFields',
136                                                        'templateEntity.typeMismatch.integer',
137                                                        [key, value.class] as Object[],
138                                                        'Property {0} must be of type Integer and is currently of type {1}'
139                                                )
140                                        }
141                                }
142                        }
143                        return (!error)
144                })
145                templateFloatFields(validator: { fields, obj, errors ->
146                        def error = false
147                        fields.each { key, value ->
148                                if ( value && value.class != Float ) {
149                                        try {
150                                                fields[key] = (value as Float)
151                                        } catch (Exception e) {
152                                                error = true
153                                                errors.rejectValue(
154                                                        'templateFloatFields',
155                                                        'templateEntity.typeMismatch.float',
156                                                        [key, value.class] as Object[],
157                                                        'Property {0} must be of type Float and is currently of type {1}'
158                                                )
159                                        }
160                                }
161                        }
162                        return (!error)
163                })
164                templateDoubleFields(validator: { fields, obj, errors ->
165                        def error = false
166                        fields.each { key, value ->
167                                if ( value && value.class != Double ) {
168                                        try {
169                                                fields[key] = (value as Double)
170                                        } catch (Exception e) {
171                                                error = true
172                                                errors.rejectValue(
173                                                        'templateDoubleFields',
174                                                        'templateEntity.typeMismatch.double',
175                                                        [key, value.class] as Object[],
176                                                        'Property {0} must be of type Double and is currently of type {1}'
177                                                )
178                                        }
179                                }
180                        }
181                        return (!error)
182                })
183                templateDateFields(validator: { fields, obj, errors ->
184                        def error = false
185                        fields.each { key, value ->
186                                if ( value && value.class != Date ) {
187                                        try {
188                                                fields[key] = (value as Date)
189                                        } catch (Exception e) {
190                                                error = true
191                                                errors.rejectValue(
192                                                        'templateDateFields',
193                                                        'templateEntity.typeMismatch.date',
194                                                        [key, value.class] as Object[],
195                                                        'Property {0} must be of type Date and is currently of type {1}'
196                                                )
197                                        }
198                                }
199                        }
200                        return (!error)
201                })
202                templateTermFields(validator: { fields, obj, errors ->
203                        def error = false
204                        fields.each { key, value ->
205                                if ( value && value.class != Term ) {
206                                        try {
207                                                fields[key] = (value as Term)
208                                        } catch (Exception e) {
209                                                error = true
210                                                errors.rejectValue(
211                                                        'templateTermFields',
212                                                        'templateEntity.typeMismatch.term',
213                                                        [key, value.class] as Object[],
214                                                        'Property {0} must be of type Term and is currently of type {1}'
215                                                )
216                                        }
217                                }
218                        }
219                        return (!error)
220                })
221        }
222
223        /**
224         * Get the proper templateFields Map for a specific field type
225         * @param TemplateFieldType
226         * @return pointer
227         * @visibility private
228         * @throws NoSuchFieldException
229         */
230        private Map getStore(TemplateFieldType fieldType) {
231                switch(fieldType) {
232                        case TemplateFieldType.STRING:
233                                return templateStringFields
234                        case TemplateFieldType.TEXT:
235                                return templateTextFields
236                        case TemplateFieldType.STRINGLIST:
237                                return templateStringListFields
238                        case TemplateFieldType.INTEGER:
239                                return templateIntegerFields
240                        case TemplateFieldType.DATE:
241                                return templateDateFields
242                        case TemplateFieldType.FLOAT:
243                                return templateFloatFields
244                        case TemplateFieldType.DOUBLE:
245                                return templateDoubleFields
246                        case TemplateFieldType.ONTOLOGYTERM:
247                                return templateTermFields
248                        default:
249                                throw new NoSuchFieldException("Field type ${fieldType} not recognized")
250                }
251        }
252
253        /**
254         * Find a field domain or template field by its name and return its description
255         * @param fieldsCollection the set of fields to search in, usually something like this.giveFields()
256         * @param fieldName The name of the domain or template field
257         * @return the TemplateField description of the field
258         * @throws NoSuchFieldException If the field is not found or the field type is not supported
259         */
260        private static def getField(Collection fieldsCollection, String fieldName) {
261                // escape the fieldName for easy matching
262                // (such escaped names are commonly used
263                // in the HTTP forms of this application)
264                String escapedLowerCaseFieldName = fieldName.toLowerCase().replaceAll("([^a-z0-9])","_")
265
266                // Find the target template field, if not found, throw an error
267                def field = fieldsCollection.find { it.name.toLowerCase().replaceAll("([^a-z0-9])", "_") == escapedLowerCaseFieldName }
268
269                if (field) {
270                        return field
271                }
272                else {
273                        throw new NoSuchFieldException("Field ${fieldName} not recognized")
274                }
275        }
276
277        /**
278         * Find a domain or template field by its name and return its value for this entity
279         * @param fieldName The name of the domain or template field
280         * @return the value of the field (class depends on the field type)
281         * @throws NoSuchFieldException If the field is not found or the field type is not supported
282         */
283        def getFieldValue(String fieldName) {
284                TemplateField field = getField(this.giveFields(),fieldName)
285                if (isDomainField(field)) {
286                        return this[field.name]
287                }
288                else {
289                        return getStore(field.type)[fieldName]
290                }
291        }
292
293        /**
294         * Check whether a given template field exists or not
295         * @param fieldName The name of the template field
296         * @return true if the given field exists and false otherwise
297         */
298        boolean fieldExists(String fieldName) {
299                // getField should throw a NoSuchFieldException if the field does not exist
300                try {
301                        TemplateField field = getField(this.giveFields(),fieldName)
302                }
303                // so return false in that case
304                catch(NoSuchFieldException e) {
305                        return false
306                }
307                // otherwise, return true (but double check if field really is not null)
308                if (field) {
309                        return true
310                }
311                else {
312                        return false
313                }
314        }
315
316        /**
317         * Set a template/entity field value
318         * @param fieldName The name of the template or entity field
319         * @param value The value to be set, this should align with the (template) field type, but there are some convenience setters
320         */
321        def setFieldValue(String fieldName, value) {
322
323                TemplateField field = getField(this.giveFields(),fieldName)
324
325                // Convenience setter for template string list fields: find TemplateFieldListItem by name
326                if (field.type == TemplateFieldType.STRINGLIST && value && value.class == String) {
327                        value = getField(field.listEntries,value).name
328                        println value
329                }
330
331                // Convenience setter for dates: handle string values for date fields
332                if (field.type == TemplateFieldType.DATE && value && value.class == String) {
333                        // a string was given, attempt to transform it into a date instance
334                        // and -for now- assume the dd/mm/yyyy format
335                        def dateMatch = value =~ /^([0-9]{1,})([^0-9]{1,})([0-9]{1,})([^0-9]{1,})([0-9]{1,})((([^0-9]{1,})([0-9]{1,2}):([0-9]{1,2})){0,})/
336                        if (dateMatch.matches()) {
337                                // create limited 'autosensing' datetime parser
338                                // assume dd mm yyyy  or dd mm yy
339                                def parser = 'd' + dateMatch[0][2] + 'M' + dateMatch[0][4] + (((dateMatch[0][5] as int) > 999) ? 'yyyy' : 'yy')
340
341                                // add time as well?
342                                if (dateMatch[0][7] != null) {
343                                        parser += dateMatch[0][6] + 'HH:mm'
344                                }
345
346                                value = new Date().parse(parser, value)
347                        }
348                }
349
350                // Set the field value
351                if (isDomainField(field)) {
352                        this[field.name] = value
353                }
354                else {
355                        // Caution: this assumes that all template...Field Maps are already initialized (as is done now above as [:])
356                        // If that is ever changed, the results are pretty much unpredictable (random Java object pointers?)!
357                        def store = getStore(field.type)
358                        if (!value && store[fieldName]) {
359                                println "removing " + ((super) ? super.class : '??') + " template field: " + fieldName
360
361                                // remove the item from the Map (if present)
362                                store.remove(fieldName)
363                        } else if (value) {
364                                println "setting " + ((super) ? super.class : '??') + " template field: " + fieldName + " ([" + value.toString() + "] of type [" + value.class + "])"
365
366                                // set value
367                                store[fieldName] = value
368                        }
369                }
370
371                return this
372        }
373
374        boolean isDomainField(TemplateField field) {
375                return this.giveDomainFields()*.name.contains(field.name)
376        }
377
378        boolean isDomainField(String fieldName) {
379                return this.giveDomainFields()*.name.contains(fieldName)
380        }
381
382        /**
383         * Return all fields defined in the underlying template and the built-in
384     * domain fields of this entity
385         */
386        def List<TemplateField> giveFields() {
387                return this.giveDomainFields() + this.giveTemplateFields();
388        }
389
390        /**
391         * Return all templated fields defined in the underlying template of this entity
392         */
393        def List<TemplateField> giveTemplateFields() {
394                return this.template.fields;
395        }
396
397        /**
398         * Return all relevant 'built-in' domain fields of the super class
399         * @return List with DomainTemplateFields
400     * @see TemplateField
401         */
402        abstract List<TemplateField> giveDomainFields()
403
404        /**
405         * Convenience method. Returns all unique templates used within a collection of TemplateEntities.
406         */
407        static List<Template> giveTemplates(Set<TemplateEntity> entityCollection) {
408                return entityCollection*.template.unique();
409        }
410
411        /**
412         * Convenience method. Returns the template used within a collection of TemplateEntities.
413         * @throws NoSuchFieldException when 0 or multiple templates are used in the collection
414         * @return The template used by all members of a collection
415         */
416        static Template giveSingleTemplate(Set<TemplateEntity> entityCollection) {
417                def templates = giveTemplates(entityCollection);
418                if (templates.size() == 0) {
419                        throw new NoSuchFieldException("No templates found in collection!")
420                } else if (templates.size() == 1) {
421                        return templates[0];
422                } else {
423                        throw new NoSuchFieldException("Multiple templates found in collection!")
424                }
425        }
426}
Note: See TracBrowser for help on using the repository browser.