File:  [Public] / xmlschema / XMLSchema.py
Revision 1.190: download - view: text, annotated - select for diffs
Sat May 12 14:20:48 2001 UTC (23 years, 1 month ago) by ht
Branches: MAIN
CVS tags: HEAD
fix old (?) bug in home location for compiled version

# Copyright (C) 2000 LTG -- See accompanying COPYRIGHT and COPYING files
# Implementation of XML Schema on top of layer.py
# $Id: XMLSchema.py,v 1.190 2001/05/12 14:20:48 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.190 $ of $Date: 2001/05/12 14:20:48 $")
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 <prop>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 <include>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<!ENTITY %"+" s ':%s'>\n<!ENTITY %% p '%s:'>"%(scp,scp)
            else:
              pref=""
              pds="\n<!ENTITY % s ''>\n<!ENTITY % p ''>"
          else:
            pds=""
            pref="xs:"
          ns=""
          for p in prefs:
            if p:
              ns=ns+"\n<!ATTLIST %sschema xmlns:%s CDATA #IMPLIED>"%(pref,p)
          home='.'
          for p in sys.path:
            if os.path.isfile("%s/%s"%(p or '.','XMLSchema.dtd')):
              home=p or '.'
              break
          home = os.path.abspath(home)
          home=string.replace(home,"\\","/")
          if home[1]==':':
            home='file:///'+home
          if pds or ns or intDecls:
            fake1="<!DOCTYPE %sschema SYSTEM '%s/XMLSchema.dtd'"%(pref, home)
            fake2=" [%s"%pds;
            fake3="%s\n%s]>\n"%(ns, intDecls)
            fake=fake1+fake2+fake3
          else:
            fake="<!DOCTYPE %sschema SYSTEM '%s/XMLSchema.dtd'>\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'),
                 ('facets','special',0,'facetsReflect'),
                 # XXX
                 ('fundamentalFacets','special',0, 'fundamentalFacetsReflect'),
                 # XXX
                 ('final','special',0,'finalReflect'),
                 ('variety','string',0,'variety'),
                 ('primitiveTypeDefinition','special',
                  1,'primitiveTypeReflect'),
                 ('itemTypeDefinition','component',1,'itemType'),
                 ('memberTypeDefinitions','components',1,'memberTypes'),
                 ('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)
    elif self.derivationMethod=='restriction':
      mine=Particle(self.factory,None,
                    Sequence(self.factory,None,self.xrpr))
      mine.occurs=(1,1)
      mine.particles=[]
    else:
      return other
    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<old):
      ok=0
    else:
      old=getattr(self,otherName)
      if (old!=None and
	  ((facetName=='minInclusive' and new<=old) or
	   (facetName=='minExclusive' and new<old))):
	ok=0
    if not ok:
      td.error("attempt to reduce range lower bound from %s%d to %s%d"%(o,old,b,new))
      return
    # check against max -- ?? -- doesn't actually say anywhere . . .
    # For now I declare that an empty range, e.g. (3,4) is OK but an
    # incoherent one, e.g. (3,3) or [3,2] is not
    # Note that what consititutes an empty range depends on fractionDigits
    # I'm not sure what follows is correct, done when tired
    # [1.1,1.1] - OK  [1.1,1.1) - no (1.1,1.1] - no (1.1,1.1) no
    max=self.facetValue('maxExclusive')
    if max!=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'),
                 ('attributeDeclaration','component',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
  dummy=None # XXX for wildcard reflection, remove when implemented
  reflectedName='attributeGroupDefinition'
  reflectionMap=(('name','string',0,'name'),
                 ('targetNamespace','string',1,'targetNamespace'),
                 ('attributeUses','components',0, 'attributeDeclarations'),
                 ('attributeWildcard', 'component', 1, 'dummy'), # XXX not done
                 ('annotation','component',1,'annotation'))

  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
    # <schema> so that we can check that there was an import in that
    # very <schema>.

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):
  reflectedName='identityConstraintDefinition'
  reflectionMap=(('name','string',0,'name'),
                 ('targetNamespace','string',1,'targetNamespace'),
                 ('identityConstraintCategory','string',0,'cname'),
                 ('selector','special',0,'selectorReflect'),
                 ('fields','special',0,'fieldsReflect'),
                 ('referencedKey','component',1,'refer'),
                 ('annotation','component',1,'annotation'))
  refer=None
  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'

class uniqueElt(commonElt):
  def init(self,elt):
    self.component=Unique(self.schema.factory,self)

class Keyref(Kcons):
  cname='keyref'
  reflectionMap=(('name','string',0,'name'),
                 ('targetNamespace','string',1,'targetNamespace'),
                 ('identityConstraintCategory','string',0,'cname'),
                 ('selector','special',0,'selectorReflect'),
                 ('fields','special',0,'fieldsReflect'),
                 ('referencedKey','special',0,'referReflect'),
                 ('annotation','component',1,'annotation'))
  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'

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.190  2001/05/12 14:20:48  ht
# fix old (?) bug in home location for compiled version
#
# Revision 1.189  2001/04/27 16:00:53  ht
# fix extend-by-empty pblm
#
# Revision 1.188  2001/04/25 17:01:23  richard
# more reflection
#
# Revision 1.187  2001/04/24 14:13:25  ht
# towards reflection of identity constraint
#
# Revision 1.186  2001/04/24 13:29:13  ht
# (PSV)Infoset reorganisation
#
# 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 <included> 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 <import>
#
# 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 <any> 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 <any> (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
#


Webmaster