# Copyright (C) 2000 LTG -- See accompanying COPYRIGHT and COPYING files # Implementation of XML Schema on top of layer.py # $Id: XMLSchema.py,v 1.185 2001/04/17 11:34:33 ht Exp $ import layer import types import string import sys import copy import os from urlparse import urljoin import PyLTXML import XML import re import xpath vss=string.split("$Revision: 1.185 $ of $Date: 2001/04/17 11:34:33 $") versionString="%s of %s %s"%(vss[1],vss[5],vss[6]) # For the time being, I force every schema document to be validated by the # DTD for schemas, so there is no need to check for required daughters, # attributes, etc. # There are _3_ layers of representation here: # 1) The XML elements themselves, instances of 'element', # usually held in a variable/property called 'elt' # 2) Their normalised internal form, instances of e.g. complexTypeElt or anyElt # usually held in a variable/property called 'xrpr' # 3) The corresponding schema component, instances of e.g. complexType or any, # usually held in a variable/property called 'component' # The paradigm is that xxxElt.__init__ plugs in the schema and provides empty # lists/dicts for daughters/attrs to go in, xxxElt.init (called once # all daughters/attrs have been processed) creates and attaches component(s), # component.init copies literal properties and attaches sub-components where # possible # Most components have properties with names based on those in the REC # If a property is component-valued, it will have a clause in the # component's class's __getattr_, which attempts to dereference the # corresponding Name property, c.f. basetype/basetypeName for simpleType # I've innovated one component not in the REC, namely attributeUse, # parallel to particle # Thinking about the interaction between lazy chasing of references and # SVC constraints -- I think the REC isn't capable of a consistent # interpretation here. My inclination is to implement SVCs on an # as-used basis. I could try putting a 'checked' attribute on every component, # dividing the computed attributes into literals and non-literals, and # testing 'checked' before returning _any_ non-literals. # Note that anything along this line means that schema errors may occur # in the midst of instance validation :-( What's the desired interaction # with lax validation/validation outcome? checkFacetTable={"minInclusive":'checkMin', "minExclusive":'checkMin', "maxInclusive":'checkMax', "maxExclusive":'checkMax', "totalDigits":'checkPS', "fractionDigits":'checkPS', "enumeration":'checkEnum', "length":'vacuousCheck', "minLength":'vacuousCheck', "maxLength":'vacuousCheck', "pattern":'vacuousCheck'} XMLSchemaNS="http://www.w3.org/2001/XMLSchema" XMLSchemaInstanceNS = "http://www.w3.org/2001/XMLSchema-instance" syspat=re.compile("^[^[]* (SYSTEM|PUBLIC) ") intpat=re.compile("^[^[]*\[([^\001]*)\]") def newFactory(): factory=layer.factory() cwd=string.replace(os.getcwd(),'\\','/')+'/' if cwd[1]==':': cwd='file:///'+cwd factory.fileNames=[cwd] factory.prepared=0 factory.schemaStack=[] factory.eltStack=[] factory.schemas={} factory.schema=None factory.unprocessedImports={} factory.targetNS=None factory.processingInclude=0 return factory def fromFile(filename=None,factory=None,targetNS=None,fromInclude=0): if not factory: factory=newFactory() factory.targetNS=targetNS elif not factory.schema: factory.targetNS=targetNS else: # we save the defaults in case an d schema changes them factory.schemaStack[0:0]=[(factory.schema,factory.processingInclude, factory.schema.elementFormDefault, factory.schema.attributeFormDefault, factory.schema.blockDefault, factory.schema.finalDefault)] if targetNS!=factory.schema.targetNS: if factory.schemas.has_key(targetNS): factory.schema=factory.schemas[targetNS] else: factory.schema=None factory.targetNS=targetNS factory.processingInclude=fromInclude fullFile=filename layErr=None if fullFile in factory.fileNames: res=None sys.stderr.write("%s attempts to include/import/redefine itself, ignored"%fullFile) else: factory.fileNames[0:0]=[fullFile] try: f=PyLTXML.Open(fullFile,PyLTXML.NSL_read|PyLTXML.NSL_read_namespaces) if f: dts=f.doctype.doctypeStatement if dts and syspat.match(dts): # let them use their own doctype ff=None fake=None else: intDecls="" if dts: mres=intpat.match(dts) if mres: intDecls=mres.group(1) prefs=[] scpFound=0 b=PyLTXML.GetNextBit(f) while (b and b.type not in ('start','empty')): if b.type=='bad': b=None break b=PyLTXML.GetNextBit(f) if not b: sys.stderr.write("%s has no elements???\n"%fullFile) raise PyLTXML.error for (key,val) in b.item.nsdict.items(): if val==XMLSchemaNS: scpFound=1 scp=key elif key=='xml' and val=='http://www.w3.org/XML/1998/namespace': continue else: prefs.append(key) if not scpFound: sys.stderr.write("document element of %s is not in namespace %s\n"%(fullFile,XMLSchemaNS)) raise PyLTXML.error if scp!='xs': if scp: pref="%s:"%scp pds="\n\n"%(scp,scp) else: pref="" pds="\n\n" else: pds="" pref="xs:" ns="" for p in prefs: if p: ns=ns+"\n"%(pref,p) for p in sys.path: if os.path.isfile("%s/%s"%(p or '.','XMLSchema.py')): home=p or '.' home = os.path.abspath(home) home=string.replace(home,"\\","/") if home[1]==':': home='file:///'+home if pds or ns or intDecls: fake1="\n"%(ns, intDecls) fake=fake1+fake2+fake3 else: fake="\n"%(pref,home) fake=fake+"<%sschema/>"%pref ff=PyLTXML.OpenString(fake, PyLTXML.NSL_read|PyLTXML.NSL_read_namespaces) if ff or not fake: try: res=factory.fromFile(schemaEltDispatch, {"finalDefault":"ignore", "blockDefault":"ignore", "elementFormDefault":"ignore", "attributeFormDefault":"ignore", "nillable":"nullable"}, lookup,"instance","variable", XMLSchemaNS, fullFile, (XMLSchemaNS,"schema"),ff) except layer.LayerError,layErr: res=None else: res=None else: res=None except PyLTXML.error: res=None if not isinstance(res,schemaElt): ne=XML.Element("notASchema") ne.addAttr('filename',fullFile) if layErr: ne.addAttr('lowLevelErrorMsg',layErr) factory.resElt.children.append(ne) schema=None else: schema=res.component schema.locations.append(fullFile) # schema is a an instance of schema, if present factory.fileNames=factory.fileNames[1:] if factory.schemaStack: factory.schema=factory.schemaStack[0][0] (factory.schema,factory.processingInclude, factory.schema.elementFormDefault, factory.schema.attributeFormDefault, factory.schema.blockDefault, factory.schema.finalDefault) = factory.schemaStack[0] factory.targetNS=factory.schema.targetNS factory.schemaStack=factory.schemaStack[1:] else: factory.schema=factory.targetNS=None return schema def lookup(eltName): if eltClasses.has_key(eltName): return eltClasses[eltName] else: return userClass class userClass: def __init__(self,factory,elt): self.elt=elt self.factory=factory def prepare(factory): # before we do anything serious, check if we need to # bootstrap the schema for schema if factory.schemas.has_key(XMLSchemaNS): # we're validating a schema with a (purported) schema for schemas sfors=factory.schemas[XMLSchemaNS] factory.sfors=sfors if ((not sfors.typeTable.has_key('string')) or sfors.typeTable['string'].basetypeName.local=='anySimpleType'): # need the ab-initio types sfors.targetNS=XMLSchemaNS factory.schema=sfors factory.targetNS=XMLSchemaNS sfors.doBuiltIns(factory) else: sfors=Schema(factory,None) factory.schemas[XMLSchemaNS]=sfors factory.sfors=sfors sfors.targetNS=XMLSchemaNS factory.schema=sfors factory.targetNS=XMLSchemaNS sfors.doBuiltIns(factory) sforsi=Schema(factory,None) factory.schemas[XMLSchemaInstanceNS]=sforsi factory.sforsi=sforsi sforsi.targetNS=XMLSchemaInstanceNS factory.schema=sforsi factory.targetNS=XMLSchemaInstanceNS sforsi.installInstanceAttrs(factory) factory.schema=sfors factory.targetNS=XMLSchemaNS ec=0 for sch in factory.schemas.values(): ec=ec+sch.errors factory.prepared=1 return ec class Schema: def __init__(self,factory,xrpr): self.xrpr=xrpr self.factory=factory self.errors=0 self.locations=[] if xrpr: # these are needed during schema accumulation self.maybeSetVar('targetNS','targetNamespace',None) self.maybeSetVar('elementFormDefault','elementFormDefault','unqualified') self.maybeSetVar('attributeFormDefault','attributeFormDefault', 'unqualified') self.maybeSetVar('finalDefault','finalDefault','') self.maybeSetVar('blockDefault','blockDefault','') if self.factory.targetNS: if self.targetNS!=self.factory.targetNS: if ((not self.targetNS) and factory.processingInclude): # chameleon include, OK self.targetNS=self.factory.targetNS factory.processingInclude=2 else: self.error("targetNamespace mismatch: %s expected, %s found" % (self.factory.targetNS, self.targetNS),xrpr.elt) else: self.factory.targetNS=self.targetNS if factory.schemas.has_key(self.targetNS): oldSchema=factory.schemas[self.targetNS] # use real tables, we're ephemeral factory.schema=oldSchema self.typeTable=oldSchema.typeTable self.elementTable=oldSchema.elementTable self.attributeTable=oldSchema.attributeTable self.groupTable=oldSchema.groupTable self.attributeGroupTable=oldSchema.attributeGroupTable self.vTypeTable=oldSchema.vTypeTable self.vElementTable=oldSchema.vElementTable self.vAttributeTable=oldSchema.vAttributeTable self.vGroupTable=oldSchema.vGroupTable self.vAttributeGroupTable=oldSchema.vAttributeGroupTable # copy defaults (they've been saved, will be restored) oldSchema.elementFormDefault=self.elementFormDefault oldSchema.attributeFormDefault=self.attributeFormDefault oldSchema.blockDefault=self.blockDefault oldSchema.finalDefault=self.finalDefault return else: factory.schemas[self.targetNS]=self factory.schema=self # either we're the first for this NS, or we're the FIRST self.typeTable={} self.elementTable={} self.attributeTable={} self.groupTable={} self.attributeGroupTable={} self.vTypeTable=VMapping(self, "typeTable") self.vElementTable=VMapping(self, "elementTable") self.vAttributeTable=VMapping(self, "attributeTable") self.vGroupTable=VMapping(self, "groupTable") self.vAttributeGroupTable=VMapping(self, "attributeGroupTable") def __str__(self): types=map(str,self.typeTable.values()) groups=map(str, self.groupTable.values()) attributeGroups=map(str, self.attributeGroupTable.values()) elts=map(str,self.elementTable.values()) attrs=map(str,self.attributeTable.values()) return "{Target:%s}{Types:%s}{Groups:%s}{AttrGroups:%s}{Elements:%s}{Attributes:%s}"%(self.targetNS,string.join(types,''),string.join(groups,''),string.join(attributeGroups,''),string.join(elts,''),string.join(attrs,'')) def maybeSetVar(self,varName,attrName,default): if self.xrpr.elt.hasAttrVal(attrName): setattr(self,varName,self.xrpr.elt.attrVal(attrName)) else: setattr(self,varName,default) def doBuiltIns(self,factory): self.doAbInitios(factory) # TODO: implement fixed facets for (bitn,basen,facets) in builtinTypeNames: fake=simpleTypeElt(factory,None) fake.name=bitn fake.restriction=restrictionElt(factory,None) fake.restriction.init(None) fake.init(None) fake.component.basetypeName=QName(None,basen,XMLSchemaNS) fake.component.variety='atomic' bit=fake.restriction.component bit.rootName=fake.component.basetype.rootName for (fc,fv) in facets: nf=fc(factory,None) bit.primitiveType.facets[fc.name]=nf nf.value=fv self.typeTable[bitn]=fake.component for (bitn,basen) in builtinLists: fake=simpleTypeElt(factory,None) fake.name=bitn fake.list=listElt(factory,None) fake.list.init(None) fake.init(None) fake.component.basetype=urType fake.component.variety='list' fake.list.component.itemtypeName=QName(None,basen,XMLSchemaNS) self.typeTable[bitn]=fake.component ap=Particle(factory,None,AnyAny(factory,None),None) ap.term.processContents='lax' ap.occurs=(0,None) urType.model.term.particles.append(ap) self.typeTable['anyType']=urType urSimpleType.factory=urType.factory=factory self.typeTable['anySimpleType']=urSimpleType def doAbInitios(self,factory): wsf1=Whitespace(factory,None) wsf1.value="collapse" wsf1.fixed="true" wsf2=Whitespace(factory,None) wsf2.value="preserve" for (ain,ait) in abInitioTypes: aiti=ait(factory,None) aiti.rootName=ain aiti.basetype=ait # for use in creating effective types aiti.effectiveType=aiti if ain=='string': aiti.facets['whiteSpace']=wsf2 else: aiti.facets['whiteSpace']=wsf1 self.typeTable[ain]=aiti def installInstanceAttrs(self,factory): factory.eltStack=['a'] # hack for (attrn,basen) in instanceAttrs: fake=attributeElt(factory,None) fake.name=attrn fake.type=basen fake.init(None) fake.component.typeDefinitionName=QName(None,basen,XMLSchemaNS) self.attributeTable[attrn]=fake.component for (attrn,itemn) in instanceLists: fake=attributeElt(factory,None) fake.name=attrn fake.simpleType=simpleTypeElt(factory,None) fake.simpleType.list=listElt(factory,None) fake.simpleType.list.init(None) fake.simpleType.list.component.itemtypeName=QName(None,itemn,XMLSchemaNS) fake.simpleType.init(None) fake.simpleType.component.variety='list' fake.simpleType.component.basetype=urType fake.init(None) self.attributeTable[attrn]=fake.component factory.eltStack=[] # hack ap=Particle(factory,None,AnyAny(factory,None),None) ap.term.processContents='lax' ap.occurs=(0,None) def prepare(self): # try to touch everything that might cause errors cool=1 for i in self.typeTable.values(): if isinstance(i,Type): cool=i.prepare() and cool for tab in (self.elementTable, self.attributeTable, self.groupTable, self.attributeGroupTable): for i in tab.values(): cool=i.prepare() and cool return cool def error(self,message,elt=None,warning=0): # should have code argument to identify SRC/COS if warning: ee=XML.Element("schemaWarning") else: ee=XML.Element("schemaError") if self.factory.prepared: ee.addAttr("phase","instance") else: ee.addAttr("phase","schema") if not warning: self.errors=self.errors+1 if elt: where(ee,elt.where) ee.children=[XML.Pcdata(message)] self.factory.resElt.children.append(ee) def newComponent(self,table,kind,comp): if not comp: return if comp.name: if table.has_key(comp.name): comp.schema.error("attempt to overwrite %s {%s}%s, ignored"%(kind, self.targetNS, comp.name), comp.xrpr.elt, 1) else: table[comp.name]=comp comp.qname=QName(None,comp.name,self.targetNS) else: shouldnt('nc: %s'%comp) instanceAttrs=[('nil','boolean'),('type','QName'), ('noNamespaceSchemaLocation','anyURI')] instanceLists=[('schemaLocation','anyURI')] class schemaElt: version=None id=None def __init__(self,factory,elt): self.elt=elt factory.eltStack[0:0]=[self] self.component=Schema(factory,self) self.dds=[] def init(self,elt): sch=self.component sch.factory.eltStack=sch.factory.eltStack[1:] for dd in self.dds: if not dd.name: continue if dd.__class__==complexTypeElt: sch.newComponent(sch.typeTable,'type',dd.component) elif dd.__class__==elementElt: sch.newComponent(sch.elementTable,'element',dd.component) elif dd.__class__==attributeElt: sch.newComponent(sch.attributeTable,'attribute',dd.component) elif isinstance(dd,groupElt): sch.newComponent(sch.groupTable,'group',dd.component) elif dd.__class__==simpleTypeElt: sch.newComponent(sch.typeTable,'type',dd.component) elif dd.__class__==attributeGroupElt: sch.newComponent(sch.attributeGroupTable,'attributeGroup',dd.component) else: shouldnt('dd') class commonElt: def __init__(self,factory,elt): self.schema=factory.schema self.elt=elt def error(self,msg,warn=0): self.schema.error(msg,self.elt,warn) fixNSN1=re.compile("^[^a-zA-Z_]") fixNSN=re.compile("[^a-zA-Z0-9._]") class Component: prepared=0 name=None targetNamespace=None idCounter=1 annotation=None # TODO: implement def __init__(self,factory,xrpr,ns='ns'): self.factory=factory if factory: self.schema=factory.schema if ns=='ns': self.targetNamespace=self.schema.targetNS self.xrpr=xrpr if xrpr and hasattr(xrpr,'name'): self.name=xrpr.name self.id=self.idCounter Component.idCounter=self.id+1 def error(self,message,warning=0): self.schema.error(message,self.xrpr.elt,warning) class Type(Component): annotations=[] # TODO: implement this def __init__(self,factory,xrpr): Component.__init__(self,factory,xrpr,'ns') def __getattr__(self,name): if name=='basetype': st=None if self.basetypeName: if self.schema.vTypeTable.has_key(self.basetypeName): st=self.schema.vTypeTable[self.basetypeName] if isinstance(st,ComplexType): if isinstance(self,ComplexType): if (self.xrpr.content=="textOnly" and not (st.contentType=="textOnly" or (st.contentType=="mixed" and st.emptiable()))): self.error("textOnly type %s must have textOnly or emptiable mixed basetype %s:%s"%(self.name,st.name,st.contentType)) self.basetype=None return if self.derivationMethod in st.final: self.error("Error, %s declares %s as base, which is final"%(self.name, st.name)) self.basetype=None return else: # we're a SimpleType, this only happens if we're the # {content type} of a text-only ComplexType, so need to go inside # its basetype if st.contentType!="textOnly": self.error("textOnly type %s may not have non-textOnly basetype %s, content type %s"%(self.name,st.name,st.contentType)) self.basetype=None return st=st.model else: if (isinstance(self,ComplexType) and self.xrpr.content and self.xrpr.content!="textOnly"): self.error("type %s with simple basetype %s may not have %s content"%(self.name or '[anonymous]',st.name,self.xrpr.content)) self.basetype=None return else: self.error("Undefined type %s referenced as basetype of %s"%(self.basetypeName, self.name or '[anonymous]')) self.basetype=None return # removed thorough check for circularity: was stale, # not actually forbidden in the spec.???? if st==self: self.error("Basing a type on itself is forbidden") self.basetype=None return else: # note the ab-initio types are initialised with themselves # as their own effective type and basetype, so we don't come here # for them if self.xrpr.content=='textOnly': # not quite right . . . using self.contentType # produces infinite loop st=self.factory.sfors.typeTable['string'] else: st=urType if st: self.basetype=st return self.basetype else: raise AttributeError,name def isSubtype(self,other): if self==other: return 1 if self.basetype==self or not self.basetype: return 0 if (isinstance(self,SimpleType) and self.basetype.variety=='union' and self in self.basetype.memberTypes): return 1 return self.basetype.isSubtype(other) def redefine(self): # we have a component which should be based on itself # note this forces some reference resolution normally left until later # TODO: check complex vs. complex, simple vs. simple! base=self.basetype if (not base): self.error("attempt to redefine in terms of non-existent type: %s"%self.name) return if base.name!=self.name: # note namespace identity already enforced by including self.error("attempt to redefine in terms of type other than self: %s vs. %s"% (self.name,base.name)) else: base.name="original "+base.name self.schema.typeTable[self.name]=self self.qname=QName(None,self.name,self.schema.targetNS) class typeElt(commonElt): name=None basetype=None derivedBy=None id=None def __init__(self,factory,elt): commonElt.__init__(self,factory,elt) ## Note that primitive builtins are _not_ SimpleTypes, see AbInitio. ## SimpleType itself is largely a placeholder: it has a targetNamespace and ## may have a name. In principle it always has a basetype, but in practice ## may only have a basetypeName, and basetype is filled in lazily. ## It also should have a variety, but this is _also_ lazy, as it may depend ## on the basetype. ## The real action is in the subComp, which should be an instance of Atomic, ## List or Union, but may be a Restriction which contains one of these as ## its actual. Opportunities for improved efficiency obviously exist, by ## eliminating one or both indirections once the truth is known. class SimpleType(Type): basetypeName=None attributeDeclarations={} # for use when this is a ct's basetype contentType='textOnly' # ditto elementTable={} # ditto reflectedName='simpleTypeDefinition' reflectionMap=(('name','string',1,'name'), ('targetNamespace','string',1,'targetNamespace'), ('baseTypeDefinition','component',1,'basetype'), ('variety','string',0,'variety'), ('primitiveTypeDefinition','special', 1,'primitiveTypeReflect'), ('itemTypeDefinition','component',2,'itemType'), ('memberTypeDefinitions','components',2,'memberTypes'), ('facets','special',0,'facetsReflect'), ('annotation','component',1,'annotation')) def __init__(self,factory,xrpr,derivedBy,basetypeName,subComponent): Type.__init__(self,factory,xrpr) self.basetypeName=basetypeName self.subComp=subComponent if subComponent: self.subComp.super=self self.derivedBy=derivedBy def __str__(self): if self.basetype and self.basetype!=self: if isinstance(self.basetype,QName): bt=" based on %s"%self.basetype else: bt=" based on %s"%self.basetype.name else: bt="" v='???' return str("{Simple %s type {%s}%s%s}"%(v,self.targetNamespace, self.name,bt)) def __getattr__(self,name): if name not in ('basetype','variety','rootName','primitiveType', 'memberTypes','itemType','restrict'): raise AttributeError,name elif name=='basetype': if Type.__getattr__(self,name)==urType and self.variety=='atomic': # not allowed for simple types! self.error("Must have basetype for atomic simpleType %s"%(self.name or '[anonymous]')) self.basetype=None return self.basetype elif name=='variety': # lazy because it involves the real basetype if self.derivedBy=='restriction': self.variety=self.subComp.variety or 'atomic' # in case of error else: self.variety=self.derivedBy or 'atomic' # in case of error return self.variety else: return getattr(self.subComp,name) def prepare(self): if self.prepared: return 1 self.prepared=1 p1=self.basetype.prepare() return p1 def emptiable(self): # should do some real work . . . return self==urType class simpleTypeElt(typeElt): content='textOnly' restriction=None list=None union=None def init(self,elt): basetypeName=None if self.restriction: derivedBy='restriction' if hasattr(self.restriction,'base'): basetypeName=QName(self.restriction.base,elt,self.schema.factory) if (self.restriction.facets and basetypeName.local=='anySimpleType' and basetypeName.uri==XMLSchemaNS and len(self.restriction.facets)>1 and not(isinstance(self.restriction.facets[0],Whitespace))): self.error("anySimpleType may not be directly restricted with facets") basetypeName=QName('string',elt,self.schema.factory) # hack to keep going elif self.list: derivedBy='list' elif self.union: derivedBy='union' else: # no elt for fakes for builtins if elt: self.error("simpleType must have one of restriction, list or union as a child") self.component=SimpleType(self.schema.factory,self,'unknown', None,None) return self.component=SimpleType(self.schema.factory,self,derivedBy,basetypeName, (self.restriction or self.list or self.union).component) class rulElt(commonElt): facets=[] def init(self,elt): self.component=self.comp(self.schema.factory,self) class Restriction(Component): def __getattr__(self,name): if name not in ('variety','rootName','primitiveType','memberTypes', 'itemType','actual','validateText'): raise AttributeError,name if not self.__dict__.has_key('actual'): if self.super.basetype: self.actual=self.super.basetype.restrict(self) else: # error already signalled . . . self.actual=None return None if name=='actual': return self.actual elif self.actual: return getattr(self.actual,name) else: return None def restrict(self,restr): return self.actual.restrict(restr) class Atomic(Component): variety='atomic' def __init__(self,factory,restr): Component.__init__(self,factory,restr.xrpr) self.super=restr.super def __getattr__(self,name): if name=='rootName': if self.super.basetype: if self.super.basetype!=urSimpleType: self.rootName=self.super.basetype.rootName else: # I must be a root self.rootName=self.name or 'anySimpleType' else: # missing basetype, cheat to forestall worse errors self.rootName='string' return self.rootName elif name=='primitiveType': # the basetype of ab initio types is a class, not an instance # the only allowed form of type derivation is restriction self.primitiveType=None # TODO: should we handle 'list' here? if self.super.basetype: rn=self.rootName else: rn='string' # save catastrophe ptd=self.factory.sfors.typeTable[rn] if ptd==urSimpleType: self.primitiveType=urSimpleType else: self.primitiveType=ptd.basetype(self.factory,self.super.basetype) if self.super.basetype: # no basetype means an earlier error self.primitiveType.mergeFacets(self.super,self.xrpr.facets) # the above is slightly different to the REC: we merge the facets in return self.primitiveType else: raise AttributeError,name def restrict(self,restr): return Atomic(self.factory,restr) class List(Component): variety='list' itemtypeName=None def __init__(self,factory,xrpr): Component.__init__(self,factory,xrpr) if xrpr.itemType: self.itemtypeName=QName(xrpr.itemType,xrpr.elt,factory) if xrpr.simpleType: self.error("list with 'type' attribute must not have nested type declaration") elif xrpr.simpleType: self.itemType=xrpr.simpleType.component else: # no elt means builtin if xrpr.elt: self.error("list must have 'type' attribute or SimpleType child") def __getattr__(self,name): if name=='itemType': self.itemType=None if self.itemtypeName: if self.schema.vTypeTable.has_key(self.itemtypeName): self.itemType=self.schema.vTypeTable[self.itemtypeName] else: self.error("Undefined type %s referenced as type definition of %s"%(self.itemtypeName, self.super.name)) else: shouldnt('nlt') return self.itemType else: raise AttributeError,name def restrict(self,restr): if hasattr(restr.xrpr,'list'): self.error("restriction of a list with a list not checked yet",1) return restr.xrpr.list.component else: self.error("restricting a list with facets not implemented yet",1) return self class Union(Component): variety='union' membertypeNames=[] someMembers=None def __init__(self,factory,xrpr): Component.__init__(self,factory,xrpr) if xrpr.memberTypes: self.membertypeNames=map(lambda n,e=xrpr.elt,f=factory:QName(n,e,f), string.split(xrpr.memberTypes)) if xrpr.subTypes: self.someMembers=map(lambda sub:sub.component, xrpr.subTypes) elif not xrpr.memberTypes: # no elt means builtin if xrpr.elt: self.error("union must have 'memberTypes' attribute or some SimpleType children") def __getattr__(self,name): if name=='memberTypes': self.memberTypes=self.someMembers or [] for mtn in self.membertypeNames: if self.schema.vTypeTable.has_key(mtn): self.memberTypes.append(self.schema.vTypeTable[mtn]) else: self.error("Undefined type %s referenced as type definition of %s"%(mtn, self.super.name)) return self.memberTypes else: raise AttributeError,name def restrict(self,restr): if hasattr(restr.xrpr,'union'): self.error("restriction of a union with a union not checked yet",1) return restr.xrpr.union.component else: self.error("restricting a union with facets not implemented yet",1) return self class restrictionElt(rulElt): # TODO: check base vs content??? group=None all=None choice=None sequence=None attrs=[] comp=Restriction def init(self,elt): if elt and 'complexContent'==(elt.parent.llabel or elt.parent.label): # don't init yet, complexContent itself will handle this pass else: tab={} if self.facets: for facet in self.facets: facet.register(tab) self.facets=tab rulElt.init(self,elt) class listElt(rulElt): # TODO: check base vs content comp=List simpleType=None itemType=None class unionElt(rulElt): # TODO: check base vs content comp=Union subTypes=[] memberTypes=None class ComplexType(Type): reflectedName='complexTypeDefinition' reflectionMap=(('name','string',1,'name'), ('targetNamespace','string',1,'targetNamespace'), ('baseTypeDefinition','component',1,'basetype'), ('derivationMethod','string',1,'derivationMethod'), ('final','list',0,'final'), ('abstract','boolean',0,'abstract'), ('attributeUse','special',0,'attributesReflect'), ('attributeWildcard','special',1,'attributeWildcardReflect'), ('contentType','special',0,'contentTypeReflect'), ('prohibitedSubstitutions','list', 0,'prohibitedSubstitutions'), ('annotations','components',0,'annotations')) def __init__(self,factory,xrpr): Type.__init__(self,factory,xrpr) self.basetypeName=xrpr.basetype self.facets=xrpr.facets self.abstract=xrpr.abstract self.final=string.split(xrpr.final) self.prohibitedSubstitutions=string.split(xrpr.block) def __str__(self): if self.basetype: if isinstance(self.basetype,QName): bt=" based on %s"%self.basetype else: bt=" based on {%s}%s"%(self.basetype.targetNamespace, self.basetype.name) else: bt="" c = " contentType "+self.contentType; if (self.contentType in ('elementOnly','mixed')) and self.model: model="%s: %s"%(self.model.term.compositor,string.join(map(str,self.model.term.particles),'')) elif self.contentType=='textOnly' and self.basetype: model="{%s}%s"%(self.basetype.targetNamespace, self.basetype.name) else: model="" if self.attributeDeclarations: attrs=string.join(map(str,self.attributeDeclarations.values()),'') else: attrs="" return str("{Complex type %s%s%s:%s%s}"%(self.name,bt,c,model,attrs)) def __getattr__(self,name): if name=='basetype': return Type.__getattr__(self,name) # the next _two_ properties taken together make the REC's {content type} elif name=='derivationMethod': self.derivationMethod=self.xrpr.derivedBy return self.derivationMethod elif name=='contentType': if self.xrpr.content=='elementOnly' and not self.model: self.contentType='empty' else: self.contentType=self.xrpr.content return self.contentType elif name=='model': if self.xrpr.content=='textOnly': if self.xrpr.simpleContent.restriction: if hasattr(self.xrpr.simpleContent.restriction,'simpleType'): # nested simpleType, use it as base # TODO: will ignore higher-level facets??? self.model=self.xrpr.simpleContent.restriction.simpleType.component else: # no nested simpleType, shared basetypeName better do it fake=simpleTypeElt(self.factory,self.xrpr.elt) fake.name=None fake.restriction=self.xrpr.simpleContent.restriction fake.init(self.xrpr.elt) self.model=fake.component else: fake=simpleTypeElt(self.factory,self.xrpr.elt) fake.name=None fake.restriction=restrictionElt(self.factory,self.xrpr.elt) fake.restriction.init(None) fake.restriction.facets={} fake.restriction.base=self.xrpr.simpleContent.extension.base fake.restriction.attrs=self.xrpr.simpleContent.extension.attrs fake.init(self.xrpr.elt) self.model=fake.component else: self.model=self.realModel(self.xrpr.model) return self.model elif name=='attributeDeclarations': # a dictionary of attributeUse instances keyed by qname self.attributeDeclarations=self.mergeAttrs(self.basetype,self.derivationMethod) return self.attributeDeclarations elif name=='elementTable': self.elementTable={} if self.contentType in ('elementOnly','mixed') and self.model: self.model.note(self.elementTable) return self.elementTable elif name=='fsm': if self.contentType not in ("elementOnly","mixed"): self.fsm = None return self.fsm ndfsm = FSM() end = FSMNode(ndfsm) end.isEndNode = 1 start = self.model.translate(end, ()) ndfsm.startNode = start self.ndfsm = ndfsm self.fsm = ndfsm.determinise() relabelFSM(self.fsm) nd = checkFSM(self.fsm) if nd: self.error("non-deterministic content model for type %s: %s" % (self.name, nd)) self.fsm = self.fsm.determinise() return self.fsm else: raise AttributeError,name def prepare(self): if self.prepared: return 1 self.prepared=1 p1=self.basetype.prepare() p2=self.model p3=self.attributeDeclarations if p3: for au in p3.values(): ad=au.attributeDeclaration if isinstance(ad,Attribute): p3=ad.prepare() and p3 p4=self.elementTable if p4: for ed in p4.values(): p4=ed.prepare() and p4 p5=self.fsm return (p1 and p2 and p3 and p4 and p5) def mergeContent(self,basetype,derivedBy): if self.xrpr.content and basetype: own=self.xrpr.content other=basetype.contentType if own==other: return own if own=="empty": # sigh, we need to compute emptiable . . . if (derivedBy=='extension' or not basetype.emptiable()): self.error("attempt to extend to empty or restrict non-emptiable model of {%s}%s to empty for %s"%(basetype.targetNamespace,basetype.name,self.name)) return other # bogus, but how else to keep going? else: return own elif ((derivedBy=='restriction' and (other in ({"textOnly":("mixed",), "elementOnly":("mixed",), "mixed":()}[own]))) or (other in ({"elementOnly":("empty",), "mixed":(), "textOnly":()}[own]))): return own else: self.error("incompatible content for %s on type %s (%s) and source %s (%s)"%(derivedBy, self.name, own, basetype.name, other)) return other # bogus, but how else to keep going? else: if basetype and isinstance(basetype,ComplexType): return basetype.contentType else: return 'textOnly' def realModel(self,raw): # XXX doesn't deal with all groups yet # deals with derivation simple cases only if self.basetype: other=self.basetype.model else: # error already other=None if raw: mine=topGroup(self.factory,raw) else: mine=Particle(self.factory,None, Sequence(self.factory,None,self.xrpr)) mine.occurs=(1,1) mine.particles=[] if self.derivationMethod=='restriction': if (not other) or self.basetype==urType or other==urType.model: return mine else: res=mine.merge(other) if res: return res else: if self.contentType=='elementOnly': self.contentType='empty' return None elif ((not self.basetype) or self.basetype.contentType=='empty'): return mine else: if (self.basetype.contentType=='textOnly' and (self.xrpr.content!='textOnly' or raw!=other)): self.error("extension of simple content %s may not have content model"%other) return mine # needs more checks for allowed extension. . . if other and isinstance(other,Particle) and other.term and other.term.compositor=='sequence' and other.occurs==(1,1): newp=copy.copy(other) newp.term=copy.copy(other.term) newp.term.particles=other.term.particles+[mine] return newp else: np=Particle(self.factory,None, Sequence(self.factory,None,mine.xrpr),mine.xrpr) np.occurs=(1,1) np.term.particles=[other,mine] return np def mergeAttrs(self,basetype,derivedBy): mine=self.expandAttrGroups() if basetype: others=basetype.attributeDeclarations else: others={} for (adn,ad) in others.items(): if mine.has_key(adn): if derivedBy=='extension': self.error("attempt to extend with an attribute already declared {%s}"%adn) else: # restriction me=mine[adn] if me.maxOccurs==0: if ad.minOccurs==1: self.error("attempt to eliminate required attribute %s"%me.qname) else: del mine[adn] else: if ad.minOccurs==1: if me.minOccurs==0: self.error("attempt to make required attribute %s optional"%me.qname) me.minOccurs=1 if ad.valueConstraint: if (ad.valueConstraint[0]=='fixed' and ((not me.valueConstraint) or me.valueConstraint[0]!='fixed' or me.valueConstraint[1]!=ad.valueConstraint[1])): self.error("attempt to change or abandon fixed value for attribute %s"%me.qname) me.attributeDeclaration.checkSubtype(ad.attributeDeclaration) else: mine[adn]=ad return mine def expandAttrGroups(self): tab={} for ad in self.xrpr.attrs: ad.component.expand(tab) return tab def note(self,table): self.term.note(table) def emptiable(self): # TODO: should do some real work . . . return (self==urType or isinstance(self.model,SimpleType) or # !!might be wrong!! self.model.occurs[0]==0 or (len(filter(lambda p:p.occurs[0]==0,self.model.term.particles))== len(self.model.term.particles))) class complexTypeElt(typeElt): sub=None derivedBy=None final="" block="" abstract="false" complexContent=None simpleContent=None mixed=None def __init__(self,factory,elt): typeElt.__init__(self,factory,elt) factory.eltStack[0:0]=[self] def __getattr__(self,name): if self.sub: if name in ('facets','sequence','choice','all','group','attrs','model'): return getattr(self.sub,name) else: if name in ('facets','attrs','model'): return [] raise AttributeError,name def init(self,elt): basetypeName=None if self.complexContent: self.sub=self.complexContent if self.complexContent.mixed=='true': self.content='mixed' elif self.complexContent.mixed=='unspecified' and self.mixed=='true': self.content='mixed' else: self.content='elementOnly' if self.complexContent.restriction: if self.content=='elementOnly' and not self.complexContent.model: self.content='empty' self.derivedBy='restriction' if hasattr(self.complexContent.restriction,'base'): # must be a complex type basetypeName=QName(self.complexContent.restriction.base,elt, self.schema.factory) elif self.complexContent.extension: self.derivedBy='extension' if hasattr(self.complexContent.extension,'base'): # must be a simple type basetypeName=QName(self.complexContent.extension.base,elt, self.schema.factory) elif self.simpleContent: self.sub=self.simpleContent self.content='textOnly' if self.simpleContent.restriction: self.derivedBy='restriction' if hasattr(self.simpleContent.restriction,'base'): basetypeName=QName(self.simpleContent.restriction.base,elt, self.schema.factory) elif self.simpleContent.extension: self.derivedBy='extension' if hasattr(self.simpleContent.extension,'base'): basetypeName=QName(self.simpleContent.extension.base,elt, self.schema.factory) else: shouldnt('stcm') else: # handle shorthand case with no complex/simpleContent self.derivedBy='restriction' if self.mixed=='true': self.content='mixed' else: self.content='elementOnly' if self.__dict__.has_key('sequence'): self.model=self.sequence elif self.__dict__.has_key('choice'): self.model=self.choice elif self.__dict__.has_key('group'): self.model=self.group elif self.__dict__.has_key('all'): self.model=self.all elif self.__dict__.has_key('attrs'): self.model=None # TODO: check this actually works! if self.content=='elementOnly': self.content='empty' # attrs case works as is if not self.__dict__.has_key('model'): # renaming the urType # TODO: check this actually works! self.model=None self.attrs=[] if self.content=='elementOnly': self.content='empty' self.schema.factory.eltStack=self.schema.factory.eltStack[1:] if not hasattr(self,'final'): self.final=self.schema.finalDefault self.component=ComplexType(self.schema.factory,self) if basetypeName: self.component.basetypeName=basetypeName else: self.component.basetype=urType class Ur(ComplexType,SimpleType): def __init__(self,factory): Component.__init__(self,factory,None) self.attrTable={} self.elemTable={} def isSubtype(self,other): return 1 class QName: # either QName(qname, item, factory) or QName(prefix, local, uri) or # QName(qname, nsdict, factory) (doesn't happen????) # TODO: check the NSURI has been imported and give error if not def __init__(self, arg1, arg2, arg3): # print "QName(%s,%s,%s)" % (arg1, arg2, arg3) if isinstance(arg2,layer.Mapper): (self.prefix,self.local) = splitQName(arg1) if self.prefix or not arg3.processingInclude==2: self.uri=arg2.lookupPrefix(self.prefix) else: # chameleon include fix self.uri=arg3.targetNS if self.prefix and not self.uri: self.uri="error: prefixWasNotDeclared" elif type(arg2) == types.DictType: (self.prefix,self.local) = splitQName(arg1) if arg2.has_key(self.prefix): self.uri=arg2[self.prefix] elif self.prefix: self.uri="error: prefixWasNotDeclared" else: self.uri=None else: self.prefix = arg1 self.local = arg2 self.uri = arg3 self.pair = (self.uri, self.local) def __str__(self): return str(self.string()) # str is in case we're unicode def __cmp__(self, other): # print "comparing %s and %s" % (self,other) if not isinstance(other, QName): # ??? XXX return -1 return cmp(self.pair, other.pair) def __hash__(self): return hash(self.pair) def string(self): # may be Unicode return "%s{%s}:%s" % (self.prefix or "",self.uri,self.local) def splitQName(qname): n=string.find(qname,':') if n>-1: prefix=qname[0:n] local=qname[n+1:] else: prefix=None local=qname return (prefix, local) class contentElt(commonElt): sub=None restriction=None extension=None def init(self,elt): if self.restriction: self.sub=self.restriction elif self.extension: self.sub=self.extension else: shouldnt('cecm') def __getattr__(self,name): if (self.sub and name in ('facets','sequence','choice','all','group','attrs')): return getattr(self.sub,name) else: raise AttributeError,name class complexContentElt(contentElt): mixed='unspecified' def init(self,elt): contentElt.init(self,elt) self.model=self.sequence or self.choice or self.group or self.all class simpleContentElt(contentElt): def init(self,elt): contentElt.init(self,elt) class extensionElt(commonElt): group=None all=None choice=None sequence=None facets=[] attrs=[] class Group(Component): compositor=None particles=[] reflectedName='modelGroup' reflectionMap=(('compositor','string',0,'compositor'), ('particles','components',0,'particles'), ('annotation','component',1,'annotation')) def __init__(self,factory,xrpr,surrogate=None): Component.__init__(self,factory,xrpr) if xrpr and self.compositor: self.particles=map(lambda p:p.component, filter(lambda p:p.component,xrpr.model)) elif surrogate: # really for errors only self.xrpr=surrogate def __str__(self): name = self.name or "[anon]" model = map(str, self.particles) return "{Group %s comp %s:%s}" % (name, self.compositor, string.join(model, '')) def merge(self,other): # should check cardinality # should check for deletions op=other.particles res=[] for mp in self.particles: sub=mp.merge(op[0]) if sub: res.append(sub) op=op[1:] self.particles=res return self def note(self,table): map(lambda p,t=table:p.note(t), self.particles) def prepare(self): if self.prepared: return 1 p1=self.note({}) self.prepared=1 return p1 class Particle(Component): termName=None reflectedName='particle' reflectionMap=(('minOccurs','string',0,'minOccurs'), ('maxOccurs','string',0,'maxOccurs'), ('term','component',1,'term')) def __init__(self,factory,xrpr,term,surrogate=None): Component.__init__(self,factory,xrpr,None) if xrpr: self.occurs=computeMinMax(xrpr.minOccurs or "1",xrpr.maxOccurs,self) elif surrogate: # for errors only self.xrpr=surrogate if term: self.term=term def __getattr__(self,name): if name=='term': self.term=None if self.termName: if self.termType=='element': if self.schema.vElementTable.has_key(self.termName): self.term=self.schema.vElementTable[self.termName] else: self.error("Undefined element %s referenced from content model"%self.termName) elif self.termType=='group': if self.schema.vGroupTable.has_key(self.termName): self.term=self.schema.vGroupTable[self.termName] else: self.error("Undefined group %s referenced from content model"%self.termName) else: shouldnt('tnt') else: shouldnt('tn') return self.term elif name=='minOccurs': return str(self.occurs[0]) elif name=='maxOccurs': return str(self.occurs[1] or 'unbounded') else: raise AttributeError,name def merge(self,other): if self.occurs==(0,0): return None if (hasattr(self,'termName') and hasattr(other,'termName') and self.termName==other.termName): return self if self.term.__class__==other.term.__class__: if (self.occurs[0]>=other.occurs[0] and ((not other.occurs[1]) or (self.occurs[1] and self.occurs[1]<=other.occurs[1]))): self.term=self.term.merge(other.term) else: self.schema.error('restriction range %s not a sub-range of base %s'% (self.occurs,other.occurs),self.xrpr.elt) return self if other.term.__class__==Choice and self.term.__class__==Element: # special case this because it occurs in SforS for om in other.term.particles: if (om.term.__class__==Element and om.term.name==self.term.name and om.term.targetNamespace==self.term.targetNamespace): return self.merge(om) self.xrpr.error('non-like-for-like restriction not checked yet: %s vs. %s'%(self.term.__class__,other.term.__class__),1) return self def note(self,table): # may be pointless if we ref an undefined element if self.term: self.term.note(table) def translate(self, next, place): fsm = next.fsm n = next if not self.term: if self.termType=='element' and self.termName: # we can check the content model, recursive type check may fail qq=self.termName else: qq=UndefQName if self.occurs[1] and self.occurs[1]>101: self.schema.error('performance restriction means max>101 means unbounded', self.xrpr.elt,1) self.occurs=(self.occurs[0],None) if not self.occurs[1]: t = FSMNode(fsm) if self.term: n = self.term.translate(t, place) else: n = FSMNode(n.fsm) FSMEdge((qq,place),n,t) FSMEdge(None, t, n) FSMEdge(None, n, next) else: for i in range(self.occurs[0], self.occurs[1]): if self.term: n = self.term.translate(n, place) else: m=n n = FSMNode(n.fsm) FSMEdge((qq,place),n,m) FSMEdge(None, n, next) if self.occurs[0]>101: self.schema.error('performance restrictions means min>101 means 101', self.xrpr.elt,1) self.occurs=(101,self.occurs[1]) for i in range(0, self.occurs[0]): if self.term: n = self.term.translate(n, place) else: m=n n = FSMNode(n.fsm) FSMEdge((qq,place),n,m) return n def exponent(self): if self.occurs[0]==0: if self.occurs[1]==1: return '?' else: return '*' if (self.occurs[0]==1 and (self.occurs[1]==1)): return '' else: return '+' UndefQName=QName(None,'#undef#',None) class Sequence(Group): compositor='sequence' def translate(self, next, place): n = next rmodel = copy.copy(self.particles) rmodel.reverse() i = 0 for particle in rmodel: n = particle.translate(n, (place,i)) i = i+1 return n class Choice(Group): compositor='choice' def translate(self, next, place): n = FSMNode(next.fsm) i = 0 for particle in self.particles: m = particle.translate(next, (place,i)) i = i+1 FSMEdge(None, n, m) return n # TODO: why is anybody looking at the details of this, c.f. prisc.{xml,xsd} urType=Ur(None) urType.basetype=urType urType.effectiveType=urType urType.final=[] urType.prohibitedSubstitutions=[] urType.contentType='mixed' urType.model=Particle(None,None,Sequence(None,None)) urType.model.occurs=(1,1) urType.model.term.particles=[] urType.name='anyType' urType.targetNamespace=XMLSchemaNS urType.attributeDeclarations={} urType.derivationMethod='restriction' urType.abstract='false' urType.extendable=0 # stale!! class AbInitio: name=None attributeDeclarations={} # for use when this is a ct's basetype contentType='textOnly' # ditto elementTable={} # ditto basetype=None final="" content=None abstract="false" variety='atomic' targetNamespace=XMLSchemaNS allowedFacets=[] def __init__(self,factory,basetype): self.factory=factory self.elements=[] self.facets={} if basetype and basetype.__class__==SimpleType: basetype=basetype.primitiveType for fn in self.allowedFacets: if basetype and basetype.facets.has_key(fn): self.facets[fn]=basetype.facets[fn] else: self.facets[fn]=None def prepare(self): return 1 def restrict(self,restr): return Atomic(self.factory,restr) def facetValue(self,name): if self.facets.has_key(name): f=self.facets[name] return f and f.value def mergeFacets(self,surfaceType,newTable): # Called from surfaceType.primitiveType # We are always correct, because we started from the basetype # facets are all instances of class facet or None tname=surfaceType.basetype.name allowed=self.allowedFacets for facetName in newTable.keys(): if facetName in allowed: newFacet=newTable[facetName] old=self.facets[facetName] if old and old.fixed and newFacet.value!=old.value: surfaceType.error("facet %s is fixed in basetype %s, cannot be changed"%(facetName,tname)) if not old or getattr(self,checkFacetTable[facetName])(facetName, old.value, newFacet.value, newTable, surfaceType): self.facets[facetName]=newFacet else: surfaceType.error("facet %s not allowed on type %s"%(facetName,tname)) def checkMax(self,facetName,old,newVal,newTable,td): return 1 def checkMin(self,facetName,old,newVal,newTable,td): if facetName=='minInclusive': b='[' o='(' otherName='minExclusive' else: b='(' o='[' otherName='minInclusive' if newTable.has_key(otherName): td.error("can't use minInclusive and minExclusive in same simple type") return return self.checkMinVals(facetName,newVal,otherName,old,b,o,td) def checkEnum(self,facetName,old,newVal,newTable,td): return 1 def checkPS(self,facetName,old,newVal,newTable,td): return 1 def vacuousCheck(self,facetName,old,newVal,newTable,td): return 1 def isSubtype(self,other): if self==other: return 1 if isinstance(self.basetype,AbInitio): return self.basetype.isSubtype(other) elif other==urType: return 1 else: return 0 def checkMinVals(self,facetName,newVal,otherName,old,b,o,td): # should be overriden return urSimpleType=AbInitio(None,None) urSimpleType.basetype=urType urSimpleType.rootName=urSimpleType.name='anySimpleType' urSimpleType.effectiveType=urSimpleType AbInitio.basetype=urSimpleType class BooleanST(AbInitio): name='boolean' allowedFacets=['pattern','whiteSpace'] class StringST(AbInitio): name='string' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'length', 'maxLength', 'minLength', 'pattern','whiteSpace'] class NumericST(AbInitio): def checkMinVals(self,facetName,newVal,otherName,old,b,o,td): # implement lots of fiddley constraints on min facets new=convertToNum(newVal,facetName,td) # some or all of this could be shared with other types . . . if new==None: return ok=1 if (old!=None and new=max: ok=0 o=')' else: max=self.facetValue('maxInclusive') if (max!=None and ((facetName=='minExclusive' and new>=max) or # minInclusive new>max)): ok=0 o=']' if not ok: td.error("attempt to raise range lower bound above upper bound: %s%d,%d%s"%(b,new,max,o)) return return 1 class FloatST(NumericST): name='float' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class DoubleST(NumericST): name='double' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class DecimalST(NumericST): name='decimal' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'pattern', 'maxInclusive', 'enumeration', 'totalDigits', 'fractionDigits', 'whiteSpace'] class TimeDurationST(AbInitio): name='duration' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class DateTimeST(AbInitio): name='dateTime' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class TimeST(AbInitio): name='time' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class DateST(AbInitio): name='date' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class gYearMonthST(AbInitio): name='gYearMonth' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class gYearST(AbInitio): name='gYear' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class gMonthDayST(AbInitio): name='gMonthDay' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class gDayST(AbInitio): name='gDay' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class gMonthST(AbInitio): name='gMonth' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'pattern','whiteSpace'] class HexBinaryST(AbInitio): name='hexBinary' allowedFacets=['length', 'minLength', 'maxLength', 'pattern', 'enumeration','whiteSpace'] class Base64BinaryST(AbInitio): name='base64Binary' allowedFacets=['length', 'minLength', 'maxLength', 'pattern', 'enumeration','whiteSpace'] class URIReferenceST(AbInitio): name='anyURI' allowedFacets=['length', 'minLength', 'maxLength', 'pattern', 'enumeration','whiteSpace'] class NOTATIONST(AbInitio): name='NOTATION' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'length', 'maxLength', 'minLength', 'pattern','whiteSpace'] class QNameST(AbInitio): name='QName' allowedFacets=['minExclusive', 'maxExclusive', 'minInclusive', 'maxInclusive', 'enumeration', 'length', 'maxLength', 'minLength', 'pattern','whiteSpace'] abInitioTypes=(('boolean',BooleanST), ('string',StringST), ('float',FloatST), ('double',DoubleST), ('decimal',DecimalST), ('duration',TimeDurationST), ('dateTime',DateTimeST), ('time',TimeST), ('date',DateST), ('gYearMonth',gYearMonthST), ('gYear',gYearST), ('gMonthDay',gMonthDayST), ('gDay',gDayST), ('gMonth',gMonthST), ('base64Binary',Base64BinaryST), ('hexBinary',HexBinaryST), ('anyURI',URIReferenceST), ('NOTATION',NOTATIONST),('QName',QNameST)) class Facet(Component,commonElt): # note this is schizo -- both elt and component annotation=None fixed=0 def __init__(self,factory,elt): commonElt.__init__(self,factory,elt) def init(self,elt): self.xrpr=self self.stringValue=self.value self.value=self.val() def register(self,table): if table.has_key(self.name): self.error("Not allowed multiple values for %s"%self.name) else: table[self.name]=self def val(self): return self.value def convertToNum(val,facetName,context): if type(val) in (types.IntType,types.LongType,types.FloatType): return val try: if ('.' in val) or ('E' in val): return string.atof(val) else: return string.atol(val) except: context.error("facet %s value not a valid numeric literal: %s"% (facetName,val)) return class NumFacet(Facet): def val(self): return convertToNum(self.value,self.name,self) class MaxInclusive(NumFacet): name='maxInclusive' class MinInclusive(NumFacet): name='minInclusive' class MinExclusive(NumFacet): name='minExclusive' class MaxExclusive(NumFacet): name='maxExclusive' class FractionDigits(NumFacet): name='fractionDigits' class TotalDigits(NumFacet): name='totalDigits' class Length(NumFacet): name='length' class MaxLength(NumFacet): name='maxLength' class MinLength(NumFacet): name='minLength' class Whitespace(Facet): name='whiteSpace' class ListFacet(Facet): def register(self,table): if table.has_key(self.name): table[self.name].value.append(self.value) else: table[self.name]=self self.value=[self.value] class Pattern(ListFacet): name='pattern' class Enumeration(ListFacet): name='enumeration' builtinTypeNames=[ ('normalizedString','string',((Whitespace,"replace"),)), ('token','normalizedString',((Whitespace,"collapse"),)), ('language','string', ((Pattern,["([a-zA-Z]{2}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]+)(-[a-zA-Z]+)"]),)), ('NMTOKEN','string',((Pattern,["\c+"]),)), ('Name','string',((Pattern,["\i\c*"]),)), ('NCName','Name',((Pattern,["[\i-[:]][\c-[:]]"]),)), ('ID','NCName',()), ('IDREF','NCName',()), ('ENTITY','NCName',()), ('integer','decimal',((FractionDigits,0),)), ('nonPositiveInteger','integer',((MaxInclusive,0),)), ('negativeInteger','nonPositiveInteger', ((MaxInclusive,-1),)), ('long','integer',((MinInclusive,-9223372036854775808L), (MaxInclusive,9223372036854775807L))), ('int','long',((MinInclusive,-2147483648L),(MaxInclusive,2147483647))), ('short','int',((MinInclusive,-32768),(MaxInclusive,32767))), ('byte','short',((MinInclusive,-128),(MaxInclusive,127))), ('nonNegativeInteger','integer',((MinInclusive,0),)), ('unsignedLong','nonNegativeInteger',((MaxInclusive,18446744073709551615L),)), ('unsignedInt','unsignedLong',((MaxInclusive,4294967295L),)), ('unsignedShort','unsignedInt',((MaxInclusive,65535),)), ('unsignedByte','unsignedShort',((MaxInclusive,255),)), ('positiveInteger','nonNegativeInteger',((MinInclusive,1),))] builtinLists=[('NMTOKENS','NMTOKEN'), ('ENTITIES','ENTITY'), ('NOTATIONS','NOTATION'), ('IDREFS','IDREF')] class Wildcard(Component): reflectedName='wildcard' reflectionMap=(('namespaceConstraint','special', 0,'wildcardNamespaceReflect'), ('processContents','string',0,'processContents'), ('annotation','component',1,'annotation')) negated=0 def __init__(self,factory,xrpr,extra=0): Component.__init__(self,factory,xrpr) if xrpr: self.processContents=xrpr.processContents def __str__(self): return "{Wildcard: %s}"%self.allowed def allows(self,namespace): if self.negated: return namespace not in self.namespaces else: return namespace in self.namespaces def expand(self,tab,use): # Called when this wildcard is in a complexType or attributeGroup # have to copy when expanded, as could happen several times if # we're in a group mine=self if tab.has_key("#any"): mine=self.intersect(tab["#any"]) newUse=AttributeUse(use.factory,use.xrpr,mine) newUse.minOccurs=use.minOccurs newUse.maxOccurs=use.maxOccurs tab["#any"] = newUse def intersect(self,other): if self.negated==other.negated: if (self.namespaces==other.namespaces and self.processContents==other.processContents): # no copy in this case? return self nw=Wildcard(self.factory,self.xrpr) nw.negated=self.negated nw.namespaces=[] if nw.negated: # neg/neg for n in self.namespaces+other.namespaces: if n not in nw.namespaces: nw.namespaces.append(n) else: # pos/pos for n in self.namespaces: if n in other.namespaces: nw.namespaces.append(n) else: nw=Wildcard(self.factory,self.xrpr) nw.negated=0 if self.negated: # neg/pos inN=other.namespaces outN=self.namespaces else: # pos/neg inN=self.namespaces outN=other.namespaces if not outN: nw.namespaces=inN else: nw.namespaces=[] for n in inN: if n not in outN: nw.namespaces.append(n) if nw.negated: nw.allowed='not %s'%nw.namespaces else: nw.allowed='%s'%nw.namespaces if self.processContents==other.processContents: nw.processContents=self.processContents else: if self.processContents=='strict' or other.processContents=='strict': nw.processContents='strict' elif self.processContents=='lax' or other.processContents=='lax': nw.processContents='lax' else: nw.processContents='skip' return nw def checkSubtype(self,other): # TODO: something pass def isEmpty(self): return (not self.negated) and self.namespaces == [] def note(self,table): pass def translate(self, next, place): n = FSMNode(next.fsm) FSMEdge((self,place), n, next) return n class AnyAny(Wildcard): allowed='##any' # for trace info namespaces=[] negated=1 class AnyOther(Wildcard): allowed='##other' # for trace info negated=1 def __init__(self,factory,xrpr,isAW=0): Wildcard.__init__(self,factory,xrpr) self.namespaces=[self.targetNamespace] if isAW and self.namespaces[0]!=None: self.namespaces.append(None) class AnyInList(Wildcard): def __init__(self,factory,xrpr,extra=None): Wildcard.__init__(self,factory,xrpr) self.namespaces=map(self.namespaceCode,string.split(xrpr.namespace)) self.allowed=self.namespaces def namespaceCode(self,arg): if arg=='##local': return None elif arg=='##targetNamespace': return self.targetNamespace else: return arg class anyElt(commonElt): namespace="##any" minOccurs="1" maxOccurs=None processContents="strict" def __init__(self,factory,elt): commonElt.__init__(self,factory,elt) def init(self,elt): if self.maxOccurs=="0": self.component=None else: self.component=Particle(self.schema.factory,self, RefineAny(self,self.namespace)) def RefineAny(xrpr,namespace,extra=0): if namespace=='##any': anyinst=AnyAny elif namespace=='##other': anyinst=AnyOther else: anyinst=AnyInList return anyinst(xrpr.schema.factory,xrpr,extra) def computeMinMax(minStr,maxStr,comp): try: min = string.atoi(minStr) except ValueError: min=1 comp.error("%s not a valid minOccurs value"%minStr) if maxStr == "unbounded": max = None elif maxStr: try: max = string.atoi(maxStr) except ValueError: max=1 comp.error("%s not a valid maxOccurs value"%maxStr) else: max = 1 return (min,max) class Element(Component): typeDefinitionName=None equivClassName=None valueConstraint=None # TODO: implement this! (and in aps) reflectedName='elementDeclaration' reflectionMap=(('name','string',0,'name'), ('targetNamespace','string',1,'targetNamespace'), ('typeDefinition','component',1,'typeDefinition'), ('scope','special',1,'scopeReflect'), ('valueConstraint','special',1,'vcReflect'), ('nillable','boolean',0,'nullable'), ('identityConstraintDefinitions','components', 0,'identityConstraints'), ('substitutionGroupAffiliation','component',1,'equivalenceClassAffiliation'), ('substitutionGroupExclusions','list',0,'final'), ('disallowedSubstitutions','list', 0,'prohibitedSubstitutions'), ('abstract','boolean',0,'abstract'), ('annotation','component',1,'annotation')) def __init__(self,factory,xrpr,scope): if scope=='global' or xrpr.form=='qualified': ns='ns' else: ns=None Component.__init__(self,factory,xrpr,ns) if scope=='global': self.scope=scope else: self.scopeRepr=scope # an xrpr, component not available yet self.abstract=xrpr.abstract or 'false' self.nullable=xrpr.nullable or 'false' if xrpr.substitutionGroup: self.equivClassName = QName(xrpr.substitutionGroup,xrpr.elt, factory) self.final=string.split(xrpr.final) self.prohibitedSubstitutions=string.split(xrpr.block) if xrpr.type: self.typeDefinitionName=QName(xrpr.type,xrpr.elt, factory) if xrpr.simpleType or xrpr.complexType: self.error("declaration with 'type' attribute must not have nested type declaration") elif xrpr.simpleType: self.typeDefinition=xrpr.simpleType.component elif xrpr.complexType: self.typeDefinition=xrpr.complexType.component elif not self.equivClassName: self.typeDefinition=urType self.keys=map(lambda e:e.component,xrpr.keys) self.keyrefs=map(lambda e:e.component,xrpr.keyrefs) self.uniques=map(lambda e:e.component,xrpr.uniques) def __str__(self): if (self.typeDefinition and self.typeDefinition.name and self.typeDefinition.name[0]!='['): return "{Element {%s}%s:%s}"%(self.targetNamespace,self.name, self.typeDefinition.name) else: return "{Element {%s}%s:%s}"%(self.targetNamespace,self.name, str(self.typeDefinition)) def __getattr__(self,name): if name=='equivalenceClassAffiliation': self.equivalenceClassAffiliation=None if self.equivClassName: if self.schema.vElementTable.has_key(self.equivClassName): self.equivalenceClassAffiliation=exemplar=self.schema.vElementTable[self.equivClassName] if (self.typeDefinition and exemplar.typeDefinition and not self.typeDefinition.isSubtype(exemplar.typeDefinition)): self.error("type {%s}%s not subtype of type {%s}%s of exemplar %s"%(self.typeDefinition.targetNamespace,self.typeDefinition.name, exemplar.typeDefinition.targetNamespace,exemplar.typeDefinition.name, exemplar.qname)) else: self.error("Undefined element %s referenced as equivalence class affiliation"%self.equivClassName) return self.equivalenceClassAffiliation elif name=='equivClass': # first access propagates everything if self.scope!='global': shouldnt('not global %s'%self.name) for schema in self.factory.schemas.values(): for ed in schema.elementTable.values(): if not ed.__dict__.has_key('equivClass'): ed.equivClass=[] if (ed.abstract!='true' and ed.equivalenceClassAffiliation): ed.equivalenceClassAffiliation.addECM(ed) return self.equivClass elif name=='typeDefinition': self.typeDefinition=None if self.typeDefinitionName: if self.schema.vTypeTable.has_key(self.typeDefinitionName): self.typeDefinition=self.schema.vTypeTable[self.typeDefinitionName] else: self.error("Undefined type %s referenced as type definition of %s"%(self.typeDefinitionName, self.name)) elif self.equivClassName: self.typeDefinition=self.equivalenceClassAffiliation.typeDefinition else: shouldnt('etd') return self.typeDefinition elif name=='identityConstraints': return self.uniques+self.keys+self.keyrefs elif name=='scope': self.scope=self.scopeRepr.component return self.scope else: raise AttributeError,name def prepare(self): if self.prepared: return 1 self.prepared=1 p5=self.scope if p5=='global': p1=self.equivalenceClassAffiliation p2=self.equivClass else: p1=p2=1 p3=self.typeDefinition.prepare() p4=self.identityConstraints return (p1 and p2 and p3 and p4 and p5) def addECM(self,member): if not self.__dict__.has_key('equivClass'): self.equivClass=[member] else: self.equivClass.append(member) if self.equivalenceClassAffiliation: self.equivalenceClassAffiliation.addECM(member) def note(self,table): qname=QName(None,self.name,self.targetNamespace) if table.has_key(qname): if self.typeDefinition != table[qname].typeDefinition: self.error("illegal redeclaration of %s" % qname) elif not (self.scope=='global' and table[qname].scope=='global'): self.error("redeclaration of %s ok - same type\n" % qname,1) return table[qname] = self if (self.scope=='global' and 'substitution' not in self.prohibitedSubstitutions): # check for equivalence classes # is this necessary -- it's quite expensive for decl in self.equivClass: table[QName(None,decl.name,decl.schema.targetNS)]=decl def merge(self,other): # TODO: check default/fixed -- what else? if self.name!=other.name or self.targetNamespace!=other.targetNamespace: self.error("declaration in a restriction not same name as declaration it corresponds to: {%s}%s vs. {%s}%s"%(self.targetNamespace,self.name,other.targetNamespace,other.name)) if (self.typeDefinition and other.typeDefinition and not self.typeDefinition.isSubtype(other.typeDefinition)): self.error("type {%s}%s not subtype of type {%s}%s of {%s}%s in restriction"%(self.typeDefinition.targetNamespace,self.typeDefinition.name, other.typeDefinition.targetNamespace,other.typeDefinition.name, self.targetNamespace, self.name)) return self def translate(self, next, place): # what do I do if there are no edges, i.e. abstract element with # no descendants? fsm = next.fsm n = FSMNode(fsm) qname=QName(None, self.name, self.targetNamespace) if self.abstract!='true': FSMEdge((qname,place), n, next) if (self.scope=='global' and 'substitution' not in self.prohibitedSubstitutions): for e in self.schema.vElementTable[qname].equivClass: FSMEdge((QName(None,e.name,e.targetNamespace),place),n,next) return n class particleElt: # shared by groupElt and elementElt minOccurs=None maxOccurs=None def init(self): pass class defRefElt(commonElt): # shared by groupElt,attributeElt and elementElt name=None ref=None parent=None def init(self,eltName,nestingElt,badAttrs=('minOccurs','maxOccurs','ref')): if not (self.name or self.ref): self.error("%s with no name or ref"%eltName) # die? parent=self.schema.factory.eltStack[0] if isinstance(parent,complexTypeElt) or isinstance(parent,nestingElt): self.parent=parent if self.ref: # TODO: check ref syntax self.checkRefed() else: # TODO: check name syntax self.checkInternal() else: for an in badAttrs: if getattr(self,an)!=None: self.error("top-level %s may not have %s"%(eltName,an)) setattr(self,an,None) # top-level def # TODO: check name syntax more thoroughly if ':' in self.name: self.error("'name' must be an NCName") # die? self.checkTop() class elementElt(defRefElt,particleElt): type=None complexType=None simpleType=None form=None default=None fixed=None substitutionGroup=None nullable=None parent=None abstract=None final=None block=None def __init__(self,factory,elt): defRefElt.__init__(self,factory,elt) self.keys=[] self.keyrefs=[] self.uniques=[] def init(self,elt): # does some simple checks and calls back on of three following methods defRefElt.init(self,'element',groupElt) particleElt.init(self) def checkRefed(self): # called if nested 'ref' form for ba in ('type','block','default','nullable','fixed','complexType','simpleType','key','keyref','unique'): if hasattr(self,ba) and getattr(self,ba): self.error("element with ref can't have %s"%ba) setattr(self,ba,None) if self.maxOccurs=="0": self.component=None else: self.component=Particle(self.schema.factory,self,None) self.component.termName=QName(self.ref,self.elt, self.schema.factory) self.component.termType='element' def checkInternal(self): # local def if not self.form: self.form=self.schema.elementFormDefault self.final='' self.block='' nElt=Element(self.schema.factory,self,self.parent) if self.maxOccurs=="0": self.component=None else: self.component=Particle(self.schema.factory,self,nElt) def checkTop(self): # top-level def if self.final==None: self.final=self.schema.finalDefault if self.final=='#all': self.final='restriction extension' if self.block==None: self.block=self.schema.blockDefault if self.block=='#all': self.block='restriction extension substitution' self.component=Element(self.schema.factory,self,'global') def merge(self,other): # called in content model restricting myName=self.name or self.ref if other.__class__==Element: otherName=other.name or other.ref if myName==otherName: # should do subsumption check, construct merged type, of course return self else: self.error("can't restrict %s with %s"%(otherName,myName)) attrOccurs={'prohibited':(0,0), 'optional':(0,1), 'default':(0,1), 'fixed':(0,1), 'required':(1,1)} class AttributeUse(Component): nameType=None attributeDeclarationName=None valueConstraint=None minOccurs=1 maxOccurs=1 reflectedName='attributeUse' reflectionMap=(('required','boolean',0,'minOccurs'), ('attribute','namedComponent',1,'attributeDeclaration'), ('valueConstraint','special',1,'vcReflect')) def __init__(self,factory,xrpr,attributeDeclaration,use=None,vct=None, value=None): Component.__init__(self,factory,xrpr,None) if use: (self.minOccurs,self.maxOccurs)=attrOccurs[use] if vct: self.valueConstraint=(vct,value) if attributeDeclaration: self.attributeDeclaration=attributeDeclaration def __getattr__(self,name): if name=='attributeDeclaration': if self.attributeDeclarationName and self.nameType=='attribute': if self.schema.vAttributeTable.has_key(self.attributeDeclarationName): self.attributeDeclaration=self.schema.vAttributeTable[self.attributeDeclarationName] else: self.error("Undeclared attribute %s referenced"%(self.attributeDeclarationName)) self.attributeDeclaration=None return self.attributeDeclaration else: shouldnt('attrUse1') elif name=='attributeGroup': if self.attributeDeclarationName and self.nameType=='attributeGroup': if self.schema.vAttributeGroupTable.has_key(self.attributeDeclarationName): self.attributeGroup=self.schema.vAttributeGroupTable[self.attributeDeclarationName] else: self.error("Undeclared attribute group %s referenced"%(self.attributeDeclarationName)) self.attributeGroup=None return self.attributeGroup else: shouldnt('attrUse2') elif name=='qname': # allow type derivation without chasing refs if self.attributeDeclarationName: self.qname=self.attributeDeclarationName else: self.qname=QName(None,self.attributeDeclaration.name, self.attributeDeclaration.targetNamespace) return self.qname else: raise AttributeError,name def expand(self,table): # ref might be broken, so check before forwarding if self.nameType=='attributeGroup': if self.attributeGroup: self.attributeGroup.expand(table) elif self.attributeDeclaration: # might lose, so check first self.attributeDeclaration.expand(table,self) class Attribute(Component): attrName=None attrDef=None typeDefinitionName=None valueConstraint=None reflectedName='attributeDeclaration' reflectionMap=(('name','string',0,'name'), ('targetNamespace','string',1,'targetNamespace'), ('typeDefinition','component',1,'typeDefinition'), ('scope','special',1,'scopeReflect'), ('valueConstraint','special',1,'vcReflect'), ('annotation','component',1,'annotation')) def __init__(self,factory,xrpr,scope): if scope=='global' or xrpr.form=='qualified': ns='ns' else: ns=None Component.__init__(self,factory,xrpr,ns) if scope=='global': self.scope=scope if xrpr.default!=None: self.valueConstraint=('default',xrpr.default) elif xrpr.fixed!=None: self.valueConstraint=('fixed',xrpr.fixed) else: self.scopeRepr=scope if xrpr.type: self.typeDefinitionName=QName(xrpr.type,xrpr.elt, factory) if xrpr.simpleType: self.error("declaration with 'type' attribute must not have nested type declaration") elif xrpr.simpleType: self.typeDefinition=xrpr.simpleType.component else: self.typeDefinition=urType def __str__(self): if (self.typeDefinition and self.typeDefinition.name and self.typeDefinition.name[0]!='['): return "{Attribute {%s}%s:%s}"%(self.targetNamespace,self.name,self.typeDefinition.name) else: return "{Attribute {%s}%s:%s}"%(self.targetNamespace,self.name,str(self.typeDefinition)) def __getattr__(self,name): if name=='typeDefinition': self.typeDefinition=None if self.typeDefinitionName: if self.schema.vTypeTable.has_key(self.typeDefinitionName): self.typeDefinition=self.schema.vTypeTable[self.typeDefinitionName] if isinstance(self.typeDefinition,ComplexType): self.error("type definition for an attribute ({%s}%s) must not be complex: %s"%(self.targetNamespace,self.name,self.typeDefinitionName)) self.typeDefinition=None else: self.error("Undefined type %s referenced as type definition of {%s}%s"%(self.typeDefinitionName, self.targetNamespace, self.name)) return self.typeDefinition elif name=='scope': self.scope=self.scopeRepr.component return self.scope else: raise AttributeError,name def prepare(self): if self.prepared: return 1 self.prepared=1 p1=self.typeDefinition.prepare() p2=self.scope return p1 and p2 def expand(self,tab,use): qn=use.qname if tab.has_key(qn): self.error("attempt to redeclare attribute %s, ignored" % qn) else: tab[qn]=use def checkSubtype(self,other): mytype=self.typeDefinition if (mytype and other.typeDefinition and not mytype.isSubtype(other.typeDefinition)): self.error("restricting attribute with type {%s}%s not derived from declared base's attribute's type %s{%s}"%(mytype.targetNamespace,mytype.name,other.typeDefinition.targetNamespace,other.typeDefinition.name)) class attributeElt(defRefElt): type=None simpleType=None form=None use=None default=None fixed=None def init(self,elt): defRefElt.init(self,'attribute',attributeGroupElt,('ref',)) def checkRefed(self): if self.type: self.error("attribute with ref %s can't have type %s"%(self.ref,self.type)) self.type=None elif self.simpleType: self.error("attribute with ref %s can't have simpleType"%self.ref) self.simpleType=None if self.default!=None: vct='default' value=self.default elif self.fixed!=None: vct='fixed' value=self.fixed else: vct=value=None self.component=AttributeUse(self.schema.factory,self,None, self.use or 'optional',vct,value) self.component.attributeDeclarationName=QName(self.ref,self.elt, self.schema.factory) self.component.nameType='attribute' def checkInternal(self): # local def if self.default!=None: vct='default' value=self.default elif self.fixed!=None: vct='fixed' value=self.fixed else: vct=value=None if not self.form: self.form=self.schema.attributeFormDefault nAttr=Attribute(self.schema.factory,self,self.parent) self.component=AttributeUse(self.schema.factory,self,nAttr, self.use or 'optional',vct,value) def checkTop(self): # top-level def self.component=Attribute(self.schema.factory,self,'global') class AttributeGroup(Component): # TODO: check wildcard intersection during expansion base=None reflectedName='attributeGroupDefinition' reflectionMap=(('name','string',0,'name'), ('targetNamespace','string',1,'targetNamespace'), ('attributeDeclarations','components',0, 'attributeDeclarations')) def __str__(self): return "{AttrGroup %s}" % self.name def __getattr__(self,name): if name=='attributeDeclarations': tab={} for xa in self.xrpr.attrs: if xa.component.maxOccurs!=0: xa.component.expand(tab) if self.base: # we were redefined wrt self.base, check restriction for ad in self.base.attributeDeclarations: if tab.has_key(ad.qname): me=tab[ad.qname] if ad.minOccurs==1: if me.minOccurs==0: self.error("attempt to make required attribute %s optional"%me.qname) me.minOccurs=1 if ad.valueConstraint: if (ad.valueConstraint[0]=='fixed' and ((not me.valueConstraint) or me.valueConstraint[0]!='fixed' or me.valueConstraint[1]!=ad.valueConstraint[1])): self.error("attempt to change or abandon fixed value for attribute %s"%me.qname) me.attributeDeclaration.checkSubtype(ad.attributeDeclaration) elif ad.minOccurs==1: self.error("attempt to eliminate required attribute %s"%ad.qname) self.attributeDeclarations=tab.values() return self.attributeDeclarations else: raise AttributeError,name def prepare(self): if self.prepared: return 1 self.prepared=1 p1=self.attributeDeclarations if p1: for au in p1: ad=au.attributeDeclaration if isinstance(ad,Attribute): p1=ad.prepare() and p1 return p1 def expand(self,table): for au in self.attributeDeclarations: au.expand(table) def redefine(self): # we have a component which should be based on itself # note this forces some reference resolution normally left until later if not self.schema.attributeGroupTable.has_key(self.name): self.error("attempt to redefine in terms of non-existent attribute group: %s"%self.name) return else: redefed=self.schema.attributeGroupTable[self.name] qn=QName(None,self.name,self.schema.targetNS) selfRefs=self.findSelfRefs(qn) if len(selfRefs)>1: self.error("more than one self-reference not allowed in attribute group redefinition") else: redefed.name="original "+self.name self.schema.attributeGroupTable[redefed.name]=redefed self.schema.attributeGroupTable[self.name]=self self.qname=qn if len(selfRefs)==0: # must be a restriction -- postpone the real work self.base=redefed elif len(selfRefs)==1: # an extension, just use it, duplicates will be detected later selfRefs[0].component.attributeDeclarationName=QName(None,redefed.name, self.schema.targetNS) def findSelfRefs(self,qn): return filter(lambda d,qn=qn:(isinstance(d,attributeGroupElt) and d.component.qname==qn), self.xrpr.attrs) class attributeGroupElt(defRefElt): def __init__(self,factory,elt): defRefElt.__init__(self,factory,elt) factory.eltStack[0:0]=[self] self.attrs=[] def init(self,elt): self.schema.factory.eltStack=self.schema.factory.eltStack[1:] defRefElt.init(self,'attributeGroup',attributeGroupElt,('ref',)) def checkRefed(self): if self.attrs: self.error("can't have ref %s and attrs in attributeGroup"%self.ref) if self.name: self.error("internal attributeGroup with name %s"%self.name) self.name='' self.component=AttributeUse(self.schema.factory,self,None) self.component.attributeDeclarationName=QName(self.ref,self.elt, self.schema.factory) self.component.nameType='attributeGroup' def checkInternal(self): self.error("internal attributeGroup must have ref") def checkTop(self): # only called if we are a top-level attributeGroup self.component=AttributeGroup(self.schema.factory,self) class explicitGroupElt(commonElt): minOccurs=None maxOccurs=None def __init__(self,factory,elt): commonElt.__init__(self,factory,elt) self.model=[] def init(self,elt): if self.maxOccurs=="0": self.component=None else: self.component=Particle(self.schema.factory,self, self.compClass(self.schema.factory,self)) class All(Group): compositor='all' def translate(self, next, place): # doesn't enforce cardinality yet n = FSMNode(next.fsm) i = 0 for particle in self.particles: m = particle.translate(n, (place,i)) i = i+1 FSMEdge(None, n, m) FSMEdge(None, n, next) return n class allElt(explicitGroupElt): compClass=All class choiceElt(explicitGroupElt): compClass=Choice class sequenceElt(explicitGroupElt): compClass=Sequence class groupElt(defRefElt,particleElt): # Note this is _not_ parallel to group -- it is not a common superclass of # choiceElt, etc. # It actually always disappears -- if nested with a ref, into a particle # with a termRef; if top-level, into a named sequence, choice or all def __init__(self,factory,elt): defRefElt.__init__(self,factory,elt) factory.eltStack[0:0]=[self] self.model=[] def init(self,elt): self.schema.factory.eltStack=self.schema.factory.eltStack[1:] defRefElt.init(self,'group',groupElt) particleElt.init(self) def checkRefed(self): if self.model: self.error("can't have ref %s and model in group"%self.ref) if self.name: self.error("internal group with name %s"%self.name) self.name='' if self.maxOccurs=="0": self.component=None else: self.component=Particle(self.schema.factory,self,None) self.component.termName=QName(self.ref,self.elt, self.schema.factory) self.component.termType='group' def checkInternal(self): self.error("internal group must have ref") self.component=None def checkTop(self): # only called if we are a top-level group # have to transform into our model # our xrpr is lost! # note that top-level groups must contain exactly one choice/sequence/all if not len(self.model)==1: self.error("Top-level model group definitions must contain exactly one choice/sequence/all") if len(self.model)==0: # arghh self.component=None return mod=self.model[0].component.term if mod: mod.name=self.name self.component=mod class includeElt(commonElt): schemaLocation=None def init(self,elt): schemas=self.schema.factory.schemas target=self.schema.targetNS ne=XML.Element("includeAttempt") ne.addAttr('namespace',target) loc=urljoin(self.schema.factory.fileNames[0],self.schemaLocation) ne.addAttr('URI',loc) self.schema.factory.resElt.children.append(ne) if (schemas.has_key(target) and loc in schemas[target].locations): ne.addAttr('outcome','redundant') return res=fromFile(loc, self.schema.factory,target,1) if res: ne.addAttr('outcome','success') else: ne.addAttr('outcome','failure') return res class redefineElt(includeElt): schemaLocation=None def init(self,elt): schemas=self.schema.factory.schemas res=includeElt.init(self,elt) if res: for dd in self.dds: if dd.name: dd.component.redefine() class importElt(commonElt): schemaLocation=None namespace=None def init(self,elt): checkinSchema(self.schema.factory, self.namespace, self.schemaLocation, elt, self.schema.factory.fileNames[0]) # TODO: we should really record the import statements present in a # so that we can check that there was an import in that # very . def checkinSchema(factory,namespace,location,elt,base): ne=XML.Element("importAttempt") factory.resElt.children.append(ne) if location: fullLoc=urljoin(base,location) else: # what about relative NS URIs? fullLoc=namespace ne.addAttr('namespace',namespace) ne.addAttr('URI',fullLoc) if factory.schemas.has_key(namespace): other=factory.schemas[namespace] if fullLoc in other.locations: ne.addAttr('outcome','redundant') else: ne.addAttr('outcome','skipped') ne.addAttr('otherLocs',string.join(other.locations,' ')) return else: res=fromFile(fullLoc,factory,namespace) if res: ne.addAttr('outcome','success') else: ne.addAttr('outcome','failure') return res class notationElt(commonElt): pass class Kcons(Component): def __init__(self,factory,xrpr): Component.__init__(self,factory,xrpr) self.fields=map(lambda x:xpath.XPath(x.xpath,x.elt.elt.nsdict),xrpr.fields) self.selector=xpath.XPath(xrpr.selector.xpath,xrpr.selector.elt.elt.nsdict) # could these all be double-rooted?? class Unique(Kcons): cname='unique' reflectedName='uniqueDefinition' reflectionMap=() class uniqueElt(commonElt): def init(self,elt): self.component=Unique(self.schema.factory,self) class Keyref(Kcons): cname='keyref' reflectedName='keyrefDefinition' reflectionMap=() def __init__(self,factory,xrpr): Kcons.__init__(self,factory,xrpr) self.refer=xrpr.refer class keyrefElt(commonElt): def init(self,elt): self.component=Keyref(self.schema.factory,self) class Key(Kcons): cname='key' reflectedName='keyDefinition' reflectionMap=() class keyElt(commonElt): def init(self,elt): self.component=Key(self.schema.factory,self) class xpathElt(commonElt): # TODO: check syntax def init(self,elt): pass class fieldElt(xpathElt): cname='field' class selectorElt(xpathElt): cname='selector' class AnyAttribute(Component): namespace=None reflectedName='wildcard' def __init__(self,factory,xrpr,wildcard): Component.__init__(self,factory,xrpr,None) self.wildcard=wildcard def merge(self,mine,other): self.error("*** merging anyAttrs %s and %s, not implemented yet\n"% (mine, other), 1) return mine class anyAttributeElt(commonElt): namespace="##any" processContents="lax" def init(self,elt): self.component=AttributeUse(self.schema.factory,self, RefineAny(self,self.namespace,1), 'optional') class annotationElt(commonElt): documentation=[] appinfo=[] class appinfoElt(commonElt): pass class documentationElt(commonElt): pass eltClasses={} for en in ["schema","complexType","element","unique","key","keyref", "group","all","choice","sequence","any","anyAttribute","simpleType", "restriction","list","union","simpleContent","complexContent", "field","selector","annotation","appinfo","documentation", "extension","attribute","attributeGroup", "include","import","redefine","notation"]: eltClasses[en]=eval(en+"Elt") for en in [ "Enumeration","Length","Pattern"]: eltClasses[string.lower(en)]=eval(en) for (en,cn) in [("fractionDigits",FractionDigits), ("totalDigits",TotalDigits), ("whiteSpace",Whitespace)]: eltClasses[en]=cn for rcn in [ "Inclusive","Exclusive","Length" ]: for pre in [ "Max", "Min"]: eltClasses["%s%s"%(string.lower(pre),rcn)]=eval("%s%s"%(pre,rcn)) schemaEltDispatch= {("schema","element"):("group","dds"), ("group","element"):("group","model"), ("all","element"):("group","model"), ("choice","element"):("group","model"), ("sequence","element"):("group","model"), ("complexType","element"):"error", ("complexType","any"):"error", ("schema","group"):("group","dds"), ("redefine","group"):("group","dds"), ("restriction","group"):"self", ("restriction","all"):"self", ("restriction","choice"):"self", ("restriction","sequence"):"self", ("extension","group"):"self", ("extension","all"):"self", ("extension","choice"):"self", ("extension","sequence"):"self", ("complexType","group"):"self", ("complexType","all"):"self", ("complexType","choice"):"self", ("complexType","sequence"):"self", "complexContent":"self", "simpleContent":"self", ("group","group"):("group","model"), ("group","all"):("group","model"), ("group","choice"):("group","model"), ("group","sequence"):("group","model"), ("all","group"):("group","model"), ("all","all"):("group","model"), ("all","choice"):("group","model"), ("all","sequence"):("group","model"), ("choice","group"):("group","model"), ("choice","all"):("group","model"), ("choice","choice"):("group","model"), ("choice","sequence"):("group","model"), ("sequence","group"):("group","model"), ("sequence","all"):("group","model"), ("sequence","choice"):("group","model"), ("sequence","sequence"):("group","model"), "any":("group","model"), "anyAttribute":("group","attrs"), ("attributeGroup","attribute"):("group","attrs"), ("restriction","attribute"):("group","attrs"), ("extension","attribute"):("group","attrs"), ("complexType","attribute"):("group","attrs"), ("schema","attribute"):("group","dds"), "annotation":("group","annot"), # broken for schema "documentation":("group","documentation"), "appinfo":("group","appinfo"), "key":("group","keys"), "keyref":("group","keyrefs"), "unique":("group","uniques"), ("attributeGroup","attributeGroup"):("group","attrs"), ("complexType","attributeGroup"):("group","attrs"), ("restriction","attributeGroup"):("group","attrs"), ("extension","attributeGroup"):("group","attrs"), ("schema","attributeGroup"):("group","dds"), ("redefine","attributeGroup"):("group","dds"), ("element","complexType"):"self", ("element","simpleType"):"self", ("attribute","simpleType"):"self", "restriction":"self", "extension":"self", ("restriction","simpleType"):"self", "list":"self", ("list","simpleType"):"self", "union":"self", ("union","simpleType"):("group","subTypes"), ("schema","complexType"):("group","dds"), ("redefine","complexType"):("group","dds"), ("schema","simpleType"):("group","dds"), ("redefine","simpleType"):("group","dds"), "maxInclusive":("group","facets"), "maxExclusive":("group","facets"), "minInclusive":("group","facets"), "minExclusive":("group","facets"), "enumeration":("group","facets"), "fractionDigits":("group","facets"), "totalDigits":("group","facets"), "length":("group","facets"), "maxLength":("group","facets"), "minLength":("group","facets"), "pattern":("group","facets"), "whiteSpace":("group","facets"), "field":("group","fields"), "selector":"self" } class FSM: def __init__(self): self.nodes = [] self.startNode = None def assignIDs(self): if not self.nodes: return for n in self.nodes: n.id = None self.nextID = 1 s = self.startNode s.assignIDs() def printme(self,file): self.assignIDs() self.nodes.sort(FSMNode.compareIDs) for n in self.nodes: if n.isEndNode: file.write("*") else: file.write(" ") file.write("%2d:" % n.id) # if n.label: # sys.stdout.write("[") # for nn in n.label: # sys.stdout.write("%d " % nn.id) # sys.stdout.write("]") for e in n.edges: file.write(" %s->%d" % (e.label, e.dest.id)) file.write("\n") def asXML(self): res=XML.Element("fsm") self.assignIDs() self.nodes.sort(FSMNode.compareIDs) for n in self.nodes: ne=XML.Element("node") res.children.append(ne) ne.addAttr('id',("%d" % n.id)) if n.isEndNode: ne.addAttr('final','true') n.edges.sort(FSMEdge.compareLabels) for e in n.edges: ee=XML.Element("edge") ne.children.append(ee) if isinstance(e.label,Wildcard): lab=str(e.label.allowed) else: lab=str(e.label) ee.addAttr('label',lab) ee.addAttr('dest',("%d" % e.dest.id)) return res # cf Aho & Ullman p93 def determinise(self): D = FSM() D.startNode = FSMNode(D) D.startNode.label = eclosure([self.startNode]) for nn in D.startNode.label: if nn.isEndNode: D.startNode.isEndNode = 1 break D.unmarkedNodes = [D.startNode] while D.unmarkedNodes: x = D.unmarkedNodes[0] del D.unmarkedNodes[0] destnodes = {} for n in x.label: for e in n.edges: if destnodes.has_key(e.label): if not e.dest in destnodes[e.label]: destnodes[e.label].append(e.dest) elif e.label: destnodes[e.label] = [e.dest] for label in destnodes.keys(): T = destnodes[label] y = eclosure(T) found = 0 for n in D.nodes: if y == n.label: for e in x.edges: if e.label == label and e.dest == y: break else: FSMEdge(label, x, n) break else: n = FSMNode(D) D.unmarkedNodes.append(n) n.label = y for nn in y: if nn.isEndNode: n.isEndNode = 1 break FSMEdge(label, x, n) return D class FSMEdge: def __init__(self, label, source, dest): self.label = label self.source = source self.dest = dest source.edges.append(self) def compareLabels(self,other): if self.label < other.label: return -1 elif self.label > other.label: return +1 else: return 0 class FSMNode: def __init__(self, fsm): fsm.nodes.append(self) self.fsm = fsm self.isEndNode = 0 self.edges = [] self.label = None self.mark = 0 def assignIDs(self): if self.id: return # print "assigning id %d to %s" % (self.fsm.nextID,self) self.id = self.fsm.nextID self.fsm.nextID = self.fsm.nextID + 1 for e in self.edges: e.dest.assignIDs() def compareIDs(self,other): if self.id < other.id: return -1 elif self.id > other.id: return +1 else: return 0 # cf Aho & Ullman p92 def eclosure(T): for n in T: n.mark=1 STACK = copy.copy(T) ECLOSURE = copy.copy(T) while STACK: s = STACK[0] del STACK[0] for e in s.edges: if e.label == None: t = e.dest if not t.mark: t.mark=1 ECLOSURE.append(t) STACK.insert(0, t) # ECLOSURE.sort() # so we can compare them easily??? for n in ECLOSURE: n.mark=0 return ECLOSURE # Change labels of form (name, place) to name def relabelFSM(fsm): for n in fsm.nodes: for e in n.edges: if type(e.label) == types.TupleType: e.label = e.label[0] # Check a FSM is deterministic def checkFSM(fsm): for n in fsm.nodes: for e in range(len(n.edges)): l = n.edges[e].label for f in range(0,e): m = n.edges[f].label if isinstance(l, Wildcard): if(isinstance(m, Wildcard)): if not l.intersect(m).isEmpty(): return "%s/%s" % (l,m) else: if l.allows(m.uri): return "%s/%s" % (l,m) else: if(isinstance(m, Wildcard)): if m.allows(l.uri): return "%s/%s" % (l,m) else: if l == m: return "%s/%s" % (l,m) return 0 class VMapping: def __init__(self, schema, tablename): self.schema = schema self.tablename = tablename def findSchema(self, uri, local): if uri == self.schema.targetNS: return self.schema # look it up if self.schema.factory.schemas.has_key(uri): return self.schema.factory.schemas[uri] else: return 0 def has_key(self, key): if not key: return 0 if not isinstance(key, QName): shouldnt('nk: '+str(key)) return 0 s = self.findSchema(key.uri, key.local) if not s: # not an error to check, we'll record one error when we really go for it return 0 return s.__dict__[self.tablename].has_key(key.local) def __getitem__(self, key): s = self.findSchema(key.uri, key.local) if not s: self.schema.error("unknown namespace for %s for %s" % (key.uri, key.local)) return None return s.__dict__[self.tablename][key.local] class SchemaError(Exception): pass def shouldnt(msg): error("Shouldn't happen "+msg) def error(msg): raise SchemaError,msg def where(elt,w): if w and w[3]!=0: if w[0]!='unnamed entity': elt.addAttr('entity',w[0]) elt.addAttr('line',str(w[1])) elt.addAttr('char',str(w[2])) elt.addAttr('resource',w[3]) def whereString(w): if w and w[3]!=0: return ("in %s at line %d char %d of %s" % w) else: return "location unknown" def topGroup(factory,rawModel): if isinstance(rawModel.component.term,Group): # we have exactly one group, so use it return rawModel.component else: shouldnt('tg') # $Log: XMLSchema.py,v $ # Revision 1.185 2001/04/17 11:34:33 ht # make sure no particle -> empty sequence when needed # # Revision 1.184 2001/04/10 15:35:22 ht # fix some pblms with independent mode # # Revision 1.183 2001/04/06 21:00:54 ht # cap min/max at 101 for performance # # Revision 1.182 2001/04/04 20:56:30 ht # implement -i switch to do forced schema assessment independent of any instance # # Revision 1.181 2001/04/04 18:46:09 ht # make home determination more robust # # Revision 1.180 2001/03/31 11:43:34 ht # number back to decimal # # Revision 1.179 2001/03/30 14:50:14 ht # fix xs: bug, # remove debugging print # # Revision 1.178 2001/03/20 09:28:02 ht # actually get rid of max=0 components # # Revision 1.177 2001/03/17 12:11:14 ht # merge v2001 back in to main line # # Revision 1.176 2001/02/12 11:34:23 ht # catch extension error # # Revision 1.175.2.9 2001/03/15 12:35:19 ht # simple type rename, # clear out facet dead wood, # actually process whiteSpace facet, which had been ignored (oops) # # Revision 1.175.2.8 2001/03/15 11:31:01 ht # another clause in emptiable # shift to xs: as default in DTD # # Revision 1.175.2.7 2001/03/10 23:52:58 ht # uriReference -> anyURI # implement block=substitution wrt substitution groups # # Revision 1.175.2.6 2001/02/24 23:51:22 ht # detect chameleon include and treat all unprefixed QNames therein # specially # # Revision 1.175.2.5 2001/02/18 22:12:26 ht # implement lazy checking of restriction wrt rere attribute groups # catch deletion of required attr in ct restriction # # Revision 1.175.2.4 2001/02/18 21:33:38 ht # allow elt for choice restriction, to cover new SforS # do minimal name and type checking on elt for elt restriction # eliminate forbidden attrs from attribute groups # # Revision 1.175.2.3 2001/02/17 23:37:54 ht # check that restriction doesn't relax/change fixed value constraint on attrs # restructure attribute group attrDecls to make them properly lazy in preparation # for doing restrictive redefinition right, but remove that for the # time being # # Revision 1.175.2.2 2001/02/14 16:59:56 ht # merge attr use changes back in to main line # implement attribute group redefinition # # Revision 1.175.2.1.2.1 2001/02/07 17:33:03 ht # make AttrUse a full-fledged one # # Revision 1.175.2.1 2001/02/07 14:30:01 ht # change NS to 2001, implement null->nil # # Revision 1.175 2001/02/06 14:19:37 ht # parse XPaths at component build time # fix crash on subst subtype check error message # # Revision 1.174 2001/02/06 11:21:17 ht # merged forceDTD+infoset back to mainline # # Revision 1.173.2.15.2.5 2001/01/15 14:19:24 ht # fix bug in wildcard as edge label in reflection # # Revision 1.173.2.15.2.4 2001/01/04 20:36:06 ht # support PSVI of attrGroupDefn, # protect better against missing types # # Revision 1.173.2.15.2.3 2000/12/23 13:07:24 ht # fix spelling of whiteSpace, # make equivClass computation less inefficient, # catch loops in schema doc reading themselves # # Revision 1.173.2.15.2.2 2000/12/22 18:34:50 ht # add hooks for whitespace # # Revision 1.173.2.15.2.1 2000/12/21 18:31:45 ht # real facets # # Revision 1.173.2.15 2000/12/16 12:11:41 ht # fix equiv name, add stubs for identity constraint reflection # # Revision 1.173.2.14 2000/12/14 15:59:13 ht # put facets back in reflection, in the right place, # move assignUid out # # Revision 1.173.2.13 2000/12/14 14:21:15 ht # fix conflicting message on local elt redef # # Revision 1.173.2.12 2000/12/13 23:29:12 ht # bring urtype in line with spec, # reflection for particle, model group # # Revision 1.173.2.11 2000/12/13 18:22:50 ht # fix bug in instanceList implementation, # treat primitiveType reflection as special (always pointer), # make non-global scope lazy, # derive CDATA from string and token from CDATA # # Revision 1.173.2.10 2000/12/12 17:36:34 ht # fix a few minor properties, # add reflectedName and reflectionMap to many components # # Revision 1.173.2.9 2000/12/08 18:06:53 ht # assign printable proper IDs to components on request, # provide reflectedNames for some components # # Revision 1.173.2.8 2000/12/07 10:17:24 ht # fix type name in builtin -instance types # # Revision 1.173.2.7 2000/12/06 22:43:49 ht # start adding built-in attr decls for xsi:type etc. # # Revision 1.173.2.6 2000/10/31 14:58:13 ht # store whole declaration in typeTable # enforce new restrictions on element ref= # handle defaulting of nullable properly # # Revision 1.173.2.5 2000/10/30 14:55:11 ht # Another order of magnitude speedup of eclosure, by not sorting the # results # # Revision 1.173.2.4 2000/10/30 14:39:49 ht # speed up eclosure (by factor of 10 :-) # # Revision 1.173.2.3 2000/10/30 12:36:26 ht # removed errors now caught by DTD checking # # Revision 1.173.2.2 2000/10/27 14:40:23 ht # handle errors during DTD enforcement on schemas # # Revision 1.173.2.1 2000/10/27 14:16:47 ht # Use a local XMLSchema.dtd to check schema documents if they dont # supply an external DTD themselves. Some error cases not well-handled, yet. # # Revision 1.174 2000/10/27 14:13:38 ht # Use a local XMLSchema.dtd to check schema documents if they dont # supply an external DTD themselves. Some error cases not well-handled, yet. # # Revision 1.174 2000/10/27 11:28:40 ht # Use a local XMLSchema.dtd to check schema documents if they don't # supply an external DTD themselves. Some error cases not well-handled # yet. # # Revision 1.173 2000/10/19 09:36:04 ht # fix bug in local scoping of defaults to schemas # # Revision 1.172 2000/10/19 09:08:46 ht # allow use on global elt attrs # # Revision 1.171 2000/10/18 15:53:32 ht # add actual urSimpleType, use it appropriately # fix recording of 'fixed', 'default' for attrs # use subordinate simpleType in double-restriction case under # simpleContent # # Revision 1.170 2000/10/17 13:22:08 ht # typo in last fix :-( # # Revision 1.169 2000/10/17 13:13:08 ht # allow for empty model wiht 'mixed' # # Revision 1.168 2000/10/17 12:44:22 ht # keep going if restriction missing # # Revision 1.167 2000/10/16 12:18:30 ht # fix spelling of NotASchema # allow restriction to a member of a union for xsi:type # fix empty vs. mixed bug # add CDATA and token as synonyms of string, for now # # Revision 1.166 2000/09/29 14:37:59 ht # actually put urType in type table under name anyType # # Revision 1.165 2000/09/28 15:53:13 ht # always count errors # # Revision 1.164 2000/09/28 10:00:34 ht # realModel tries to recover from no basetype # # Revision 1.163 2000/09/27 17:17:08 richard # Add handy splitQName function and use in in class QName # # Revision 1.162 2000/09/26 14:03:42 richard # Move checkString methods to applyschema.py, because they may need to look # at *instance* in-scope namespaces # # Revision 1.161 2000/09/25 11:57:51 ht # can't restrict list with list # # Revision 1.160 2000/09/23 11:17:31 ht # merge in CR branch # # Revision 1.159 2000/09/11 17:49:25 ht # implement chameleon include # # Revision 1.158.2.13 2000/09/23 11:06:46 ht # new NS URI # # Revision 1.158.2.12 2000/09/21 09:15:37 ht # don't collapse as much wrt sequences when extending # # Revision 1.158.2.11 2000/09/20 15:18:03 ht # implement redefine # back out restricition of union by union # change concrete syntax of field and selector # # Revision 1.158.2.10 2000/09/05 11:31:39 ht # more urtype name changes # # Revision 1.158.2.9 2000/09/04 21:37:35 ht # accommodate (partly) to new name for simple ur type # # Revision 1.158.2.8 2000/09/03 15:59:16 ht # minimal support for restrictions of lists and unions # # Revision 1.158.2.7 2000/09/03 13:47:48 ht # implement changes to content model of complexContent # # Revision 1.158.2.6 2000/08/31 21:26:49 ht # a few more simpletype change leftovers # # Revision 1.158.2.5 2000/08/31 15:29:10 ht # residual small bugs from simple type changes # # Revision 1.158.2.4 2000/08/31 11:47:47 ht # fix a residual bug in SimpleType, implement List and Union # # Revision 1.158.2.3 2000/08/31 09:44:42 ht # Change SimpleType component to agree with new design # # Revision 1.158.2.2 2000/08/30 12:33:42 ht # convert to new XML repr of simpleType # # Revision 1.158.2.1 2000/08/29 21:01:56 ht # New XML Schema NS for CR # implement equivClass -> substitutionGroup move # # Revision 1.158 2000/08/22 15:47:35 ht # fix bug ignoring nested attributeGroup references # # Revision 1.157 2000/08/22 13:12:46 ht # treat simple content as emptiable for now # provide null default for checkMinVals # # Revision 1.156 2000/07/25 14:50:39 ht # don't die if no group in group, if no type for attr # # Revision 1.155 2000/07/12 09:33:32 ht # handle no basetype in mergeContent # stub for min checking of strings # # Revision 1.154 2000/07/10 12:12:00 ht # recover from no-ref internal group # handle missing base when checking isSubtype # check 'content' attribute value # # Revision 1.153 2000/07/07 12:57:38 ht # provide a printable name for Wildcards # # Revision 1.152 2000/07/05 14:48:05 ht # check restriction occurs only if groups match; # handle one trivial case of emptiable; # allow textonly from emptiable mixed derivation # # Revision 1.151 2000/07/05 09:06:35 ht # replace complex with simplistic check for circularity, give builtins a targetNamespace # # Revision 1.150 2000/07/04 11:04:19 ht # deal with knock-on effect of group defn fix # # Revision 1.149 2000/07/04 10:39:43 ht # catch LayerError # check model group defns for exactly one child # sort FSM edges on output # # Revision 1.148 2000/07/03 15:52:19 ht # at least give better error messages for facets when derivedBy='list' #xs # Revision 1.147 2000/07/03 15:02:17 ht # try once again to simplify/fix include/import/namespace processing # # Revision 1.146 2000/07/03 09:40:13 ht # give float, double, decimal a common superclass to host CheckMinMax # give error if facet missing value attr # # Revision 1.145 2000/06/27 09:40:27 ht # fix bug (circularity) introduced in previous fix for textOnly stuff # # Revision 1.144 2000/06/27 09:09:01 ht # catch use of complexType as base for simpleType, type of attr # fix (?) indentation bugs in checking for textOnly in right places # # Revision 1.143 2000/06/26 08:22:48 ht # cover empty # # Revision 1.142 2000/06/24 11:17:07 ht # fix bug in unqualified xsi:type # # Revision 1.141 2000/06/24 09:11:26 ht # Make fixed not == required # # Revision 1.140 2000/06/22 12:03:24 ht # give error if type attr and nested type defn # # Revision 1.139 2000/06/20 14:50:16 richard # wildcard intersection fixes # any/any determinacy checking # # Revision 1.138 2000/06/20 12:47:57 ht # typo in new allows code for Wildcard # # Revision 1.137 2000/06/20 12:41:25 ht # typo in minInclusive fix # # Revision 1.136 2000/06/20 12:05:38 ht # Fixed bugs in attr type error message, fullName/primitiveType # loophole, minInclusive # Reworked implementation of wildcards and wildcard intersection # # Revision 1.135 2000/06/20 08:07:42 ht # merge xmlout branches back in to main line # # Revision 1.134 2000/05/25 07:56:12 ht # integrate basetype calculation patch from other branch # # Revision 1.133 2000/05/14 12:16:28 ht # change handling of prefix-lookup failure # # Revision 1.132 2000/05/14 12:12:16 ht # Add method to QName to check basic validity # # Revision 1.131 2000/05/13 11:44:13 ht # pop schema stack after fromFile even if it fails # # Revision 1.130.2.12 2000/06/16 15:48:56 richard # Content model determinism checking (not complete: any/any not done) # # Revision 1.130.2.11 2000/06/15 16:02:07 ht # protect against more missing definitions # # Revision 1.130.2.10 2000/06/15 09:50:58 ht # cope with missing attrDecl # # Revision 1.130.2.9 2000/05/31 11:30:09 ht # use convertToNum throughout, raise error if not a numeral # # Revision 1.130.2.8 2000/05/29 08:42:51 ht # register values, not instances # change enumeration to cope with above change # # Revision 1.130.2.7 2000/05/29 08:21:04 ht # make fsm asXML label readable when wildcard # # Revision 1.130.2.6 2000/05/24 20:43:28 ht # fix handling of complex type with simple model derived from complex # type (with simple model) # # Revision 1.130.2.5 2000/05/24 12:01:28 ht # handle enumerations a bit # change import handling # # Revision 1.130.2.4 2000/05/16 16:29:03 ht # manage unnamed top-level components without crashing # fix bug in handling of undefined *ed element ref in content model # allow undefined refed elements to match in fsm # # Revision 1.130.2.3 2000/05/14 12:30:21 ht # merge QName checking from main branch, # fix QName error messages # # Revision 1.130.2.2 2000/05/13 12:15:23 ht # integrate import/null-schema patch from other branch # # Revision 1.130.2.1 2000/05/11 14:09:42 ht # convert error to log an element onto factory.resElt # convert where to add location attributes to an element # add asXML method to fsm for applyschema error logging # name the elt we want from layer.fromFile # # Revision 1.133 2000/05/14 12:16:28 ht # change handling of prefix-lookup failure # # Revision 1.132 2000/05/14 12:12:16 ht # Add method to QName to check basic validity # # Revision 1.131 2000/05/13 11:44:13 ht # pop schema stack after fromFile even if it fails # # Revision 1.130 2000/05/11 11:09:49 ht # protect against missing basetype, # handle Unicode names in QName # # Revision 1.130 2000/05/11 11:09:49 ht # protect against missing basetype, # handle Unicode names in QName # # Revision 1.130 2000/05/11 11:09:49 ht # protect against missing basetype, # handle Unicode names in QName # # Revision 1.129 2000/05/09 14:52:52 ht # Check for strings in a way that works with or without 16-bit support # # Revision 1.128 2000/05/09 12:34:23 ht # shift to using python's built-in url parsing # # Revision 1.127 2000/05/05 17:41:39 ht # handle value and use on attributes # # Revision 1.126 2000/04/28 22:19:42 ht # fix name error in group ref comparison in cm merger # # Revision 1.125 2000/04/28 17:37:07 ht # various missing property initialisations fixed # # Revision 1.124 2000/04/28 11:08:08 ht # oops, remove debugging printout # # Revision 1.123 2000/04/28 11:06:55 ht # fix bug (in spec. too:-( wrt anyAttribute ##other) # # Revision 1.122 2000/04/27 16:01:28 ht # catch and allow more cases of restrictive derivations from the ur-type # # Revision 1.121 2000/04/27 09:30:20 ht # check that inputs are actually schemas, # remove schema arg to doImport, checkInSchema # # Revision 1.120 2000/04/26 17:21:53 ht # replace stale use of anyElt in urType # # Revision 1.119 2000/04/26 16:59:25 ht # handle undefed ref in note and translate # # Revision 1.118 2000/04/26 13:00:40 ht # add copyright # # Revision 1.117 2000/04/24 20:46:40 ht # cleanup residual bugs with massive rename, # rename Any to Wildcard, # replace AnyAttribute with Wildcard, # get validation of Wildcard working in both element and attribute contexts # # Revision 1.116 2000/04/24 15:00:09 ht # wholesale name changes -- init. caps for all classes, # schema.py -> XMLSchema.py # # Revision 1.115 2000/04/24 13:51:01 ht # get rid of AnyWrap, separate any and anyElt # structure sub-classes of any for validation use # # Revision 1.114 2000/04/24 12:26:00 ht # move fsm translation to 'translate' methods on appropriate classes # # Revision 1.113 2000/04/24 11:09:50 ht # add version string # fix typo in previous equivClass fix :-( # # Revision 1.112 2000/04/22 17:31:09 ht # fixed race condition in type inheritance check # # Revision 1.111 2000/04/22 17:10:19 ht # remove lots of dead wood # # Revision 1.110 2000/04/21 14:23:36 ht # fix missing particle in urtype # # Revision 1.109 2000/04/21 09:31:11 ht # value check on min/max occurs # # Revision 1.108 2000/04/21 09:15:00 ht # removed dump, dumpForDTD and all related methods to separate file # # Revision 1.107 2000/04/20 22:12:11 ht # move resolveURL around a bit to get it right # # Revision 1.106 2000/04/20 15:45:08 ht # better handling of use of ns uri for loc # # Revision 1.105 2000/04/20 14:39:44 ht # merge in private and comp branches # # Revision 1.104.2.13 2000/04/20 14:38:19 ht # merge in comp branch # # Revision 1.104.2.12 2000/04/08 11:54:12 ht # odd attrs patch # # Revision 1.104.2.11 2000/04/07 11:38:45 ht # add modest attempt at resolving relative URLs # # Revision 1.104.2.10 2000/04/06 09:52:45 ht # residual bug in import # # Revision 1.104.2.9 2000/04/03 14:51:50 ht # lots of futzy fixes to effectiveStuff for Last Call schemas # # Revision 1.104.2.9.2.15 2000/04/20 14:23:18 ht # remove special casing of schema creation for no elt # # Revision 1.104.2.9.2.14 2000/04/20 12:02:05 ht # Do imports inline # Use namespace URI for import if no schemaLoc # Hack textonly complextypes w/o base # Fix bug in hard-case for extension construction # A few defaults to allow DTD-free operation # # Revision 1.104.2.9.2.13 2000/04/20 08:45:41 ht # completed basic conversion to components, # works for triv, tiny, tiny-nns and self # # Revision 1.104.2.9.2.12 2000/04/18 09:33:43 ht # begin work on content model merger # fold in url normalisation # attribute groups improved # # Revision 1.104.2.9.2.11 2000/04/16 16:47:02 ht # working on unifying particles and defrefs # # Revision 1.104.2.9.2.10 2000/04/14 21:11:09 ht # much improved, minimal coverage almost everywhere, # groups properly handled, # facets and keys # # Revision 1.104.2.9.2.9 2000/04/13 23:03:31 ht # get builtin facets right, finally # fix bug in numerical checkString, cover for non-numbers # propagate particle/term/group structure through translation into FSMs # get noteElement, noteParticle working again # # Revision 1.104.2.9.2.8 2000/04/12 17:29:37 ht # begin work on model merger, # # Revision 1.104.2.9.2.7 2000/04/11 18:13:18 ht # interpolate attributeUse between complexType and attributeDeclaration, # parallel to particle # # Revision 1.104.2.9.2.6 2000/04/10 17:20:26 ht # bring built-in datatypes in to line with WD # add list types # # Revision 1.104.2.9.2.5 2000/04/10 15:51:04 ht # fix facet initialisation of derived simple types # stub for string validation # minimal complex type support # # Revision 1.104.2.9.2.4 2000/04/09 16:13:27 ht # working on complex type, attribute; # back out component.qname # # Revision 1.104.2.9.2.3 2000/04/06 09:43:47 ht # starting on complex type # # Revision 1.104.2.9.2.2 2000/04/05 12:11:34 ht # Getting the basics sorted out: bare minimum of simple type def and # element # # Revision 1.104.2.9.2.1 2000/04/03 14:55:15 ht # beginning upheaval # # Revision 1.104.2.8 2000/04/01 18:04:15 ht # lots of hacks to get effective type more involved # # Revision 1.104.2.7 2000/03/25 12:10:44 ht # change to full import, no duplicates; # redo error handling, use line nums if available # # Revision 1.104.2.6 2000/03/21 16:03:47 ht # allow 208 override, # some cleanup of complexType attr checking and defaulting # # Revision 1.104.2.5 2000/03/20 17:17:35 ht # top-level attrs, # implement 208 # # Revision 1.104.2.4 2000/03/14 18:37:48 ht # convert to camelCase for builtin datatype names # # Revision 1.104.2.3 2000/03/14 18:11:13 ht # remove abstract elements from equivClass expansion for 'dump' # # Revision 1.104.2.2 2000/03/14 11:26:45 ht # trivial move to new def'n of restriction # # Revision 1.104.2.1 2000/03/11 11:14:19 ht # convert * to unbounded for maxOccurs # # Revision 1.104 2000/03/08 15:39:35 ht # exact->block, tentatively # # Revision 1.103 2000/03/08 15:28:46 ht # merge private branches back into public after 20000225 release # # Revision 1.102.2.6 2000/02/24 23:40:33 ht # fix any bug # # Revision 1.102.2.5 2000/02/23 09:12:35 ht # source->base, restrictions goes away, uri -> uri-reference # # Revision 1.102.2.4 2000/02/16 17:52:27 ht # convert dump to use simple/complexType # expand equivClass in dumped models (should have a switch) # # Revision 1.102.2.3 2000/02/08 21:59:24 ht # fix minor datatypes initialisation bug # # Revision 1.102.1.3 2000/02/08 17:57:56 ht # fix minor datatypes initialisation bug # # Revision 1.102.1.2 2000/02/08 17:32:48 ht # handle #any --> anyAttribute in dumping # # Revision 1.102.1.1 2000/02/08 14:00:19 ht # fork branck for non-public changes # lots of changes to accommodate datatype/type -> simple/complexType, # group -> all, choice, sequence # builtins in separate namespace # got rid of subtypes and leaves, not used # # Revision 1.102 2000/01/27 13:28:27 ht # info->documentation; fix bug wrt dump of empty type with attrs # # Revision 1.101 2000/01/18 15:56:24 ht # implement inheritance for attribute restrictions # # Revision 1.100 2000/01/18 14:38:58 ht # fix long-standing serious bug in implementation of restriction for # complex types # # Revision 1.99 2000/01/17 18:02:28 ht # simple indentation # key etc. handled # namespace prefixes are broken for all but schema for schemas # # Revision 1.98 2000/01/17 17:22:54 ht # dump now works, I think, except for keys etc. and indentation # # Revision 1.97 2000/01/17 14:01:20 ht # fix bug ignoring inline types for attributes # part-way into normalisation 'dump' method on schema # # Revision 1.96 2000/01/10 17:36:21 richard # minor changes for xsi:schemaLocation # # Revision 1.95 2000/01/08 23:33:50 ht # towards support for xsi:schemaLocation # # Revision 1.94 2000/01/07 17:09:20 richard # QNames using python-level NS dictionary # # Revision 1.93 2000/01/05 13:00:13 ht # remove debugging prints # turn references to element class exemplars into disjunctions # # Revision 1.92 2000/01/04 20:47:54 ht # fix bug in setting up occurs; # working on dumpToDTD: equivClasses # # Revision 1.91 2000/01/04 17:35:38 ht # Got DumpToDTD working again, minimal support for QNames # # Revision 1.90 2000/01/03 14:10:51 ht # define error to raise a SchemaError exception # # Revision 1.89 2000/01/02 19:29:05 ht # minor initialisations for key processing at schema levels # # Revision 1.88 1999/12/27 20:02:29 ht # better message on import failure # # Revision 1.87 1999/12/26 15:43:41 ht # missing defaults # # Revision 1.86 1999/12/22 10:40:56 ht # init keys etc. # # Revision 1.85 1999/12/21 15:02:19 ht # fix anyattr merge # groups for key types # # Revision 1.84 1999/12/21 13:45:36 richard # start anyAttribute support # # Revision 1.83 1999/12/20 17:51:35 ht # Make urType more real, use it for ultimate default # Fix another attr ns bug, uncover incoherence in use of attrGroupRef to # get ns-qualified attr names in instances! # # Revision 1.82 1999/12/20 16:13:07 ht # correct check of equivClass type derivation from exemplar # # Revision 1.81 1999/12/20 15:44:27 ht # allow for qualified attr names # # Revision 1.80 1999/12/20 15:21:50 ht # fix (?) some attribute type bugs # # Revision 1.79 1999/12/20 14:12:45 ht # stub for anyAttr # # Revision 1.78 1999/12/17 17:59:37 ht # began getting dumpTo/ForDTD back in shape # # Revision 1.77 1999/12/17 16:24:02 ht # more stubs # # Revision 1.76 1999/12/17 15:59:44 ht # stubs for keys etc. # # Revision 1.75 1999/12/14 23:10:35 ht # fixed ns bug in noteElement # # Revision 1.74 1999/12/14 18:07:51 richard # save and restore factory.schema in fromFile # # Revision 1.73 1999/12/14 15:06:48 richard # implement import statements # # Revision 1.72 1999/12/14 14:34:45 ht # pull equivalence class building up front in preparation phase # # Revision 1.71 1999/12/14 12:55:51 ht # Save ALL table & type initialisation stuff until user calls 'prepare', # to avoid deadly embraces. Handle abinitio/builtin stuff lazily. # # Revision 1.70 1999/12/14 10:42:19 ht # handle targetNamespace earlier and better # build stub schema for schemas properly if necessary # # Revision 1.69 1999/12/13 20:09:24 richard # lots of changes for qnames # # Revision 1.68 1999/12/13 15:07:59 ht # attempting to start QName processing # # Revision 1.67 1999/12/13 12:29:59 ht # merged in new-design branch # # Revision 1.66.1.5 1999/12/13 10:30:25 ht # recursive handling of equiv. classes, equiv. classes into fsm # # Revision 1.66.1.4 1999/12/12 23:20:38 ht # slowly working through type derivation # # Revision 1.66.1.3 1999/12/11 18:30:14 ht # more work on new-design # # Revision 1.66.1.2 1999/12/10 20:13:11 ht # working on new-design # # Revision 1.66.1.1 1999/12/10 14:47:47 ht # begun to work on new-design upgrade # # Revision 1.66 1999/12/03 11:29:56 ht # fix info # # Revision 1.65 1999/12/01 12:27:13 richard # fix bug in translating min/max occurs to fsm # a little more support # # Revision 1.64 1999/12/01 11:14:05 ht # fix typo, add/correct some facets # # Revision 1.63 1999/12/01 10:57:03 ht # handle more classes, rename source to basetype # # Revision 1.62 1999/12/01 09:25:43 ht # add some facets # # Revision 1.61 1999/11/30 15:30:02 richard # handle (not complete yet) # # Revision 1.60 1999/11/26 17:22:51 richard # use realorder not order # # Revision 1.59 1999/11/26 13:30:03 richard # fix case where start node of determinsed fsm is also end node # # Revision 1.58 1999/11/26 12:32:47 ht # resurrect dumpTo/ForDTD, preliminary # # Revision 1.57 1999/11/26 10:01:29 aqw # change handling of default attribute values -- make them class vars # which get shadowed by assignments to instances # # Revision 1.56 1999/11/25 21:31:52 ht # fix basetype window bug # # Revision 1.55 1999/11/25 17:14:19 aqw # minor bugfixes, make rootName a self-computing property # # Revision 1.54 1999/11/25 15:57:44 richard # minor bugs # # Revision 1.53 1999/11/25 15:28:45 aqw # fix some bugs in interaction between complex types and new approach to # simple types # give textonly complex types a coreType to carry their datatype part # # Revision 1.52 1999/11/25 13:13:46 ht # merge in branch which switched to separate classes for ab initio types # # Revision 1.48.1.2 1999/11/25 10:21:24 aqw # convert to classes for primitive types, use them as effectiveType for # all simpleTypes # # Revision 1.48.1.1 1999/11/22 16:03:10 aqw # classes for ab initio types #