source: TI02-CSML/trunk/csml/csml2Moles/molesReadWrite.py @ 2166

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI02-CSML/trunk/csml/csml2Moles/molesReadWrite.py@2166
Revision 2166, 15.7 KB checked in by selatham, 14 years ago (diff)

added classes

Line 
1#Dominic Lowe, BADC  18 October 2006
2#Updated Sue Latham, BADC 6 December 2006 - add dgStructuredKeyword class
3
4import cElementTree as ET
5import sys
6
7
8#this is the NEW xml schema class
9
10class xmlSchema(object):
11    ''' this class creates a mapping of the xml schema sequences so that it can be referred to when
12    writing out a new moles document - this enables elements to be written in the order specified by the schema
13    If a particular sequence mapping can't be found then the elements will be written in whichever order python sees fit.
14    It tries various searches of the schema to pick out the sequences (which can be nested)
15    '''
16    def __init__(self, schema):
17        self.schema=schema
18        self.types={}
19        self.sequences={}
20        self.names=[]
21        self.setup()
22
23    def __isParent(self,elem):
24        #if this element is a parent of other elements (excluding annotation and documentation)
25        #then return True else return false
26        if elem[:]==[]:
27            return False
28        for child in elem[:]:
29            if child.tag == '{http://www.w3.org/2001/XMLSchema}element':
30                return True
31            elif child.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
32                return True
33            elif child.tag == '{http://www.w3.org/2001/XMLSchema}simpleType':
34                return True
35            elif child.tag == '{http://www.w3.org/2001/XMLSchema}annotation':
36                pass
37            elif child.tag == '{http://www.w3.org/2001/XMLSchema}sequence':
38                return True
39            elif child.tag == '{http://www.w3.org/2001/XMLSchema}choice':
40                return True
41            elif child.tag == '{http://www.w3.org/2001/XMLSchema}complexContent':
42                return True
43            elif child.tag == '{http://www.w3.org/2001/XMLSchema}documentation':
44                pass
45            elif child.tag == '{http://www.w3.org/2001/XMLSchema}restriction':
46                return True
47            elif child.tag == '{http://www.w3.org/2001/XMLSchema}extension':
48                pass
49            elif child.tag == '{http://www.w3.org/2001/XMLSchema}attribute':
50                pass #?
51            elif child.tag == '{http://www.w3.org/2001/XMLSchema}enumeration':
52                pass #?
53       
54             #pattern
55             #attributeGroup
56             #minLength
57             #minInclusive
58             #maxInclusive
59
60        return False
61       
62       
63       
64       
65    def __checkChildren(self, parentclassname, elem):     
66        print 'checking %s'%parentclassname
67        #if parentclassname == 'dgMetadataProvenance':
68            #pdb.set_trace()
69        for child in elem[:]:
70            childname = None
71            if child.attrib.has_key('name'):       
72                if child.attrib['name'][-4:]=='Type':
73                    childname=child.attrib['name'][:-4]
74                else:
75                    childname=child.attrib['name']       
76                if child.attrib.has_key('type'):
77                    if child.attrib['type'][:6]=='moles:':
78                        typename=child.attrib['type'][6:-4]
79                        self.types[childname]=typename             
80            elif child.attrib.has_key('ref'):
81                if child.attrib['ref'][:6]=='moles:':
82                    childname=child.attrib['ref'][6:]
83            if childname is not None:
84                if self.sequences[parentclassname] is None:
85                    self.sequences[parentclassname]=[childname]
86                else:
87                    tmp= self.sequences[parentclassname]                   
88                    tmp.append(childname)
89                    self.sequences[parentclassname]=tmp             
90                if self.__isParent(child):
91                    if not self.sequences.has_key(childname):
92                        self.sequences[childname]=[]
93                    self. __checkChildren(childname,child)   
94            else:
95                if self.__isParent(child):
96                    self. __checkChildren(parentclassname,child)   
97
98    def __resolveTypes(self):
99        for ty in self.types:
100            try:
101                self.sequences[ty]=self.sequences[self.types[ty]]
102            except:
103                pass
104           
105    def setup(self):   
106        tree = ET.parse(self.schema)
107        root = tree.getroot()
108        self.sequences['dummy']=[]
109        self.__checkChildren('dummy',root)
110        self.__resolveTypes()
111        self.sequences['dummy']=None
112   
113    def lookupOrder(self, dict,classname):
114        '''takes the attributes in a dictionary and orders them according to the schema sequence'''
115        try:       
116            order=self.sequences[classname]
117        except KeyError:
118            order = []
119            for key in dict:
120                #print key error
121                print 'KEY ERROR %s'%classname
122                if key is not 'schema':
123                    if key is not 'ns':
124                         order.append(key) # if it can't be found an unordered list is returned from the original dictionary items
125            #print 'returning %s'%order
126        return order
127
128
129#this is the old XML schema class. hopefully we can throw this away if the other one works better
130class OLDxmlSchema(object):
131    ''' this class creates a mapping of the xml schema sequences so that it can be referred to when
132    writing out a new moles document - this enables elements to be written in the order specified by the schema
133    If a particular sequence mapping can't be found then the elements will be written in whichever order python sees fit.
134    It tries various searches of the schema to pick out the sequences (which can be nested)
135    '''
136    def __init__(self, schema):
137        self.sequences={}
138        def __addSequence(molesname, sequence):
139            '''adds a new sequences to the list, also handles xs:choice'''
140            seqlist=[]
141            for elem in sequence[:]:
142                if elem.attrib.has_key('name'):
143                    seqlist.append(elem.attrib['name'])
144                if elem.attrib.has_key('ref'):
145                    seqlist.append(elem.attrib['ref'].split(':')[1])
146                if elem.tag == '{http://www.w3.org/2001/XMLSchema}choice':
147                    for subelem in elem[:]:
148                        if subelem.attrib.has_key('name'):
149                            seqlist.append(subelem.attrib['name'])
150            self.sequences[molesname]=seqlist
151       
152        def __checkSubElem(elem):
153            for subelem in elem[:]:
154                if subelem.tag=='{http://www.w3.org/2001/XMLSchema}sequence':
155                    __addSequence(molesclassname,subelem)
156                    __checkSubElem(subelem)
157       
158        def __checkDeepSubElem(elem):
159            for subelem in elem[:]:
160                if subelem.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
161                    for subsubelem in subelem[:]:
162                        if subsubelem.tag=='{http://www.w3.org/2001/XMLSchema}sequence':
163                            __addSequence(molesclassname,subsubelem)
164                            __checkDeepSubElem(subsubelem)   
165
166        for event, elem in ET.iterparse(schema):
167            '''look for sequences in complexTypes'''
168            if elem.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
169                if elem.attrib.has_key('name'):
170                    molesclassname= elem.attrib['name']
171                    if elem.attrib['name'][-4:]=='Type':
172                        molesclassname=molesclassname[:-4]
173                    __checkSubElem(elem)                   
174
175            if elem.tag == '{http://www.w3.org/2001/XMLSchema}element':
176                '''Look for parents of complex types - sometimes a complexType is declared but does not have a name and
177              the name belongs to the parent 'element' element.'''
178                if elem.attrib.has_key('name'):
179                    molesclassname= elem.attrib['name']
180                    __checkDeepSubElem(elem)
181
182            '''this bit handles the use of types. If some element has a name and a type attribute then if that type is complex it has
183            the same sequence as the type e.g. <xs:element name="dataModelID" type="moles:dgMetadataIDType">
184            need to run iterparse again as this needs to be done when all elements have already been parsed. '''
185            for event, elem in ET.iterparse(schema):
186                if elem.tag == '{http://www.w3.org/2001/XMLSchema}element':
187                    if elem.attrib.has_key('name'):
188                        molesclassname= elem.attrib['name']
189                        if elem.attrib.has_key('type'):
190                            typename=elem.attrib['type']
191                            if typename[-4:]=='Type':
192                                 typename=typename[:-4].split(':')[1]  #may also be prefixed by moles
193                            try:
194                                seq=self.sequences[typename]
195                                self.sequences[molesclassname]=seq
196                            except KeyError:
197                                pass  #can't find anything
198       
199           
200    def lookupOrder(self, dict,classname):
201        '''takes the attributes in a dictionary and orders them according to the schema sequence'''
202        try:       
203            order=self.sequences[classname]
204        except KeyError:
205            order = []
206            for key in dict:
207                #print key error
208                print 'KEY ERROR %s'%classname
209                if key is not 'schema':
210                    if key is not 'ns':
211                         order.append(key) # if it can't be found an unordered list is returned from the original dictionary items
212            #print 'returning %s'%order
213        return order
214
215class molesElement(object):
216    ''' molesElement class - base class of all elements '''
217    def __init__(self, namespace=None, **kwargs):
218        if namespace !=None:
219            self.ns=namespace
220        else:
221            self.ns = '{http://ndg.nerc.ac.uk/moles}'
222        self.__dict__.update(kwargs)
223   
224    def __combineattributes(self,attname, newChild):
225        att = getattr(self,attname)
226        if isinstance(att,molesElement):
227            setattr(self,attname,[att, newChild])
228        else:
229            att.append(newChild)
230            setattr(self, attname,att)
231           
232    def _stripNS(self, tagtostrip):
233        try:
234            elemname=tagtostrip.split('}')[1]
235            ns=tagtostrip.split('}')[0]+'}'
236        except IndexError:
237            elemname=tagtostrip
238            ns='{https://ndg.nerc.ac.uk/moles}'
239        return elemname, ns
240           
241        ns=tagtostrip.split('}')[1]
242     
243   
244    def addChildElem(self, childname, childobj):
245        #sometimes you want to add a child element but don't know if there is one already. In which case you want to create a list of child objects.
246        if hasattr(self, childname):
247            currentattribute=getattr(self,childname)
248            if type(getattr(self,childname)) is list:
249                currentattribute.append(childobj)
250            else:
251                newlist=[currentattribute]
252                newlist.append(childobj)
253                setattr(self,childname, newlist)
254        else:
255            setattr(self,childname, childobj)
256   
257    def toXML(self,molesFrag, schema=None):
258        if schema != None:
259            self.schema=schema
260        else:
261            self.schema=None
262        #print molesFrag.tag
263        orderedAttribs=schema.lookupOrder(self.__dict__,molesFrag.tag)
264        for item in orderedAttribs:
265           #print "item in orderedAttribs is %s" %item
266           if type(item) is xmlSchema:
267                        continue
268           if item == '{http://ndg.nerc.ac.uk/moles}':
269                continue
270           if hasattr(self, item):
271                if isinstance(self.__dict__[item], molesElement):
272                    frag=ET.Element(item)
273                    self.__dict__[item].toXML(frag,schema=self.schema)
274                    molesFrag.append(frag)
275                elif isinstance(self.__dict__[item], list):
276                    for it in self.__dict__[item]:
277                        if isinstance(it, molesElement):
278                            frag=ET.Element(item)
279                            it.toXML(frag, schema=self.schema)
280                            molesFrag.append(frag)
281                        else:
282                            frag=ET.Element(item)
283                            frag.text=it
284                else:
285                    frag=ET.Element(item)
286                    frag.text=self.__dict__[item]
287                    molesFrag.append(frag)
288        return molesFrag
289           
290    def fromXML(self,molesFrag):
291        children = molesFrag.getchildren()
292       
293        if children ==[]:
294            elementWithoutNS, ns=self._stripNS(molesElement.tag)
295            setattr(self,elementWithoutNS, molesElement.text)
296        if children!=[]:
297            for child in children:
298                if child.getchildren()!=[]:
299                    childWithoutNS, ns=self._stripNS(child.tag)
300                    newClass=type(childWithoutNS, (molesElement,),{})
301                    newChild=newClass(ns)
302                    newChild.fromXML(child)
303                    kw=child.tag
304                    if hasattr(self, childWithoutNS):
305                        self.__combineattributes(childWithoutNS, newChild)
306                    else:
307                        setattr(self,childWithoutNS, newChild)
308                else:
309                    childWithoutNS, ns=self._stripNS(child.tag)
310                    setattr(self,childWithoutNS, child.text)
311       
312                   
313class dgMetadata(molesElement):   
314    def __init__(self, **kwargs):
315        molesElement.__init__(self, **kwargs)
316       
317    def toXML(self):
318        self.schema = xmlSchema('ndgmetadata1.3.xsd')
319        molesFrag=ET.Element('dgMetadata')
320        molesFrag.attrib['xmlns']='http://ndg.nerc.ac.uk/moles'
321        molesElement.toXML(self,molesFrag,schema=self.schema)
322        return molesFrag
323
324
325class MolesDoc(object):
326    def __init__(self):
327        self._createClasses()
328   
329    def _create_a_class(self,name, base_class):
330        aNewClass=type(name, (base_class,),{})
331        return aNewClass
332
333    def _createClasses(self):
334        #if you want more classes just add their names to this list
335        #could probably examine the schema here....
336        classList= \
337        ['dataModelID', \
338        'dgDataGranule', \
339        'dgDataEntity', \
340        'dgMetadataRecord', \
341        'dgMetadataID',
342        'dgCoverage', \
343        'dgDataCoverage',\
344        'dgSpatioTemporalCoverage', \
345        'dgSpatialCoverage', \
346        'dgTemporalCoverage', \
347        'dgBoundingBox', \
348        'DateRange', \
349        'dgDataSummary', \
350        'dgParameterSummary', \
351        'dgParameterValue', \
352        'dgValueDataParameter', \
353        'dgStandardUnit', \
354        'dgOriginalUnit', \
355        'dgRangeDataParameter', \
356        'dgEnumerationParameter', \
357        'dgParameterGroup', \
358        'dgComponentParameter', \
359        'dgStdParameterMeasured', \
360        'dgStandardUnit', \
361        'dgValidTermID', \
362        'dgValidTermParentID', \
363        'dgValidSubterm', \
364        'metadataDescriptionID', \
365        'dgMetadataDescription', \
366        'dgStructuredKeyword', \
367        'abstract',\
368        'descriptionSection',\
369        'descriptionOnlineReference',\
370        'dgSimpleLink',\
371        'logos',\
372        'logoURI',\
373        'dgDataSetType',\
374        'dgSimulation',\
375        'dgAnalysis',\
376        'dgMeasurement',\
377        'dgFeatureType',\
378        'dgDataRoles',\
379        'dgDataCreator',\
380        'dgDataCurator',\
381        'dgRoleHolder',\
382        'dgOrganisationID',\
383        'dgPersonID',\
384        'dgRoleID',\
385        'contactDetails',\
386        'address',\
387        ]
388
389        for className in classList:
390            newClass=self._create_a_class(className, molesElement)
391            setattr(self,className,newClass)
Note: See TracBrowser for help on using the repository browser.