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

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

recursion handled better in schema checking

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
7class xmlSchema(object):
8    ''' this class creates a mapping of the xml schema sequences so that it can be referred to when
9    writing out a new moles document - this enables elements to be written in the order specified by the schema
10    If a particular sequence mapping can't be found then the elements will be written in whichever order python sees fit.
11    It tries various searches of the schema to pick out the sequences (which can be nested)
12    '''
13    def __init__(self, schema):
14        self.sequences={}
15        def __addSequence(molesname, sequence):
16            '''adds a new sequences to the list, also handles xs:choice'''
17            seqlist=[]
18            for elem in sequence[:]:
19                if elem.attrib.has_key('name'):
20                    seqlist.append(elem.attrib['name'])
21                if elem.attrib.has_key('ref'):
22                    seqlist.append(elem.attrib['ref'].split(':')[1])
23                if elem.tag == '{http://www.w3.org/2001/XMLSchema}choice':
24                    for subelem in elem[:]:
25                        if subelem.attrib.has_key('name'):
26                            seqlist.append(subelem.attrib['name'])
27            self.sequences[molesname]=seqlist
28       
29        def __checkSubElem(elem):
30            for subelem in elem[:]:
31                if subelem.tag=='{http://www.w3.org/2001/XMLSchema}sequence':
32                    __addSequence(molesclassname,subelem)
33                    __checkSubElem(subelem)
34       
35        def __checkDeepSubElem(elem):
36            for subelem in elem[:]:
37                if subelem.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
38                    for subsubelem in subelem[:]:
39                        if subsubelem.tag=='{http://www.w3.org/2001/XMLSchema}sequence':
40                            __addSequence(molesclassname,subsubelem)
41                            __checkDeepSubElem(subsubelem)   
42
43        for event, elem in ET.iterparse(schema):
44            '''look for sequences in complexTypes'''
45            if elem.tag == '{http://www.w3.org/2001/XMLSchema}complexType':
46                if elem.attrib.has_key('name'):
47                    molesclassname= elem.attrib['name']
48                    if elem.attrib['name'][-4:]=='Type':
49                        molesclassname=molesclassname[:-4]
50                    __checkSubElem(elem)                   
51
52            if elem.tag == '{http://www.w3.org/2001/XMLSchema}element':
53                '''Look for parents of complex types - sometimes a complexType is declared but does not have a name and
54              the name belongs to the parent 'element' element.'''
55                if elem.attrib.has_key('name'):
56                    molesclassname= elem.attrib['name']
57                    __checkDeepSubElem(elem)
58
59            '''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
60            the same sequence as the type e.g. <xs:element name="dataModelID" type="moles:dgMetadataIDType">
61            need to run iterparse again as this needs to be done when all elements have already been parsed. '''
62            for event, elem in ET.iterparse(schema):
63                if elem.tag == '{http://www.w3.org/2001/XMLSchema}element':
64                    if elem.attrib.has_key('name'):
65                        molesclassname= elem.attrib['name']
66                        if elem.attrib.has_key('type'):
67                            typename=elem.attrib['type']
68                            if typename[-4:]=='Type':
69                                 typename=typename[:-4].split(':')[1]  #may also be prefixed by moles
70                            try:
71                                seq=self.sequences[typename]
72                                self.sequences[molesclassname]=seq
73                            except KeyError:
74                                pass  #can't find anything
75       
76           
77    def lookupOrder(self, dict,classname):
78        '''takes the attributes in a dictionary and orders them according to the schema sequence'''
79        try:       
80            order=self.sequences[classname]
81        except KeyError:
82            order = []
83            for key in dict:
84                #print key error
85                print 'KEY ERROR %s'%classname
86                if key is not 'schema':
87                    if key is not 'ns':
88                         order.append(key) # if it can't be found an unordered list is returned from the original dictionary items
89            #print 'returning %s'%order
90        return order
91
92class molesElement(object):
93    ''' molesElement class - base class of all elements '''
94    def __init__(self, namespace=None, **kwargs):
95        if namespace !=None:
96            self.ns=namespace
97        else:
98            self.ns = '{http://ndg.nerc.ac.uk/moles}'
99        self.__dict__.update(kwargs)
100   
101    def __combineattributes(self,attname, newChild):
102        att = getattr(self,attname)
103        if isinstance(att,molesElement):
104            setattr(self,attname,[att, newChild])
105        else:
106            att.append(newChild)
107            setattr(self, attname,att)
108           
109    def _stripNS(self, tagtostrip):
110        try:
111            elemname=tagtostrip.split('}')[1]
112            ns=tagtostrip.split('}')[0]+'}'
113        except IndexError:
114            elemname=tagtostrip
115            ns='{https://ndg.nerc.ac.uk/moles}'
116        return elemname, ns
117           
118        ns=tagtostrip.split('}')[1]
119     
120   
121    def addChildElem(self, childname, childobj):
122        #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.
123        if hasattr(self, childname):
124            currentattribute=getattr(self,childname)
125            if type(getattr(self,childname)) is list:
126                currentattribute.append(childobj)
127            else:
128                newlist=[currentattribute]
129                newlist.append(childobj)
130                setattr(self,childname, newlist)
131        else:
132            setattr(self,childname, childobj)
133   
134    def toXML(self,molesFrag, schema=None):
135        if schema != None:
136            self.schema=schema
137        else:
138            self.schema=None
139        print molesFrag.tag
140        orderedAttribs=schema.lookupOrder(self.__dict__,molesFrag.tag)
141        for item in orderedAttribs:
142           print "item in orderedAttribs is %s" %item
143           if type(item) is xmlSchema:
144                        continue
145           if item == '{http://ndg.nerc.ac.uk/moles}':
146                continue
147           if hasattr(self, item):
148                if isinstance(self.__dict__[item], molesElement):
149                    frag=ET.Element(item)
150                    self.__dict__[item].toXML(frag,schema=self.schema)
151                    molesFrag.append(frag)
152                elif isinstance(self.__dict__[item], list):
153                    for it in self.__dict__[item]:
154                        if isinstance(it, molesElement):
155                            frag=ET.Element(item)
156                            it.toXML(frag, schema=self.schema)
157                            molesFrag.append(frag)
158                        else:
159                            frag=ET.Element(item)
160                            frag.text=it
161                else:
162                    frag=ET.Element(item)
163                    frag.text=self.__dict__[item]
164                    molesFrag.append(frag)
165        return molesFrag
166           
167    def fromXML(self,molesFrag):
168        children = molesFrag.getchildren()
169       
170        if children ==[]:
171            elementWithoutNS, ns=self._stripNS(molesElement.tag)
172            setattr(self,elementWithoutNS, molesElement.text)
173        if children!=[]:
174            for child in children:
175                if child.getchildren()!=[]:
176                    childWithoutNS, ns=self._stripNS(child.tag)
177                    newClass=type(childWithoutNS, (molesElement,),{})
178                    newChild=newClass(ns)
179                    newChild.fromXML(child)
180                    kw=child.tag
181                    if hasattr(self, childWithoutNS):
182                        self.__combineattributes(childWithoutNS, newChild)
183                    else:
184                        setattr(self,childWithoutNS, newChild)
185                else:
186                    childWithoutNS, ns=self._stripNS(child.tag)
187                    setattr(self,childWithoutNS, child.text)
188       
189                   
190class dgMetadata(molesElement):   
191    def __init__(self, **kwargs):
192        molesElement.__init__(self, **kwargs)
193       
194    def toXML(self):
195        self.schema = xmlSchema('ndgmetadata1.3.xsd')
196        molesFrag=ET.Element('dgMetadata')
197        molesFrag.attrib['xmlns']='http://ndg.nerc.ac.uk/moles'
198        molesElement.toXML(self,molesFrag,schema=self.schema)
199        return molesFrag
200
201
202class MolesDoc(object):
203    def __init__(self):
204        self._createClasses()
205   
206    def _create_a_class(self,name, base_class):
207        aNewClass=type(name, (base_class,),{})
208        return aNewClass
209
210    def _createClasses(self):
211        #if you want more classes just add their names to this list
212        #could probably examine the schema here....
213        classList= \
214        ['dataModelID', \
215        'dgDataGranule', \
216        'dgDataEntity', \
217        'dgMetadataRecord', \
218        'dgMetadataID',
219        'dgCoverage', \
220        'dgDataCoverage',\
221        'dgSpatioTemporalCoverage', \
222        'dgSpatialCoverage', \
223        'dgTemporalCoverage', \
224        'dgBoundingBox', \
225        'DateRange', \
226        'dgDataSummary', \
227        'dgParameterSummary', \
228        'dgParameterValue', \
229        'dgValueDataParameter', \
230        'dgStandardUnit', \
231        'dgOriginalUnit', \
232        'dgRangeDataParameter', \
233        'dgEnumerationParameter', \
234        'dgParameterGroup', \
235        'dgComponentParameter', \
236        'dgStdParameterMeasured', \
237        'dgStandardUnit', \
238        'dgValidTermID', \
239        'dgValidTermParentID', \
240        'dgValidSubterm', \
241        'metadataDescriptionID', \
242        'dgMetadataDescription', \
243        'dgStructuredKeyword', \
244        ]
245
246        for className in classList:
247            newClass=self._create_a_class(className, molesElement)
248            setattr(self,className,newClass)
Note: See TracBrowser for help on using the repository browser.