# xxxElt classes, intermediate between schema doc infosets and components
import string
import types
import urlparse
import XMLSchema
import XML
class schemaElt:
version=None
id=None
def __init__(self,sschema,elt):
self.elt=elt
sschema.eltStack[0:0]=[self]
if elt.hasAttrVal('targetNamespace'):
myns=elt.attrVal('targetNamespace')
else:
myns=None
if sschema.current is not None:
if (myns!=sschema.targetNS and
(not myns) and
sschema.processingInclude):
# chameleon include, OK
myns=sschema.targetNS
sschema.processingInclude=2
sschema.targetNS=myns
if sschema.schemas.has_key(myns):
oldSchema=sschema.schemas[myns]
oldSchema.xrpr=self
sschema.targetNS=myns
oldSchema.maybeSetVar('elementFormDefault',
'elementFormDefault','unqualified')
oldSchema.maybeSetVar('attributeFormDefault','attributeFormDefault',
'unqualified')
oldSchema.maybeSetVar('finalDefault','finalDefault','')
oldSchema.maybeSetVar('blockDefault','blockDefault','')
self.component=oldSchema
else:
self.component=XMLSchema.Schema(sschema,self)
sschema.current=self.component
self.dds=[]
def init(self,elt):
sch=self.component
# todo:oldSchema.annotations=oldSchema.annotations.append(self.annotations)
if self.__dict__.has_key('annot') and self.annot:
sch.annotations=map(lambda a:a.component,self.annot)
if elt is not None:
ooba=filter(lambda a:a.nsuri,
elt.actualAttrs)
if ooba:
ooba=map(lambda a:a.elt,ooba)
if not sch.annotations:
sch.annotations=[Annotation(sch.sschema,None)]
if sch.annotations[0].attrs:
sch.annotations[0].attrs=sch.annotations[0].attrs.append(ooba)
else:
sch.annotations[0].attrs=ooba
sch.sschema.eltStack=sch.sschema.eltStack[1:]
for dd in self.dds:
if not dd.name:
continue
if isinstance(dd,complexTypeElt):
sch.newComponent(sch.typeTable,'type',dd.component)
elif isinstance(dd,elementElt):
sch.newComponent(sch.elementTable,'element',dd.component)
elif isinstance(dd,attributeElt):
sch.newComponent(sch.attributeTable,'attribute',dd.component)
elif isinstance(dd,groupElt):
sch.newComponent(sch.groupTable,'group',dd.component)
elif isinstance(dd,simpleTypeElt):
sch.newComponent(sch.typeTable,'type',dd.component)
elif isinstance(dd,attributeGroupElt):
sch.newComponent(sch.attributeGroupTable,'attributeGroup',dd.component)
else:
shouldnt('dd')
class commonElt:
def __init__(self,sschema,elt):
self.schema=sschema.current
self.elt=elt
def error(self,msg,warn=0):
self.schema.error(msg,self.elt,warn)
class typeElt(commonElt):
name=None
basetype=None
derivedBy=None
id=None
annot=None
def __init__(self,sschema,elt):
commonElt.__init__(self,sschema,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 simpleTypeElt(typeElt):
content='textOnly'
restriction=None
list=None
union=None
final=""
def init(self,elt):
basetypeName=None
if self.restriction is not None:
derivedBy='restriction'
if self.restriction.__dict__.has_key('base'):
basetypeName=XMLSchema.QName(self.restriction.base,
elt,self.schema.sschema)
if (self.restriction.facets and
basetypeName.local=='anySimpleType' and
basetypeName.uri==XMLSchema.XMLSchemaNS and
len(self.restriction.facets)>1 and
not(isinstance(self.restriction.facets[0],XMLSchema.Whitespace))):
self.error("anySimpleType may not be directly restricted with facets")
basetypeName=XMLSchema.QName('string',elt,self.schema.sschema)
elif self.list is not None:
derivedBy='list'
basetypeName=XMLSchema.QName(None,'anySimpleType',
XMLSchema.XMLSchemaNS)
elif self.union is not None:
derivedBy='union'
basetypeName=XMLSchema.QName(None,'anySimpleType',XMLSchema.XMLSchemaNS)
else:
# no elt for fakes for builtins
if elt is not None:
self.error("simpleType must have one of restriction, list or union as a child")
self.component=XMLSchema.SimpleType(self.schema.sschema,self,'unknown',
None,None)
return
self.component=XMLSchema.SimpleType(self.schema.sschema,
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.sschema,self)
class restrictionElt(rulElt):
# TODO: check base vs content???
group=None
all=None
choice=None
sequence=None
simpleType=None
attrs=[]
def init(self,elt):
if (elt is not None 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)
if self.simpleType is not None:
self.component.basetype=self.simpleType.component
class listElt(rulElt):
# TODO: check base vs content
simpleType=None
itemType=None
class unionElt(rulElt):
# TODO: check base vs content
subTypes=[]
memberTypes=None
class complexTypeElt(typeElt):
sub=None
derivedBy=None
final=""
block=""
abstract="false"
complexContent=None
simpleContent=None
mixed=None
def __init__(self,sschema,elt):
typeElt.__init__(self,sschema,elt)
sschema.eltStack[0:0]=[self]
def __getattr__(self,name):
if self.sub is not None:
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 is not None:
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 is not None:
if self.content=='elementOnly' and not self.complexContent.model:
self.content='empty'
self.derivedBy='restriction'
if self.complexContent.restriction.__dict__.has_key('base'):
# must be a complex type
basetypeName=XMLSchema.QName(self.complexContent.restriction.base,
elt,self.schema.sschema)
elif self.complexContent.extension is not None:
self.derivedBy='extension'
if self.complexContent.extension.__dict__.has_key('base'):
# must be a simple type
basetypeName=XMLSchema.QName(self.complexContent.extension.base,
elt,self.schema.sschema)
elif self.simpleContent is not None:
self.sub=self.simpleContent
self.content='textOnly'
if self.simpleContent.restriction is not None:
self.derivedBy='restriction'
if self.simpleContent.restriction.__dict__.has_key('base'):
basetypeName=XMLSchema.QName(self.simpleContent.restriction.base,elt,
self.schema.sschema)
elif self.simpleContent.extension is not None:
self.derivedBy='extension'
if self.simpleContent.extension.__dict__.has_key('base'):
basetypeName=XMLSchema.QName(self.simpleContent.extension.base,elt,
self.schema.sschema)
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.sschema.eltStack=self.schema.sschema.eltStack[1:]
if not self.__dict__.has_key('final'):
self.final=self.schema.finalDefault
self.component=XMLSchema.ComplexType(self.schema.sschema,self)
if basetypeName:
self.component.basetypeName=basetypeName
else:
self.component.basetype=XMLSchema.urType
class contentElt(commonElt):
sub=None
restriction=None
extension=None
def init(self,elt):
if self.restriction is not None:
self.sub=self.restriction
elif self.extension is not None:
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=[]
init=None
class anyElt(commonElt):
namespace="##any"
minOccurs="1"
maxOccurs=None
processContents="strict"
def __init__(self,sschema,elt):
commonElt.__init__(self,sschema,elt)
def init(self,elt):
if self.maxOccurs=="0":
self.component=None
else:
self.component=XMLSchema.Particle(self.schema.sschema,self,
XMLSchema.RefineAny(self,
self.namespace))
def RefineAny(xrpr,namespace,extra=0):
if namespace=='##any':
anyinst=XMLSchema.AnyAny
elif namespace=='##other':
anyinst=XMLSchema.AnyOther
else:
anyinst=XMLSchema.AnyInList
return anyinst(xrpr.schema.sschema,xrpr,extra)
class Facet(commonElt):
# note this is schizo -- both elt and component
annotation=None
fixed=0
def __init__(self,sschema,elt):
commonElt.__init__(self,sschema,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'
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.sschema.eltStack[0]
if isinstance(parent,complexTypeElt) or isinstance(parent,nestingElt):
self.parent=parent
if self.ref is not None:
# 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 (self.name and (':' 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,sschema,elt):
defRefElt.__init__(self,sschema,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 self.__dict__.has_key(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=XMLSchema.Particle(self.schema.sschema,self,None)
self.component.termName=XMLSchema.QName(self.ref,self.elt,
self.schema.sschema)
self.component.termType='element'
def checkInternal(self):
# local def
if self.form is None:
self.form=self.schema.elementFormDefault
self.final=''
self.block=''
nElt=XMLSchema.Element(self.schema.sschema,self,self.parent)
if self.maxOccurs=="0":
self.component=None
else:
self.component=XMLSchema.Particle(self.schema.sschema,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=XMLSchema.Element(self.schema.sschema,self,'global')
def merge(self,other):
# called in content model restricting
shouldnt('merge3')
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))
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 is not None:
self.error("attribute with ref %s can't have type %s"%(self.ref,self.type))
self.type=None
elif self.simpleType is not None:
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=XMLSchema.AttributeUse(self.schema.sschema,self,None,
self.use or 'optional',vct,value)
self.component.attributeDeclarationName=XMLSchema.QName(self.ref,self.elt,
self.schema.sschema)
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 self.form is None:
self.form=self.schema.attributeFormDefault
nAttr=XMLSchema.Attribute(self.schema.sschema,self,self.parent)
self.component=XMLSchema.AttributeUse(self.schema.sschema,self,nAttr,
self.use or 'optional',vct,value)
def checkTop(self):
# top-level def
self.component=XMLSchema.Attribute(self.schema.sschema,self,'global')
class attributeGroupElt(defRefElt):
def __init__(self,sschema,elt):
defRefElt.__init__(self,sschema,elt)
sschema.eltStack[0:0]=[self]
self.attrs=[]
def init(self,elt):
self.schema.sschema.eltStack=self.schema.sschema.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=XMLSchema.AttributeUse(self.schema.sschema,self,None)
self.component.attributeDeclarationName=XMLSchema.QName(self.ref,self.elt,
self.schema.sschema)
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=XMLSchema.AttributeGroup(self.schema.sschema,self)
class explicitGroupElt(commonElt):
minOccurs=None
maxOccurs=None
def __init__(self,sschema,elt):
commonElt.__init__(self,sschema,elt)
self.model=[]
def init(self,elt):
if self.maxOccurs=="0":
self.component=None
else:
self.component=XMLSchema.Particle(self.schema.sschema,self,
self.compClass(self.schema.sschema,
self))
class allElt(explicitGroupElt):
pass
class choiceElt(explicitGroupElt):
pass
class sequenceElt(explicitGroupElt):
pass
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,sschema,elt):
defRefElt.__init__(self,sschema,elt)
sschema.eltStack[0:0]=[self]
self.model=[]
def init(self,elt):
self.schema.sschema.eltStack=self.schema.sschema.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=XMLSchema.Particle(self.schema.sschema,self,None)
self.component.termName=XMLSchema.QName(self.ref,self.elt,
self.schema.sschema)
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 is not None:
mod.name=self.name
# we're a mgd, hack to get two levels of reflection
mod.reflectedName=XMLSchema.ModelGroup.reflectedName
mod.reflectionMap=XMLSchema.ModelGroup.reflectionMap
self.component=mod
class includeElt(commonElt):
schemaLocation=None
def init(self,elt):
self.schema.sschema.checkinSchema(self.schema.targetNS,
self.schemaLocation,
self.schema.sschema.fileNames[0],
"include",0,0,elt)
class redefineElt(includeElt):
schemaLocation=None
def init(self,elt):
res=self.schema.sschema.checkinSchema(self.schema.targetNS,
self.schemaLocation,
self.schema.sschema.fileNames[0],
"redefine",0,0,elt)
if res is not None and self.__dict__.has_key('dds'):
for dd in self.dds:
if dd.name:
dd.component.redefine()
class importElt(commonElt):
schemaLocation=None
namespace=None
def init(self,elt):
# check now to avoid going to net if poss.
ss=self.schema.sschema
sct=ss.schemas
if sct.has_key(self.namespace):
other=sct[self.namespace]
ne=XML.Element("schemaDocAttempt")
ne.addAttr('source','import')
ne.addAttr('namespace',self.namespace)
fullLoc=urlparse.urljoin(ss.fileNames[0],
self.schemaLocation or self.namespace)
ne.addAttr('URI',fullLoc)
ss.resElt.children.append(ne)
if fullLoc in other.locations:
ne.addAttr('outcome','redundant') # i.e. we've seen this before
else:
ne.addAttr('outcome','skipped') # i.e. we chose to only pursue
# one file per import
ne.addAttr('otherLocs',string.join(other.locations,' '))
else:
ss.checkinSchema(self.namespace,
self.schemaLocation or self.namespace,
ss.fileNames[0],
"import",0,0,elt)
class notationElt(commonElt):
init=None
class uniqueElt(commonElt):
def init(self,elt):
self.component=XMLSchema.Unique(self.schema.sschema,self)
class keyrefElt(commonElt):
def init(self,elt):
self.component=XMLSchema.Keyref(self.schema.sschema,self)
class keyElt(commonElt):
def init(self,elt):
self.component=XMLSchema.Key(self.schema.sschema,self)
class xpathElt(commonElt):
# TODO: check syntax
def init(self,elt):
pass
class fieldElt(xpathElt):
cname='field'
class selectorElt(xpathElt):
cname='selector'
class anyAttributeElt(commonElt):
namespace="##any"
processContents="lax"
def init(self,elt):
self.component=XMLSchema.AttributeUse(self.schema.sschema,self,
XMLSchema.RefineAny(self,
self.namespace,
1),
'optional')
class annotationElt(commonElt):
documentation=[]
appinfo=[]
def init(self,elt):
self.component=XMLSchema.Annotation(self.schema.sschema,self)
class appinfoElt(commonElt):
init=None
class documentationElt(commonElt):
init=None
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))
# $Log: XMLSchemaElt.py,v $
# Revision 1.5 2002/06/12 18:46:20 ht
# restructure in preparation for allowing either normal form to be reflected
#
# Revision 1.4 2002/05/27 22:30:25 ht
# remove debuggin print,
# actually _use_ reloaded ind dump,
# handle esspecial properly
#
# Revision 1.3 2002/05/27 16:11:44 ht
# working on new restore
#
# Revision 1.2 2002/05/25 21:54:31 ht
# make new sources split actually work
#
# Revision 1.1 2002/05/24 22:33:14 ht
# split out of XMLSchema
#
Webmaster