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

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