root/trunk/grails-app/domain/dbnp/studycapturing/TemplateEntity.groovy @ 397

Revision 397, 13.0 KB (checked in by duh, 4 years ago)

- reformatted source code
- added mega huge big ASCII comments to UNDOCUMMENTED of not properly DOCUMENTED classess... we have agreed many many MANY times that everyone should document their code according to JavaDoc? style... but why does nobody do it, or just keep SLACKING! /annoyed modus

  • Property svn:keywords set to Date Author Rev
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$
11 * $Author$
12 * $Date$
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 domain or template field by its name and return its value for this entity
255         * @param fieldName The name of the domain or template field
256         * @return the value of the field (class depends on the field type)
257         * @throws NoSuchFieldException If the field is not found or the field type is not supported
258         */
259        def getFieldValue(String fieldName) {
260                // escape the fieldName for easy matching
261                // (such escaped names are commonly used
262                // in the HTTP forms of this application)
263                def escapedLowerCaseFieldName = fieldName.toLowerCase().replaceAll("([^a-z0-9])","_")
264
265                // Find the target template field, if not found, throw an error
266                TemplateField field = this.giveFields().find { it.name.toLowerCase().replaceAll("([^a-z0-9])", "_") == escapedLowerCaseFieldName }
267
268                // field found?
269                if (field == null) {
270                        // no such field
271                        throw new NoSuchFieldException("Field ${fieldName} not recognized")
272                } else {
273                        return getStore(field.type)[fieldName]
274                }
275        }
276
277        /**
278         * Check whether a given template field exists or not
279         * @param fieldName The name of the template field
280         * @return true if the given field exists and false otherwise
281         */
282        def fieldExists(String fieldName) {
283                // escape the fieldName for easy matching
284                // (such escaped names are commonly used
285                // in the HTTP forms of this application)
286                def escapedLowerCaseFieldName = fieldName.toLowerCase().replaceAll("([^a-z0-9])","_")
287
288                // check if this domain class has got this property
289                if (this.properties.containsKey(fieldName)) {
290                        // domain class contains this property
291                        return true
292                } else if (template == null) {
293                        // no, and we haven't got a template set either
294                        return false
295                } else {
296                        // the domain class doesn't have this property but
297                        // it has a template defined. Check the template
298                        // fields to see if such a template field exists
299                        TemplateField field = this.template.fields.find { it.name.toLowerCase().replaceAll("([^a-z0-9])","_") == escapedLowerCaseFieldName }
300
301                        // does the template field exist?
302                        if (field == null) {
303                                // no such template field
304                                return false
305                        } else {
306                                // found!
307                                return true
308                        }
309                }
310        }
311
312        /**
313         * Set a template/entity field value
314         * @param fieldName The name of the template or entity field
315         * @param value The value to be set, this should align with the (template) field type, but there are some convenience setters
316         */
317        def setFieldValue(String fieldName, value) {
318                // escape the fieldName for easy matching
319                // (such escaped names are commonly used
320                // in the HTTP forms of this application)
321                def escapedLowerCaseFieldName = fieldName.toLowerCase().replaceAll("([^a-z0-9])","_")
322
323                // Find the target template field, if not found, throw an error
324                TemplateField field = this.giveFields().find { it.name.toLowerCase().replaceAll("([^a-z0-9])", "_") == escapedLowerCaseFieldName }
325
326                // field found?
327                if (field == null) {
328                        // no such field
329                        throw new NoSuchFieldException("Field ${fieldName} not found in class template fields")
330                } else {
331                        // Set the value of the found template field
332                        // Convenience setter for template string list fields: find TemplateFieldListItem by name
333                        if (field.type == TemplateFieldType.STRINGLIST && value && value.class == String) {
334                                // Kees insensitive pattern matching ;)
335                                def escapedLowerCaseValue = value.toLowerCase().replaceAll("([^a-z0-9])", "_")
336                                value = field.listEntries.find {
337                                        it.name.toLowerCase().replaceAll("([^a-z0-9])", "_") == escapedLowerCaseValue
338                                }
339                        }
340
341                        // Convenience setter for dates: handle string values for date fields
342                        if (field.type == TemplateFieldType.DATE && value && value.class == String) {
343                                // a string was given, attempt to transform it into a date instance
344                                // and -for now- assume the dd/mm/yyyy format
345                                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,})/
346                                if (dateMatch.matches()) {
347                                        // create limited 'autosensing' datetime parser
348                                        // assume dd mm yyyy  or dd mm yy
349                                        def parser = 'd' + dateMatch[0][2] + 'M' + dateMatch[0][4] + (((dateMatch[0][5] as int) > 999) ? 'yyyy' : 'yy')
350
351                                        // add time as well?
352                                        if (dateMatch[0][7] != null) {
353                                                parser += dateMatch[0][6] + 'HH:mm'
354                                        }
355
356                                        value = new Date().parse(parser, value)
357                                }
358                        }
359
360                        // Set the field value
361                        def store = getStore(field.type)
362                        if (!value && store[fieldName]) {
363                                println "removing " + ((super) ? super.class : '??') + " template field: " + fieldName
364
365                                // remove the item from the Map (if present)
366                                store.remove(fieldName)
367                        } else if (value) {
368                                println "setting " + ((super) ? super.class : '??') + " template field: " + fieldName + " ([" + value.toString() + "] of type [" + value.class + "])"
369
370                                // set value
371                                store[fieldName] = value
372                        }
373                }
374                return this
375        }
376
377        /**
378         * Return all fields defined in the underlying template and the built-in
379     * domain fields of this entity
380         */
381        def List<TemplateField> giveFields() {
382                return this.giveDomainFields() + this.giveTemplateFields();
383        }
384
385        /**
386         * Return all templated fields defined in the underlying template of this entity
387         */
388        def List<TemplateField> giveTemplateFields() {
389                return this.template.fields;
390        }
391
392        /**
393         * Return all relevant 'built-in' domain fields of the super class
394         * @return List with DomainTemplateFields
395     * @see TemplateField
396         */
397        abstract List<TemplateField> giveDomainFields()
398
399        /**
400         * Convenience method. Returns all unique templates used within a collection of TemplateEntities.
401         */
402        static List<Template> giveTemplates(Set<TemplateEntity> entityCollection) {
403                return entityCollection*.template.unique();
404        }
405
406        /**
407         * Convenience method. Returns the template used within a collection of TemplateEntities.
408         * @throws NoSuchFieldException when 0 or multiple templates are used in the collection
409         * @return The template used by all members of a collection
410         */
411        static Template giveSingleTemplate(Set<TemplateEntity> entityCollection) {
412                def templates = giveTemplates(entityCollection);
413                if (templates.size() == 0) {
414                        throw new NoSuchFieldException("No templates found in collection!")
415                } else if (templates.size() == 1) {
416                        return templates[0];
417                } else {
418                        throw new NoSuchFieldException("Multiple templates found in collection!")
419                }
420        }
421}
Note: See TracBrowser for help on using the browser.