source: trunk/grails-app/domain/dbnp/studycapturing/Template.groovy @ 1264

Last change on this file since 1264 was 1264, checked in by work@…, 10 years ago

resolves issue #233, validation of an object which inherits from TemplateEntity? does not result in an error when required templateFields are missing.

  • Property svn:keywords set to Author Date Rev
File size: 8.3 KB
Line 
1package dbnp.studycapturing
2
3import dbnp.authentication.SecUser
4import dbnp.authentication.AuthenticationService
5
6/**
7 * The Template class describes a TemplateEntity template, which is basically an extension of the study capture entities
8 * in terms of extra fields (which are described by classes that extend the TemplateField class).
9 * Study, Subject, Sample and Event are all TemplateEntities.
10 *
11 * Within a Template, we have two different types of fields: 'domain fields' and 'template fields'.
12 * The domain fields are TemplateFields which are given by the TemplateEntity itself and therefore always present
13 * in any instance of that TemplateEntity. They are specified by implementing TemplateEntity.giveDomainFields()
14 * The template fields are TemplateFields which are added specifically by the Template. They are specified
15 * in the fields property of the Template object which is referenced by the TemplateEntity.template property.
16 *
17 * Revision information:
18 * $Rev: 1264 $
19 * $Author: work@osx.eu $
20 * $Date: 2010-12-14 16:44:23 +0000 (di, 14 dec 2010) $
21 */
22class Template extends Identity {
23
24        /** The name of the template */
25        String name
26
27        /** A string describing the template to other users */
28        String description
29
30        /** The target TemplateEntity for this template */
31        Class entity
32
33        /** The owner of the template. If the owner is not defined, it is a shared/public template */
34        SecUser owner
35
36        /** The template fields which are the members of this template. This is a List to preserve the field order */
37        List fields
38
39        static hasMany = [fields: TemplateField]
40        static mapping = {
41        }
42
43        // constraints
44        static constraints = {
45                owner(nullable: true, blank: true)
46                description(nullable: true, blank: true)
47
48                fields(validator: { fields, obj, errors ->
49                        // 'obj' refers to the actual Template object
50
51                        // define a boolean
52                        def error = false
53
54                        // iterate through fields
55                        fields.each { field ->
56                                // check if the field entity is the same as the template entity
57                                if (!field.entity.equals(obj.entity)) {
58                                        error = true
59                                        errors.rejectValue(
60                                                'fields',
61                                                'templateEntity.entityMismatch',
62                                                [field.name, obj.entity, field.entity] as Object[],
63                                                'Template field {0} must be of entity {1} and is currently of entity {2}'
64                                                )
65                                }
66                        }
67
68                        // got an error, or not?
69                        return (!error)
70                })
71
72                // outcommented for now due to bug in Grails / Hibernate
73                // see http://jira.codehaus.org/browse/GRAILS-6020
74                // This is to verify that the template name is unique with respect to the parent entity.
75                // TODO: this probably has to change in the case of private templates of different users,
76                // which can co-exist with the same name. See also TemplateField
77                // name(unique:['entity'])
78
79        }
80
81        public Template() {
82                super()
83        }
84
85        /**
86         * Creates a clone of the given other template and also copies its owner
87         *
88         * @param       otherTemplate
89         */
90        public Template( Template otherTemplate) {
91                this( otherTemplate, otherTemplate.owner )
92        }
93
94        /**
95         * Creates a clone of the given other template. The currently logged in user
96         * is set as the owner
97         * @param       otherTemplate
98         */
99        public Template( Template otherTemplate, SecUser owner ) {
100                this()
101
102                //authenticationService = new AuthenticationService()
103
104                this.name = otherTemplate.name + " (Copy)"
105                this.description = otherTemplate.description
106                this.entity = otherTemplate.entity
107                this.owner = owner
108
109                // The fields are copied by reference
110                this.fields = []
111                otherTemplate.fields.each {
112                        this.fields.add( it )
113                }
114        }
115
116        /**
117         * overloaded toString method
118         * @return String
119         */
120        def String toString() {
121                return this.name;
122        }
123
124        /**
125         * Check whether the contents of the other template and the current template are equal.
126         * For this check the name, description and owner don't matter. Also, the order of
127         * template fields doesn't matter
128         *
129         * @return      true iff this template and the other template are used for the same entity and
130         *                      the template contain the same template fields
131         */
132        public boolean contentEquals( Template otherTemplate ) {
133                if( otherTemplate == this )
134                        return true
135
136                if( otherTemplate == null )
137                        return false
138
139                if( otherTemplate.entity != this.entity )
140                        return false
141
142                // Check all template fields
143                if( otherTemplate.fields?.size() != this.fields?.size() )
144                        return false
145
146                if( otherTemplate.fields != null && this.fields != null ) {
147                        for( def field in this.fields ) {
148                                def fieldFound = false;
149                                for( def otherField in otherTemplate.fields ) {
150                                        if( otherField.contentEquals( field ) ) {
151                                                fieldFound = true;
152                                                break
153                                        }
154                                }
155
156                                if( !fieldFound ) {
157                                        return false
158                                }
159                        }
160                }
161
162                // If all tests pass, the objects are content-equal
163                return true
164        }
165
166        /**
167         * Look up the type of a certain template subject field
168         * @param String fieldName The name of the template field
169         * @return String       The type (static member of TemplateFieldType) of the field, or null of the field does not exist
170         */
171        def TemplateFieldType getFieldType(String fieldName) {
172                def field = fields.find {
173                        it.name == fieldName
174                }
175                field?.type
176        }
177
178        /**
179         * get all field of a particular type
180         * @param Class fieldType
181         * @return Set < TemplateField >
182         */
183        def getFieldsByType(TemplateFieldType fieldType) {
184                def result = fields.findAll {
185                        it.type == fieldType
186                }
187                return result;
188        }
189
190        /**
191         * get all required fields
192         * @param Class fieldType
193         * @return Set < TemplateField >
194         */
195        def getRequiredFields() {
196                def result = fields.findAll {
197                        it.required == true
198                }
199                return result;
200        }
201
202        /**
203         * get all required fields
204         * @param Class fieldType
205         * @return Set < TemplateField >
206         */
207        def getRequiredFieldsByType(TemplateFieldType fieldType) {
208                def result = fields.findAll {
209                        (it.required == true && it.type == fieldType)
210                }
211                return result;
212        }
213
214        /**
215         * Checks whether this template is used by any object
216         *
217         * @returns             true iff this template is used by any object, false otherwise
218         */
219        def inUse() {
220                return (numUses() > 0 );
221        }
222
223        /**
224         * The number of objects that use this template
225         *
226         * @returns             the number of objects that use this template.
227         */
228        def numUses() {
229                // This template can only be used in objects of the right entity. Find objects of that
230                // entity and see whether they use this method.
231                //
232                // Unfortunately, due to the grails way of creating classes, we can not use reflection for this
233                def elements;
234                switch( this.entity ) {
235                        case Event:
236                                elements = Event.findAllByTemplate( this ); break;
237                        case Sample:
238                                elements = Sample.findAllByTemplate( this ); break;
239                        case Study:
240                                elements = Study.findAllByTemplate( this ); break;
241                        case Subject:
242                                elements = Subject.findAllByTemplate( this ); break;
243                        default:
244                                return 0;
245                }
246
247                return elements.size();
248        }
249
250        /**
251         * overloading the findAllByEntity method to make it function as expected
252         * @param Class entity (for example: dbnp.studycapturing.Subject)
253         * @return ArrayList
254         */
255        public static findAllByEntity(java.lang.Class entity) {
256                def results = []
257                // 'this' should not work in static context, so taking Template instead of this
258                Template.findAll().each() {
259                        if (entity.equals(it.entity)) {
260                                results[results.size()] = it
261                        }
262                }
263
264                return results
265        }
266
267        /**
268         * Create a new template based on the parsed XML object.
269         *
270         * @see grails.converters.XML#parse(java.lang.String)
271         * @throws IllegalArgumentException
272         * @throws ClassNotFoundException
273         *
274         */
275        public static parse(Object xmlObject, SecUser loggedInUser) {
276                def t = new Template();
277                t.name = xmlObject?.name?.text()
278                t.description = xmlObject?.description?.text()
279
280                // Check whether a correct entity is given. The parseEntity method might
281                // throw a ClassNotFoundException, but that is OK, it should be thrown if the class
282                // doesn't exist.
283                def entity = TemplateEntity.parseEntity( xmlObject.entity?.text() );
284                if( !entity ) {
285                        throw new Exception( "Incorrect entity given" );
286                }
287
288                t.entity = entity
289
290                // Set the owner to the currently logged in user
291                t.owner = loggedInUser
292
293                // Set all template fields
294                xmlObject.templateFields.templateField.each {
295                        def field = TemplateField.parse( it, entity );
296
297                        // Check whether a similar field already exists. For that, we search in all
298                        // template fields with the same name, in order to have as little comparisons
299                        // as possible
300                        for( def otherField in TemplateField.findAllByName( field.name ) ) {
301                                if( field.contentEquals( otherField ) ) {
302                                        field = otherField;
303                                        break;
304                                }
305                        }
306                       
307                        t.addToFields( field );
308                }
309
310                return t
311        }
312}
Note: See TracBrowser for help on using the repository browser.