source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/models/xmlHandler2.py @ 2688

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/models/xmlHandler2.py@2760
Revision 2688, 11.2 KB checked in by lawrence, 13 years ago (diff)

Beginning to add the framework for an openlayer map,
but it's very incomplete ...

Line 
1try:
2    from xml.etree import cElementTree as ET
3    from xml.etree import ElementTree as pET
4except ImportError:
5    try:
6        import cElementTree as ET
7        import ElementTree as pET
8    except ImportError:
9        # For some reason when I install ElementTree with easyinstall it
10        # is called "elementree".
11        import elementtree.ElementTree as ET
12        pET=ET
13       
14from xml.parsers.expat import ExpatError
15import StringIO, re
16
17class xmlHandler:
18
19    def __init__(self,xml,string=0):
20        ''' Open an xml file (or string) and
21           - if necessary correct nasty characters and/or orphans before passing to ET
22           - load up an element-tree
23           - collect a namespace map '''
24       
25        self.r1=None   # we only use the regex if we need them
26       
27        if string:
28            self.xmls=xml
29            xmlf=None
30        else:
31            xmlf=xml
32            self.xmls=file(xmlf,'r').read()
33       
34        self.realns={}           
35        self.__getns()
36       
37        if xmlf is None: 
38            xmlf=StringIO.StringIO(self.xmls) # StringIO is supposed to be unicode! .encode('utf-8'))
39       
40        try:
41            self.tree=ET.parse(xmlf).getroot()
42        except SyntaxError:
43            self.xmls=self.__fixXML(self.xmls)
44            xmlf=StringIO.StringIO(self.xmls.encode('utf-8'))
45            self.tree=ET.parse(xmlf).getroot()
46           
47        self.__updatens()
48   
49    def __getns(self):
50       
51        ''' Get what the user intended out of elementtree namespaces '''
52        #ought to do this with a regular expression, but needs must
53        # or bettter yet, use iterparse in the first place, but that seemed slow.
54       
55
56        if self.xmls[0:19]=='<?xml version="1.0"':
57            self.root=1
58            hb1=self.xmls.find('>')+1
59        else:
60            hb1=0
61            self.root=0
62       
63        hb=self.xmls[hb1:].find('<')+1,self.xmls[hb1:].find('>')
64        s=self.xmls[hb1+hb[0]:hb1+hb[1]]
65        for w in s.split():
66            n=w.split('=')
67            if n[0]=='xmlns':
68                self.realns[n[1][1:-1]]='default'
69                self.defns=n[1][1:-1]#self.realns[n[1][1:-1]]
70            elif n[0][0:6]=='xmlns:':
71                self.realns[n[1][1:-1]]=n[0][6:]
72           
73    def tohtml(self):
74        '''Lightweight HTML pretty printing of elementTree elements
75           and formatted using a css something like this:
76            ===
77            DIV.xmlElem {PADDING-LEFT: 20px;}
78            .xmlAttrVal {COLOR:Red; }
79            .xmlAttrTyp {COLOR:Green; }
80            .xmlElemTag {COLOR:Blue; }
81        .   highlight {BACKGROUND-COLOR:Yellow; }
82            ===
83            Line number is not yet implemented.
84            '''
85        lt,gt='<b>&lt;</b>','<b>&gt;</b>'
86        def span(x,c): return '<span class="%s">%s</span>'%(c,x)
87        def div(x,c): return '<div class="%s">%s</div>'%(c,x)
88        def fix(x): 
89            if x is None: return ''
90            return x
91        def et2html(elem):   
92            strAttrib=''
93            for att in elem.attrib:
94                strAttrib+=' %s="%s"'%(span(att,'xmlAttrTyp'),span(elem.attrib[att],'xmlAttrVal'))
95            result='%s%s%s%s%s'%(lt,span(elem.tag,"xmlElemTag"),strAttrib,gt,fix(elem.text))
96            children=len(elem)
97            if children:
98                for item in elem:
99                    result+=et2html(item)
100                result+='%s%s/%s%s'%(fix(elem.tail),lt,span(elem.tag,'xmlElemTag'),gt)
101            else:
102                result+='%s/%s%s'%(lt,span(elem.tag,'xmlElemTag'),gt)
103            return div(result,'xmlElem')
104           
105        ss=et2html(self.tree)
106        h=''
107        if self.root:h='%s%s %s="%s" %s="%s"%s'%(
108            lt,'?xml',span('version','xmlAttrTyp'),'1.0',span('encoding','xmlAttrTyp'),'utf-8',gt)
109        ss=self.__fixXML(ss)
110        if self.realns=={}: 
111            r=h+ss
112        else: r= h+self.__nsfixpretty(ss,span)
113        return '<div class="xmlDoc">%s</div>'%r
114         
115    def __nsfixpretty(self,s,span):
116        ''' Yet another careful fix '''
117        for ns in self.realns:
118            r='{%s}'%ns
119            if self.realns[ns]=='default':
120                s=s.replace(r,'')
121            else:
122                s=s.replace(r,'%s:'%self.realns[ns])
123        if self.realns=={}: return s
124        # at this point we have no namespace list at the top
125        rightArrow=s.find('</span>') # this is just after the tag, where we do want the namespace list
126        nslist={} 
127        for ns in self.realns: nslist[self.realns[ns]]=ns
128        r=' %s="%s"'%(span('xmlns','xmlAttrTyp'),span(nslist['default'],'xmlAttrVal'))
129        for ns in nslist:
130            if ns<>'default': r+=' %s="%s"'%(span('xmlns:%s'%ns,'xmlAttrTyp'),span(nslist[ns],'xmlAttrVal'))
131        return s[:rightArrow]+r+s[rightArrow:]
132       
133    def __updatens(self):
134        ''' Update the element tree namespace map with our own map '''
135        # *c*ElementTree doesn't have this update method (or at
136        # least I can't find it), so you have to import ElementTree and call it on
137        # that, then it all mysteriously works in cElementTree...
138
139        pET._namespace_map.update(self.realns)
140
141         
142    def __str__(self):
143        ### actually we should consider whether this was in the input or not
144        h=''
145        if self.root:h='<?xml version="1.0" encoding="utf-8">'
146        ss=ET.tostring(self.tree)
147        ### ugly as sin, what happens if default: is in the text? We really ought to do this
148        #properly in iterparse on loading the thing ...
149        if self.realns=={}: return h+ss
150        return self.__fixns(h,ss)
151       
152    def __fixns(self,h,ss):
153        ''' Fix the namespaces after ET has produced a string '''
154        ss=ss.replace('default:','')
155        for ns in self.realns:
156            r='xmlns:%s="%s"'%(self.realns[ns],ns)
157            ss=ss.replace(r,'')
158        #now fix the namespaces back in the first element
159        rightArrow=ss.find('>')
160        #reorder dictionary (I know I didn't need to do it but
161        #code readability is worth a millisecond or two.
162        nslist={}
163        for ns in self.realns: nslist[self.realns[ns]]=ns
164        r='xmlns="%s"'%nslist['default']
165        for ns in nslist:
166            if ns<>'default': r+=' xmlns:%s="%s"'%(ns,nslist[ns])
167        h+=ss[:rightArrow]+r+ss[rightArrow:]
168        return h
169     
170    def __fixXML(self,s):
171        #first those nasty ampersands
172        s=re.sub(r'&(?!\w+;)', '&amp;', s)
173        #and now orphan > < signs
174        if self.r1 is None:
175            self.r1=re.compile('<([^>]*(<|$))')
176            self.r2=re.compile('((^|>)[^<]*)>')
177        old=''
178        while s != old:
179            old=s
180            s=self.r1.sub(r'&lt;\1',s)
181            s=self.r2.sub(r'\1&gt;',s)
182        return s
183       
184    def _distributens(self,xpathExpression):
185        ''' Actually we only support tag finding in this '''
186        tags=xpathExpression.split('/')
187        new=''
188        for t in tags: 
189            if t[1]<>'{': 
190                new+='{%s}%s/'%(self.defns,t)
191            else:
192                new+=t+'/'
193        new=new[0:-1]
194        return new
195       
196    def getText(self,xpathExpression,multiple=0):
197        ''' Get a text object sensibly, given ET API for xml doesn't handle
198        namespaces gracefully '''
199        elem=self.tree
200        if multiple:
201                r=elem.findall(self._distributens(xpathExpression))
202        else:
203                r=[elem.find(self._distributens(xpathExpression)),]
204        try:  # if element is None, this should fail ...
205                rr=[]
206                for i in r:
207                    t=i.text
208                    if t is not None: 
209                        rr.append(t)
210                    else: rr.append('')
211        except:
212                rr=['',]
213        if multiple: 
214                return rr
215        else: return rr[0]
216         
217         
218if __name__=="__main__":
219   
220    import unittest
221   
222    class TestCase(unittest.TestCase): 
223       
224        def setup(self):
225            self.ss='''<?xml version="1.0" encoding="UTF-8"?>
226                <Dataset xmlns:swe="http://www.opengis.net/swe" xmlns:gml="http://www.opengis.net/gml"
227                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:moles="http://ndg.nerc.ac.uk/moles"
228                 xmlns:om="http://www.opengis.net/om" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://ndg.nerc.ac.uk/csml"
229                 id="FGPfF9i0"><CSMLFeatureCollection gml:id="AfEj15o6"/><om:blah>blahvalue</om:blah><foo>foovalue</foo></Dataset>'''
230                 
231        def testns(self):
232            ''' Make sure we extract the namespaces correctly '''
233            self.setup()
234            x=xmlHandler(self.ss,string=1)
235            self.assertEqual(x.realns,{'http://www.opengis.net/om':'om', 'http://www.opengis.net/gml':'gml', 
236            'http://ndg.nerc.ac.uk/csml':'default', 'http://www.opengis.net/swe':'swe', 
237            'http://www.w3.org/1999/xlink':'xlink', 
238            'http://www.w3.org/2001/XMLSchema-instance':'xsi', 'http://ndg.nerc.ac.uk/moles':'moles'})
239           
240        def teststr(self):
241            ''' Make sure we can get a string version after loading '''
242            self.setup()
243            x=xmlHandler(self.ss,string=1)
244            self.assertEqual('<?xml version="1.0" encoding="utf-8"><Dataset id="FGPfF9i0" xmlns="http://ndg.nerc.ac.uk/csml" xmlns:om="http://www.opengis.net/om" xmlns:gml="http://www.opengis.net/gml" xmlns:swe="http://www.opengis.net/swe" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:moles="http://ndg.nerc.ac.uk/moles"><CSMLFeatureCollection gml:id="AfEj15o6"  /><om:blah >blahvalue</om:blah><foo>foovalue</foo></Dataset>',str(x))
245           
246        def testorphans(self):
247            ''' Make sure we can handle orphan characters properly '''
248            s='<data> 1<2</data>'
249            x=xmlHandler(s,string=1)
250            self.assertEqual('<data> 1&lt;2</data>',str(x))
251
252        def testAmpsersand1(self):
253            ''' Can we load unescaped ampersands?'''
254            s='<data> a & b </data>'
255            x=xmlHandler(s,string=1)
256            self.assertEqual('<data> a &amp; b </data>',str(x))
257           
258        def testAmpersand2(self):
259            ''' Do we output proper things? '''
260            s='<data> 2 &amp; 3 &lt; 8 </data>'
261            x=xmlHandler(s,string=1)
262            self.assertEqual('<data> 2 &amp; 3 &lt; 8 </data>',str(x))
263           
264        def testPrettyPrint(self):
265            ''' Test a simple pretty print '''
266            s='<?xml version="1.0" encoding="utf-8"?><data><element>stuff</element></data>'
267            x=xmlHandler(s,string=1)
268            h=x.tohtml() # only testing the mechanics, not the result
269           
270           
271        #turn off the test
272        def AtestRealDIF(self):
273            ''' Test a real DIF from the ndgRetrieve stable '''
274            f='ndgRetrieve.badc.nerc.ac.uk__DIF__dataent_11738019833217179.debug.xml'
275            x=xmlHandler(f)
276            y=str(x)  # only testing the mechanics, not the result
277           
278           
279        def testDIF(self):
280            s='''<DIF xmlns="http://gcmd.gsfc.nasa.gov/Aboutus/xml/dif/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Entry_ID>badc.nerc.ac.uk__DIF__dataent_11738019833217179</Entry_ID></DIF>'''
281            x=xmlHandler(s,string=1)
282            print x.realns
283            print str(x)
284            h=x.tohtml()
285            print h
286           
287    unittest.main()
288           
289           
Note: See TracBrowser for help on using the repository browser.