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

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

added giveDomainFields to TemplateEntity? entities, fixed some problems in BootStrap?

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