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

Last change on this file since 386 was 386, checked in by roberth, 9 years ago

Updated the template entities to be able to return domain fields, template fields and both.
Rolled back the change of Kees in the Event object, so the startTime and endTime fields returned.
Also updated the studies list with a new layout.

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