source: ZMS/trunk/_xmllib.py @ 1831

Revision 1831, 39.7 KB checked in by zmsdev, 2 months ago (diff)

ZMS2Go: prepared support for new simplified content-object model

Line 
1################################################################################
2# _xmllib.py
3#
4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License
6# as published by the Free Software Foundation; either version 2
7# of the License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17################################################################################
18
19# Imports.
20import pyexpat
21from App.Common import package_home
22from OFS.Image import File
23import Globals
24import copy
25import os
26import re
27import tempfile
28import time
29# Product Imports.
30from _objattrs import *
31import _blobfields
32import _globals
33import _fileutil
34
35
36INDENTSTR = '  '
37
38
39"""
40################################################################################
41#
42#  Datatypes
43#
44################################################################################
45"""
46
47# ------------------------------------------------------------------------------
48#  _xmllib.getXmlType:
49# ------------------------------------------------------------------------------
50def getXmlType(v):
51  t = ''
52  if type(v) is float:
53    t = ' type="float"'
54  elif type(v) is int:
55    t = ' type="int"'
56  elif type(v) is dict:
57    t = ' type="dictionary"'
58  elif type(v) is list:
59    t = ' type="list"'
60  elif type(v) is tuple or type(v) is time.struct_time:
61    t = ' type="datetime"'
62  elif isinstance(v,_blobfields.MyImage):
63    t = ' type="image"'
64  elif isinstance(v,_blobfields.MyFile):
65    t = ' type="file"'
66  return t
67
68
69# ------------------------------------------------------------------------------
70#  _xmllib.getXmlTypeSaveValue:
71# ------------------------------------------------------------------------------
72def getXmlTypeSaveValue(v, attrs):
73  # Strip.
74  if type(v) is str:
75    while len(v) > 0 and v[0] <= ' ':
76      v = v[1:]
77    while len(v) > 0 and v[-1] <= ' ':
78      v = v[:-1]
79  # Type.
80  t = attrs.get( 'type', '?')
81  if t == 'float':
82    try:
83      v = float(v)
84    except:
85      _globals.writeError(self,"[_xmllib.getXmlTypeSaveValue]: Conversion to '%s' failed for '%s'!"%(t,str(v)))
86  elif t == 'int':
87    try:
88      v = int(v)
89    except:
90      _globals.writeError(self,"[_xmllib.getXmlTypeSaveValue]: Conversion to '%s' failed for '%s'!"%(t,str(v)))
91  elif t == 'datetime':
92    new = _globals.parseLangFmtDate(v)
93    if new is not None:
94      v = new
95  # Return value.
96  return v
97
98
99"""
100################################################################################
101#
102#  XML-Encoding
103#
104################################################################################
105"""
106
107# ------------------------------------------------------------------------------
108#  _xmllib.xml_header:
109# ------------------------------------------------------------------------------
110def xml_header(encoding='utf-8'):
111  return '<?xml version="1.0" encoding="%s"?>\n'%encoding
112
113
114"""
115################################################################################
116#
117#  Import
118#
119################################################################################
120"""
121
122# ------------------------------------------------------------------------------
123#  _xmllib.xmlInitObjProperty:
124#
125#  Only for internal use: init specified property with value.
126# ------------------------------------------------------------------------------
127def xmlInitObjProperty(self, key, value, lang=None):
128 
129  #-- DEFINITION
130  obj_attr = self.getObjAttr(key)
131 
132  #-- ATTR
133  attr = self.getObjAttrName(obj_attr,lang)
134 
135  #-- DATATYPE
136  datatype = obj_attr['datatype_key']
137 
138  if value is not None:
139    #-- Date-Fields
140    if datatype in _globals.DT_DATETIMES:
141      if type(value) is str and len(value) > 0:
142        value = self.parseLangFmtDate(value)
143    #-- Integer-Fields
144    elif datatype in _globals.DT_INTS:
145      if type(value) is str and len(value) > 0:
146        value = int(value)
147    #-- Float-Fields
148    elif datatype == _globals.DT_FLOAT:
149      if type(value) is str and len(value) > 0:
150        value = float(value)
151    #-- String-Fields
152    elif datatype in _globals.DT_STRINGS:
153      value = str(value)
154 
155  #-- INIT
156  for ob in self.objectValues(['ZMSAttributeContainer']):
157    setattr(ob,attr,value)
158
159
160# ------------------------------------------------------------------------------
161#  _xmllib.xmlOnCharacterData:
162# ------------------------------------------------------------------------------
163def xmlOnCharacterData(self, sData, bInCData):
164
165  #-- TAG-STACK
166  if self.dTagStack.size() > 0:
167    tag = self.dTagStack.pop()
168    tag['cdata'] += sData
169    self.dTagStack.push(tag)
170
171  #-- Return
172  return 1  # accept any character data
173
174
175# ------------------------------------------------------------------------------
176#  _xmllib.xmlOnUnknownStartTag:
177# ------------------------------------------------------------------------------
178def xmlOnUnknownStartTag(self, sTagName, dTagAttrs):
179 
180  #-- TAG-STACK
181  tag = {'name':sTagName,'attrs':dTagAttrs,'cdata':''}
182  tag['dValueStack'] = self.dValueStack.size()
183  self.dTagStack.push(tag)
184 
185  #-- VALUE-STACK
186  if sTagName == 'lang':
187    if self.dValueStack.size()==0:
188      self.dValueStack.push({})
189  elif sTagName == 'dictionary':
190    self.dValueStack.push({})
191  elif sTagName == 'list':
192    self.dValueStack.push([])
193   
194  #-- Return
195  return 1  # accept any unknown tag
196
197
198# ------------------------------------------------------------------------------
199#  _xmllib.xmlOnUnknownEndTag:
200# ------------------------------------------------------------------------------
201def xmlOnUnknownEndTag(self, sTagName):
202 
203  #-- TAG-STACK
204  tag = self.dTagStack.pop()
205  name = tag['name']
206  if name==sTagName:
207   
208    attrs = _globals.unencode( tag['attrs'])
209    cdata = _globals.unencode( tag['cdata'])
210   
211    #-- ITEM (DICTIONARY|LIST) --
212    #----------------------------
213    if sTagName in ['list','dictionary']:
214      pass
215    elif sTagName in ['item']:
216      item = cdata
217      if tag['dValueStack'] < self.dValueStack.size():
218        item = self.dValueStack.pop()
219      else:
220        item = cdata
221      item = getXmlTypeSaveValue(item,attrs)
222      value = self.dValueStack.pop()
223      if type(value) is dict:
224        key = attrs.get( 'key')
225        value[key] = item
226      if type(value) is list:
227        value.append(item)
228      self.dValueStack.push(value)
229   
230    #-- DATA (IMAGE|FILE) --
231    #-----------------------
232    elif sTagName=='data':
233      value = attrs
234      if cdata is not None and len( cdata) > 0:
235        filename = attrs.get( 'filename')
236        content_type = attrs.get( 'content_type')
237        if content_type.find('text/') == 0:
238          data = cdata
239        else:
240          data = _globals.hex2bin(cdata)
241        value['data'] = data
242      self.dValueStack.push(value)
243   
244    #-- LANGUAGE --
245    #--------------
246    elif sTagName=='lang':
247      lang = attrs.get( 'id', self.getPrimaryLanguage())
248      if self.dValueStack.size()==1:
249        item = cdata
250      else:
251        item = self.dValueStack.pop()
252      values = self.dValueStack.pop()
253      values[lang] = item
254      self.dValueStack.push(values)
255      ##### Object State ####
256      req = {}
257      req['lang'] = lang
258      req['AUTHENTICATED_USER'] = 'xml'
259      self.setObjStateNew(req,reset=0)
260   
261    #-- OBJECT-ATTRIBUTES --
262    #-----------------------
263    elif sTagName in self.getObjAttrs().keys():
264      obj_attr = self.getObjAttr(sTagName)
265     
266      #-- DATATYPE
267      datatype = obj_attr['datatype_key']
268     
269      #-- Multi-Language Attributes.
270      if obj_attr['multilang']:
271        item = self.dValueStack.pop()
272        if item is not None:
273          if not type(item) is dict:
274            item = {self.getPrimaryLanguage():item}
275          for s_lang in item.keys():
276            value = item[s_lang]
277            # Data
278            if datatype in _globals.DT_BLOBS:
279              if type(value) is dict and len(value.keys()) > 0:
280                ob = _blobfields.createBlobField(self,datatype)
281                for key in value.keys():
282                  setattr(ob,key,value[key])
283                xmlInitObjProperty(self,sTagName,ob,s_lang)
284            # Others
285            else:
286              #-- Init Properties.
287              self.setObjProperty('change_uid','xml',s_lang)
288              self.setObjProperty('change_dt',time.time(),s_lang)
289              xmlInitObjProperty(self,sTagName,value,s_lang)
290     
291      else:
292        #-- Complex Attributes (Blob|Dictionary|List).
293        value = None
294        if self.dValueStack.size() > 0:
295          value = self.dValueStack.pop()
296        if value is not None and \
297           ( datatype in _globals.DT_BLOBS or \
298             datatype == _globals.DT_LIST or \
299             datatype == _globals.DT_DICT):
300          # Data
301          if datatype in _globals.DT_BLOBS:
302            if type(value) is dict and len(value.keys()) > 0:
303              ob = _blobfields.createBlobField(self,datatype)
304              for key in value.keys():
305                setattr(ob,key,value[key])
306              xmlInitObjProperty(self,sTagName,ob)
307          # Others
308          else:
309            if self.getType() == 'ZMSRecordSet':
310              if type( value) is list:
311                for item in value:
312                  if type( item) is dict:
313                    for key in item.keys():
314                      item_obj_attr = self.getObjAttr( key)
315                      item_datatype = item_obj_attr['datatype_key']
316                      if item_datatype in _globals.DT_BLOBS:
317                        item_data = item[ key]
318                        if type( item_data) is dict:
319                          blob = _blobfields.createBlobField( self, item_datatype, item_data)
320                          item[ key] = blob
321            #-- Convert multilingual to monolingual attributes.
322            if obj_attr['multilang']==0 and \
323               type(value) is dict and \
324               len(value.keys()) == 1 and \
325               value.keys()[0] == self.getPrimaryLanguage():
326              value = value[value.keys()[0]]
327            xmlInitObjProperty(self,sTagName,value)
328          if self.dValueStack.size() > 0:
329            raise "Items on self.dValueStack=%s"%self.dValueStack
330       
331        #-- Simple Attributes (String, Integer, etc.)
332        else:
333          if value is not None:
334            _globals.writeBlock( self, "[xmlOnUnknownEndTag]: WARNING - Skip %s=%s"%(sTagName,str(value)))
335          value = cdata
336          #-- OPTIONS
337          if obj_attr.has_key('options'):
338            options = obj_attr['options']
339            if type(options) is list:
340              try:
341                i = options.index(int(value))
342                if i%2==1: value = options[i-1]
343              except:
344                try:
345                  i = options.index(str(value))
346                  if i%2==1: value = options[i-1]
347                except:
348                  pass
349          xmlInitObjProperty(self,sTagName,value)
350     
351      # Clear value stack.
352      self.dValueStack.clear()
353   
354    #-- OTHERS --
355    #------------
356    else:
357      value = self.dTagStack.pop()
358      # ZMS < 2.11: titleshort => titlealt
359      if value is None and sTagName == 'titleshort' and 'titlealt' in self.getObjAttrs().keys():
360        sTagName = 'titlealt'
361        tag['name'] = sTagName
362        self.dTagStack.push( tag)
363        self.xmlOnUnknownEndTag( sTagName)
364      # ZMS >= 2.11
365      else:
366        if value is None: value = {'cdata':''}
367        cdata = value.get('cdata','')
368        cdata += '<' + tag['name']
369        for attr_name in attrs.keys():
370          attr_value = attrs.get( attr_name)
371          cdata += ' ' + attr_name + '="' + attr_value + '"'
372        cdata += '>' + tag['cdata']
373        cdata += '</' + tag['name'] + '>'
374        value['cdata'] = cdata
375        self.dTagStack.push(value)
376   
377    return 1 # accept matching end tag
378  else:
379    return 0  # don't accept any unknown tag
380
381
382"""
383################################################################################
384#
385#  Export
386#
387################################################################################
388"""
389
390# ------------------------------------------------------------------------------
391#  _xmllib.toCdata
392# ------------------------------------------------------------------------------
393def toCdata(self, s, xhtml=0):
394  rtn = ''
395 
396  # Return Text (HTML) in CDATA as XHTML.
397  from _filtermanager import processCommand
398  processId = 'tidy'
399  if xhtml == 0 \
400     and self.getConfProperty('ZMS.export.xml.tidy',0) \
401     and processId in self.getProcessIds():
402
403    # Create temporary folder.
404    folder = tempfile.mktemp()
405    os.mkdir(folder)
406
407    # Save <HTML> to file.
408    filename = _fileutil.getOSPath('%s/xhtml.html'%folder)
409    _fileutil.exportObj(s, filename)
410
411    # Call <HTML>Tidy
412    processOb = self.getProcess(processId)
413    command = processOb.get('command')
414    if command.find('{trans}') >= 0:
415      trans = _fileutil.getOSPath(package_home(globals())+'/conf/xsl/tidy.html2xhtml.conf')
416      command = command.replace('{trans}',trans)
417    filename = processCommand(self, filename, command)
418
419    # Read <XHTML> from file.
420    f = open(htmfilename,'rb')
421    rtn = f.read().strip()
422    f.close()
423   
424    # Read Error-Log from file.
425    f = open(logfilename,'rb')
426    log = f.read().strip()
427    f.close()
428
429    # Remove temporary files.
430    _fileutil.remove(folder,deep=1)
431
432    # Process <XHTML>.
433    prefix = '<p>'
434    if s[:len(prefix)] != prefix and rtn[:len(prefix)] == prefix:
435      rtn = rtn[len(prefix):]
436      suffix = '</p>'
437      if s[-len(suffix):] != suffix and rtn[-len(suffix):] == suffix:
438        rtn = rtn[:-len(suffix)]
439    f.close()
440
441    # Process Error-Log.
442    if log.find('0 errors') < 0:
443      rtn += '<!-- ' + log + '-->'
444
445  # Return Text.
446  elif xhtml == -1 \
447     or type(s) is float \
448     or type(s) is int:
449    rtn = s
450
451  # Return Text in CDATA.
452  elif s is not None:
453    # Hack for invalid characters
454    s = s.replace(chr(30),'')
455    # Hack for nested CDATA
456    s = re.compile( '\<\!\[CDATA\[(.*?)\]\]\>').sub( '<!{CDATA{\\1}}>', s)
457    rtn = '<![CDATA[%s]]>'%s
458
459  # Return.
460  return rtn
461
462
463# ------------------------------------------------------------------------------
464#  _xmllib.toXml:
465# ------------------------------------------------------------------------------
466def toXml(self, value, indentlevel=0, xhtml=0, encoding='utf-8'):
467  xml = []
468 
469  if value is not None:
470   
471    # Image
472    if isinstance(value,_blobfields.MyImage):
473      xml.append( '\n'+indentlevel*INDENTSTR+value.toXml( self))
474   
475    # File
476    elif isinstance(value,_blobfields.MyFile):
477      xml.append( '\n'+indentlevel*INDENTSTR+value.toXml( self))
478   
479    # File (Zope-native)
480    elif isinstance(value,File):
481      tagname = 'data'
482      xml.append( '\n'+indentlevel*INDENTSTR)
483      xml.append( '<%s'%tagname)
484      xml.append( ' content_type="%s"'%value.content_type)
485      xml.append( ' filename="%s"'%value.title)
486      xml.append( ' type="file"')
487      xml.append( '>')
488      if value.content_type.find( 'text/') == 0:
489        xml.append( '<![CDATA[%s]]>'%str(value.data))
490      else:
491        xml.append( _globals.bin2hex(value.data))
492      xml.append( '</%s>'%tagname)
493   
494    # Dictionaries
495    elif type(value) is dict:
496      keys = value.keys()
497      keys.sort()
498      xml.append( '\n'+indentlevel*INDENTSTR)
499      xml.append('<dictionary>')
500      indentstr = '\n'+(indentlevel+1)*INDENTSTR
501      for x in keys:
502        k = ' key="%s"'%x
503        xv = value[x]
504        tv = getXmlType(xv)
505        sv = toXml(self,xv,indentlevel+2,xhtml,encoding)
506        xml.append(indentstr)
507        xml.append('<item%s%s>'%(k,tv))
508        xml.append(sv)
509        if sv.find('\n') >= 0:
510          xml.append(indentstr)
511        xml.append('</item>')
512      xml.append( '\n'+indentlevel*INDENTSTR)
513      xml.append('</dictionary>')
514   
515    # Lists
516    elif type(value) is list:
517      xml.append( '\n'+indentlevel*INDENTSTR)
518      xml.append('<list>')
519      indentstr = '\n'+(indentlevel+1)*INDENTSTR
520      for xv in value:
521        k = ''
522        tv = getXmlType(xv)
523        sv = toXml(self,xv,indentlevel+2,xhtml,encoding)
524        xml.append(indentstr)
525        xml.append('<item%s%s>'%(k,tv))
526        xml.append(sv)
527        if sv.startswith('\n'):
528          xml.append(indentstr)
529        xml.append('</item>')
530      xml.append( '\n'+indentlevel*INDENTSTR)
531      xml.append('</list>')
532     
533    # Tuples (DateTime)
534    elif type(value) is tuple or type(value) is time.struct_time:
535      try:
536        s_value = self.getLangFmtDate(value,'eng','DATETIME_FMT')
537        if len(s_value) > 0:
538          xml.append( '\n'+indentlevel*INDENTSTR)
539          xml.append(toCdata(self,s_value,-1))
540      except:
541        pass
542   
543    # Numbers
544    elif type(value) is int or type(value) is float:
545      xml.append(str(value))
546 
547    # Others
548    else:
549      s_value = str(value)
550      if len(s_value) > 0:
551        xml.append(toCdata(self,s_value,xhtml))
552 
553  # Return xml.
554  return ''.join(map(lambda x: str(x),xml))
555
556
557# ------------------------------------------------------------------------------
558#  _xmllib.getAttrToXml:
559# ------------------------------------------------------------------------------
560def getAttrToXml(self, base_path, data2hex, obj_attr, REQUEST):
561  xml = ''
562 
563  #-- DATATYPE
564  datatype = obj_attr['datatype_key']
565 
566  #-- VALUE
567  obj_vers = self.getObjVersion(REQUEST)
568  value = self._getObjAttrValue(obj_attr,obj_vers,REQUEST.get('lang',self.getPrimaryLanguage()))
569 
570  if value is not None:
571   
572    # Retrieve value from options.
573    if obj_attr.has_key('options'):
574      options = obj_attr['options']
575      try:
576        i = options.index(int(value))
577        if i%2==0: value = options[i+1]
578      except:
579        try:
580          i = options.index(str(value))
581          if i%2==0: value = options[i+1]
582        except:
583          pass
584   
585    # Objects.
586    if datatype in _globals.DT_BLOBS:
587      xml += value.toXml( self, base_path, data2hex)
588   
589    # XML.
590    elif datatype == _globals.DT_XML or \
591         datatype == _globals.DT_BOOLEAN or \
592         datatype in _globals.DT_NUMBERS:
593      xml += toXml( self, value, -1)
594
595    # Others.
596    else:
597      xml += toXml(self,value)
598   
599  # Return xml.
600  return xml
601
602
603# ------------------------------------------------------------------------------
604#  _xmllib.getObjPropertyToXml:
605# ------------------------------------------------------------------------------
606def getObjPropertyToXml(self, base_path, data2hex, obj_attr, REQUEST):
607  xml = ''
608  # Multi-Language Attributes.
609  if obj_attr['multilang']:
610    lang = REQUEST.get('lang')
611    langIds = self.getLangIds()
612    for langId in langIds:
613      REQUEST.set('lang',langId)
614      s_attr_xml = getAttrToXml( self, base_path, data2hex, obj_attr, REQUEST)
615      if len(s_attr_xml) > 0:
616        xml += '\n<lang id="%s"'%langId
617        xml += '>%s</lang>'%s_attr_xml
618    REQUEST.set('lang',lang)
619  # Simple Attributes.
620  else:
621    xml += getAttrToXml( self, base_path, data2hex, obj_attr, REQUEST)
622  # Return xml.
623  return xml
624
625
626# ------------------------------------------------------------------------------
627#  _xmllib.getObjToXml:
628# ------------------------------------------------------------------------------
629def getObjToXml(self, REQUEST, incl_embedded=False, deep=True, base_path='', data2hex=False):
630  # Check Constraints.
631  root = getattr( self, '__root__', None)
632  if root is not None:
633    return ''
634  ob = self
635  if ob.meta_type == 'ZMSLinkElement' and ob.isEmbedded( REQUEST) and incl_embedded:
636    ob = ob.getRefObj()
637  xml = []
638  # Start tag.
639  xml.append('<%s'%ob.xmlGetTagName())
640  id = self.id
641  prefix = _globals.id_prefix(id)
642  if id == prefix:
643    xml.append(' id_fix="%s"'%id)
644  else:
645    xml.append(' id="%s"'%id)
646  if ob.getParentNode() is not None and ob.getParentNode().meta_type == 'ZMSCustom':
647    xml.append('\n id_prefix="%s"'%_globals.id_prefix(ob.id))
648  xml.append('>')
649  # Attributes.
650  keys = ob.getObjAttrs().keys()
651  if ob.getType()=='ZMSRecordSet':
652    keys = ['active',ob.getMetaobjAttrIds(ob.meta_id)[0]]
653  for key in keys:
654    obj_attr = ob.getObjAttr(key)
655    if obj_attr['xml']:
656      ob_prop = getObjPropertyToXml( ob, base_path, data2hex, obj_attr, REQUEST)
657      if len(ob_prop) > 0:
658        xml.append('\n<%s>%s</%s>'%(key,ob_prop,key))
659  # Process children.
660  if deep:
661    xml.extend(map(lambda x: getObjToXml( x, REQUEST, incl_embedded, deep, base_path+x.id+'/', data2hex), ob.getChildNodes()))
662  # End tag.
663  xml.append('</%s>\n'%ob.xmlGetTagName())
664  # Return xml.
665  return ''.join(xml)
666
667
668
669"""
670################################################################################
671# class ParseError(Exception):
672#
673# General exception class to indicate parsing errors.
674################################################################################
675"""
676class ParseError(Exception): pass
677
678
679
680"""
681################################################################################
682# class XmlAttrBuilder:
683#
684# Parser for complex Python-Attributes (dictionaries, lists).
685################################################################################
686"""
687class XmlAttrBuilder:
688    "class XmlAttrBuilder"
689
690    ######## class variables ########
691    iBufferSize=1028 * 32   # buffer size for XML file parsing
692
693    ############################################################################
694    # XmlAttrBuilder.__init__(self):
695    #
696    # Constructor.
697    ############################################################################
698    def __init__(self):
699      """ XmlAttrBuilder.__init__ """
700      pass
701
702
703    ############################################################################
704    # XmlAttrBuilder.parse(self, input):
705    #
706    # Parse a given XML document.
707    #
708    # IN:  input = XML document as string
709    #            = XML document as file object
710    # OUT: value or None, if nothing was parsed
711    ############################################################################
712    def parse(self, input, mediadbStorable=True):
713      """ XmlAttrBuilder.parse """
714     
715      # prepare builder
716      self.mediadbStorable = mediadbStorable
717      self.dValueStack     = _globals.MyStack()
718      self.dTagStack       = _globals.MyStack()
719     
720      # create parser object
721      p = pyexpat.ParserCreate()
722     
723      # connect parser object with handler methods
724      p.StartElementHandler = self.OnStartElement
725      p.EndElementHandler = self.OnEndElement
726      p.CharacterDataHandler = self.OnCharacterData
727      p.StartCdataSectionHandler = self.OnStartCData
728      p.EndCdataSectionHandler = self.OnEndCData
729      p.ProcessingInstructionHandler = self.OnProcessingInstruction
730      p.CommentHandler = self.OnComment
731      p.StartNamespaceDeclHandler = self.OnStartNamespaceDecl
732      p.EndNamespaceDeclHandler = self.OnEndNamespaceDecl
733     
734      #### parsing ####
735      if type(input) is str:
736        # input is a string!
737        rv = p.Parse(input, 1)
738      else:
739        # input is a file object!
740        while True:
741         
742          v=input.read(self.iBufferSize)
743          if v=="":
744            rv = 1
745            break
746         
747          rv = p.Parse(v, 0)
748          if not rv:
749            break
750           
751      # raise parser exception
752      if not rv:
753        raise ParseError('%s at line %s' % (pyexpat.ErrorString(p.ErrorCode), p.ErrorLineNumber))
754     
755      return self.dValueStack.pop()
756
757
758    ############################################################################
759    # XmlAttrBuilder.OnStartElement(self, name, attrs):
760    #
761    # Handler of XML-Parser:
762    # Called at the start of a XML element (resp. on occurence of a XML start tag).
763    # Usually, the occurence of a XML tag induces the instanciation of a new node object. Therefore,
764    # XmlAttrBuilder contains a mapping table ("dGlobalAttrs"), that maps XML tags to python classes. The
765    # newly created node object is then made current. If no matching class is found for a XML tag,
766    # the event handler "xmlOnUnknownStart()" is called on the current object.
767    #
768    # IN: name  = element name (=tag name)
769    #     attrs = dictionary of element attributes
770    ############################################################################
771    def OnStartElement(self, sTagName, dTagAttrs):
772      """ XmlAttrBuilder.OnStartElement """
773     
774      #-- TAG-STACK
775      tag = {'name':sTagName,'attrs':dTagAttrs,'cdata':''}
776      tag['dValueStack'] = self.dValueStack.size()
777      self.dTagStack.push(tag)
778     
779      #-- VALUE-STACK
780      if sTagName == 'data':
781        self.dValueStack.push(None)
782      elif sTagName == 'dictionary':
783        self.dValueStack.push({})
784      elif sTagName == 'list':
785        self.dValueStack.push([])
786
787
788    ############################################################################
789    # XmlAttrBuilder.OnEndElement(self, name):
790    #
791    # Handler of XML-Parser:
792    # Called at the end of a XML element (resp. on occurence of a XML end tag).
793    #
794    # IN: name  = element name (=tag name)
795    ############################################################################
796    def OnEndElement(self, sTagName):
797      """ XmlAttrBuilder.OnEndElement """
798     
799      #-- TAG-STACK
800      tag = self.dTagStack.pop()
801      name = _globals.unencode( tag['name'])
802      attrs = _globals.unencode( tag['attrs'])
803      cdata = _globals.unencode( tag['cdata'])
804      # Hack for nested CDATA
805      cdata = re.compile( '\<\!\{CDATA\{(.*?)\}\}\>').sub( '<![CDATA[\\1]]>', cdata)
806     
807      if name != sTagName:
808        raise ParseError("Unmatching end tag (" + str(sTagName) + ")")
809     
810      #-- DATA
811      if sTagName in ['data']:
812        filename = attrs.get( 'filename')
813        content_type = attrs.get( 'content_type')
814        if content_type.find('text/') == 0:
815          data = cdata
816        else:
817          data = _globals.hex2bin( cdata)
818        file = {'data':data,'filename':filename,'content_type':content_type}
819        objtype = attrs.get('type')
820        item = None
821        if objtype == 'image':
822          item = _blobfields.createBlobField( None, _globals.DT_IMAGE, file, self.mediadbStorable)
823        elif objtype == 'file':
824          item = _blobfields.createBlobField( None, _globals.DT_FILE, file, self.mediadbStorable)
825        for key in attrs.keys():
826          value = attrs.get( key)
827          setattr(item,key,value)
828        self.dValueStack.pop()
829        self.dValueStack.push(item)
830     
831      #-- ITEM
832      elif sTagName in ['item']:
833        if tag['dValueStack'] < self.dValueStack.size():
834          item = self.dValueStack.pop()
835        else:
836          item = cdata
837        item = getXmlTypeSaveValue(item,attrs)
838        value = self.dValueStack.pop()
839        if type(value) is dict:
840          key = attrs.get( 'key')
841          value[key] = item
842        if type(value) is list:
843          value.append(item)
844        self.dValueStack.push(value)
845
846
847    ############################################################################
848    # XmlAttrBuilder.OnCharacterData(self, data):
849    #
850    # Handler of XML-Parser:
851    # Called after plain character data was parsed. Forwards the character data to the current
852    # node. The class attribute "bInCData" determines, wether the character data is nested in a
853    # CDATA block.
854    #
855    # IN: data = character data string
856    ############################################################################
857    def OnCharacterData(self, sData):
858      """ XmlAttrBuilder.OnCharacterData """
859     
860      #-- TAG-STACK
861      if self.dTagStack.size() > 0:
862        tag = self.dTagStack.pop()
863        tag['cdata'] += sData
864        self.dTagStack.push(tag)
865
866
867    ############################################################################
868    # XmlAttrBuilder.OnStartCData(self):
869    #
870    # Handler of XML-Parser:
871    # Called at the start of a CDATA block (resp. on occurence of the "CDATA[" tag).
872    ############################################################################
873    def OnStartCData(self):
874      """ XmlAttrBuilder.OnStartCData """
875      self.bInCData = 1
876
877
878    ############################################################################
879    # XmlAttrBuilder.OnEndCData(self):
880    #
881    # Handler of XML-Parser:
882    # Called at the end of a CDATA block (resp. on occurence of the "]" tag).
883    ############################################################################
884    def OnEndCData(self):
885      """ XmlAttrBuilder.OnEndCData """
886      self.bInCData = 0
887
888
889    ############################################################################
890    # XmlAttrBuilder.OnProcessingInstruction(self, target, data):
891    #
892    # Handler of XML-Parser:
893    # Called on occurence of a processing instruction.
894    #
895    # IN: target = target (processing instruction)
896    #     data   = dictionary of data
897    ############################################################################
898    def OnProcessingInstruction(self, target, data):
899      """ XmlAttrBuilder.OnProcessingInstruction """
900      pass  # ignored
901
902
903    ############################################################################
904    # XmlAttrBuilder.OnComment(self, data):
905    #
906    # Handler of XML-Parser:
907    # Called on occurence of a comment.
908    #
909    # IN: data = comment string
910    ############################################################################
911    def OnComment(self, data):
912      """ XmlAttrBuilder.OnComment """
913      pass  # ignored
914
915
916    ############################################################################
917    # XmlAttrBuilder.OnStartNamespaceDecl(self, prefix, uri):
918    #
919    # Handler of XML-Parser:
920    # Called at the start of a namespace declaration.
921    #
922    # IN: prefix = prefix of namespace
923    #     uri    = namespace identifier
924    ############################################################################
925    def OnStartNamespaceDecl(self, prefix, uri):
926      """ XmlAttrBuilder.OnStartNamespaceDecl """
927      pass  # ignored
928
929
930    ############################################################################
931    # XmlAttrBuilder.OnEndNamespaceDecl(self, prefix):
932    #
933    # Handler of XML-Parser:
934    # Called at the end of a namespace declaration.
935    #
936    # IN: prefix = prefix of namespace
937    ############################################################################
938    def OnEndNamespaceDecl(self, prefix):
939      """ XmlAttrBuilder.OnEndNamespaceDecl """
940      pass  # ignored
941
942
943def xmlNodeSet(mNode, sTagName='', iDeep=0):
944  """
945  Retrieve node-set for given tag-name from dictionary of XML-Node-Structure.
946  @return: List of dictionaries of XML-Structure.
947  @rtype: C{list}
948  """
949  lNodeSet = []
950  lNode = mNode
951  if type(mNode) is list and len(mNode) == 2:
952    lNode = mNode[1]
953  lTags = lNode.get('tags',[])
954  for i in range(0,len(lTags)/2):
955    lTagName = lTags[i*2]
956    lNode = lTags[i*2+1]
957    if sTagName in [lTagName,'']:
958      lNodeSet.append(lNode)
959    if iDeep==1:
960      lNodeSet.extend(xmlNodeSet(lNode,sTagName,iDeep))
961  return lNodeSet
962
963
964"""
965################################################################################
966# class XmlBuilder:
967#
968# Parser for custom xml.
969################################################################################
970"""
971class XmlBuilder:
972    "class XmlBuilder"
973
974    ######## class variables ########
975    iBufferSize=1028 * 32   # buffer size for XML file parsing
976
977    ############################################################################
978    # XmlBuilder.__init__(self):
979    #
980    # Constructor.
981    ############################################################################
982    def __init__(self):
983      """ XmlBuilder.__init__ """
984      pass
985
986
987    ############################################################################
988    # XmlBuilder.parse(self, input):
989    #
990    # Parse a given XML document.
991    #
992    # IN:  input = XML document as string
993    #            = XML document as file object
994    # OUT: value or None, if nothing was parsed
995    ############################################################################
996    def parse(self, input):
997        """ XmlBuilder.parse """
998       
999        # prepare builder
1000        self.dTagStack = _globals.MyStack()
1001        self.dTagStack.push({'tags':[]})
1002       
1003        # create parser object
1004        p = pyexpat.ParserCreate()
1005       
1006        # connect parser object with handler methods
1007        p.StartElementHandler = self.OnStartElement
1008        p.EndElementHandler = self.OnEndElement
1009        p.CharacterDataHandler = self.OnCharacterData
1010        p.StartCdataSectionHandler = self.OnStartCData
1011        p.EndCdataSectionHandler = self.OnEndCData
1012        p.ProcessingInstructionHandler = self.OnProcessingInstruction
1013        p.CommentHandler = self.OnComment
1014        p.StartNamespaceDeclHandler = self.OnStartNamespaceDecl
1015        p.EndNamespaceDeclHandler = self.OnEndNamespaceDecl
1016       
1017        #### parsing ####
1018        if type(input) is str:
1019          # input is a string!
1020          rv = p.Parse(input, 1)
1021        else:
1022          # input is a file object!
1023          while True:
1024           
1025            v=input.read(self.iBufferSize)
1026            if v=="":
1027              rv = 1
1028              break
1029           
1030            rv = p.Parse(v, 0)
1031            if not rv:
1032              break
1033       
1034        # raise parser exception
1035        if not rv:
1036          raise ParseError('%s at line %s' % (pyexpat.ErrorString(p.ErrorCode), p.ErrorLineNumber))
1037       
1038        return self.dTagStack.pop()['tags']
1039
1040
1041    ############################################################################
1042    # XmlBuilder.OnStartElement(self, name, attrs):
1043    #
1044    # Handler of XML-Parser:
1045    # Called at the start of a XML element (resp. on occurence of a XML start tag).
1046    # Usually, the occurence of a XML tag induces the instanciation of a new node object. Therefore,
1047    # XmlBuilder contains a mapping table ("dGlobalAttrs"), that maps XML tags to python classes. The
1048    # newly created node object is then made current. If no matching class is found for a XML tag,
1049    # the event handler "xmlOnUnknownStart()" is called on the current object.
1050    #
1051    # IN: name  = element name (=tag name)
1052    #     attrs = dictionary of element attributes
1053    ############################################################################
1054    def OnStartElement(self, sTagName, dTagAttrs):
1055      """ XmlBuilder.OnStartElement """
1056      tag = {'name':sTagName,'attrs':dTagAttrs,'cdata':'','tags':[]}
1057      self.dTagStack.push(tag)
1058
1059
1060    ############################################################################
1061    # XmlBuilder.OnEndElement(self, name):
1062    #
1063    # Handler of XML-Parser:
1064    # Called at the end of a XML element (resp. on occurence of a XML end tag).
1065    #
1066    # IN: name  = element name (=tag name)
1067    ############################################################################
1068    def OnEndElement(self, sTagName):
1069      """ XmlBuilder.OnEndElement """
1070      lTag = self.dTagStack.pop()
1071      name = _globals.unencode( lTag['name'])
1072      attrs = _globals.unencode( lTag['attrs'])
1073      lCdata = _globals.unencode( lTag['cdata'])
1074      lTags = _globals.unencode( lTag['tags'])
1075
1076      if name != sTagName:
1077        raise ParseError("Unmatching end tag (" + sTagName + ")")
1078     
1079      lTag = {}
1080      lTag['level'] = self.dTagStack.size()
1081      lTag['name'] = name
1082      lTag['attrs'] = attrs
1083      lCdata = lCdata.strip()
1084      if len(lCdata) > 0:
1085        lTag['cdata'] = lCdata
1086      if len(lTags) > 0:
1087        lTag['tags'] = lTags
1088      parent = self.dTagStack.pop()
1089      parent['tags'].append(name)
1090      parent['tags'].append(lTag)
1091      self.dTagStack.push(parent)
1092
1093
1094    ############################################################################
1095    # XmlBuilder.OnCharacterData(self, data):
1096    #
1097    # Handler of XML-Parser:
1098    # Called after plain character data was parsed. Forwards the character data to the current
1099    # node. The class attribute "bInCData" determines, wether the character data is nested in a
1100    # CDATA block.
1101    #
1102    # IN: data = character data string
1103    ############################################################################
1104    def OnCharacterData(self, sData):
1105      """ XmlBuilder.OnCharacterData """
1106      tag = self.dTagStack.pop()
1107      tag['cdata'] = tag['cdata'] + sData
1108      self.dTagStack.push(tag)
1109
1110
1111    ############################################################################
1112    # XmlBuilder.OnStartCData(self):
1113    #
1114    # Handler of XML-Parser:
1115    # Called at the start of a CDATA block (resp. on occurence of the "CDATA[" tag).
1116    ############################################################################
1117    def OnStartCData(self):
1118      """ XmlBuilder.OnStartCData """
1119      self.bInCData=1
1120
1121
1122    ############################################################################
1123    # XmlBuilder.OnEndCData(self):
1124    #
1125    # Handler of XML-Parser:
1126    # Called at the end of a CDATA block (resp. on occurence of the "]" tag).
1127    ############################################################################
1128    def OnEndCData(self):
1129      """ XmlBuilder.OnEndCData """
1130      self.bInCData=0
1131
1132
1133    ############################################################################
1134    # XmlBuilder.OnProcessingInstruction(self, target, data):
1135    #
1136    # Handler of XML-Parser:
1137    # Called on occurence of a processing instruction.
1138    #
1139    # IN: target = target (processing instruction)
1140    #     data   = dictionary of data
1141    ############################################################################
1142    def OnProcessingInstruction(self, target, data):
1143      """ XmlBuilder.OnProcessingInstruction """
1144      pass  # ignored
1145
1146
1147    ############################################################################
1148    # XmlBuilder.OnComment(self, data):
1149    #
1150    # Handler of XML-Parser:
1151    # Called on occurence of a comment.
1152    #
1153    # IN: data = comment string
1154    ############################################################################
1155    def OnComment(self, data):
1156      """ XmlBuilder.OnComment """
1157      pass  # ignored
1158
1159
1160    ############################################################################
1161    # XmlBuilder.OnStartNamespaceDecl(self, prefix, uri):
1162    #
1163    # Handler of XML-Parser:
1164    # Called at the start of a namespace declaration.
1165    #
1166    # IN: prefix = prefix of namespace
1167    #     uri    = namespace identifier
1168    ############################################################################
1169    def OnStartNamespaceDecl(self, prefix, uri):
1170      """ XmlBuilder.OnStartNamespaceDecl """
1171      pass  # ignored
1172
1173
1174    ############################################################################
1175    # XmlBuilder.OnEndNamespaceDecl(self, prefix):
1176    #
1177    # Handler of XML-Parser:
1178    # Called at the end of a namespace declaration.
1179    #
1180    # IN: prefix = prefix of namespace
1181    ############################################################################
1182    def OnEndNamespaceDecl(self, prefix):
1183      """ XmlBuilder.OnEndNamespaceDecl """
1184      pass  # ignored
1185
1186###############################################################################################
Note: See TracBrowser for help on using the repository browser.