source: trunk/grails-app/services/dbnp/studyexport/ExportService.groovy @ 1581

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

set keyword expansion

  • Property svn:keywords set to Rev Author Date
File size: 9.8 KB
Line 
1/**
2 *  ExporterService
3 * 
4 *  @author Jahn
5 * 
6 *  This service for exporting a Study and all domain objects depending on it to XML.
7 */ 
8
9
10
11package dbnp.studyexport
12
13import dbnp.authentication.*
14import dbnp.studycapturing.*
15import grails.converters.XML
16import groovy.util.slurpersupport.*
17import org.dbnp.gdt.*
18
19class ExportService 
20{
21
22    static transactional = true
23    static scope = "session"
24
25
26    def grailsApplication
27    /**
28     *  List of classes that recursion does not go further into when building an XML 
29     *  document.
30         * 
31         *  @see #getRelatedObjects().
32     */ 
33        def static IgnoredClasses = [ String, long, Date, Boolean, SecUser, Publication, SecUser ]
34
35
36    /**
37     *  List of classes that recursion does not go further into when building an XML 
38     *  document; the elements are still included.
39         * 
40         *  (For importing Study objects)
41         * 
42         *  @see #getRelatedObjects().
43     */ 
44        def static TerminalClasses = [ AssayModule, Identity, Ontology, PersonAffiliation, 
45                        PersonRole, Template, TemplateField, 
46                        TemplateFieldListItem, TemplateFieldType, Term ] 
47
48
49    /**
50     *  List of domain classes related to Study.
51     */ 
52        def static DomainClasses = [ 'RegistrationCode':dbnp.authentication.RegistrationCode,
53                        'SecRole':dbnp.authentication.SecRole, 'SecUser':dbnp.authentication.SecUser,
54                        'SecUserSecRole':dbnp.authentication.SecUserSecRole,
55                        'SessionAuthenticatedUser':dbnp.authentication.SessionAuthenticatedUser,
56                        'Assay':dbnp.studycapturing.Assay,
57                        'Event':dbnp.studycapturing.Event, 'EventGroup':dbnp.studycapturing.EventGroup,
58                        'PersonAffiliation':dbnp.studycapturing.PersonAffiliation,
59                        'Person':dbnp.studycapturing.Person,
60                        'PersonRole':dbnp.studycapturing.PersonRole,
61                        'Publication':dbnp.studycapturing.Publication,
62                        'Sample':dbnp.studycapturing.Sample,
63                        'SamplingEvent':dbnp.studycapturing.SamplingEvent,
64                        'Study':dbnp.studycapturing.Study,
65                        'StudyPerson':dbnp.studycapturing.StudyPerson,
66                        'Subject':dbnp.studycapturing.Subject,
67                        'AssayModule':org.dbnp.gdt.AssayModule,
68                        'Identity':org.dbnp.gdt.Identity,
69                        'RelTime':org.dbnp.gdt.RelTime,
70                        'TemplateEntity':org.dbnp.gdt.TemplateEntity,
71                        'TemplateField':org.dbnp.gdt.TemplateField,
72                        'TemplateFieldListItem':org.dbnp.gdt.TemplateFieldListItem,
73                        'TemplateFieldType':org.dbnp.gdt.TemplateFieldType,
74                        'Template':org.dbnp.gdt.Template ]
75
76
77        /**
78         *  Returns a list of all Grails domain objects relevant for creating a full
79         *  XML representation of a Study.
80         * 
81         *  The actual XML is then created by the controller using Grails' XML converter.
82         * 
83         *  @param Study
84         *
85         *  @return List of all Grails domain objects
86         */ 
87
88        def getDependentObjects( Study study ) {
89                return getRelatedObjects( study )
90        }
91
92
93        /**
94         *  Returns a list of Grails domain objects. 
95         * 
96         *  Helper method for getDependentObjects().
97         * 
98         *  This method produces a list of all objects that need to be
99         *  written out in order to get an XML representation of a Study object.
100         * 
101         *  This is achieved by recursion. The recursion stops at objects whose
102         *  class is member of IgnoredClasses or TerminalClasses.
103         * 
104         *      Example call:
105         * 
106         *              def objects = getDependentObjects( Study.get(1) )
107         *              (objects*.class).unique().sort().each { println it }
108         * 
109         *  @param domainObject  A grails domain object.
110         * 
111         *  @return List of all Grails domain objects
112         */ 
113
114        def     getRelatedObjects( domainObject ) {
115
116                if(domainObject==null) {
117                        return []
118                }
119
120                def domainClass = domainObject.class
121                def objects = []
122
123
124                if( IgnoredClasses.contains(domainClass) )   {
125                        return objects
126                }
127
128                if( domainClass.toString()==~/class dbnp.authentication.SecUser.+/ || 
129                    domainClass.toString()==~/class dbnp.studycapturing.Publication.+/ ) {
130                        return objects
131                }
132
133
134                if(domainObject instanceof TemplateEntity ) {
135                        objects.push(domainObject.template)
136                        domainObject.template.fields.each { objects.push(it) }
137                }
138
139
140                if( TerminalClasses.contains(domainClass) )  {
141                        objects.push(domainObject)
142                        return objects
143                }
144
145                objects.push(domainObject)
146
147                                                                                                // enter recursion with regular domain fields
148                domainObject.properties.domainFields.each { field ->
149                        objects.addAll( getRelatedObjects(field) )
150                }
151
152                                                                                                // enter recursion with hasMany fields
153                domainObject.getProperties().hasMany.each { property, theClass ->
154
155                        boolean isTemplateField = ( domainObject instanceof TemplateEntity  && 
156                                property==~/template(.+)Fields/ ) 
157                        if( !isTemplateField ) {
158                                domainObject."$property".each { 
159                                                objects.addAll( getRelatedObjects(it) )
160                                }
161                        }
162
163                }
164                return objects.unique()
165        }
166
167
168
169
170        /**
171         *  Parse XML object to List of objects to be translated into
172         *  a Study by getStudy().
173         * 
174         *  @param Study
175         *
176         *  @return List of all Grails domain objects
177         *
178         *  @see getStudy()
179         */ 
180
181        def     parseXMLStudy() {
182                XML.parse(new FileInputStream('/tmp/test.xml'), "UTF-8")
183        }
184
185
186
187        def     parseXMLStudyList( GPathResult result ) {
188                def parseObjects = 
189                        result.childNodes().collect { 
190                                new ParseObject(it) 
191                        }               
192                def study = createStudy( parseObjects ) 
193                study.save()   
194        }
195
196
197
198
199        /**
200         *  Create Study from list of objects repesenting a Study.
201         * 
202         *  (For importing Study objects)
203         * 
204         *  @param  List of objects representing a Study.
205         *
206         *  @return void
207         */ 
208        def createStudy( List parseObjects ) {
209                parseObjects.each{ 
210                        populateOneToManies( it.domainObject, it.node, parseObjects )
211                }
212                parseObjects.each{ 
213                        if( it.domainObject instanceof TemplateEntity ) {
214                                addTemplateRelations( it.domainObject, it.node, parseObjects )
215                                addTemplateFields( it.domainObject, it.node, parseObjects )
216                        }
217                }
218        }
219
220
221        /** Set a TemplateEntity's template field. Find the right Template in the list 
222          *     of ParseObjects based on parsed id. If the TemplateEntity instance does
223          * not have an matching template in the list of ParseObjects, it remains empty.
224          *
225          * @param domainObject Some Template Entity
226          *
227          * @param node Node with parse information
228          *
229          * @param parseObjects List of ParseObjects
230          */
231        def addTemplateRelations( TemplateEntity domainObject, Node node, List parseObjects ) {
232                def id = node.children().find{it.name=='template'}?.attributes()?.id
233                if(id) {
234                        def template = parseObjects.find{ it.theClass==Template && it.id==id }?.domainObject
235                        if(template) {
236                                domainObject.template = template
237                        }
238                }
239        }
240
241
242
243        /** Set a TemplateEntity's template fields with values from a Node.
244          * The template fields are fields such as TemplateStringField or TemplateFieldType.
245          *
246          * @param domainObject Some Template Entity
247          *
248          * @param node Node with parse information
249          *
250          */
251        def addTemplateFields( TemplateEntity domainObject, Node node ) {
252                domainObject.metaClass.getProperties().each{ property ->
253                        def name = property.name      // name of templateFields, e.g., templateStringFields
254                        if( name ==~/template(.+)Fields/ ) {
255                                node.children().find{it.name==name}?.children()?.each{ fieldNode ->     
256                                        def key = fieldNode.attributes()?.key
257                                        def value = fieldNode.text()
258                                        //domainObject.setFieldValue(key,value)  -> needs to be fixed based on class/type
259                                }
260                        }
261                }
262        }
263
264
265
266        /**
267         *  Populate one-to-many maps of a new domainObject 
268         *  from list of ParseObjects.
269         * 
270         *  (For importing Study objects)
271         * 
272         *  @param domainObject   domainObject to be fielled 
273         * 
274         *  @param  List of parseObjects representing a Study.
275         *
276         *  @return the new domainObject
277         */ 
278        def populateOneToManies( domainObject, node, parseObjects ) {
279                if( !domainObject.class.metaClass.getProperties().find{ it.name=='hasMany' } ) {
280                        return
281                }
282
283                domainObject.class.hasMany.each{ name, theClass ->
284                        node.children().each { child -> 
285                                if(child.name==name) {
286                                        child.children().each { grandChild ->
287                                                def id = grandChild.attributes.id
288                                                if(id) {
289                                                        def ref = parseObjects.find{ it.theClass==theClass && it.id==id }
290                                                        if(ref) {
291                                                                def addTo = "addTo" + name.replaceFirst(name[0],name[0].toUpperCase()) 
292                                                                domainObject.invokeMethod(addTo,(Object) ref.domainObject )
293                                                        }
294                                                }
295                                        }
296                                }
297                        }
298                }
299        }
300
301
302
303        /**
304         *  Populate one-to-many maps of a new domainObject  from list of ParseObjects.
305         *
306         *  (For importing Study objects)
307         *
308         *  @param domainObject   domainObject to be fielled 
309         * 
310         *  @param  List of parseObjects representing a Study.
311         *
312         *  @return the new domainObject
313         */ 
314
315        private class ParseObject { 
316                String tag
317                String id
318                Class theClass
319                Object domainObject
320                Node node
321
322
323                public ParseObject( node ){
324                        tag = node.name()
325                        theClass = getClassForTag( tag ) 
326                        domainObject = theClass.newInstance() 
327                        id = null
328                        if(node.attributes && node.attributes.id) {
329                                id = node.attributes.id
330                        }
331                        this.node=node
332
333                        if(theClass==Template) {
334                                                                // Templates are suppsed to have been imported before
335                                                                // importing a study. Study.template is matched to a
336                                                                // Template by the template's name.
337                                def child = node.children.find{ "name"==it.name }
338                                domainObject = Template.findByName( child.text() )
339                        }
340                        else { 
341                                setSimpleFields()
342                        }
343                }
344
345
346
347                /**
348                 *  Populate this.domainObject's String and Date fields of
349                 *  a domainObject from parsed XML node.
350                 */ 
351                private void setSimpleFields() {
352
353                        def fields = 
354                                domainObject.getProperties().domainFields.collect { it.toString() }
355
356                        def map = [:]
357                        domainObject.metaClass.getProperties().each { property ->
358                                def name = property.name
359                                def field = fields.find{ it == name }
360
361                                if(field) { 
362                                        def type = property.type
363                                        def value = node.children().find{ it.name == field }.text()
364
365                                        switch(type) {
366                                                case String: map[field]=value; break
367                                                case Date: map[field] = Date.parse( 'yyyy-MM-dd', value ); break
368                                                //case Boolean: ???; break
369                                        }
370                                }
371                        }
372
373                        def newDomainObject = domainObject.class.newInstance(map)
374                        newDomainObject.id = 1  // neccessary?
375                        domainObject = newDomainObject
376                }
377
378        }
379
380
381}
Note: See TracBrowser for help on using the repository browser.