# HG changeset patch # User laurent # Date 1317395762 -7200 # Node ID 94c11207aa6f1ec1765f1bdb19e273237c9bcf8f # Parent 5024d42e1050866fb1c60dedd26fdf303580248b Moving xmlclass and docutils into plcopeneditor diff -r 5024d42e1050 -r 94c11207aa6f .hgignore --- a/.hgignore Fri Sep 23 20:07:40 2011 +0200 +++ b/.hgignore Fri Sep 30 17:16:02 2011 +0200 @@ -3,6 +3,4 @@ syntax: regexp \.pyc$ syntax: regexp -^xmlclass$ -syntax: regexp ^\.settings.*$ diff -r 5024d42e1050 -r 94c11207aa6f debian/control --- a/debian/control Fri Sep 23 20:07:40 2011 +0200 +++ b/debian/control Fri Sep 30 17:16:02 2011 +0200 @@ -7,7 +7,7 @@ Package: plcopeneditor Architecture: any -Depends: libwxgtk2.8-0, libwxbase2.8-0, xmlclass, docutils +Depends: libwxgtk2.8-0, libwxbase2.8-0 Description: The PLCopen Editor saves and loads XML projects accordingly to PLCopen TC6-XML Schemes. Edits all 5 of the IEC-61131-3 languages : - FBD -> Function Block Diagram diff -r 5024d42e1050 -r 94c11207aa6f docutils/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docutils/__init__.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from dochtml import * +from docpdf import * +from docsvg import * \ No newline at end of file diff -r 5024d42e1050 -r 94c11207aa6f docutils/dochtml.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docutils/dochtml.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import wx, os, wx.html, subprocess + +HtmlFrameOpened = [] + +def OpenHtmlFrame(self, title, file, size): + if title not in HtmlFrameOpened: + HtmlFrameOpened.append(title) + window = HtmlFrame(self, HtmlFrameOpened) + window.SetTitle(title) + window.SetHtmlPage(file) + window.SetClientSize(size) + window.Show() + +[ID_HTMLFRAME, ID_HTMLFRAMEHTMLCONTENT] = [wx.NewId() for _init_ctrls in range(2)] +EVT_HTML_URL_CLICK = wx.NewId() + +class HtmlWindowUrlClick(wx.PyEvent): + def __init__(self, linkinfo): + wx.PyEvent.__init__(self) + self.SetEventType(EVT_HTML_URL_CLICK) + self.linkinfo = (linkinfo.GetHref(), linkinfo.GetTarget()) + +class UrlClickHtmlWindow(wx.html.HtmlWindow): + """ HTML window that generates and OnLinkClicked event. + + Use this to avoid having to override HTMLWindow + """ + def OnLinkClicked(self, linkinfo): + wx.PostEvent(self, HtmlWindowUrlClick(linkinfo)) + + def Bind(self, event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY): + if event == HtmlWindowUrlClick: + self.Connect(-1, -1, EVT_HTML_URL_CLICK, handler) + else: + wx.html.HtmlWindow.Bind(event, handler, source=source, id=id, id2=id2) + +class HtmlFrame(wx.Frame): + def _init_ctrls(self, prnt): + wx.Frame.__init__(self, id=ID_HTMLFRAME, name='HtmlFrame', + parent=prnt, pos=wx.Point(320, 231), size=wx.Size(853, 616), + style=wx.DEFAULT_FRAME_STYLE, title='') + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) + + self.HtmlContent = UrlClickHtmlWindow(id=ID_HTMLFRAMEHTMLCONTENT, + name='HtmlContent', parent=self, pos=wx.Point(0, 0), + size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO|wx.html.HW_NO_SELECTION) + self.HtmlContent.Bind(HtmlWindowUrlClick, self.OnLinkClick) + + def __init__(self, parent, opened): + self._init_ctrls(parent) + self.HtmlFrameOpened = opened + + def SetHtmlCode(self, htmlcode): + self.HtmlContent.SetPage(htmlcode) + + def SetHtmlPage(self, htmlpage): + self.HtmlContent.LoadPage(htmlpage) + + def OnCloseFrame(self, event): + self.HtmlFrameOpened.remove(self.GetTitle()) + event.Skip() + + def OnLinkClick(self, event): + url = event.linkinfo[0] + try: + if wx.Platform == '__WXMSW__': + import webbrowser + webbrowser.open(url) + elif subprocess.call("firefox %s"%url, shell=True) != 0: + wx.MessageBox("""Firefox browser not found.\nPlease point your browser at :\n%s""" % url) + except ImportError: + wx.MessageBox('Please point your browser at: %s' % url) \ No newline at end of file diff -r 5024d42e1050 -r 94c11207aa6f docutils/docpdf.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docutils/docpdf.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import wx, os + +readerexepath = None + +def get_acroversion(): + " Return version of Adobe Acrobat executable or None" + import _winreg + adobesoft = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, r'Software\Adobe') + for index in range(_winreg.QueryInfoKey(adobesoft)[0]): + key = _winreg.EnumKey(adobesoft, index) + if "acrobat" in key.lower(): + acrokey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'Software\\Adobe\\%s' % key) + for index in range(_winreg.QueryInfoKey(acrokey)[0]): + numver = _winreg.EnumKey(acrokey, index) + try: + res = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, 'Software\\Adobe\\%s\\%s\\InstallPath' % (key, numver)) + return res + except: + pass + return None + +def open_win_pdf(readerexepath, pdffile, pagenum = None): + if pagenum != None : + os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", "/A", "page=%d=OpenActions" % pagenum, '"%s"'%pdffile) + else: + os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"'%pdffile) + +def open_lin_pdf(readerexepath, pdffile, pagenum = None): + if pagenum == None : + os.system("%s -remote DS301 %s &"%(readerexepath, pdffile)) + else: + print "Open pdf %s at page %d"%(pdffile, pagenum) + os.system("%s -remote DS301 %s %d &"%(readerexepath, pdffile, pagenum)) + +def open_pdf(pdffile, pagenum = None): + if wx.Platform == '__WXMSW__' : + try: + readerpath = get_acroversion() + except: + wx.MessageBox("Acrobat Reader is not found or installed !") + return None + + readerexepath = os.path.join(readerpath, "AcroRd32.exe") + if(os.path.isfile(readerexepath)): + open_win_pdf(readerexepath, pdffile, pagenum) + else: + return None + else: + readerexepath = os.path.join("/usr/bin","xpdf") + if(os.path.isfile(readerexepath)): + open_lin_pdf(readerexepath, pdffile, pagenum) + else: + wx.MessageBox("xpdf is not found or installed !") + return None diff -r 5024d42e1050 -r 94c11207aa6f docutils/docsvg.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docutils/docsvg.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import wx, os, subprocess + +def get_inkscape_path(): + """ Return the Inkscape path """ + import _winreg + svgexepath = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, + 'Software\\Classes\\svgfile\\shell\\Inkscape\\command') + svgexepath = svgexepath.replace('"%1"', '') + return svgexepath.replace('"', '') + +def open_win_svg(svgexepath, svgfile): + """ Open Inkscape on Windows platform """ + popenargs = [svgexepath] + if svgfile is not None : + popenargs.append(svgfile) + subprocess.Popen(popenargs).pid + +def open_lin_svg(svgexepath, svgfile): + """ Open Inkscape on Linux platform """ + if os.path.isfile("/usr/bin/inkscape"): + os.system("%s %s &"%(svgexepath , svgfile)) + +def open_svg(svgfile): + """ Generic function to open SVG file """ + if wx.Platform == '__WXMSW__' : + svgexepath = get_inkscape_path() + try: + open_win_svg(svgexepath , svgfile) + except: + wx.MessageBox("Inkscape is not found or installed !") + return None + else: + svgexepath = os.path.join("/usr/bin","inkscape") + if(os.path.isfile(svgexepath)): + open_lin_svg(svgexepath, svgfile) + else: + wx.MessageBox("Inkscape is not found or installed !") + return None + diff -r 5024d42e1050 -r 94c11207aa6f xmlclass/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmlclass/__init__.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,28 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Package initialisation + +from xmlclass import ClassFactory, GenerateClasses, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr +from xsdschema import XSDClassFactory, GenerateClassesFromXSD, GenerateClassesFromXSDstring diff -r 5024d42e1050 -r 94c11207aa6f xmlclass/po.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmlclass/po.xml Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,32 @@ + + + + Alice Smith + 123 Maple Street + Mill Valley + CA + 90952 + + + Robert Smith + 8 Oak Avenue + Old Town + PA + 95819 + + Hurry, my lawn is going wild! + + + Lawnmower + 1 + 148.95 + Confirm this is electric + + + Baby Monitor + 1 + 39.98 + 1999-05-21 + + + diff -r 5024d42e1050 -r 94c11207aa6f xmlclass/test.xsd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmlclass/test.xsd Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,66 @@ + + + + + Purchase order schema for Example.com. + Copyright 2000 Example.com. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 5024d42e1050 -r 94c11207aa6f xmlclass/xmlclass.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmlclass/xmlclass.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,1670 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import sys +import re +import datetime +from types import * +from xml.dom import minidom +from xml.sax.saxutils import escape, unescape, quoteattr +from new import classobj + +def CreateNode(name): + node = minidom.Node() + node.nodeName = name + node._attrs = {} + node.childNodes = [] + return node + +def NodeRenameAttr(node, old_name, new_name): + node._attrs[new_name] = node._attrs.pop(old_name) + +def NodeSetAttr(node, name, value): + attr = minidom.Attr(name) + text = minidom.Text() + text.data = value + attr.childNodes[0] = text + node._attrs[name] = attr + +""" +Regular expression models for checking all kind of string values defined in XML +standard +""" +Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$') +Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$') +NMToken_model = re.compile('([\w\.\-\:]*)$') +NMTokens_model = re.compile('([\w\.\-\:]*(?: [\w\.\-\:]*)*)$') +QName_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)$') +QNames_model = re.compile('((?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*(?: (?:[a-zA-Z_][\w]*:)?[a-zA-Z_][\w]*)*)$') +NCName_model = re.compile('([a-zA-Z_][\w]*)$') +URI_model = re.compile('((?:http://|/)?(?:[\w.-]*/?)*)$') +LANGUAGE_model = re.compile('([a-zA-Z]{1,8}(?:-[a-zA-Z0-9]{1,8})*)$') + +ONLY_ANNOTATION = re.compile("((?:annotation )?)") + +""" +Regular expression models for extracting dates and times from a string +""" +time_model = re.compile('([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)(?:Z)?$') +date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') +datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') + +class xml_timezone(datetime.tzinfo): + + def SetOffset(self, offset): + if offset == "Z": + self.__offset = timedelta(minutes = 0) + self.__name = "UTC" + else: + sign = {"-" : -1, "+" : 1}[offset[0]] + hours, minutes = [int(val) for val in offset[1:].split(":")] + self.__offset = timedelta(minutes=sign * (hours * 60 + minutes)) + self.__name = "" + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return ZERO + +[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, + ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG +] = range(12) + +def NotSupportedYet(type): + """ + Function that generates a function that point out to user that datatype + used is not supported by xmlclass yet + @param type: data type + @return: function generated + """ + def GetUnknownValue(attr): + raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % \ + type) + return GetUnknownValue + +""" +This function calculates the number of whitespace for indentation +""" +def getIndent(indent, balise): + first = indent * 2 + second = first + len(balise) + 1 + return u'\t'.expandtabs(first), u'\t'.expandtabs(second) + + +def GetAttributeValue(attr, extract=True): + """ + Function that extracts data from a tree node + @param attr: tree node containing data to extract + @param extract: attr is a tree node or not + @return: data extracted as string + """ + if not extract: + return attr + if len(attr.childNodes) == 1: + return unescape(attr.childNodes[0].data.encode()) + else: + # content is a CDATA + text = "" + for node in attr.childNodes: + if node.nodeName != "#text": + text += node.data.encode() + return text + + +def GetNormalizedString(attr, extract=True): + """ + Function that normalizes a string according to XML 1.0. Replace + tabulations, line feed and carriage return by white space + @param attr: tree node containing data to extract or data to normalize + @param extract: attr is a tree node or not + @return: data normalized as string + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + return value.replace("\t", " ").replace("\r", " ").replace("\n", " ") + + +def GetToken(attr, extract=True): + """ + Function that tokenizes a string according to XML 1.0. Remove any leading + and trailing white space and replace internal sequence of two or more + spaces by only one white space + @param attr: tree node containing data to extract or data to tokenize + @param extract: attr is a tree node or not + @return: data tokenized as string + """ + return " ".join([part for part in + GetNormalizedString(attr, extract).split(" ") + if part]) + + +def GetHexInteger(attr, extract=True): + """ + Function that extracts an hexadecimal integer from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as an integer + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if len(value) % 2 != 0: + raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) + try: + return int(value, 16) + except: + raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) + + +def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, + minExclusive=None, maxExclusive=None): + """ + Function that generates an extraction function for integer defining min and + max of integer value + @param minInclusive: inclusive minimum + @param maxInclusive: inclusive maximum + @param minExclusive: exclusive minimum + @param maxExclusive: exclusive maximum + @return: function generated + """ + def GetInteger(attr, extract=True): + """ + Function that extracts an integer from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as an integer + """ + + if extract: + value = GetAttributeValue(attr) + else: + value = attr + try: + # TODO: permit to write value like 1E2 + value = int(value) + except: + raise ValueError("\"%s\" isn't a valid integer!" % value) + if minInclusive is not None and value < minInclusive: + raise ValueError("\"%d\" isn't greater or equal to %d!" % \ + (value, minInclusive)) + if maxInclusive is not None and value > maxInclusive: + raise ValueError("\"%d\" isn't lesser or equal to %d!" % \ + (value, maxInclusive)) + if minExclusive is not None and value <= minExclusive: + raise ValueError("\"%d\" isn't greater than %d!" % \ + (value, minExclusive)) + if maxExclusive is not None and value >= maxExclusive: + raise ValueError("\"%d\" isn't lesser than %d!" % \ + (value, maxExclusive)) + return value + return GetInteger + + +def GenerateFloatExtraction(type, extra_values=[]): + """ + Function that generates an extraction function for float + @param type: name of the type of float + @return: function generated + """ + def GetFloat(attr, extract = True): + """ + Function that extracts a float from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a float + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if value in extra_values: + return value + try: + return float(value) + except: + raise ValueError("\"%s\" isn't a valid %s!" % (value, type)) + return GetFloat + + +def GetBoolean(attr, extract=True): + """ + Function that extracts a boolean from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a boolean + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if value == "true" or value == "1": + return True + elif value == "false" or value == "0": + return False + else: + raise ValueError("\"%s\" isn't a valid boolean!" % value) + + +def GetTime(attr, extract=True): + """ + Function that extracts a time from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a time + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + result = time_model.match(value) + if result: + values = result.groups() + time_values = [int(v) for v in values[:2]] + seconds = float(values[2]) + time_values.extend([int(seconds), int((seconds % 1) * 1000000)]) + return datetime.time(*time_values) + else: + raise ValueError("\"%s\" isn't a valid time!" % value) + + +def GetDate(attr, extract=True): + """ + Function that extracts a date from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a date + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + result = date_model.match(value) + if result: + values = result.groups() + date_values = [int(v) for v in values[:3]] + if values[3] is not None: + tz = xml_timezone() + tz.SetOffset(values[3]) + date_values.append(tz) + return datetime.date(*date_values) + else: + raise ValueError("\"%s\" isn't a valid date!" % value) + + +def GetDateTime(attr, extract=True): + """ + Function that extracts date and time from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as date and time + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + result = datetime_model.match(value) + if result: + values = result.groups() + datetime_values = [int(v) for v in values[:5]] + seconds = float(values[5]) + datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)]) + if values[6] is not None: + tz = xml_timezone() + tz.SetOffset(values[6]) + datetime_values.append(tz) + return datetime.datetime(*datetime_values) + else: + raise ValueError("\"%s\" isn't a valid datetime!" % value) + + +def GenerateModelNameExtraction(type, model): + """ + Function that generates an extraction function for string matching a model + @param type: name of the data type + @param model: model that data must match + @return: function generated + """ + def GetModelName(attr, extract=True): + """ + Function that extracts a string from a tree node or not and check that + string extracted or given match the model + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a string if matching + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + result = model.match(value) + if not result: + raise ValueError("\"%s\" isn't a valid %s!" % (value, type)) + return value + return GetModelName + + +def GenerateLimitExtraction(min=None, max=None, unbounded=True): + """ + Function that generates an extraction function for integer defining min and + max of integer value + @param min: minimum limit value + @param max: maximum limit value + @param unbounded: value can be "unbounded" or not + @return: function generated + """ + def GetLimit(attr, extract=True): + """ + Function that extracts a string from a tree node or not and check that + string extracted or given is in a list of values + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a string + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if value == "unbounded": + if unbounded: + return value + else: + raise ValueError("Member limit can't be defined to \"unbounded\"!") + try: + limit = int(value) + except: + raise ValueError("\"%s\" isn't a valid value for this member limit!" % value) + if limit < 0: + raise ValueError("Member limit can't be negative!") + elif min is not None and limit < min: + raise ValueError("Member limit can't be lower than \"%d\"!" % min) + elif max is not None and limit > max: + raise ValueError("Member limit can't be upper than \"%d\"!" % max) + return limit + return GetLimit + + +def GenerateEnumeratedExtraction(type, list): + """ + Function that generates an extraction function for enumerated values + @param type: name of the data type + @param list: list of possible values + @return: function generated + """ + def GetEnumerated(attr, extract=True): + """ + Function that extracts a string from a tree node or not and check that + string extracted or given is in a list of values + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a string + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if value in list: + return value + else: + raise ValueError("\"%s\" isn't a valid value for %s!" % \ + (value, type)) + return GetEnumerated + + +def GetNamespaces(attr, extract=True): + """ + Function that extracts a list of namespaces from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: list of namespaces + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if value == "": + return [] + elif value == "##any" or value == "##other": + namespaces = [value] + else: + namespaces = [] + for item in value.split(" "): + if item == "##targetNamespace" or item == "##local": + namespaces.append(item) + else: + result = URI_model.match(item) + if result is not None: + namespaces.append(item) + else: + raise ValueError("\"%s\" isn't a valid value for namespace!" % value) + return namespaces + + +def GenerateGetList(type, list): + """ + Function that generates an extraction function for a list of values + @param type: name of the data type + @param list: list of possible values + @return: function generated + """ + def GetLists(attr, extract=True): + """ + Function that extracts a list of values from a tree node or a string + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: list of values + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + if value == "": + return [] + elif value == "#all": + return [value] + else: + values = [] + for item in value.split(" "): + if item in list: + values.append(item) + else: + raise ValueError("\"%s\" isn't a valid value for %s!" % \ + (value, type)) + return values + return GetLists + + +def GenerateModelNameListExtraction(type, model): + """ + Function that generates an extraction function for list of string matching + a model + @param type: name of the data type + @param model: model that list elements must match + @return: function generated + """ + def GetModelNameList(attr, extract=True): + """ + Function that extracts a list of string from a tree node or not and + check that all extracted items match the model + @param attr: tree node containing data to extract or data as a string + @param extract: attr is a tree node or not + @return: data as a list of string if matching + """ + if extract: + value = GetAttributeValue(attr) + else: + value = attr + values = [] + for item in value.split(" "): + result = model.match(item) + if result is not None: + values.append(item) + else: + raise ValueError("\"%s\" isn't a valid value for %s!" % \ + (value, type)) + return values + return GetModelNameList + +def GenerateAnyInfos(): + def ExtractAny(tree): + return tree.data.encode("utf-8") + + def GenerateAny(value, name=None, indent=0): + try: + value = value.decode("utf-8") + except: + pass + return u'\n' % value + + return { + "type": COMPLEXTYPE, + "extract": ExtractAny, + "generate": GenerateAny, + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + } + +def GenerateTagInfos(name): + def ExtractTag(tree): + if len(tree._attrs) > 0: + raise ValueError("\"%s\" musn't have attributes!" % name) + if len(tree.childNodes) > 0: + raise ValueError("\"%s\" musn't have children!" % name) + return None + + def GenerateTag(value, name=None, indent=0): + if name is not None: + ind1, ind2 = getIndent(indent, name) + return ind1 + "<%s/>\n" % name + else: + return "" + + return { + "type": TAG, + "extract": ExtractTag, + "generate": GenerateTag, + "initial": lambda: None, + "check": lambda x: x == None + } + +def GenerateContentInfos(factory, choices): + def GetContentInitial(): + content_name, infos = choices[0] + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos["elmt_type"]) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: + return {"name": content_name, + "value": map(infos["elmt_type"]["initial"], + range(infos["minOccurs"]))} + else: + return {"name": content_name, + "value": infos["elmt_type"]["initial"]()} + + def CheckContent(value): + for content_name, infos in choices: + if content_name == value["name"]: + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos["elmt_type"]) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: + if isinstance(value["value"], ListType) and \ + infos["minOccurs"] <= len(value["value"]) <= infos["maxOccurs"]: + return reduce(lambda x, y: x and y, + map(infos["elmt_type"]["check"], + value["value"]), + True) + else: + return infos["elmt_type"]["check"](value["value"]) + return False + + def ExtractContent(tree, content): + for content_name, infos in choices: + if content_name == tree.nodeName: + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos["elmt_type"]) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: + if isinstance(content, ListType) and len(content) > 0 and \ + content[-1]["name"] == content_name: + content_item = content.pop(-1) + content_item["value"].append(infos["elmt_type"]["extract"](tree)) + return content_item + elif not isinstance(content, ListType) and \ + content is not None and \ + content["name"] == content_name: + return {"name": content_name, + "value": content["value"] + \ + [infos["elmt_type"]["extract"](tree)]} + else: + return {"name": content_name, + "value": [infos["elmt_type"]["extract"](tree)]} + else: + return {"name": content_name, + "value": infos["elmt_type"]["extract"](tree)} + raise ValueError("Invalid element \"%s\" for content!" % tree.nodeName) + + def GenerateContent(value, name=None, indent=0): + for content_name, infos in choices: + if content_name == value["name"]: + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos["elmt_type"]) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if infos["maxOccurs"] == "unbounded" or infos["maxOccurs"] > 1: + text = "" + for item in value["value"]: + text += infos["elmt_type"]["generate"](item, content_name, indent) + return text + else: + return infos["elmt_type"]["generate"](value["value"], content_name, indent) + return "" + + return { + "initial": GetContentInitial, + "check": CheckContent, + "extract": ExtractContent, + "generate": GenerateContent + } + +#------------------------------------------------------------------------------- +# Structure extraction functions +#------------------------------------------------------------------------------- + + +def DecomposeQualifiedName(name): + result = QName_model.match(name) + if not result: + raise ValueError("\"%s\" isn't a valid QName value!" % name) + parts = result.groups()[0].split(':') + if len(parts) == 1: + return None, parts[0] + return parts + +def GenerateElement(element_name, attributes, elements_model, + accept_text=False): + def ExtractElement(factory, node): + attrs = factory.ExtractNodeAttrs(element_name, node, attributes) + children_structure = "" + children_infos = [] + children = [] + for child in node.childNodes: + if child.nodeName not in ["#comment", "#text"]: + namespace, childname = DecomposeQualifiedName(child.nodeName) + children_structure += "%s "%childname + result = elements_model.match(children_structure) + if not result: + raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName) + valid = result.groups()[0] + if len(valid) < len(children_structure): + raise ValueError("Invalid structure for \"%s\" children!. Element number %d invalid." % (node.nodeName, len(valid.split(" ")) - 1)) + for child in node.childNodes: + if child.nodeName != "#comment" and \ + (accept_text or child.nodeName != "#text"): + if child.nodeName == "#text": + children.append(GetAttributeValue(node)) + else: + namespace, childname = DecomposeQualifiedName(child.nodeName) + infos = factory.GetQualifiedNameInfos(childname, namespace) + if infos["type"] != SYNTAXELEMENT: + raise ValueError("\"%s\" can't be a member child!" % name) + if infos["extract"].has_key(element_name): + children.append(infos["extract"][element_name](factory, child)) + else: + children.append(infos["extract"]["default"](factory, child)) + return node.nodeName, attrs, children + return ExtractElement + + +""" +Class that generate class from an XML Tree +""" +class ClassFactory: + + def __init__(self, document, debug=False): + self.Document = document + self.Debug = debug + + # Dictionary for stocking Classes and Types definitions created from + # the XML tree + self.XMLClassDefinitions = {} + + self.DefinedNamespaces = {} + self.Namespaces = {} + self.SchemaNamespace = None + self.TargetNamespace = None + + self.CurrentCompilations = [] + + # Dictionaries for stocking Classes and Types generated + self.ComputeAfter = [] + self.ComputedClasses = {} + self.ComputedClassesInfos = {} + self.AlreadyComputed = {} + + def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False): + if namespace is None: + if self.Namespaces[self.SchemaNamespace].has_key(name): + return self.Namespaces[self.SchemaNamespace][name] + for space, elements in self.Namespaces.items(): + if space != self.SchemaNamespace and elements.has_key(name): + return elements[name] + parts = name.split("_", 1) + if len(parts) > 1: + group = self.GetQualifiedNameInfos(parts[0], namespace) + if group is not None and group["type"] == ELEMENTSGROUP: + elements = [] + if group.has_key("elements"): + elements = group["elements"] + elif group.has_key("choices"): + elements = group["choices"] + for element in elements: + if element["name"] == parts[1]: + return element + if not canbenone: + raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) + elif self.Namespaces.has_key(namespace): + if self.Namespaces[namespace].has_key(name): + return self.Namespaces[namespace][name] + parts = name.split("_", 1) + if len(parts) > 1: + group = self.GetQualifiedNameInfos(parts[0], namespace) + if group is not None and group["type"] == ELEMENTSGROUP: + elements = [] + if group.has_key("elements"): + elements = group["elements"] + elif group.has_key("choices"): + elements = group["choices"] + for element in elements: + if element["name"] == parts[1]: + return element + if not canbenone: + raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace)) + elif not canbenone: + raise ValueError("Unknown namespace \"%s\"!" % namespace) + return None + + def SplitQualifiedName(self, name, namespace=None, canbenone=False): + if namespace is None: + if self.Namespaces[self.SchemaNamespace].has_key(name): + return name, None + for space, elements in self.Namespaces.items(): + if space != self.SchemaNamespace and elements.has_key(name): + return name, None + parts = name.split("_", 1) + if len(parts) > 1: + group = self.GetQualifiedNameInfos(parts[0], namespace) + if group is not None and group["type"] == ELEMENTSGROUP: + elements = [] + if group.has_key("elements"): + elements = group["elements"] + elif group.has_key("choices"): + elements = group["choices"] + for element in elements: + if element["name"] == parts[1]: + return part[1], part[0] + if not canbenone: + raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) + elif self.Namespaces.has_key(namespace): + if self.Namespaces[namespace].has_key(name): + return name, None + parts = name.split("_", 1) + if len(parts) > 1: + group = self.GetQualifiedNameInfos(parts[0], namespace) + if group is not None and group["type"] == ELEMENTSGROUP: + elements = [] + if group.has_key("elements"): + elements = group["elements"] + elif group.has_key("choices"): + elements = group["choices"] + for element in elements: + if element["name"] == parts[1]: + return parts[1], parts[0] + if not canbenone: + raise ValueError("Unknown element \"%s\" for namespace \"%s\"!" % (name, namespace)) + elif not canbenone: + raise ValueError("Unknown namespace \"%s\"!" % namespace) + return None, None + + def ExtractNodeAttrs(self, element_name, node, valid_attrs): + attrs = {} + for qualified_name, attr in node._attrs.items(): + namespace, name = DecomposeQualifiedName(qualified_name) + if name in valid_attrs: + infos = self.GetQualifiedNameInfos(name, namespace) + if infos["type"] != SYNTAXATTRIBUTE: + raise ValueError("\"%s\" can't be a member attribute!" % name) + elif name in attrs: + raise ValueError("\"%s\" attribute has been twice!" % name) + elif element_name in infos["extract"]: + attrs[name] = infos["extract"][element_name](attr) + else: + attrs[name] = infos["extract"]["default"](attr) + elif namespace == "xmlns": + infos = self.GetQualifiedNameInfos("anyURI", self.SchemaNamespace) + self.DefinedNamespaces[infos["extract"](attr)] = name + else: + raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName)) + for attr in valid_attrs: + if attr not in attrs and \ + self.Namespaces[self.SchemaNamespace].has_key(attr) and \ + self.Namespaces[self.SchemaNamespace][attr].has_key("default"): + if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name): + default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name] + else: + default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"] + if default is not None: + attrs[attr] = default + return attrs + + def ReduceElements(self, elements, schema=False): + result = [] + for child_infos in elements: + if child_infos[1].has_key("name") and schema: + self.CurrentCompilations.append(child_infos[1]["name"]) + namespace, name = DecomposeQualifiedName(child_infos[0]) + infos = self.GetQualifiedNameInfos(name, namespace) + if infos["type"] != SYNTAXELEMENT: + raise ValueError("\"%s\" can't be a member child!" % name) + result.append(infos["reduce"](self, child_infos[1], child_infos[2])) + if child_infos[1].has_key("name") and schema: + self.CurrentCompilations.pop(-1) + annotations = [] + children = [] + for element in result: + if element["type"] == "annotation": + annotations.append(element) + else: + children.append(element) + return annotations, children + + def AddComplexType(self, typename, infos): + if not self.XMLClassDefinitions.has_key(typename): + self.XMLClassDefinitions[typename] = infos + else: + raise ValueError("\"%s\" class already defined. Choose another name!" % typename) + + def ParseSchema(self): + pass + + def ExtractTypeInfos(self, name, parent, typeinfos): + if isinstance(typeinfos, (StringType, UnicodeType)): + namespace, name = DecomposeQualifiedName(typeinfos) + infos = self.GetQualifiedNameInfos(name, namespace) + if infos["type"] == COMPLEXTYPE: + name, parent = self.SplitQualifiedName(name, namespace) + result = self.CreateClass(name, parent, infos) + if result is not None and not isinstance(result, (UnicodeType, StringType)): + self.Namespaces[self.TargetNamespace][result["name"]] = result + return result + elif infos["type"] == ELEMENT and infos["elmt_type"]["type"] == COMPLEXTYPE: + name, parent = self.SplitQualifiedName(name, namespace) + result = self.CreateClass(name, parent, infos["elmt_type"]) + if result is not None and not isinstance(result, (UnicodeType, StringType)): + self.Namespaces[self.TargetNamespace][result["name"]] = result + return result + else: + return infos + elif typeinfos["type"] == COMPLEXTYPE: + return self.CreateClass(name, parent, typeinfos) + elif typeinfos["type"] == SIMPLETYPE: + return typeinfos + + """ + Methods that generates the classes + """ + def CreateClasses(self): + self.ParseSchema() + for name, infos in self.Namespaces[self.TargetNamespace].items(): + if infos["type"] == ELEMENT: + if not isinstance(infos["elmt_type"], (UnicodeType, StringType)) and \ + infos["elmt_type"]["type"] == COMPLEXTYPE: + self.ComputeAfter.append((name, None, infos["elmt_type"], True)) + while len(self.ComputeAfter) > 0: + result = self.CreateClass(*self.ComputeAfter.pop(0)) + if result is not None and not isinstance(result, (UnicodeType, StringType)): + self.Namespaces[self.TargetNamespace][result["name"]] = result + elif infos["type"] == COMPLEXTYPE: + self.ComputeAfter.append((name, None, infos)) + while len(self.ComputeAfter) > 0: + result = self.CreateClass(*self.ComputeAfter.pop(0)) + if result is not None and \ + not isinstance(result, (UnicodeType, StringType)): + self.Namespaces[self.TargetNamespace][result["name"]] = result + elif infos["type"] == ELEMENTSGROUP: + elements = [] + if infos.has_key("elements"): + elements = infos["elements"] + elif infos.has_key("choices"): + elements = infos["choices"] + for element in elements: + if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \ + element["elmt_type"]["type"] == COMPLEXTYPE: + self.ComputeAfter.append((element["name"], infos["name"], element["elmt_type"])) + while len(self.ComputeAfter) > 0: + result = self.CreateClass(*self.ComputeAfter.pop(0)) + if result is not None and \ + not isinstance(result, (UnicodeType, StringType)): + self.Namespaces[self.TargetNamespace][result["name"]] = result + return self.ComputedClasses + + def CreateClass(self, name, parent, classinfos, baseclass = False): + if parent is not None: + classname = "%s_%s" % (parent, name) + else: + classname = name + + # Checks that classe haven't been generated yet + if self.AlreadyComputed.get(classname, False): + if baseclass: + self.AlreadyComputed[classname].IsBaseClass = baseclass + return self.ComputedClassesInfos.get(classname, None) + + # If base classes haven't been generated + bases = [] + if classinfos.has_key("base"): + result = self.ExtractTypeInfos("base", name, classinfos["base"]) + if result is None: + namespace, base_name = DecomposeQualifiedName(classinfos["base"]) + if self.AlreadyComputed.get(base_name, False): + self.ComputeAfter.append((name, parent, classinfos)) + if self.TargetNamespace is not None: + return "%s:%s" % (self.TargetNamespace, classname) + else: + return classname + elif result is not None: + classinfos["base"] = self.ComputedClasses[result["name"]] + bases.append(self.ComputedClasses[result["name"]]) + bases.append(object) + bases = tuple(bases) + classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass} + + self.AlreadyComputed[classname] = True + + for attribute in classinfos["attributes"]: + infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"]) + if infos is not None: + if infos["type"] != SIMPLETYPE: + raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"]) + attrname = attribute["name"] + if attribute["use"] == "optional": + classmembers[attrname] = None + classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute) + classmembers["delete%s"%attrname] = generateDeleteMethod(attrname) + else: + classmembers[attrname] = infos["initial"]() + classmembers["set%s"%attrname] = generateSetMethod(attrname) + classmembers["get%s"%attrname] = generateGetMethod(attrname) + else: + raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"]) + attribute["attr_type"] = infos + + for element in classinfos["elements"]: + if element["type"] == CHOICE: + elmtname = element["name"] + choices = [] + for choice in element["choices"]: + if choice["elmt_type"] == "tag": + choice["elmt_type"] = GenerateTagInfos(choice["name"]) + else: + infos = self.ExtractTypeInfos(choice["name"], name, choice["elmt_type"]) + if infos is not None: + choice["elmt_type"] = infos + choices.append((choice["name"], choice)) + classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"]) + if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: + classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) + classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) + else: + classmembers["set%sbytype" % elmtname] = generateSetChoiceByTypeMethod(self, element["choices"]) + infos = GenerateContentInfos(self, choices) + elif element["type"] == ANY: + elmtname = element["name"] = "text" + element["minOccurs"] = element["maxOccurs"] = 1 + infos = GenerateAnyInfos() + else: + elmtname = element["name"] + infos = self.ExtractTypeInfos(element["name"], name, element["elmt_type"]) + if infos is not None: + element["elmt_type"] = infos + if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: + classmembers[elmtname] = [] + classmembers["append%s" % elmtname] = generateAppendMethod(elmtname, element["maxOccurs"], self, element) + classmembers["insert%s" % elmtname] = generateInsertMethod(elmtname, element["maxOccurs"], self, element) + classmembers["remove%s" % elmtname] = generateRemoveMethod(elmtname, element["minOccurs"]) + classmembers["count%s" % elmtname] = generateCountMethod(elmtname) + else: + if element["minOccurs"] == 0: + classmembers[elmtname] = None + classmembers["add%s" % elmtname] = generateAddMethod(elmtname, self, element) + classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname) + elif not isinstance(element["elmt_type"], (UnicodeType, StringType)): + classmembers[elmtname] = element["elmt_type"]["initial"]() + else: + classmembers[elmtname] = None + classmembers["set%s" % elmtname] = generateSetMethod(elmtname) + classmembers["get%s" % elmtname] = generateGetMethod(elmtname) + + classmembers["__init__"] = generateInitMethod(self, classinfos) + classmembers["__setattr__"] = generateSetattrMethod(self, classinfos) + classmembers["getStructure"] = generateStructureMethod(classinfos) + classmembers["loadXMLTree"] = generateLoadXMLTree(self, classinfos) + classmembers["generateXMLText"] = generateGenerateXMLText(self, classinfos) + classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos) + classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos) + classmembers["setElementValue"] = generateSetElementValue(self, classinfos) + classmembers["singleLineAttributes"] = True + classmembers["compatibility"] = lambda x, y: None + + class_definition = classobj(str(classname), bases, classmembers) + class_infos = {"type": COMPILEDCOMPLEXTYPE, + "name": classname, + "check": generateClassCheckFunction(class_definition), + "initial": generateClassCreateFunction(class_definition), + "extract": generateClassExtractFunction(class_definition), + "generate": class_definition.generateXMLText} + + self.ComputedClasses[classname] = class_definition + self.ComputedClassesInfos[classname] = class_infos + + return class_infos + + """ + Methods that print the classes generated + """ + def PrintClasses(self): + items = self.ComputedClasses.items() + items.sort() + for classname, xmlclass in items: + print "%s : %s" % (classname, str(xmlclass)) + + def PrintClassNames(self): + classnames = self.XMLClassDefinitions.keys() + classnames.sort() + for classname in classnames: + print classname + +""" +Method that generate the method for checking a class instance +""" +def generateClassCheckFunction(class_definition): + def classCheckfunction(instance): + return isinstance(instance, class_definition) + return classCheckfunction + +""" +Method that generate the method for creating a class instance +""" +def generateClassCreateFunction(class_definition): + def classCreatefunction(): + return class_definition() + return classCreatefunction + +""" +Method that generate the method for extracting a class instance +""" +def generateClassExtractFunction(class_definition): + def classExtractfunction(node): + instance = class_definition() + instance.loadXMLTree(node) + return instance + return classExtractfunction + +""" +Method that generate the method for loading an xml tree by following the +attributes list defined +""" +def generateSetattrMethod(factory, classinfos): + attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) + optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"]) + elements = dict([(element["name"], element) for element in classinfos["elements"]]) + + def setattrMethod(self, name, value): + if attributes.has_key(name): + if isinstance(attributes[name]["attr_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + attributes[name]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) + if value is None: + if optional_attributes.get(name, False): + return object.__setattr__(self, name, None) + else: + raise ValueError("Attribute '%s' isn't optional." % name) + elif attributes[name].has_key("fixed") and value != attributes[name]["fixed"]: + raise ValueError, "Value of attribute '%s' can only be '%s'."%(name, str(attributes[name]["fixed"])) + elif attributes[name]["attr_type"]["check"](value): + return object.__setattr__(self, name, value) + else: + raise ValueError("Invalid value for attribute '%s'." % (name)) + elif elements.has_key(name): + if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if value is None: + if elements[name]["minOccurs"] == 0 and elements[name]["maxOccurs"] == 1: + return object.__setattr__(self, name, None) + else: + raise ValueError("Attribute '%s' isn't optional." % name) + elif elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1: + if isinstance(value, ListType) and elements[name]["minOccurs"] <= len(value) <= elements[name]["maxOccurs"]: + if reduce(lambda x, y: x and y, map(elements[name]["elmt_type"]["check"], value), True): + return object.__setattr__(self, name, value) + raise ValueError, "Attribute '%s' must be a list of valid elements."%name + elif elements[name].has_key("fixed") and value != elements[name]["fixed"]: + raise ValueError("Value of attribute '%s' can only be '%s'." % (name, str(elements[name]["fixed"]))) + elif elements[name]["elmt_type"]["check"](value): + return object.__setattr__(self, name, value) + else: + raise ValueError("Invalid value for attribute '%s'." % (name)) + elif classinfos.has_key("base"): + return classinfos["base"].__setattr__(self, name, value) + elif self.__class__.__dict__.has_key(name): + return object.__setattr__(self, name, value) + else: + raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name)) + + return setattrMethod + +""" +Method that generate the method for generating the xml tree structure model by +following the attributes list defined +""" +def ComputeMultiplicity(name, infos): + if infos["minOccurs"] == 0: + if infos["maxOccurs"] == "unbounded": + return "(?:%s)*" % name + elif infos["maxOccurs"] == 1: + return "(?:%s)?" % name + else: + return "(?:%s){,%d}" % (name, infos["maxOccurs"]) + elif infos["minOccurs"] == 1: + if infos["maxOccurs"] == "unbounded": + return "(?:%s)+" % name + elif infos["maxOccurs"] == 1: + return name + else: + return "(?:%s){1,%d}" % (name, infos["maxOccurs"]) + else: + if infos["maxOccurs"] == "unbounded": + return "(?:%s){%d,}" % (name, infos["minOccurs"], name) + else: + return "(?:%s){%d,%d}" % (name, infos["minOccurs"], + infos["maxOccurs"]) + +def generateStructureMethod(classinfos): + elements = [] + for element in classinfos["elements"]: + if element["type"] == ANY: + elements.append(ComputeMultiplicity("(?:#cdata-section )?", element)) + elif element["type"] == CHOICE: + elements.append(ComputeMultiplicity( + "|".join([ComputeMultiplicity("%s " % infos["name"], infos) for infos in element["choices"]]), + element)) + else: + elements.append(ComputeMultiplicity("%s " % element["name"], element)) + if classinfos.get("order", True) or len(elements) == 0: + structure = "".join(elements) + else: + raise ValueError("XSD structure not yet supported!") + + def getStructureMethod(self): + if classinfos.has_key("base"): + return classinfos["base"].getStructure(self) + structure + return structure + return getStructureMethod + +""" +Method that generate the method for loading an xml tree by following the +attributes list defined +""" +def generateLoadXMLTree(factory, classinfos): + attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) + elements = dict([(element["name"], element) for element in classinfos["elements"]]) + + def loadXMLTreeMethod(self, tree, extras=[], derived=False): + self.compatibility(tree) + if not derived: + children_structure = "" + for node in tree.childNodes: + if node.nodeName not in ["#comment", "#text"]: + children_structure += "%s " % node.nodeName + structure_model = re.compile("(%s)$" % self.getStructure()) + result = structure_model.match(children_structure) + if not result: + raise ValueError("Invalid structure for \"%s\" children!." % tree.nodeName) + required_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "required"]) + if classinfos.has_key("base"): + extras.extend([attr["name"] for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) + classinfos["base"].loadXMLTree(self, tree, extras, True) + for attrname, attr in tree._attrs.iteritems(): + if attributes.has_key(attrname): + if isinstance(attributes[attrname]["attr_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + attributes[attrname]["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) + setattr(self, attrname, attributes[attrname]["attr_type"]["extract"](attr)) + elif not classinfos.has_key("base") and attrname not in extras: + raise ValueError("Invalid attribute \"%s\" for \"%s\" element!" % (attrname, tree.nodeName)) + required_attributes.pop(attrname, None) + if len(required_attributes) > 0: + raise ValueError("Required attributes %s missing for \"%s\" element!" % (", ".join(["\"%s\""%name for name in required_attributes]), tree.nodeName)) + first = {} + for node in tree.childNodes: + name = node.nodeName + if name in ["#text", "#comment"]: + continue + if elements.has_key(name): + if isinstance(elements[name]["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + elements[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if elements[name]["maxOccurs"] == "unbounded" or elements[name]["maxOccurs"] > 1: + if first.get(name, True): + setattr(self, name, [elements[name]["elmt_type"]["extract"](node)]) + first[name] = False + else: + getattr(self, name).append(elements[name]["elmt_type"]["extract"](node)) + else: + setattr(self, name, elements[name]["elmt_type"]["extract"](node)) + elif name == "#cdata-section" and elements.has_key("text"): + if elements["text"]["maxOccurs"] == "unbounded" or elements["text"]["maxOccurs"] > 1: + if first.get("text", True): + setattr(self, "text", [elements["text"]["elmt_type"]["extract"](node)]) + first["text"] = False + else: + getattr(self, "text").append(elements["text"]["elmt_type"]["extract"](node)) + else: + setattr(self, "text", elements["text"]["elmt_type"]["extract"](node)) + elif elements.has_key("content"): + content = getattr(self, "content") + if elements["content"]["maxOccurs"] == "unbounded" or elements["content"]["maxOccurs"] > 1: + if first.get("content", True): + setattr(self, "content", [elements["content"]["elmt_type"]["extract"](node, None)]) + first["content"] = False + else: + content.append(elements["content"]["elmt_type"]["extract"](node, content)) + else: + setattr(self, "content", elements["content"]["elmt_type"]["extract"](node, content)) + return loadXMLTreeMethod + + +""" +Method that generates the method for generating an xml text by following the +attributes list defined +""" +def generateGenerateXMLText(factory, classinfos): + def generateXMLTextMethod(self, name, indent=0, extras={}, derived=False): + ind1, ind2 = getIndent(indent, name) + if not derived: + text = ind1 + u'<%s' % name + else: + text = u'' + + first = True + if not classinfos.has_key("base"): + for attr, value in extras.items(): + if not first and not self.singleLineAttributes: + text += u'\n%s' % (ind2) + text += u' %s=%s' % (attr, quoteattr(value)) + first = False + extras.clear() + for attr in classinfos["attributes"]: + if attr["use"] != "prohibited": + if isinstance(attr["attr_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + attr["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) + value = getattr(self, attr["name"], None) + if value != None: + computed_value = attr["attr_type"]["generate"](value) + else: + computed_value = None + if attr["use"] != "optional" or (value != None and \ + computed_value != attr.get("default", attr["attr_type"]["generate"](attr["attr_type"]["initial"]()))): + if classinfos.has_key("base"): + extras[attr["name"]] = computed_value + else: + if not first and not self.singleLineAttributes: + text += u'\n%s' % (ind2) + text += ' %s=%s' % (attr["name"], quoteattr(computed_value)) + first = False + if classinfos.has_key("base"): + first, new_text = classinfos["base"].generateXMLText(self, name, indent, extras, True) + text += new_text + else: + first = True + for element in classinfos["elements"]: + if isinstance(element["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + value = getattr(self, element["name"], None) + if element["minOccurs"] == 0 and element["maxOccurs"] == 1: + if value is not None: + if first: + text += u'>\n' + first = False + text += element["elmt_type"]["generate"](value, element["name"], indent + 1) + elif element["minOccurs"] == 1 and element["maxOccurs"] == 1: + if first: + text += u'>\n' + first = False + text += element["elmt_type"]["generate"](value, element["name"], indent + 1) + else: + if first and len(value) > 0: + text += u'>\n' + first = False + for item in value: + text += element["elmt_type"]["generate"](item, element["name"], indent + 1) + if not derived: + if first: + text += u'/>\n' + else: + text += ind1 + u'\n' % (name) + return text + else: + return first, text + return generateXMLTextMethod + +def gettypeinfos(name, facets): + if facets.has_key("enumeration") and facets["enumeration"][0] is not None: + return facets["enumeration"][0] + elif facets.has_key("maxInclusive"): + limits = {"max" : None, "min" : None} + if facets["maxInclusive"][0] is not None: + limits["max"] = facets["maxInclusive"][0] + elif facets["maxExclusive"][0] is not None: + limits["max"] = facets["maxExclusive"][0] - 1 + if facets["minInclusive"][0] is not None: + limits["min"] = facets["minInclusive"][0] + elif facets["minExclusive"][0] is not None: + limits["min"] = facets["minExclusive"][0] + 1 + if limits["max"] is not None or limits["min"] is not None: + return limits + return name + +def generateGetElementAttributes(factory, classinfos): + def getElementAttributes(self): + attr_list = [] + if classinfos.has_key("base"): + attr_list.extend(classinfos["base"].getElementAttributes(self)) + for attr in classinfos["attributes"]: + if attr["use"] != "prohibited": + attr_params = {"name" : attr["name"], "use" : attr["use"], + "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]), + "value" : getattr(self, attr["name"], "")} + attr_list.append(attr_params) + return attr_list + return getElementAttributes + +def generateGetElementInfos(factory, classinfos): + attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) + elements = dict([(element["name"], element) for element in classinfos["elements"]]) + + def getElementInfos(self, name, path=None, derived=False): + attr_type = "element" + value = None + use = "required" + children = [] + if path is not None: + parts = path.split(".", 1) + if attributes.has_key(parts[0]): + if len(parts) != 0: + raise ValueError("Wrong path!") + attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], + attributes[parts[0]]["attr_type"]["facets"]) + value = getattr(self, parts[0], "") + elif elements.has_key(parts[0]): + if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: + if len(parts) != 0: + raise ValueError("Wrong path!") + attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], + elements[parts[0]]["elmt_type"]["facets"]) + value = getattr(self, parts[0], "") + elif parts[0] == "content": + return self.content["value"].getElementInfos(self.content["name"], path) + else: + attr = getattr(self, parts[0], None) + if attr is None: + raise ValueError("Wrong path!") + if len(parts) == 1: + return attr.getElementInfos(parts[0]) + else: + return attr.getElementInfos(parts[0], parts[1]) + else: + raise ValueError("Wrong path!") + else: + if not derived: + children.extend(self.getElementAttributes()) + if classinfos.has_key("base"): + children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"]) + for element_name, element in elements.items(): + if element["minOccurs"] == 0: + use = "optional" + if element_name == "content": + attr_type = [(choice["name"], None) for choice in element["choices"]] + if self.content is None: + value = "" + else: + value = self.content["name"] + if self.content["value"] is not None: + children.extend(self.content["value"].getElementInfos(self.content["name"])["children"]) + elif element["elmt_type"]["type"] == SIMPLETYPE: + children.append({"name": element_name, "require": element["minOccurs"] != 0, + "type": gettypeinfos(element["elmt_type"]["basename"], + element["elmt_type"]["facets"]), + "value": getattr(self, element_name, None)}) + else: + instance = getattr(self, element_name, None) + if instance is None: + instance = element["elmt_type"]["initial"]() + children.append(instance.getElementInfos(element_name)) + return {"name": name, "type": attr_type, "value": value, "use": use, "children": children} + return getElementInfos + +def generateSetElementValue(factory, classinfos): + attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) + elements = dict([(element["name"], element) for element in classinfos["elements"]]) + + def setElementValue(self, path, value): + if path is not None: + parts = path.split(".", 1) + if attributes.has_key(parts[0]): + if len(parts) != 1: + raise ValueError("Wrong path!") + if attributes[parts[0]]["attr_type"]["basename"] == "boolean": + setattr(self, parts[0], value) + else: + setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False)) + elif elements.has_key(parts[0]): + if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: + if len(parts) != 1: + raise ValueError("Wrong path!") + if elements[parts[0]]["elmt_type"]["basename"] == "boolean": + setattr(self, parts[0], value) + else: + setattr(self, parts[0], elements[parts[0]]["elmt_type"]["extract"](value, False)) + else: + instance = getattr(self, parts[0], None) + if instance != None: + if len(parts) > 1: + instance.setElementValue(parts[1], value) + else: + instance.setElementValue(None, value) + elif elements.has_key("content"): + if len(parts) > 0: + self.content["value"].setElementValue(path, value) + elif classinfos.has_key("base"): + classinfos["base"].setElementValue(self, path, value) + elif elements.has_key("content"): + if value == "": + if elements["content"]["minOccurs"] == 0: + self.setcontent(None) + else: + raise ValueError("\"content\" element is required!") + else: + self.setcontentbytype(value) + return setElementValue + +""" +Methods that generates the different methods for setting and getting the attributes +""" +def generateInitMethod(factory, classinfos): + def initMethod(self): + if classinfos.has_key("base"): + classinfos["base"].__init__(self) + for attribute in classinfos["attributes"]: + if isinstance(attribute["attr_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(attribute["attr_type"]) + attribute["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) + if attribute["use"] == "required": + setattr(self, attribute["name"], attribute["attr_type"]["initial"]()) + elif attribute["use"] == "optional": + if attribute.has_key("default"): + setattr(self, attribute["name"], attribute["attr_type"]["extract"](attribute["default"], False)) + else: + setattr(self, attribute["name"], None) + for element in classinfos["elements"]: + if isinstance(element["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(element["elmt_type"]) + element["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if element["minOccurs"] == 0 and element["maxOccurs"] == 1: + if "default" in element: + setattr(self, element["name"], element["elmt_type"]["extract"](element["default"], False)) + else: + setattr(self, element["name"], None) + elif element["minOccurs"] == 1 and element["maxOccurs"] == 1: + setattr(self, element["name"], element["elmt_type"]["initial"]()) + else: + value = [] + for i in xrange(element["minOccurs"]): + value.append(element["elmt_type"]["initial"]()) + setattr(self, element["name"], value) + return initMethod + +def generateSetMethod(attr): + def setMethod(self, value): + setattr(self, attr, value) + return setMethod + +def generateGetMethod(attr): + def getMethod(self): + return getattr(self, attr, None) + return getMethod + +def generateAddMethod(attr, factory, infos): + def addMethod(self): + if infos["type"] == ATTRIBUTE: + if isinstance(infos["attr_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + infos["attr_type"] = factory.GetQualifiedNameInfos(name, namespace) + initial = infos["attr_type"]["initial"] + extract = infos["attr_type"]["extract"] + elif infos["type"] == ELEMENT: + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + initial = infos["elmt_type"]["initial"] + extract = infos["elmt_type"]["extract"] + else: + raise ValueError("Invalid class attribute!") + if infos.has_key("default"): + setattr(self, attr, extract(infos["default"], False)) + else: + setattr(self, attr, initial()) + return addMethod + +def generateDeleteMethod(attr): + def deleteMethod(self): + setattr(self, attr, None) + return deleteMethod + +def generateAppendMethod(attr, maxOccurs, factory, infos): + def appendMethod(self, value): + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + attr_list = getattr(self, attr) + if maxOccurs == "unbounded" or len(attr_list) < maxOccurs: + if infos["elmt_type"]["check"](value): + attr_list.append(value) + else: + raise ValueError("\"%s\" value isn't valid!" % attr) + else: + raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) + return appendMethod + +def generateInsertMethod(attr, maxOccurs, factory, infos): + def insertMethod(self, index, value): + if isinstance(infos["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(infos) + infos["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + attr_list = getattr(self, attr) + if maxOccurs == "unbounded" or len(attr_list) < maxOccurs: + if infos["elmt_type"]["check"](value): + attr_list.insert(index, value) + else: + raise ValueError("\"%s\" value isn't valid!" % attr) + else: + raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) + return insertMethod + +def generateGetChoicesMethod(choice_types): + def getChoicesMethod(self): + return [choice["name"] for choice in choice_types] + return getChoicesMethod + +def generateSetChoiceByTypeMethod(factory, choice_types): + choices = dict([(choice["name"], choice) for choice in choice_types]) + def setChoiceMethod(self, type): + if not choices.has_key(type): + raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) + if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"]) + choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + new_element = choices[type]["elmt_type"]["initial"]() + self.content = {"name": type, "value": new_element} + return new_element + return setChoiceMethod + +def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types): + choices = dict([(choice["name"], choice) for choice in choice_types]) + def appendChoiceMethod(self, type): + if not choices.has_key(type): + raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) + if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"]) + choices[type]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if maxOccurs == "unbounded" or len(self.content) < maxOccurs: + new_element = choices[type]["elmt_type"]["initial"]() + self.content.append({"name": type, "value": new_element}) + return new_element + else: + raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) + return appendChoiceMethod + +def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types): + choices = dict([(choice["name"], choice) for choice in choice_types]) + def insertChoiceMethod(self, index, type): + if not choices.has_key(type): + raise ValueError("Unknown \"%s\" choice type for \"content\"!" % type) + if isinstance(choices[type]["elmt_type"], (UnicodeType, StringType)): + namespace, name = DecomposeQualifiedName(choices[type]["elmt_type"]) + choices[name]["elmt_type"] = factory.GetQualifiedNameInfos(name, namespace) + if maxOccurs == "unbounded" or len(self.content) < maxOccurs: + new_element = choices[type]["elmt_type"]["initial"]() + self.content.insert(index, {"name" : type, "value" : new_element}) + return new_element + else: + raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) + return insertChoiceMethod + +def generateRemoveMethod(attr, minOccurs): + def removeMethod(self, index): + attr_list = getattr(self, attr) + if len(attr_list) > minOccurs: + getattr(self, attr).pop(index) + else: + raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr)) + return removeMethod + +def generateCountMethod(attr): + def countMethod(self): + return len(getattr(self, attr)) + return countMethod + +""" +This function generate the classes from a class factory +""" +def GenerateClasses(factory, declare=False): + ComputedClasses = factory.CreateClasses() + #factory.PrintClasses() + if declare: + for ClassName, Class in pluginClasses.items(): + sys._getframe(1).f_locals[ClassName] = Class + for TypeName, Type in pluginTypes.items(): + sys._getframe(1).f_locals[TypeName] = Type + globals().update(ComputedClasses) + return ComputedClasses + diff -r 5024d42e1050 -r 94c11207aa6f xmlclass/xsdschema.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmlclass/xsdschema.py Fri Sep 30 17:16:02 2011 +0200 @@ -0,0 +1,2456 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import re +import datetime +from xml.dom import minidom +from types import * + +from xmlclass import * + +def GenerateDictFacets(facets): + return dict([(name, (None, False)) for name in facets]) + +def GenerateSimpleTypeXMLText(function): + def generateXMLTextMethod(value, name=None, indent=0): + text = "" + if name is not None: + ind1, ind2 = getIndent(indent, name) + text += ind1 + "<%s>" % name + text += function(value) + if name is not None: + text += "\n" % name + return text + return generateXMLTextMethod + +def GenerateFloatXMLText(extra_values=[]): + def generateXMLTextMethod(value, name=None, indent=0): + text = "" + if name is not None: + ind1, ind2 = getIndent(indent, name) + text += ind1 + "<%s>" % name + if value in extra_values or value % 1 != 0 or isinstance(value, IntType): + text += str(value) + else: + text += "%.0f" % value + if name is not None: + text += "\n" % name + return text + return generateXMLTextMethod + +DEFAULT_FACETS = GenerateDictFacets(["pattern", "whiteSpace", "enumeration"]) +NUMBER_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["maxInclusive", "maxExclusive", "minInclusive", "minExclusive"]) +DECIMAL_FACETS = GenerateDictFacets(NUMBER_FACETS.keys() + ["totalDigits", "fractionDigits"]) +STRING_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["length", "minLength", "maxLength"]) + +ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", + "maxExclusive", "minInclusive", "minExclusive", "totalDigits", + "fractionDigits", "length", "minLength", "maxLength"] + + +#------------------------------------------------------------------------------- +# Structure reducing functions +#------------------------------------------------------------------------------- + + +# Documentation elements + +def ReduceAppInfo(factory, attributes, elements): + return {"type": "appinfo", "source": attributes.get("source", None), + "content": "\n".join(elements)} + + +def ReduceDocumentation(factory, attributes, elements): + return {"type": "documentation", "source": attributes.get("source", None), + "language": attributes.get("lang", "any"), "content": "\n".join(elements)} + + +def ReduceAnnotation(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + annotation = {"type": "annotation", "appinfo": [], "documentation": {}} + for child in children: + if child["type"] == "appinfo": + annotation["appinfo"].append((child["source"], child["content"])) + elif child["type"] == "documentation": + if child["source"] is not None: + text = "(source: %(source)s):\n%(content)s\n\n"%child + else: + text = child["content"] + "\n\n" + if not annotation["documentation"].has_key(child["language"]): + annotation["documentation"] = text + else: + annotation["documentation"] += text + return annotation + +# Simple type elements + +def GenerateFacetReducing(facetname, canbefixed): + def ReduceFacet(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + if attributes.has_key("value"): + facet = {"type": facetname, "value": attributes["value"], "doc": annotations} + if canbefixed: + facet["fixed"] = attributes.get("fixed", False) + return facet + raise ValueError("A value must be defined for the \"%s\" facet!" % facetname) + return ReduceFacet + + +def ReduceList(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + list = {"type": "list", "itemType": attributes.get("itemType", None), "doc": annotations} + + if len(children) > 0 and children[0]["type"] == SIMPLETYPE: + if list["itemType"] is None: + list["itemType"] = children[0] + else: + raise ValueError("Only one base type can be defined for restriction!") + if list["itemType"] is None: + raise ValueError("No base type has been defined for list!") + return list + + +def ReduceUnion(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + union = {"type": "union", "memberTypes": attributes.get("memberTypes", []), "doc": annotations} + + for child in children: + if child["type"] == SIMPLETYPE: + union["memberTypes"].appendchild + if len(union["memberTypes"]) == 0: + raise ValueError("No base type has been defined for union!") + return union + + +def ReduceSimpleType(factory, attributes, elements): + # Reduce all the simple type children + annotations, children = factory.ReduceElements(elements) + + typeinfos = children[0] + + # Initialize type informations + facets = {} + simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", []), "doc": annotations} + if attributes.has_key("name"): + simpleType["name"] = attributes["name"] + + if typeinfos["type"] == "restriction": + # Search for base type definition + if isinstance(typeinfos["base"], (StringType, UnicodeType)): + basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE) + if basetypeinfos is None: + raise "\"%s\" isn't defined!" % typeinfos["base"] + else: + basetypeinfos = typeinfos["base"] + + # Check that base type is a simple type + if basetypeinfos["type"] != SIMPLETYPE: + raise ValueError("Base type given isn't a simpleType!") + + simpleType["basename"] = basetypeinfos["basename"] + + # Check that derivation is allowed + if basetypeinfos.has_key("final"): + if basetypeinfos["final"].has_key("#all"): + raise ValueError("Base type can't be derivated!") + if basetypeinfos["final"].has_key("restriction"): + raise ValueError("Base type can't be derivated by restriction!") + + # Extract simple type facets + for facet in typeinfos["facets"]: + facettype = facet["type"] + if not basetypeinfos["facets"].has_key(facettype): + raise ValueError("\"%s\" facet can't be defined for \"%s\" type!" % (facettype, type)) + elif basetypeinfos["facets"][facettype][1]: + raise ValueError("\"%s\" facet is fixed on base type!" % facettype) + value = facet["value"] + basevalue = basetypeinfos["facets"][facettype][0] + if facettype == "enumeration": + value = basetypeinfos["extract"](value, False) + if len(facets) == 0: + facets["enumeration"] = ([value], False) + continue + elif facets.keys() == ["enumeration"]: + facets["enumeration"][0].append(value) + continue + else: + raise ValueError("\"enumeration\" facet can't be defined with another facet type!") + elif facets.has_key("enumeration"): + raise ValueError("\"enumeration\" facet can't be defined with another facet type!") + elif facets.has_key(facettype): + raise ValueError("\"%s\" facet can't be defined two times!" % facettype) + elif facettype == "length": + if facets.has_key("minLength"): + raise ValueError("\"length\" and \"minLength\" facets can't be defined at the same time!") + if facets.has_key("maxLength"): + raise ValueError("\"length\" and \"maxLength\" facets can't be defined at the same time!") + try: + value = int(value) + except: + raise ValueError("\"length\" must be an integer!") + if value < 0: + raise ValueError("\"length\" can't be negative!") + elif basevalue is not None and basevalue != value: + raise ValueError("\"length\" can't be different from \"length\" defined in base type!") + elif facettype == "minLength": + if facets.has_key("length"): + raise ValueError("\"length\" and \"minLength\" facets can't be defined at the same time!") + try: + value = int(value) + except: + raise ValueError("\"minLength\" must be an integer!") + if value < 0: + raise ValueError("\"minLength\" can't be negative!") + elif facets.has_key("maxLength") and value > facets["maxLength"]: + raise ValueError("\"minLength\" must be lesser than or equal to \"maxLength\"!") + elif basevalue is not None and basevalue < value: + raise ValueError("\"minLength\" can't be lesser than \"minLength\" defined in base type!") + elif facettype == "maxLength": + if facets.has_key("length"): + raise ValueError("\"length\" and \"maxLength\" facets can't be defined at the same time!") + try: + value = int(value) + except: + raise ValueError("\"maxLength\" must be an integer!") + if value < 0: + raise ValueError("\"maxLength\" can't be negative!") + elif facets.has_key("minLength") and value < facets["minLength"]: + raise ValueError("\"minLength\" must be lesser than or equal to \"maxLength\"!") + elif basevalue is not None and basevalue > value: + raise ValueError("\"maxLength\" can't be greater than \"maxLength\" defined in base type!") + elif facettype == "minInclusive": + if facets.has_key("minExclusive"): + raise ValueError("\"minExclusive\" and \"minInclusive\" facets can't be defined at the same time!") + value = basetypeinfos["extract"](facet["value"], False) + if facets.has_key("maxInclusive") and value > facets["maxInclusive"][0]: + raise ValueError("\"minInclusive\" must be lesser than or equal to \"maxInclusive\"!") + elif facets.has_key("maxExclusive") and value >= facets["maxExclusive"][0]: + raise ValueError("\"minInclusive\" must be lesser than \"maxExclusive\"!") + elif facettype == "minExclusive": + if facets.has_key("minInclusive"): + raise ValueError("\"minExclusive\" and \"minInclusive\" facets can't be defined at the same time!") + value = basetypeinfos["extract"](facet["value"], False) + if facets.has_key("maxInclusive") and value >= facets["maxInclusive"][0]: + raise ValueError("\"minExclusive\" must be lesser than \"maxInclusive\"!") + elif facets.has_key("maxExclusive") and value >= facets["maxExclusive"][0]: + raise ValueError("\"minExclusive\" must be lesser than \"maxExclusive\"!") + elif facettype == "maxInclusive": + if facets.has_key("maxExclusive"): + raise ValueError("\"maxExclusive\" and \"maxInclusive\" facets can't be defined at the same time!") + value = basetypeinfos["extract"](facet["value"], False) + if facets.has_key("minInclusive") and value < facets["minInclusive"][0]: + raise ValueError("\"minInclusive\" must be lesser than or equal to \"maxInclusive\"!") + elif facets.has_key("minExclusive") and value <= facets["minExclusive"][0]: + raise ValueError("\"minExclusive\" must be lesser than \"maxInclusive\"!") + elif facettype == "maxExclusive": + if facets.has_key("maxInclusive"): + raise ValueError("\"maxExclusive\" and \"maxInclusive\" facets can't be defined at the same time!") + value = basetypeinfos["extract"](facet["value"], False) + if facets.has_key("minInclusive") and value <= facets["minInclusive"][0]: + raise ValueError("\"minInclusive\" must be lesser than \"maxExclusive\"!") + elif facets.has_key("minExclusive") and value <= facets["minExclusive"][0]: + raise ValueError("\"minExclusive\" must be lesser than \"maxExclusive\"!") + elif facettype == "whiteSpace": + if basevalue == "collapse" and value in ["preserve", "replace"] or basevalue == "replace" and value == "preserve": + raise ValueError("\"whiteSpace\" is incompatible with \"whiteSpace\" defined in base type!") + elif facettype == "totalDigits": + if facets.has_key("fractionDigits") and value <= facets["fractionDigits"][0]: + raise ValueError("\"fractionDigits\" must be lesser than or equal to \"totalDigits\"!") + elif basevalue is not None and value > basevalue: + raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!") + elif facettype == "fractionDigits": + if facets.has_key("totalDigits") and value <= facets["totalDigits"][0]: + raise ValueError("\"fractionDigits\" must be lesser than or equal to \"totalDigits\"!") + elif basevalue is not None and value > basevalue: + raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!") + facets[facettype] = (value, facet.get("fixed", False)) + + # Report not redefined facet from base type to new created type + for facettype, facetvalue in basetypeinfos["facets"].items(): + if not facets.has_key(facettype): + facets[facettype] = facetvalue + + # Generate extract value for new created type + def ExtractSimpleTypeValue(attr, extract=True): + value = basetypeinfos["extract"](attr, extract) + for facetname, (facetvalue, facetfixed) in facets.items(): + if facetvalue is not None: + if facetname == "enumeration" and value not in facetvalue: + raise ValueError("\"%s\" not in enumerated values" % value) + elif facetname == "length" and len(value) != facetvalue: + raise ValueError("value must have a length of %d" % facetvalue) + elif facetname == "minLength" and len(value) < facetvalue: + raise ValueError("value must have a length of %d at least" % facetvalue) + elif facetname == "maxLength" and len(value) > facetvalue: + raise ValueError("value must have a length of %d at most" % facetvalue) + elif facetname == "minInclusive" and value < facetvalue: + raise ValueError("value must be greater than or equal to %s" % str(facetvalue)) + elif facetname == "minExclusive" and value <= facetvalue: + raise ValueError("value must be greater than %s" % str(facetvalue)) + elif facetname == "maxInclusive" and value > facetvalue: + raise ValueError("value must be lesser than or equal to %s" % str(facetvalue)) + elif facetname == "maxExclusive" and value >= facetvalue: + raise ValueError("value must be lesser than %s" % str(facetvalue)) + elif facetname == "pattern": + model = re.compile("(?:%s)?$" % facetvalue) + result = model.match(value) + if result is None: + raise ValueError("value doesn't follow the pattern %s" % facetvalue) + elif facetname == "whiteSpace": + if facetvalue == "replace": + value = GetNormalizedString(value, False) + elif facetvalue == "collapse": + value = GetToken(value, False) + return value + + def CheckSimpleTypeValue(value): + for facetname, (facetvalue, facetfixed) in facets.items(): + if facetvalue is not None: + if facetname == "enumeration" and value not in facetvalue: + return False + elif facetname == "length" and len(value) != facetvalue: + return False + elif facetname == "minLength" and len(value) < facetvalue: + return False + elif facetname == "maxLength" and len(value) > facetvalue: + return False + elif facetname == "minInclusive" and value < facetvalue: + return False + elif facetname == "minExclusive" and value <= facetvalue: + return False + elif facetname == "maxInclusive" and value > facetvalue: + return False + elif facetname == "maxExclusive" and value >= facetvalue: + return False + elif facetname == "pattern": + model = re.compile("(?:%s)?$" % facetvalue) + result = model.match(value) + if result is None: + raise ValueError("value doesn't follow the pattern %s" % facetvalue) + return True + + def SimpleTypeInitialValue(): + for facetname, (facetvalue, facetfixed) in facets.items(): + if facetvalue is not None: + if facetname == "enumeration": + return facetvalue[0] + elif facetname == "length": + return " "*facetvalue + elif facetname == "minLength": + return " "*minLength + elif facetname == "minInclusive" and facetvalue > 0: + return facetvalue + elif facetname == "minExclusive" and facetvalue >= 0: + return facetvalue + 1 + elif facetname == "maxInclusive" and facetvalue < 0: + return facetvalue + elif facetname == "maxExclusive" and facetvalue <= 0: + return facetvalue - 1 + return basetypeinfos["initial"]() + + GenerateSimpleType = basetypeinfos["generate"] + + elif typeinfos["type"] == "list": + # Search for item type definition + if isinstance(typeinfos["itemType"], (StringType, UnicodeType)): + itemtypeinfos = factory.FindSchemaElement(typeinfos["itemType"], SIMPLETYPE) + if itemtypeinfos is None: + raise "\"%s\" isn't defined!" % typeinfos["itemType"] + else: + itemtypeinfos = typeinfos["itemType"] + + # Check that item type is a simple type + if itemtypeinfos["type"] != SIMPLETYPE: + raise ValueError, "Item type given isn't a simpleType!" + + simpleType["basename"] = "list" + + # Check that derivation is allowed + if itemtypeinfos.has_key("final"): + if itemtypeinfos["final"].has_key("#all"): + raise ValueError("Item type can't be derivated!") + if itemtypeinfos["final"].has_key("list"): + raise ValueError("Item type can't be derivated by list!") + + # Generate extract value for new created type + def ExtractSimpleTypeValue(attr, extract = True): + values = [] + for value in GetToken(attr, extract).split(" "): + values.append(itemtypeinfos["extract"](value, False)) + return values + + def CheckSimpleTypeValue(value): + for item in value: + result = itemtypeinfos["check"](item) + if not result: + return result + return True + + SimpleTypeInitialValue = lambda: [] + + GenerateSimpleType = GenerateSimpleTypeXMLText(lambda x: " ".join(map(itemtypeinfos["generate"], x))) + + facets = GenerateDictFacets(["length", "maxLength", "minLength", "enumeration", "pattern"]) + facets["whiteSpace"] = ("collapse", False) + + elif typeinfos["type"] == "union": + # Search for member types definition + membertypesinfos = [] + for membertype in typeinfos["memberTypes"]: + if isinstance(membertype, (StringType, UnicodeType)): + infos = factory.FindSchemaElement(membertype, SIMPLETYPE) + if infos is None: + raise ValueError("\"%s\" isn't defined!" % membertype) + else: + infos = membertype + + # Check that member type is a simple type + if infos["type"] != SIMPLETYPE: + raise ValueError("Member type given isn't a simpleType!") + + # Check that derivation is allowed + if infos.has_key("final"): + if infos["final"].has_key("#all"): + raise ValueError("Item type can't be derivated!") + if infos["final"].has_key("union"): + raise ValueError("Member type can't be derivated by union!") + + membertypesinfos.append(infos) + + simpleType["basename"] = "union" + + # Generate extract value for new created type + def ExtractSimpleTypeValue(attr, extract = True): + if extract: + value = GetAttributeValue(attr) + else: + value = attr + for infos in membertypesinfos: + try: + return infos["extract"](attr, False) + except: + pass + raise ValueError("\"%s\" isn't valid for type defined for union!") + + def CheckSimpleTypeValue(value): + for infos in membertypesinfos: + result = infos["check"](value) + if result: + return result + return False + + SimpleTypeInitialValue = membertypesinfos[0]["initial"] + + def GenerateSimpleTypeFunction(value): + if isinstance(value, BooleanType): + return {True: "true", False: "false"}[value] + else: + return str(value) + GenerateSimpleType = GenerateSimpleTypeXMLText(GenerateSimpleTypeFunction) + + facets = GenerateDictFacets(["pattern", "enumeration"]) + + simpleType["facets"] = facets + simpleType["extract"] = ExtractSimpleTypeValue + simpleType["initial"] = SimpleTypeInitialValue + simpleType["check"] = CheckSimpleTypeValue + simpleType["generate"] = GenerateSimpleType + return simpleType + + +# Complex type + +def ExtractAttributes(factory, elements, base = None): + if base is not None: + basetypeinfos = factory.FindSchemaElement(base, COMPLEXTYPE) + if isinstance(basetypeinfos, (UnicodeType, StringType)): + attrnames = {} + else: + attrnames = dict(map(lambda x:(x["name"], True), basetypeinfos["attributes"])) + else: + attrnames = {} + attrs = [] + for element in elements: + if element["type"] == ATTRIBUTE: + if attrnames.get(element["name"], False): + raise ValueError("\"%s\" attribute has been defined two times!" % element["name"]) + else: + attrnames[element["name"]] = True + attrs.append(element) + elif element["type"] == "attributeGroup": + attrgroup = factory.FindSchemaElement(element["ref"], ATTRIBUTESGROUP) + for attr in attrgroup["attributes"]: + if attrnames.get(attr["name"], False): + raise ValueError("\"%s\" attribute has been defined two times!" % attr["name"]) + else: + attrnames[attr["name"]] = True + attrs.append(attr) + elif element["type"] == "anyAttribute": + raise ValueError("\"anyAttribute\" element isn't supported yet!") + return attrs + + +def ReduceRestriction(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + restriction = {"type": "restriction", "base": attributes.get("base", None), "facets": [], "doc": annotations} + if len(children) > 0 and children[0]["type"] == SIMPLETYPE: + if restriction["base"] is None: + restriction["base"] = children.pop(0) + else: + raise ValueError("Only one base type can be defined for restriction!") + if restriction["base"] is None: + raise ValueError("No base type has been defined for restriction!") + + while len(children) > 0 and children[0]["type"] in ALL_FACETS: + restriction["facets"].append(children.pop(0)) + restriction["attributes"] = ExtractAttributes(factory, children) + return restriction + + +def ReduceExtension(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + extension = {"type": "extension", "attributes": [], "elements": [], "base": attributes.get("base", None), "doc": annotations} + if len(children) > 0: + if children[0]["type"] in ["group", "all", CHOICE, "sequence"]: + group = children.pop(0) + if group["type"] in ["all", "sequence"]: + extension["elements"] = group["elements"] + extension["order"] = group["order"] + elif group["type"] == CHOICE: + content = group.copy() + content["name"] = "content" + extension["elements"].append(content) + elif group["type"] == "group": + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) + if elmtgroup.has_key("elements"): + extension["elements"] = elmtgroup["elements"] + extension["order"] = elmtgroup["order"] + else: + content = elmtgroup.copy() + content["name"] = "content" + extension["elements"].append(content) + extension["attributes"] = ExtractAttributes(factory, children, extension["base"]) + return extension + + +def ReduceSimpleContent(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + simpleContent = children[0].copy() + simpleContent["type"] = "simpleContent" + return simpleContent + + +def ReduceComplexContent(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + complexContent = children[0].copy() + complexContent["type"] = "complexContent" + return complexContent + + +def ReduceComplexType(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + if len(children) > 0: + if children[0]["type"] in ["simpleContent", "complexContent"]: + complexType = children[0].copy() + complexType.update(attributes) + complexType["type"] = COMPLEXTYPE + return complexType + elif children[0]["type"] in ["group", "all", CHOICE, "sequence"]: + complexType = {"type": COMPLEXTYPE, "elements": [], "order": True, "doc": annotations} + complexType.update(attributes) + group = children.pop(0) + if group["type"] in ["all", "sequence"]: + if group["minOccurs"] == 0 or group["maxOccurs"] != 1: + if len(group["elements"]) > 1: + raise ValueError("Not supported yet!") + if group["minOccurs"] == 0: + group["elements"][0]["minOccurs"] = group["minOccurs"] + if group["maxOccurs"] != 1: + group["elements"][0]["maxOccurs"] = group["maxOccurs"] + complexType["elements"] = group["elements"] + complexType["order"] = group["order"] + elif group["type"] == CHOICE: + content = group.copy() + content["name"] = "content" + complexType["elements"].append(content) + elif group["type"] == "group": + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) + if elmtgroup.has_key("elements"): + complexType["elements"] = elmtgroup["elements"] + complexType["order"] = elmtgroup["order"] + else: + content = elmtgroup.copy() + content["name"] = "content" + complexType["elements"].append(content) + else: + complexType = {"elements": [], "order": True, "doc": annotations} + complexType.update(attributes) + complexType["type"] = COMPLEXTYPE + complexType["attributes"] = ExtractAttributes(factory, children) + return complexType + else: + raise ValueError("\"ComplexType\" can't be empty!") + + +# Attribute elements + +def ReduceAnyAttribute(factory, attributes, elements): + return {"type" : "anyAttribute"} + + +def ReduceAttribute(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + if attributes.has_key("default"): + if attributes.has_key("fixed"): + raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!") + elif attributes.get("use", "optional") != "optional": + raise ValueError("if \"default\" present, \"use\" can only have the value \"optional\"!") + + attribute = {"type": ATTRIBUTE, "attr_type": attributes.get("type", None), "doc": annotations} + if len(children) > 0: + if attribute["attr_type"] is None: + attribute["attr_type"] = children[0] + else: + raise ValueError("Only one type can be defined for attribute!") + + if attributes.has_key("ref"): + if attributes.has_key("name"): + raise ValueError("\"ref\" and \"name\" can't be defined at the same time!") + elif attributes.has_key("form"): + raise ValueError("\"ref\" and \"form\" can't be defined at the same time!") + elif attribute["attr_type"] is not None: + raise ValueError("if \"ref\" is present, no type can be defined!") + elif attribute["attr_type"] is None: + raise ValueError("No type has been defined for attribute \"%s\"!" % attributes["name"]) + + if attributes.has_key("type"): + tmp_attrs = attributes.copy() + tmp_attrs.pop("type") + attribute.update(tmp_attrs) + else: + attribute.update(attributes) + return attribute + + +def ReduceAttributeGroup(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + if attributes.has_key("ref"): + return {"type": "attributeGroup", "ref": attributes["ref"], "doc": annotations} + else: + return {"type": ATTRIBUTESGROUP, "attributes": ExtractAttributes(factory, children), "doc": annotations} + + +# Elements groups + +def ReduceAny(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + any = {"type": ANY, "doc": annotations} + any.update(attributes) + return any + +def ReduceElement(factory, attributes, elements): + if attributes.has_key("default") and attributes.has_key("fixed"): + raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!") + + if attributes.has_key("ref"): + annotations, children = factory.ReduceElements(elements) + + for attr in ["name", "default", "fixed", "form", "block", "type"]: + if attributes.has_key(attr): + raise ValueError("\"ref\" and \"%s\" can't be defined at the same time!" % attr) + if attributes.has_key("nillable"): + raise ValueError("\"ref\" and \"nillable\" can't be defined at the same time!") + if len(children) > 0: + raise ValueError("No type and no constraints can be defined where \"ref\" is defined!") + + infos = factory.FindSchemaElement(attributes["ref"], ELEMENT) + if infos is not None: + element = infos.copy() + element["minOccurs"] = attributes["minOccurs"] + element["maxOccurs"] = attributes["maxOccurs"] + return element + else: + raise ValueError("\"%s\" base type isn't defined or circular referenced!" % name) + + elif attributes.has_key("name"): + annotations, children = factory.ReduceElements(elements) + + element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "doc": annotations} + if len(children) > 0: + if element["elmt_type"] is None: + element["elmt_type"] = children[0] + else: + raise ValueError("Only one type can be defined for attribute!") + elif element["elmt_type"] is None: + element["elmt_type"] = "tag" + element["type"] = TAG + + if attributes.has_key("type"): + tmp_attrs = attributes.copy() + tmp_attrs.pop("type") + element.update(tmp_attrs) + else: + element.update(attributes) + return element + else: + raise ValueError("\"Element\" must have at least a \"ref\" or a \"name\" defined!") + +def ReduceAll(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + for child in children: + if children["maxOccurs"] == "unbounded" or children["maxOccurs"] > 1: + raise ValueError("\"all\" item can't have \"maxOccurs\" attribute greater than 1!") + + return {"type": "all", "elements": children, "minOccurs": attributes["minOccurs"], + "maxOccurs": attributes["maxOccurs"], "order": False, "doc": annotations} + + +def ReduceChoice(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + choices = [] + for child in children: + if child["type"] in [ELEMENT, ANY, TAG]: + choices.append(child) + elif child["type"] == "sequence": + raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!") + elif child["type"] == CHOICE: + choices.extend(child["choices"]) + elif child["type"] == "group": + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) + if not elmtgroup.has_key("choices"): + raise ValueError("Only group composed of \"choice\" can be referenced in \"choice\" element!") + choices_tmp = [] + for choice in elmtgroup["choices"]: + if not isinstance(choice["elmt_type"], (UnicodeType, StringType)) and choice["elmt_type"]["type"] == COMPLEXTYPE: + elmt_type = "%s_%s" % (elmtgroup["name"], choice["name"]) + if factory.TargetNamespace is not None: + elmt_type = "%s:%s" % (factory.TargetNamespace, elmt_type) + new_choice = choice.copy() + new_choice["elmt_type"] = elmt_type + choices_tmp.append(new_choice) + else: + choices_tmp.append(choice) + choices.extend(choices_tmp) + + return {"type": CHOICE, "choices": choices, "minOccurs": attributes["minOccurs"], + "maxOccurs": attributes["maxOccurs"], "doc": annotations} + + +def ReduceSequence(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + sequence = [] + for child in children: + if child["type"] in [ELEMENT, ANY, TAG]: + sequence.append(child) + elif child["type"] == CHOICE: + content = child.copy() + content["name"] = "content" + sequence.append(content) + elif child["type"] == "sequence": + sequence.extend(child["elements"]) + elif child["type"] == "group": + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) + if not elmtgroup.has_key("elements") or not elmtgroup["order"]: + raise ValueError("Only group composed of \"sequence\" can be referenced in \"sequence\" element!") + elements_tmp = [] + for element in elmtgroup["elements"]: + if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and element["elmt_type"]["type"] == COMPLEXTYPE: + elmt_type = "%s_%s"%(elmtgroup["name"], element["name"]) + if factory.TargetNamespace is not None: + elmt_type = "%s:%s" % (factory.TargetNamespace, elmt_type) + new_element = element.copy() + new_element["elmt_type"] = elmt_type + elements_tmp.append(new_element) + else: + elements_tmp.append(element) + sequence.extend(elements_tmp) + + return {"type": "sequence", "elements": sequence, "minOccurs": attributes["minOccurs"], + "maxOccurs": attributes["maxOccurs"], "order": True, "doc": annotations} + + +def ReduceGroup(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + + if attributes.has_key("ref"): + return {"type": "group", "ref": attributes["ref"], "doc": annotations} + else: + element = children[0] + group = {"type": ELEMENTSGROUP, "doc": annotations} + if element["type"] == CHOICE: + group["choices"] = element["choices"] + else: + group.update({"elements": element["elements"], "order": group["order"]}) + group.update(attributes) + return group + +# Constraint elements + +def ReduceUnique(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"unique\" element isn't supported yet!") + +def ReduceKey(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"key\" element isn't supported yet!") + +def ReduceKeyRef(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"keyref\" element isn't supported yet!") + +def ReduceSelector(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"selector\" element isn't supported yet!") + +def ReduceField(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"field\" element isn't supported yet!") + + +# Inclusion elements + +def ReduceImport(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"import\" element isn't supported yet!") + +def ReduceInclude(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"include\" element isn't supported yet!") + +def ReduceRedefine(factory, attributes, elements): + annotations, children = factory.ReduceElements(elements) + raise ValueError("\"redefine\" element isn't supported yet!") + + +# Schema element + +def ReduceSchema(factory, attributes, elements): + factory.AttributeFormDefault = attributes["attributeFormDefault"] + factory.ElementFormDefault = attributes["elementFormDefault"] + factory.BlockDefault = attributes["blockDefault"] + factory.FinalDefault = attributes["finalDefault"] + + if attributes.has_key("targetNamespace"): + factory.TargetNamespace = factory.DefinedNamespaces.get(attributes["targetNamespace"], None) + factory.Namespaces[factory.TargetNamespace] = {} + + annotations, children = factory.ReduceElements(elements, True) + + for child in children: + if child.has_key("name"): + infos = factory.GetQualifiedNameInfos(child["name"], factory.TargetNamespace, True) + if infos is None: + factory.Namespaces[factory.TargetNamespace][child["name"]] = child + elif not CompareSchema(infos, child): + raise ValueError("\"%s\" is defined twice in targetNamespace!" % child["name"]) + +def CompareSchema(schema, reference): + if isinstance(schema, ListType): + if not isinstance(reference, ListType) or len(schema) != len(reference): + return False + for i, value in enumerate(schema): + result = CompareSchema(value, reference[i]) + if not result: + return result + return True + elif isinstance(schema, DictType): + if not isinstance(reference, DictType) or len(schema) != len(reference): + return False + for name, value in schema.items(): + ref_value = reference.get(name, None) + if ref_value is None: + return False + result = CompareSchema(value, ref_value) + if not result: + return result + return True + elif isinstance(schema, FunctionType): + if not isinstance(reference, FunctionType) or schema.__name__ != reference.__name__: + return False + else: + return True + return schema == reference + +#------------------------------------------------------------------------------- +# Base class for XSD schema extraction +#------------------------------------------------------------------------------- + + +class XSDClassFactory(ClassFactory): + + def __init__(self, document, debug = False): + ClassFactory.__init__(self, document, debug) + self.Namespaces["xml"] = { + "lang": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("lang", LANGUAGE_model) + } + } + } + self.Namespaces["xsi"] = { + "noNamespaceSchemaLocation": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": NotSupportedYet("noNamespaceSchemaLocation") + } + }, + "nil": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": NotSupportedYet("nil") + } + }, + "schemaLocation": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": NotSupportedYet("schemaLocation") + } + }, + "type": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": NotSupportedYet("type") + } + } + } + + def ParseSchema(self): + schema = self.Document.childNodes[0] + for qualified_name, attr in schema._attrs.items(): + value = GetAttributeValue(attr) + if value == "http://www.w3.org/2001/XMLSchema": + namespace, name = DecomposeQualifiedName(qualified_name) + if namespace == "xmlns": + self.DefinedNamespaces["http://www.w3.org/2001/XMLSchema"] = name + self.SchemaNamespace = name + else: + self.DefinedNamespaces["http://www.w3.org/2001/XMLSchema"] = self.SchemaNamespace + self.Namespaces[self.SchemaNamespace] = XSD_NAMESPACE + self.Schema = XSD_NAMESPACE["schema"]["extract"]["default"](self, schema) + ReduceSchema(self, self.Schema[1], self.Schema[2]) + + def FindSchemaElement(self, element_name, element_type): + namespace, name = DecomposeQualifiedName(element_name) + element = self.GetQualifiedNameInfos(name, namespace, True) + if element is None and namespace == self.TargetNamespace and name not in self.CurrentCompilations: + self.CurrentCompilations.append(name) + element = self.CreateSchemaElement(name, element_type) + self.CurrentCompilations.pop(-1) + if element is not None: + self.Namespaces[self.TargetNamespace][name] = element + if element is None: + if name in self.CurrentCompilations: + if self.Debug: + print "Warning : \"%s\" is circular referenced!" % element_name + return element_name + else: + raise ValueError("\"%s\" isn't defined!" % element_name) + if element["type"] != element_type: + raise ValueError("\"%s\" isn't a group!" % element_name) + return element + + def CreateSchemaElement(self, element_name, element_type): + for type, attributes, elements in self.Schema[2]: + namespace, name = DecomposeQualifiedName(type) + if attributes.has_key("name") and attributes["name"] == element_name: + element_infos = None + if element_type == ATTRIBUTE and name == "attribute": + element_infos = ReduceAttribute(self, attributes, elements) + elif element_type == ELEMENT and name == "element": + element_infos = ReduceElement(self, attributes, elements) + elif element_type == ATTRIBUTESGROUP and name == "attributeGroup": + element_infos = ReduceAttributeGroup(self, attributes, elements) + elif element_type == ELEMENTSGROUP and name == "group": + element_infos = ReduceGroup(self, attributes, elements) + elif element_type == SIMPLETYPE and name == "simpleType": + element_infos = ReduceSimpleType(self, attributes, elements) + elif element_type == COMPLEXTYPE and name == "complexType": + element_infos = ReduceComplexType(self, attributes, elements) + if element_infos is not None: + self.Namespaces[self.TargetNamespace][element_name] = element_infos + return element_infos + return None + +""" +This function opens the xsd file and generate the classes from the xml tree +""" +def GenerateClassesFromXSD(filename, declare = False): + xsdfile = open(filename, 'r') + factory = XSDClassFactory(minidom.parse(xsdfile)) + xsdfile.close() + factory.ParseSchema() + return GenerateClasses(factory, declare) + +""" +This function generate the classes from the xsd given as a string +""" +def GenerateClassesFromXSDstring(xsdstring, declare = False): + factory = XSDClassFactory(minidom.parseString(xsdstring)) + factory.ParseSchema() + return GenerateClasses(factory, declare) + + +#------------------------------------------------------------------------------- +# XSD schema syntax elements +#------------------------------------------------------------------------------- + +XSD_NAMESPACE = { + +#------------------------------------------------------------------------------- +# Syntax elements definition +#------------------------------------------------------------------------------- + + "all": {"struct": """ + + Content: (annotation?, element*) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"], + re.compile("((?:annotation )?(?:element )*)")) + }, + "reduce": ReduceAll + }, + + "annotation": {"struct": """ + + Content: (appinfo | documentation)* + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("annotation", ["id"], + re.compile("((?:app_info |documentation )*)")) + }, + "reduce": ReduceAnnotation + }, + + "any": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("any", + ["id", "maxOccurs", "minOccurs", "namespace", "processContents"], + re.compile("((?:annotation )?(?:simpleType )*)")) + }, + "reduce": ReduceAny + }, + + "anyAttribute": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("anyAttribute", + ["id", "namespace", "processContents"], ONLY_ANNOTATION) + }, + "reduce": ReduceAnyAttribute + }, + + "appinfo": {"struct": """ + + Content: ({any})* + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("appinfo", ["source"], re.compile("(.*)"), True) + }, + "reduce": ReduceAppInfo + }, + + "attribute": {"struct": """ + + Content: (annotation?, simpleType?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("attribute", + ["default", "fixed", "form", "id", "name", "ref", "type", "use"], + re.compile("((?:annotation )?(?:simpleType )?)")), + "schema": GenerateElement("attribute", + ["default", "fixed", "form", "id", "name", "type"], + re.compile("((?:annotation )?(?:simpleType )?)")) + }, + "reduce": ReduceAttribute + }, + + "attributeGroup": {"struct": """ + + Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("attributeGroup", + ["id", "ref"], ONLY_ANNOTATION), + "schema": GenerateElement("attributeGroup", + ["id", "name"], + re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")) + }, + "reduce": ReduceAttributeGroup + }, + + "choice": {"struct": """ + + Content: (annotation?, (element | group | choice | sequence | any)*) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"], + re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)")) + }, + "reduce": ReduceChoice + }, + + "complexContent": {"struct": """ + + Content: (annotation?, (restriction | extension)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("complexContent", ["id", "mixed"], + re.compile("((?:annotation )?(?:restriction |extension ))")) + }, + "reduce": ReduceComplexContent + }, + + "complexType": {"struct": """ + + Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("complexType", + ["abstract", "block", "final", "id", "mixed", "name"], + re.compile("((?:annotation )?(?:simpleContent |complexContent |(?:(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))))")) + }, + "reduce": ReduceComplexType + }, + + "documentation": {"struct" : """ + + Content: ({any})* + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("documentation", + ["source", "lang"], re.compile("(.*)"), True) + }, + "reduce": ReduceDocumentation + }, + + "element": {"struct": """ + + Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"], + re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")), + "schema": GenerateElement("element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"], + re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")) + }, + "reduce": ReduceElement + }, + + "enumeration": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("enumeration", ["id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("enumeration", False) + }, + + "extension": {"struct": """ + + Content: (annotation?, ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("extension", ["base", "id"], + re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")), + "complexContent": GenerateElement("extension", ["base", "id"], + re.compile("((?:annotation )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")) + }, + "reduce": ReduceExtension + }, + + "field": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("field", ["id", "xpath"], ONLY_ANNOTATION) + }, + "reduce": ReduceField + }, + + "fractionDigits": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("fractionDigits", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("fractionDigits", True) + }, + + "group": {"struct": """ + + Content: (annotation?, (all | choice | sequence)?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("group", + ["id", "maxOccurs", "minOccurs", "ref"], + re.compile("((?:annotation )?(?:all |choice |sequence )?)")), + "schema": GenerateElement("group", + ["id", "name"], + re.compile("((?:annotation )?(?:all |choice |sequence )?)")) + }, + "reduce": ReduceGroup + }, + + "import": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("import", + ["id", "namespace", "schemaLocation"], ONLY_ANNOTATION) + }, + "reduce": ReduceImport + }, + + "include": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("include", + ["id", "schemaLocation"], ONLY_ANNOTATION) + }, + "reduce": ReduceInclude + }, + + "key": {"struct": """ + + Content: (annotation?, (selector, field+)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("key", ["id", "name"], + re.compile("((?:annotation )?(?:selector |(?:field )+))")) + }, + "reduce": ReduceKey + }, + + "keyref": {"struct": """ + + Content: (annotation?, (selector, field+)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("keyref", ["id", "name", "refer"], + re.compile("((?:annotation )?(?:selector |(?:field )+))")) + }, + "reduce": ReduceKeyRef + }, + + "length": {"struct" : """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("length", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("length", True) + }, + + "list": {"struct": """ + + Content: (annotation?, simpleType?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("list", ["id", "itemType"], + re.compile("((?:annotation )?(?:simpleType )?)$")) + }, + "reduce": ReduceList + }, + + "maxExclusive": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("maxExclusive", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("maxExclusive", True) + }, + + "maxInclusive": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("maxInclusive", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("maxInclusive", True) + }, + + "maxLength": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("maxLength", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("maxLength", True) + }, + + "minExclusive": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("minExclusive", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("minExclusive", True) + }, + + "minInclusive": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("minInclusive", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("minInclusive", True) + }, + + "minLength": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("minLength", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("minLength", True) + }, + + "pattern": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("pattern", ["id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("pattern", False) + }, + + "redefine": {"struct": """ + + Content: (annotation | (simpleType | complexType | group | attributeGroup))* + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("refine", ["id", "schemaLocation"], + re.compile("((?:annotation |(?:simpleType |complexType |group |attributeGroup ))*)")) + }, + "reduce": ReduceRedefine + }, + + "restriction": {"struct": """ + + Content: (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("restriction", ["base", "id"], + re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)))")), + "simpleContent": GenerateElement("restriction", ["base", "id"], + re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")), + "complexContent": GenerateElement("restriction", ["base", "id"], + re.compile("((?:annotation )?(?:(?:simpleType )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")), + }, + "reduce": ReduceRestriction + }, + + "schema": {"struct": """ + + Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("schema", + ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"], + re.compile("((?:include |import |redefine |annotation )*(?:(?:(?:simpleType |complexType |group |attributeGroup )|element |attribute |annotation )(?:annotation )*)*)")) + } + }, + + "selector": {"struct": """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("selector", ["id", "xpath"], ONLY_ANNOTATION) + }, + "reduce": ReduceSelector + }, + + "sequence": {"struct": """ + + Content: (annotation?, (element | group | choice | sequence | any)*) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"], + re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)")) + }, + "reduce": ReduceSequence + }, + + "simpleContent": {"struct" : """ + + Content: (annotation?, (restriction | extension)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("simpleContent", ["id"], + re.compile("((?:annotation )?(?:restriction |extension ))")) + }, + "reduce": ReduceSimpleContent + }, + + "simpleType": {"struct" : """ + + Content: (annotation?, (restriction | list | union)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("simpleType", ["final", "id", "name"], + re.compile("((?:annotation )?(?:restriction |list |union ))")) + }, + "reduce": ReduceSimpleType + }, + + "totalDigits": {"struct" : """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("totalDigits", + ["fixed", "id", "value"], ONLY_ANNOTATION), + }, + "reduce": GenerateFacetReducing("totalDigits", True) + }, + + "union": {"struct": """ + + Content: (annotation?, simpleType*) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("union", ["id", "memberTypes"], + re.compile("((?:annotation )?(?:simpleType )*)")) + }, + "reduce": ReduceUnion + }, + + "unique": {"struct": """ + + Content: (annotation?, (selector, field+)) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("unique", ["id", "name"], + re.compile("((?:annotation )?(?:selector |(?:field )+))")) + }, + "reduce": ReduceUnique + }, + + "whiteSpace": {"struct" : """ + + Content: (annotation?) + """, + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement("whiteSpace", + ["fixed", "id", "value"], ONLY_ANNOTATION) + }, + "reduce": GenerateFacetReducing("whiteSpace", True) + }, + +#------------------------------------------------------------------------------- +# Syntax attributes definition +#------------------------------------------------------------------------------- + + "abstract": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetBoolean + }, + "default": { + "default": False + } + }, + + "attributeFormDefault": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction("member attributeFormDefault", ["qualified", "unqualified"]) + }, + "default": { + "default": "unqualified" + } + }, + + "base": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member base", QName_model) + } + }, + + "block": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateGetList("block", ["restriction", "extension", "substitution"]) + } + }, + + "blockDefault": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateGetList("block", ["restriction", "extension", "substitution"]) + }, + "default": { + "default": "" + } + }, + + "default": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetAttributeValue + } + }, + + "elementFormDefault": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction("member elementFormDefault", ["qualified", "unqualified"]) + }, + "default": { + "default": "unqualified" + } + }, + + "final": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateGetList("final", ["restriction", "extension", "substitution"]), + "simpleType": GenerateGetList("final", ["list", "union", "restriction"]) + } + }, + + "finalDefault": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateGetList("finalDefault", ["restriction", "extension", "list", "union"]) + }, + "default": { + "default": "" + } + }, + + "fixed": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetBoolean, + "attribute": GetAttributeValue, + "element": GetAttributeValue + }, + "default": { + "default": False, + "attribute": None, + "element": None + } + }, + + "form": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction("member form", ["qualified", "unqualified"]) + } + }, + + "id": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member id", NCName_model) + } + }, + + "itemType": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member itemType", QName_model) + } + }, + + "memberTypes": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameListExtraction("member memberTypes", QNames_model) + }, + }, + + "maxOccurs": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateLimitExtraction(), + "all": GenerateLimitExtraction(1, 1, False) + }, + "default": { + "default": 1 + } + }, + + "minOccurs": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateLimitExtraction(unbounded = False), + "all": GenerateLimitExtraction(0, 1, False) + }, + "default": { + "default": 1 + } + }, + + "mixed": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetBoolean + }, + "default": { + "default": None, + "complexType": False + } + }, + + "name": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member name", NCName_model) + } + }, + + "namespace": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member namespace", URI_model), + "any": GetNamespaces + }, + "default": { + "default": None, + "any": "##any" + } + }, + + "nillable": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetBoolean + }, + }, + + "processContents": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction("member processContents", ["lax", "skip", "strict"]) + }, + "default": { + "default": "strict" + } + }, + + "ref": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member ref", QName_model) + } + }, + + "refer": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member refer", QName_model) + } + }, + + "schemaLocation": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member schemaLocation", URI_model) + } + }, + + "source": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member source", URI_model) + } + }, + + "substitutionGroup": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member substitutionGroup", QName_model) + } + }, + + "targetNamespace": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member targetNamespace", URI_model) + } + }, + + "type": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateModelNameExtraction("member type", QName_model) + } + }, + + "use": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction("member usage", ["required", "optional", "prohibited"]) + }, + "default": { + "default": "optional" + } + }, + + "value": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetAttributeValue, + "fractionDigits": GenerateIntegerExtraction(minInclusive=0), + "length": GenerateIntegerExtraction(minInclusive=0), + "maxLength": GenerateIntegerExtraction(minInclusive=0), + "minLength": GenerateIntegerExtraction(minInclusive=0), + "totalDigits": GenerateIntegerExtraction(minExclusive=0), + "whiteSpace": GenerateEnumeratedExtraction("value", ["collapse", "preserve", "replace"]) + } + }, + + "version": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GetToken + } + }, + + "xpath": { + "type": SYNTAXATTRIBUTE, + "extract": { + "default": NotSupportedYet("xpath") + } + }, + +#------------------------------------------------------------------------------- +# Simple types definition +#------------------------------------------------------------------------------- + + "string": { + "type": SIMPLETYPE, + "basename": "string", + "extract": GetAttributeValue, + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x : x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "normalizedString": { + "type": SIMPLETYPE, + "basename": "normalizedString", + "extract": GetNormalizedString, + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x : x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "token": { + "type": SIMPLETYPE, + "basename": "token", + "extract": GetToken, + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x : x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "base64Binary": { + "type": SIMPLETYPE, + "basename": "base64Binary", + "extract": NotSupportedYet("base64Binary"), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "hexBinary": { + "type": SIMPLETYPE, + "basename": "hexBinary", + "extract": GetHexInteger, + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X"%x)/2.)*2))+"X")%x), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "integer": { + "type": SIMPLETYPE, + "basename": "integer", + "extract": GenerateIntegerExtraction(), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "positiveInteger": { + "type": SIMPLETYPE, + "basename": "positiveInteger", + "extract": GenerateIntegerExtraction(minExclusive=0), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "negativeInteger": { + "type": SIMPLETYPE, + "basename": "negativeInteger", + "extract": GenerateIntegerExtraction(maxExclusive=0), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "nonNegativeInteger": { + "type": SIMPLETYPE, + "basename": "nonNegativeInteger", + "extract": GenerateIntegerExtraction(minInclusive=0), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "nonPositiveInteger": { + "type": SIMPLETYPE, + "basename": "nonPositiveInteger", + "extract": GenerateIntegerExtraction(maxInclusive=0), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "long": { + "type": SIMPLETYPE, + "basename": "long", + "extract": GenerateIntegerExtraction(minInclusive=-2**63,maxExclusive=2**63), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "unsignedLong": { + "type": SIMPLETYPE, + "basename": "unsignedLong", + "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**64), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "int": { + "type": SIMPLETYPE, + "basename": "int", + "extract": GenerateIntegerExtraction(minInclusive=-2**31,maxExclusive=2**31), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "unsignedInt": { + "type": SIMPLETYPE, + "basename": "unsignedInt", + "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**32), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "short": { + "type": SIMPLETYPE, + "basename": "short", + "extract": GenerateIntegerExtraction(minInclusive=-2**15,maxExclusive=2**15), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "unsignedShort": { + "type": SIMPLETYPE, + "basename": "unsignedShort", + "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**16), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "byte": { + "type": SIMPLETYPE, + "basename": "byte", + "extract": GenerateIntegerExtraction(minInclusive=-2**7,maxExclusive=2**7), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "unsignedByte": { + "type": SIMPLETYPE, + "basename": "unsignedByte", + "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**8), + "facets": DECIMAL_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: 0, + "check": lambda x: isinstance(x, IntType) + }, + + "decimal": { + "type": SIMPLETYPE, + "basename": "decimal", + "extract": GenerateFloatExtraction("decimal"), + "facets": DECIMAL_FACETS, + "generate": GenerateFloatXMLText(), + "initial": lambda: 0., + "check": lambda x: isinstance(x, (IntType, FloatType)) + }, + + "float": { + "type": SIMPLETYPE, + "basename": "float", + "extract": GenerateFloatExtraction("float", ["INF", "-INF", "NaN"]), + "facets": NUMBER_FACETS, + "generate": GenerateFloatXMLText(["INF", "-INF", "NaN"]), + "initial": lambda: 0., + "check": lambda x: {"INF" : True, "-INF" : True, "NaN" : True}.get(x, isinstance(x, (IntType, FloatType))) + }, + + "double": { + "type": SIMPLETYPE, + "basename": "double", + "extract": GenerateFloatExtraction("double", ["INF", "-INF", "NaN"]), + "facets": NUMBER_FACETS, + "generate": GenerateFloatXMLText(["INF", "-INF", "NaN"]), + "initial": lambda: 0., + "check": lambda x: {"INF" : True, "-INF" : True, "NaN" : True}.get(x, isinstance(x, (IntType, FloatType))) + }, + + "boolean": { + "type": SIMPLETYPE, + "basename": "boolean", + "extract": GetBoolean, + "facets": GenerateDictFacets(["pattern", "whiteSpace"]), + "generate": GenerateSimpleTypeXMLText(lambda x:{True : "true", False : "false"}[x]), + "initial": lambda: False, + "check": lambda x: isinstance(x, BooleanType) + }, + + "duration": { + "type": SIMPLETYPE, + "basename": "duration", + "extract": NotSupportedYet("duration"), + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "dateTime": { + "type": SIMPLETYPE, + "basename": "dateTime", + "extract": GetDateTime, + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(datetime.datetime.isoformat), + "initial": lambda: datetime.datetime(1,1,1,0,0,0,0), + "check": lambda x: isinstance(x, datetime.datetime) + }, + + "date": { + "type": SIMPLETYPE, + "basename": "date", + "extract": GetDate, + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(datetime.date.isoformat), + "initial": lambda: datetime.date(1,1,1), + "check": lambda x: isinstance(x, datetime.date) + }, + + "time": { + "type": SIMPLETYPE, + "basename": "time", + "extract": GetTime, + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(datetime.time.isoformat), + "initial": lambda: datetime.time(0,0,0,0), + "check": lambda x: isinstance(x, datetime.time) + }, + + "gYear": { + "type": SIMPLETYPE, + "basename": "gYear", + "extract": NotSupportedYet("gYear"), + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "gYearMonth": { + "type": SIMPLETYPE, + "basename": "gYearMonth", + "extract": NotSupportedYet("gYearMonth"), + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "gMonth": { + "type": SIMPLETYPE, + "basename": "gMonth", + "extract": NotSupportedYet("gMonth"), + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "gMonthDay": { + "type": SIMPLETYPE, + "basename": "gMonthDay", + "extract": NotSupportedYet("gMonthDay"), + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "gDay": { + "type": SIMPLETYPE, + "basename": "gDay", + "extract": NotSupportedYet("gDay"), + "facets": NUMBER_FACETS, + "generate": GenerateSimpleTypeXMLText(str), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "Name": { + "type": SIMPLETYPE, + "basename": "Name", + "extract": GenerateModelNameExtraction("Name", Name_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "QName": { + "type": SIMPLETYPE, + "basename": "QName", + "extract": GenerateModelNameExtraction("QName", QName_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "NCName": { + "type": SIMPLETYPE, + "basename": "NCName", + "extract": GenerateModelNameExtraction("NCName", NCName_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "anyURI": { + "type": SIMPLETYPE, + "basename": "anyURI", + "extract": GenerateModelNameExtraction("anyURI", URI_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "language": { + "type": SIMPLETYPE, + "basename": "language", + "extract": GenerateModelNameExtraction("language", LANGUAGE_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "en", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "ID": { + "type": SIMPLETYPE, + "basename": "ID", + "extract": GenerateModelNameExtraction("ID", Name_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "IDREF": { + "type": SIMPLETYPE, + "basename": "IDREF", + "extract": GenerateModelNameExtraction("IDREF", Name_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "IDREFS": { + "type": SIMPLETYPE, + "basename": "IDREFS", + "extract": GenerateModelNameExtraction("IDREFS", Names_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "ENTITY": { + "type": SIMPLETYPE, + "basename": "ENTITY", + "extract": GenerateModelNameExtraction("ENTITY", Name_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "ENTITIES": { + "type": SIMPLETYPE, + "basename": "ENTITIES", + "extract": GenerateModelNameExtraction("ENTITIES", Names_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "NOTATION": { + "type": SIMPLETYPE, + "basename": "NOTATION", + "extract": GenerateModelNameExtraction("NOTATION", Name_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "NMTOKEN": { + "type": SIMPLETYPE, + "basename": "NMTOKEN", + "extract": GenerateModelNameExtraction("NMTOKEN", NMToken_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + "NMTOKENS": { + "type": SIMPLETYPE, + "basename": "NMTOKENS", + "extract": GenerateModelNameExtraction("NMTOKENS", NMTokens_model), + "facets": STRING_FACETS, + "generate": GenerateSimpleTypeXMLText(lambda x: x), + "initial": lambda: "", + "check": lambda x: isinstance(x, (StringType, UnicodeType)) + }, + + # Complex Types + "anyType": {"type": COMPLEXTYPE, "extract": lambda x:None}, +} + +if __name__ == '__main__': + classes = GenerateClassesFromXSD("test.xsd") + + # Code for test of test.xsd + xmlfile = open("po.xml", 'r') + tree = minidom.parse(xmlfile) + xmlfile.close() + test = classes["PurchaseOrderType"]() + for child in tree.childNodes: + if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "purchaseOrder": + test.loadXMLTree(child) + test.items.item[0].setquantity(2) + testfile = open("test.xml", 'w') + testfile.write(u'\n') + testfile.write(test.generateXMLText("purchaseOrder").encode("utf-8")) + testfile.close()