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

Last change on this file since 1560 was 1560, checked in by j.saito@…, 10 years ago

Added import methods for XML import of studies.

Still not workig (and therefore omitted):

Still missing:

  • Error handling for clashes and omissions in importing
  • Removing unneccessary output of static fields in exporter
  • exporting templates (connect to Template exporter)
  • Move in Modules for Assay import
File size: 8.5 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.* 
17import groovy.util.slurpersupport.*
18import groovy.util.slurpersupport.Node as Node
19import java.util.ArrayList 
20import org.codehaus.groovy.grails.web.converters.configuration.ConvertersConfigurationHolder
21import org.dbnp.bgdt.*
22import org.dbnp.gdt.*
23import org.exolab.castor.xml.Unmarshaller
24
25
26class ExportService 
27{
28
29    static transactional = true
30    static scope = "session"
31
32
33    def grailsApplication
34    /**
35     *  List of classes that recursion does not go further into when building an XML 
36     *  document.
37         * 
38         *  @see #getRelatedObjects().
39     */ 
40        def static IgnoredClasses = [ String, long, Date, Boolean, SecUser, Publication, SecUser ]
41
42
43    /**
44     *  List of classes that recursion does not go further into when building an XML 
45     *  document; the elements are still included.
46         * 
47         *  (For importing Study objects)
48         * 
49         *  @see #getRelatedObjects().
50     */ 
51        def static TerminalClasses = [ AssayModule, Identity, Ontology, PersonAffiliation, 
52                        PersonRole, Template, TemplateField, 
53                        TemplateFieldListItem, TemplateFieldType, Term ] 
54
55
56    /**
57     *  List of domain classes related to Study.
58     */ 
59        def static DomainClasses = [ 'RegistrationCode':dbnp.authentication.RegistrationCode,
60                        'SecRole':dbnp.authentication.SecRole, 'SecUser':dbnp.authentication.SecUser,
61                        'SecUserSecRole':dbnp.authentication.SecUserSecRole,
62                        'SessionAuthenticatedUser':dbnp.authentication.SessionAuthenticatedUser,
63                        'Assay':dbnp.studycapturing.Assay,
64                        'Book':dbnp.studycapturing.Book,
65                        'Event':dbnp.studycapturing.Event, 'EventGroup':dbnp.studycapturing.EventGroup,
66                        'PersonAffiliation':dbnp.studycapturing.PersonAffiliation,
67                        'Person':dbnp.studycapturing.Person,
68                        'PersonRole':dbnp.studycapturing.PersonRole,
69                        'Publication':dbnp.studycapturing.Publication,
70                        'Sample':dbnp.studycapturing.Sample,
71                        'SamplingEvent':dbnp.studycapturing.SamplingEvent,
72                        'Study':dbnp.studycapturing.Study,
73                        'StudyPerson':dbnp.studycapturing.StudyPerson,
74                        'Subject':dbnp.studycapturing.Subject,
75                        'AssayModule':org.dbnp.gdt.AssayModule,
76                        'Identity':org.dbnp.gdt.Identity,
77                        'RelTime':org.dbnp.gdt.RelTime,
78                        'TemplateEntity':org.dbnp.gdt.TemplateEntity,
79                        'TemplateField':org.dbnp.gdt.TemplateField,
80                        'TemplateFieldListItem':org.dbnp.gdt.TemplateFieldListItem,
81                        'TemplateFieldType':org.dbnp.gdt.TemplateFieldType,
82                        'Template':org.dbnp.gdt.Template ]
83
84
85        /**
86         *  Returns a list of all Grails domain objects relevant for creating a full
87         *  XML representation of a Study.
88         * 
89         *  The actual XML is then created by the controller using Grails' XML converter.
90         * 
91         *  @param Study
92         *
93         *  @return List of all Grails domain objects
94         */ 
95
96        def getDependentObjects( Study study ) {
97                return getRelatedObjects( study )
98        }
99
100
101        /**
102         *  Returns a list of Grails domain objects. 
103         * 
104         *  Helper method for getDependentObjects().
105         * 
106         *  This method produces a list of all objects that need to be
107         *  written out in order to get an XML representation of a Study object.
108         * 
109         *  This is achieved by recursion. The recursion stops at objects whose
110         *  class is member of IgnoredClasses or TerminalClasses.
111         * 
112         *      Example call:
113         * 
114         *              def objects = getDependentObjects( Study.get(1) )
115         *              (objects*.class).unique().sort().each { println it }
116         * 
117         *  @param domainObject  A grails domain object.
118         * 
119         *  @return List of all Grails domain objects
120         */ 
121
122        def     getRelatedObjects( domainObject ) {
123
124                if(domainObject==null) {
125                        return []
126                }
127
128                def domainClass = domainObject.class
129                def objects = []
130
131                if( IgnoredClasses.contains(domainClass) )   {
132                        return objects
133                }
134
135                if( domainClass.toString()==~/class dbnp.authentication.SecUser.+/ || 
136                    domainClass.toString()==~/class dbnp.studycapturing.Publication.+/ ) {
137                        return objects
138                }
139
140
141
142                if( TerminalClasses.contains(domainClass) )  {
143                        return [domainObject]
144                }
145
146                objects = [domainObject]
147
148                                                                                                // enter recursion with regular domain fields
149                domainObject.properties.domainFields.each { field ->
150                        objects.addAll( getRelatedObjects(field) )
151                }
152
153                                                                                                // enter recursion with hasMany fields
154                domainObject.getProperties().hasMany.each { property, theClass ->
155
156                        boolean isTemplateField = ( domainObject instanceof TemplateEntity  && 
157                                property==~/template(.+)Fields/ ) 
158                        if( !isTemplateField ) {
159                                domainObject."$property".each { 
160                                                objects.addAll( getRelatedObjects(it) )
161                                }
162                        }
163
164                }
165                return objects.unique()
166        }
167
168
169
170
171        /**
172         *  Parse XML object to List of objects to be translated into
173         *  a Study by getStudy().
174         * 
175         *  @param Study
176         *
177         *  @return List of all Grails domain objects
178         *
179         *  @see getStudy()
180         */ 
181
182        def     parseXMLStudy() {
183                XML.parse(new FileInputStream('/tmp/test.xml'), "UTF-8")
184        }
185
186
187
188        def     parseXMLStudyList( GPathResult result ) {
189                def parseObjects = 
190                        result.childNodes().collect { 
191                                new ParseObject(it) 
192                        }               
193                def study = createStudy( parseObjects ) 
194                study.save()   
195        }
196
197
198
199
200        /**
201         *  Create Study from list of objects repesenting a Study.
202         * 
203         *  (For importing Study objects)
204         * 
205         *  @param  List of objects representing a Study.
206         *
207         *  @return void
208         */ 
209
210        def createStudy( List parseObjects ) {
211                parseObjects.each{ 
212                        populateFields( it.domainObject, it.node, parseObjects )
213                }
214                parseObjects.each{ 
215                        populateOneToManies( it.domainObject, it.node, parseObjects )
216                }
217                parseObjects.each{ 
218                        populateTemplateFields( it.domainObject, it.node, parseObjects )
219                }
220        }
221
222
223
224        /**
225         *  Populate fields of a domainObject to be created.
226         *  The fields of a new object are filled with simple Class objects.
227         * 
228         *  (For importing Study objects)
229         * 
230         *  @param domainObject   The Object to be fielled 
231         * 
232         *  @param  List of ParseObjects representing a Study.
233         *
234         *  @return the new domainObject
235         */ 
236
237        def populateFields( domainObject, node, List parseObjects ) {
238
239                def fields = 
240                        domainObject.getProperties().domainFields.collect { it.toString() }
241
242                def map = [:]
243                domainObject.metaClass.getProperties().each { property ->
244                        def name = property.name
245                        def field = fields.find{ it == name }
246
247                        if(field) { 
248                                def type = property.type
249                                def value = node.children().find{ it.name == field }.text()
250
251                                switch(type) {
252                                        case String: map[field]=value; break
253                                        case Date: map[field] = Date.parse( 'yyyy-MM-dd', value ); break
254                                }
255                        }
256                }
257
258                def newDomainObject = domainObject.class.newInstance(map)
259                newDomainObject.id = 0
260                domainObject = newDomainObject
261        }
262
263
264
265        /**
266         *  Populate one-to-many maps of a new domainObject 
267         *  from list of ParseObjects.
268         * 
269         *  (For importing Study objects)
270         * 
271         *  @param domainObject   domainObject to be fielled 
272         * 
273         *  @param  List of parseObjects representing a Study.
274         *
275         *  @return the new domainObject
276         */ 
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        def populateTemplateFields( domainObject, Node node, List parseObjects ) {
303        }
304
305
306
307        /**
308         *  Populate one-to-many maps of a new domainObject 
309         *  from list of ParseObjects.
310         *
311         *  (For importing Study objects)
312         *
313         *  @param domainObject   domainObject to be fielled 
314         * 
315         *  @param  List of parseObjects representing a Study.
316         *
317         *  @return the new domainObject
318         */ 
319
320        private class ParseObject { 
321                String tag
322                String id
323                Class theClass
324                Object domainObject
325                groovy.util.slurpersupport.Node node
326       
327
328                public ParseObject( node ){
329                        tag = node.name()
330                        theClass = getClassForTag( tag ) 
331                        domainObject = theClass.newInstance() 
332                        id = null
333                        if(node.attributes && node.attributes.id) {
334                                id = node.attributes.id
335                        }
336                        this.node=node
337                }
338
339                private static getClassForTag( String tag ) {
340                        def shortName = tag.replaceFirst( tag[0], tag[0].toUpperCase() )
341                        return DomainClasses[ shortName ] 
342                }
343
344        }
345
346}
Note: See TracBrowser for help on using the repository browser.