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

Last change on this file since 384 was 384, checked in by keesvb, 9 years ago

FINALLY found a possible way to integrate the domain fields as TemplateFields?, see Event. Also added required attribute for template fields.

  • Property svn:keywords set to Date Author Rev
File size: 12.4 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: 384 $
11 * $Author: keesvb $
12 * $Date: 2010-04-27 07:13:47 +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 templated fields defined in the underlying template of this entity
352        */
353        def List<TemplateField> giveFields() {
354                return this.template.fields;
355        }
356
357        def List<TemplateField> giveSystemFields() {
358                return systemFields;
359        }
360       
361
362        /**
363         * Return all relevant 'built-in' domain fields of the super class
364         * @return key-value pairs describing the built-in fields, with the names as keys and type (as TemplateFieldType) as values
365         */
366        abstract Map giveDomainFields()
367        /*def giveDomainFields() {
368                def fieldSet = [:];
369                if (super.hasProperty('name')) {
370                        fieldSet['name'] = TemplateFieldType.STRING;
371                }
372                return fieldSet;
373        }*/
374
375        // See revision 237 for ideas about initializing the different templateField Maps
376        // with tailored Maps that already contain the neccessary keys
377        /**
378         * Convenience method. Returns all unique templates used within a collection of TemplateEntities.
379         */
380        static List<Template> giveTemplates(Set<TemplateEntity> entityCollection) {
381                return entityCollection*.template.unique();
382        }
383
384        /**
385         * Convenience method. Returns the template used within a collection of TemplateEntities.
386         * @throws NoSuchFieldException when 0 or multiple templates are used in the collection
387         * @return The template used by all members of a collection
388         */
389        static Template giveSingleTemplate(Set<TemplateEntity> entityCollection) {
390                def templates = giveTemplates(entityCollection);
391                if (templates.size() == 0) {
392                        throw new NoSuchFieldException("No templates found in collection!")
393                } else if (templates.size() == 1) {
394                        return templates[0];
395                } else {
396                        throw new NoSuchFieldException("Multiple templates found in collection!")
397                }
398        }
399}
Note: See TracBrowser for help on using the repository browser.