# VRML node prototype class (SGbuilder)
# Wed Oct 31 16:18:35 CET 2001

'''Prototype2 -- VRML 97 sceneGraph/Node/Script/ROUTE/IS implementations'''
import copy, types # extern
import strop as string # builtin
from utils import typeclasses, err, namespace # XXX
## TODO: namespace must go


class baseProto:
	def __vrmlStr__( self, **namedargs ):
		'''Generate a VRML 97-syntax string representing this Prototype
		**namedargs -- key:value
			passed arguments for the linearisation object
		see lineariser4.Lineariser
		'''
		import lineariser4 
		lineariser = apply( lineariser4.Lineariser, (), namedargs )
		return apply( lineariser.linear, ( self, ), namedargs )

	toString = __vrmlStr__ 
	# added stuff for linking support for target scenegraph
	def setTargetnode(self, node):
		self.__dict__['_targetnode'] = node
	def getTargetnode(self):
		try:
			return self.__dict__['_targetnode']
		except:
			return None

class Prototype(baseProto):
	''' A VRML 97 Prototype object
	
	A Prototype is a callable object which produces Node instances
	the Node uses a pointer to its Prototype to provide much of the
	Node's standard functionality.

	Prototype's are often stored in a sceneGraph's protoTypes namespace,
	where you can access them as sceneGraph.protoTypes.nodeGI . They are
	also commonly found in Nodes' PROTO attributes.
	
	Attributes:
		__gi__ -- constant string "PROTO"
		nodeGI -- string gi
			The "generic identifier" of the node type, i.e. the name of the node
		fieldDictionary -- string name: (string name, string dataType, boolean exposed)
		defaultDictionary -- string name: object defaultValue
			Will be blank for EXTERNPROTO's and Script prototypes
		eventDictionary -- string name: (string name, string dataType, boolean eventOut)
		sceneGraph -- object sceneGraph
		MFNodeNames -- list of field name strings
			Allows for easy calculation of "children" nodes
		SFNodeNames -- list of field name strings
			Allows for easy calculation of "children" nodes
	'''
	__gi__ = "PROTO"
	def __init__(self, gi, fieldDict=None, defaultDict=None, eventDict=None, sGraph=None):
		'''
		gi -- string gi
			see attribute nodeGI
		fieldDict -- string name: (string name, string dataType, boolean exposed)
			see attribute fieldDictionary
		defaultDict -- string name: object defaultValue
			see attribute defaultDictionary
		eventDict -- string name: (string name, string dataType, boolean eventOut)
			see attribute eventDictionary
		sceneGraph -- object sceneGraph
			see attribute sceneGraph
		'''
		self.nodeGI = checkName( gi )
		self.fieldDictionary = {}
		self.defaultDictionary = {}
		self.eventDictionary = {}
		self.SFNodeNames = []
		self.MFNodeNames = []
		self.sceneGraph = sGraph

		# setup the fields/events
		for definition in (fieldDict or {}).values():
			self.addField( definition, (defaultDict or {}).get( definition[0]))
		for definition in (eventDict or {}).values():
			self.addEvent( definition )
			
	def getSceneGraph( self ):
		''' Retrieve the sceneGraph object (may be None object)
		see attribute sceneGraph'''
		return self.sceneGraph
	def setSceneGraph( self, sceneGraph ):
		''' Set the sceneGraph object (may be None object)
		see attribute sceneGraph'''
		self.sceneGraph = sceneGraph
	def getChildren(self, includeSceneGraph=None, includeDefaults=1, *args, **namedargs):
		''' Calculate the current children of the PROTO and return as a list of nodes
		if includeDefaults:
			include those default values which are node values
		if includeSceneGraph:
			include the sceneGraph object if it is not None

		see attribute MFNodeNames
		see attribute SFNodeNames
		see attribute sceneGraph
		'''
		temp = []
		if includeDefaults:
			for attrname in self.SFNodeNames:
				try:
					temp.append( self.defaultDictionary[attrname] )
				except KeyError: # sceneGraph object is not copied...
					pass
			for attrname in self.MFNodeNames:
				try:
					temp[len(temp):] = self.defaultDictionary[attrname]
				except KeyError:
					pass
		if includeSceneGraph and self.sceneGraph:
			temp.append( self.getSceneGraph() )
		return temp
	def addField (self, definition, default = None):
		''' Add a single field definition to the Prototype
		definition -- (string name, string dataType, boolean exposed)
		default -- object defaultValue
		
		see attribute fieldDictionary
		see attribute defaultDictionary
		'''
		if type (definition) == types.InstanceType:
			definition = definition.getDefinition()
			default = definition.getDefault ()
		self.removeField( definition[0] )
		self.fieldDictionary[definition [0]] = definition
		if default is not None:
			default = fieldcoercian.FieldCoercian()( default, definition[1] )
			self.defaultDictionary [definition [0]] = default
		if definition[1] == 'SFNode':
			self.SFNodeNames.append(definition[0])
		elif definition[1] == 'MFNode':
			self.MFNodeNames.append(definition[0])
	def removeField (self, key):
		''' Remove a single field from the Prototype
		key -- string fieldName
			The name of the field to remove
		'''
		if self.fieldDictionary.has_key (key):
			del self.fieldDictionary [key]
		if self.defaultDictionary.has_key (key):
			del self.defaultDictionary [key]
		for attribute in (self.SFNodeNames, self.MFNodeNames):
			while key in attribute:
				attribute.remove(key)
	def addEvent(self, definition):
		''' Add a single event definition to the Prototype
		definition -- (string name, string dataType, boolean eventOut)
		
		see attribute eventDictionary
		'''
		if type (definition) == types.InstanceType:
			definition = definition.getDefinition()
		self.eventDictionary[definition [0]] = definition
	def removeEvent(self, key):
		''' Remove a single event from the Prototype
		key -- string eventName
			The name of the event to remove
		'''
		if self.eventDictionary.has_key (key):
			del self.eventDictionary [key]
	def getField( self, key ):
		'''Return a Field or Event object representing a given name
		key -- string name
			The name of the field or event to retrieve
			will attempt to match key, key[4:], and key [:-8]
			corresponding to key, set_key and key_changed

		see class Field
		see class Event
		'''
#		print self.fieldDictionary, self.eventDictionary
		for tempkey in (key, key[4:], key[:-8]):
			if self.fieldDictionary.has_key( tempkey ):
				return Field( self.fieldDictionary[tempkey], self.defaultDictionary.get(tempkey) )
			elif self.eventDictionary.has_key( tempkey ):
				return Event( self.eventDictionary[tempkey] )
		raise AttributeError, key
	def getDefault( self, key ):
		'''Return the default value for the given field
		key -- string name
			The name of the field
			Will attempt to match key, key[4:], and key [:-8]
			corresponding to key, set_key and key_changed

		see attribute defaultDictionary
		'''
		for key in (key, key[4:], key[:-8]):
			if self.defaultDictionary.has_key( key ):
				val = self.defaultDictionary[key]
				if type(val) in typeclasses.MutableTypes:
					val = copy.deepcopy( val )
				return val
			elif self.fieldDictionary.has_key( key ):
				'''We have the field, but we don't have a default, we are likely an EXTERNPROTO'''
				return None
		raise AttributeError, key
	def setDefault (self, key, value):
		'''Set the default value for the given field
		key -- string name
			The name of the field to set
		value -- object defaultValue
			The default value, will be checked for type and coerced if necessary
		'''
		field = self.getField (key)
		self.defaultDictionary [field.name]= field.coerce (value)
	def clone( self, children = 1, sceneGraph = 1 ):
		'''Return a copy of this Prototype
		children -- boolean
			if true, copy the children of the Prototype, otherwise include them
		sceneGraph -- boolean
			if true, copy the sceneGraph of the Prototype
		'''
		if sceneGraph:
			sceneGraph = self.sceneGraph
		else:
			sceneGraph = None
		# defaults should always be copied before modification, but this is still dangerous...
		defaultDictionary = self.defaultDictionary.copy()
		if not children:
			for attrname in self.SFNodeNames+self.MFNodeNames:
				try:
					del defaultDictionary[attrname]
				except KeyError: # sceneGraph object is not copied...
					pass
		# now make a copy
		if self.__gi__ == "PROTO":
			newNode = self.__class__(
				self.nodeGI,
				self.fieldDictionary,
				defaultDictionary,
				self.eventDictionary,
				sceneGraph,
			)
		else:
			newNode = self.__class__(
				self.nodeGI,
				self.url,
				self.fieldDictionary,
				self.eventDictionary,
			)
		return newNode
	def __call__(self, *args, **namedargs):
		'''Create a new Node instance associated with this Prototype
		*args, **namedargs -- passed to the Node.__init__
		see class Node
		'''
		node = apply( Node, (self, )+args, namedargs )
		return node
	def __repr__ ( self ):
		'''Create a simple Python representation'''
		return '''%s( %s )'''%( self.__class__.__name__, self.nodeGI )

class ExternalPrototype( Prototype ):
	'''Sub-class of Prototype

	The ExternalPrototype is a minor sub-classing of the Prototype
	it does not have any defaults, nor a sceneGraph
	
	Attributes:
		__gi__ -- constant string "EXTERNPROTO"
		url -- string list urls
			implementation source for the ExternalPrototype
	'''
	__gi__ = "EXTERNPROTO"
	def __init__(self, gi, url=None, fieldDict=None, eventDict=None):
		'''
		gi -- string gi
			see attribute nodeGI
		url -- string list url
			MFString-compatible list of url's for EXTERNPROTO
		fieldDict -- string name: (string name, string dataType, boolean exposed)
			see attribute fieldDictionary
		eventDict -- string name: (string name, string dataType, boolean eventOut)
			see attribute eventDictionary
		'''
		if url is None:
			url = []
		self.url = url
		Prototype.__init__( self, gi, fieldDict=fieldDict, eventDict=eventDict)
	

from vrml import fieldcoercian # XXX
class Field:
	''' Representation of a Prototype Field
	The Field object is a simple wrapper to provide convenient
	access to field coercian and meta- information
	'''
	def __init__( self, specification, default=None ):
		self.name, self.type, self.exposure = specification
		self.default = default
	def getDefinition (self):
		return self.name, self.type, self.exposure
	def getDefault (self):
		return self.default
	def coerce( self, value ):
		''' Coerce value to the appropriate dataType for this Field '''
		return fieldcoercian.FieldCoercian()( value,self.type,  )
	def __repr__( self ):
		if hasattr (self, "default"):
			return '%s( (%s,%s,%s), %s)'%( self.__class__.__name__, self.name, self.type, self.exposure, self.default)
		else:
			return '%s( (%s,%s,%s),)'%( self.__class__.__name__, self.name, self.type, self.exposure)
	def __str__( self ):
		if self.exposure:
			exposed = "exposedField"
		else:
			exposed = field
		if hasattr (self, "default"):
			default = ' ' + str( self.default)
		else:
			default = ""
		return '%s %s %s%s'%(exposed, self.type, self.name, default)

class Event (Field):
	def __str__( self ):
		if self.exposure:
			exposed = "eventOut"
		else:
			exposed = "eventIn"
		return '%s %s %s'%(exposed, self.type, self.name)


### Translation strings for VRML node names...
translationstring = '''][0123456789{}"'#,.\\ \000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023'''
NAMEFIRSTCHARTRANSLATOR = string.maketrans( translationstring, '_'*len(translationstring) )
translationstring = '''][{}"'#,.\\ \000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023'''
NAMERESTCHARTRANSLATOR = string.maketrans( translationstring, '_'*len(translationstring) )
del translationstring
def checkName( name ):
	'''Convert arbitrary string to a valid VRML id'''
	if type(name) is types.StringType:
		if not name:
			return name
		return string.translate( name[:1], NAMEFIRSTCHARTRANSLATOR) + string.translate( name[1:], NAMERESTCHARTRANSLATOR)
	else:
		raise TypeError, "VRML Node Name must be a string, was a %s: %s"%(type(name), name)

class Node(baseProto):
	''' A VRML 97 Node object
	
	A Node object represents a VRML 97 node.  Attributes of the Node
	can be set/retrieved with standard python setattr/getattr syntax.
	VRML 97 attributes may be passed to the constructor as named
	arguments.
	
	Attributes:
		__gi__ -- string PROTOname
		DEF -- string DEFName
			The DEF name of the node, will be coerced to be a valid
			identifier (with "" being considered valid)
		PROTO -- Prototype PROTO
			The node's Prototype object
		attributeDictionary -- string name: object value
			Dictionary in which VRML 97 attributes are stored
	'''
	DEF = '' # the default name for all nodes (arbitrary)
	def __init__(self, PROTO, name='', attrDict=None, *args, **namedargs):
		'''Normally this method is only called indirectly via the Prototype() interface
		PROTO -- Prototype PROTO
			see attribute PROTO
		name -- string DEFName
			see attribute DEF
		attrDict -- string name: object value
			see attribute attributeDictionary
		**namedargs -- string name: object value
			added to attrDict to create attributeDictionary
		'''
		self.__dict__["PROTO"] = PROTO
		self.DEF = name
		self.__dict__["attributeDictionary"] = {}
##		print attrDict, namedargs
		for dict in (attrDict or {}), namedargs:
			if dict:
				for key, value in dict.items ():
					self.__setattr__( key, value, check=1 )

	def __setattr__( self, key, value, check=1, raw=0 ):
		'''Set attribute on Node
		key -- string attributeName
		value -- object attributeValue
		check -- boolean check
			if false, put values for unrecognized keys into __dict__
			otherwise, raise an  AttributeError
		'''
		if key == "DEF":
			self.__dict__["DEF"] = checkName( value )
			return None
		elif key == "PROTO":
			self.__dict__["PROTO"] = value
		try:
			field = self.PROTO.getField( key )
			if (hasattr( value, "__gi__") and value.__gi__ == "IS") or raw:
				self.attributeDictionary[ field.name] = value
			else:
				self.attributeDictionary[ field.name] = field.coerce( value )
		except ValueError, x:
			raise ValueError( "Could not coerce value %s into value of VRML type %s for %s node %s's field %s"%( value, field.type, self.__gi__, self.DEF, key), x.args)
		except (AttributeError), x:
			if check:
				raise AttributeError("%s is not a known field for node %s"%(key, repr(self)))
			else:
				self.__dict__[key] = value
	def __getattr__( self, key, default = 1 ):
		''' Retrieve an attribute when standard lookup fails
		key -- string attributeName
		default -- boolean default
			if true, return the default value if the node does not have local value
			otherwise, raise AttributeError
		'''
		if key != "attributeDictionary":
			if self.__dict__.has_key( key):
				return self.__dict__[ key ]
			elif self.attributeDictionary.has_key( key):
				return self.attributeDictionary[key]
			if key != "PROTO":
				if key == "__gi__":
					return self.PROTO.nodeGI
				elif default:
					try:
						default = self.PROTO.getDefault( key )
						if type( default ) in typeclasses.MutableTypes:
							# we need a copy, not the original
							default = copy.deepcopy( default )
							self.__setattr__( key, default, check=0, raw=1 )
						return default
					except AttributeError:
						pass
		raise AttributeError, key
	def __delattr__( self, key ):
		''' Delete an attribute from the Node
		key -- string attributeName
		'''
		if key != "attributeDictionary":
			if self.attributeDictionary.has_key( key):
				del self.attributeDictionary[key]
			elif self.__dict__.has_key( key):
				del self.__dict__[ key ]
		raise AttributeError, key
			
	def __repr__(self):
		''' Create simple python representation '''
		return '<%s(%s): %s>'%(self.__gi__, `self.DEF`, self.attributeDictionary.keys() )
	def getChildrenNames( self, current = 1, *args, **namedargs ):
		''' Get the (current) children of Node
		returns two lists:  MFNode children, SFNode children
		current -- boolean currentOnly
			if true, only return current children
			otherwise, include all potential children
		'''
		MFNODES, SFNODES = self.PROTO.MFNodeNames, self.PROTO.SFNodeNames
		mns, sns = [],[]
		for key in MFNODES:
			if current and self.attributeDictionary.has_key(key):
				mns.append(key)
			elif not current:
				mns.append(key)
		for key in SFNODES:
			if self.attributeDictionary.has_key(key):
				sns.append(key)
			elif not current:
				sns.append(key)
		return mns,sns
	def calculateChildren(self, *args, **namedargs):
		'''Calculate the current children of the Node as list of Nodes
		'''
		MFNODES, SFNODES = self.getChildrenNames( )
		temp = []
		for key in MFNODES:
			try:
				temp.extend( self.__getattr__( key, default=0 ) )
			except AttributeError:
				pass
		for key in SFNODES:
			try:
				temp.append( self.__getattr__(key, default = 0 ) )
			except AttributeError:
				pass
		return temp
	def clone(self, newclass=None, name=None, children=None, attrDeepCopy=1, *args, **namedargs):
		'''Return a copy of this Node
		newclass -- object newClass  or None
			optionally use a different Prototype as base
		name -- string DEFName or None or 1
			if 1, copy from current
			elif None, set to ""
			else, set to passed value
		children -- boolean copyChildren
			if true, copy the children of this node
			otherwise, skip children
		attrDeepCopy -- boolean deepCopy
			if true, use deepcopy
			otherwise, use copy
		'''
		if attrDeepCopy:
			cpy = copy.deepcopy
		else:
			cpy = copy.copy
		newattrs = self.attributeDictionary.copy()
		if not children:
			mnames,snames = self.getChildrenNames( )
			for key in mnames+snames:
				try:
					del(newattrs[key])
				except KeyError:
					pass
		for key, val in newattrs.items():
			if type(val) in typeclasses.MutableTypes:
				newattrs[key] = cpy(val)
		# following is Node specific, won't work for sceneGraphs, scripts, etceteras
		if name == 1: # asked to copy the name
			name = self.DEF
		elif name is None: # asked to clear the name
			name = ''
		if not newclass:
			newclass = self.PROTO
		return newclass( name, newattrs )
	def __cmp__( self, other, stop=None ):
		''' Compare this node to another object/node
		other -- object otherNode
		stop -- boolean stopIfFailure
			if true, failure to find comparison causes match failure (i.e. considered unequal)
		'''
			
		if hasattr( other, '__gi__') and other.__gi__ == self.__gi__:
			try:
				return cmp( self.DEF, other.DEF) or cmp( self.attributeDictionary, other.attributeDictionary )
			except:
				if not stop:
					try:
						return other.__cmp__( self , 1) # 1 being stop...
					except:
						pass
		return -1 # could be one, doesn't really matter

def Script( name="", attrDict=None, fieldDict=None, defaultDict=None, eventDict=None, **namedarguments):
	''' Create a script node (and associated prototype)
	name -- string DEFName
	attrDict -- string name: object value
		see class Node.attributeDictionary
	fieldDict -- string name: (string name, string dataType,  boolean exposure)
		see class Prototype.fieldDictionary
	defaultDict -- string name: object value
		see class Prototype.defaultDictionary
	eventDict -- string name: (string name, string dataType, boolean eventOut)
	'''
	fieldDictionary = {
		'directOutput':('directOutput', 'SFBool',0),
		'url':('url',"MFString",0),
		'mustEvaluate':('mustEvaluate', 'SFBool',0),
	}
	fieldDictionary.update( fieldDict or {})
	defaultDictionary = {
		"directOutput":0,
		"url":[],
		"mustEvaluate":0,
	}
	defaultDictionary.update( defaultDict or {})
	PROTO = Prototype(
		"Script",
		fieldDictionary,
		defaultDictionary ,
		eventDict = eventDict,
	)
	if attrDict is not None:
		attrDict.update( namedarguments )
	else:
		attrDict = namedarguments
	return PROTO( name, attrDict )


class NullNode:
	'''NULL SFNode value
	There should only be a single NULL instance for
	any particular system.  It should, for all intents and
	purposes just sit there inertly
	'''
	__gi__ = 'NULL'
	DEF = ''
	__walker_is_temporary_item__ = 1 # hacky signal to walking engine not to reject this node as already processed
	def __repr__(self):
		return '<NULL vrml SFNode>'
	def __vrmlStr__(self,*args,**namedargs):
		return ' NULL '
	toString = __vrmlStr__
	def __nonzero__(self ):
		return 0
	def __call__(self, *args, **namedargs):
		return self
	def __cmp__( self, other ):
		if hasattr( other, '__gi__') and other.__gi__ == self.__gi__:
			return 0
		return -1 # could be one, doesn't really matter
	def clone( self ):
		return self
NULL = NullNode()
	
class fieldRef:
	'''IS Prototype field reference
	'''
	__gi__ = 'IS'
	DEF = ''
	def __init__(self, declaredName):
		self.declaredName = declaredName
	def __repr__(self):
		return 'IS %s'%self.declaredName
	def __vrmlStr__(self,*args,**namedargs):
		return 'IS %s'%self.declaredName
	toString = __vrmlStr__
	def __cmp__( self, other ):
		if hasattr( other, '__gi__') and other.__gi__ == self.__gi__:
			return cmp( self.declaredName, other.declaredName )
		return -1 # could be one, doesn't really matter
	def clone( self ):
		return self.__class__( self.declaredName )
		
IS = fieldRef

class ROUTE:
	''' VRML 97 ROUTE object
	The ROUTE object keeps track of its source and destination nodes and attributes
	It generally lives in a sceneGraph's "routes" collection
	'''
	__gi__ = 'ROUTE'
	def __init__( self, fromNode, fromField, toNode, toField ):
		if type(fromNode) is types.StringType:
			raise TypeError( "String value for ROUTE fromNode",fromNode)
		if type(toNode) is types.StringType:
			raise TypeError( "String value for ROUTE toNode",toNode)
		self.fromNode = fromNode
		self.fromField = fromField
		self.toNode = toNode
		self.toField = toField
	def __getitem__( self, index ):
		return (self.fromNode, self.fromField, self.toNode, self.toField)[index]
	def __setitem__( self, index, value ):
		attribute = ("fromNode","fromField","toNode", "toField")[index]
		setattr( self, attribute, value )
	def __repr__( self ):
		return 'ROUTE %s.%s TO %s.%s'%( self.fromNode.DEF, self.fromField, self.toNode.DEF, self.toField )
	def clone( self ):
		return self.__class__( 
			self.fromNode,
			self.fromField,
			self.toNode,
			self.toField,
		)


class sceneGraph(baseProto):
	''' A VRML 97 sceneGraph
	Attributes:
		__gi__ -- constant string "sceneGraph"
		DEF -- constant string ""
		children -- Node list
			List of the root children of the sceneGraph, nodes/scripts only
		routes -- ROUTE list
			List of the routes within the sceneGraph
		defNames -- string DEFName: Node node
			Mapping of DEF names to their respective nodes
		protoTypes -- Namespace prototypes
			Namespace (with chaining lookup) collection of prototypes
			getattr( sceneGraph.protoTypes, 'nodeGI' ) retrieves a prototype
	'''
	__gi__ = 'sceneGraph'
	DEF = ''
	def __init__(self, root=None, protoTypes=None, routes=None, defNames=None, children=None, *args, **namedargs):
		'''
		root -- sceneGraph root or Dictionary root or Module root or None
			Base object for root of protoType namespace hierarchy
		protoTypes -- string nodeGI: Prototype PROTO
			Dictionary of prototype definitions
		routes -- ROUTE list or (string sourcenode, string sourceeventOut, string destinationnode, string destinationeventOut) list
			List of route objects or tuples to be added to the sceneGraph
			see attribute routes
		defNames -- string DEFName: Node node
			see attribute defNames
		children -- Node list
			see attribute children
		'''
		if children is None:
			self.children = []
		else:
			self.children = children
		if routes is None:
			self.routes = [] # how will we efficiently handle routes?
		else:
			self.routes = routes
		if defNames == None:
			self.defNames = {} # maps 'defName':Node
		else:
			self.defNames = defNames
		if protoTypes is None:
			protoTypes = {}
		if root is None:
			from vrml import basenodes # XXX
			self.protoTypes = namespace.NameSpace(
				protoTypes,
				children = [namespace.NameSpace(basenodes)]
			)
		else: # there is a root file, so need to use it as the children instead of basenodes...
			if hasattr( root, "protoTypes"):
				self.protoTypes = namespace.NameSpace(
					protoTypes,
					children = [root.protoTypes]
				)
			else:
				self.protoTypes = namespace.NameSpace(
					protoTypes,
					children = [ namespace.NameSpace(root) ]
				)
	def __getinitargs__( self ):
		# we only copy our explicit protos, our routes, our defNames, and our children
		# inherited protos will be pulled along by their nodes...
		return None, self.protoTypes._base, self.routes, self.defNames, self.children
	def __getstate__( self ):
		return {}
	def __setstate__( self, dict ):
		pass
	def __del__( self, id=id ):
		'''
		Need to clean up the namespace's mutual references,
		this can be done without affecting the cascade by just
		eliminating the key/value pairs.  The namespaces will
		no longer contain the prototypes, but they will still
		chain up to the higher-level namespaces, and the nodes
		will have those prototypes still in use.
		'''
##		print 'del sceneGraph', id(self )
		try:
##			import pdb
##			pdb.set_trace()
##			self.protoTypes.__dict__.clear()
			self.protoTypes._base.clear()
			del self.protoTypes.__namespace_cascade__[:]
		except:
			print 'unable to free references'
		
	def addRoute(self, routeTuple, getNewNodes=0):
		''' Add a single route to the sceneGraph
		routeTuple -- ROUTE route or (string sourcenode, string sourceeventOut, string destinationnode, string destinationeventOut)
		getNewNodes -- boolean getNewNodes
			if true, look up sourcenode and destinationnode within the current defNames to determine source/destination nodes
			otherwise, just use current if available
		'''
		# create and wire together the Routes here,
		# should just be a matter of pulling the events and passing the nodes...
##		import pdb
##		pdb.set_trace()
		if type( routeTuple) in ( types.TupleType, types.ListType):
			(fromNode, fromField, toNode, toField ) = routeTuple
			if type(fromNode) is types.StringType:
				# get the node instead of the string...
				if self.defNames.has_key( fromNode ):
					fromNode = self.defNames[fromNode]
				else:
					err.err( "ROUTE from an unknown node %s "%(routeTuple) )
					return 0
			if type(toNode) is types.StringType:
				# get the node instead of the string...
				if self.defNames.has_key( toNode ):
					toNode = self.defNames[toNode]
				else:
					err.err( "ROUTE to an unknown node %s "%(routeTuple) )
					return 0
			routeTuple = ROUTE( fromNode, fromField, toNode, toField)
		elif getNewNodes:
			# get the nodes with the same names...
			if self.defNames.has_key( routeTuple[0].DEF ):
				routeTuple[0] = self.defNames[routeTuple[0].DEF]
			else:
				err.err( "ROUTE from an unknown node %s "%(routeTuple) )
				return 0
			if self.defNames.has_key( routeTuple[2].DEF ):
				routeTuple[2] = self.defNames[routeTuple[2].DEF]
			else:
				err.err( "ROUTE to an unknown node %s "%(routeTuple) )
				return 0
		# should be a Route node now, append to our ROUTE list...
		self.routes.append(routeTuple)
		return 1
	def regDefName(self, defName, object):
		''' Register a DEF name for a particular object
		defName -- string DEFName
		object -- Node node
		'''
		object.DEF = defName
		self.defNames[defName] = object
	def addProto(self, proto):
		'''Register a Prototype for this sceneGraph
		proto -- Prototype PROTO
		'''
		setattr( self.protoTypes, proto.__gi__, proto )
	#toString = __vrmlStr__ 
	#__vrmlStr__ = toString
##	def __setattr__( self, key, value ):
##		if key == 'protoTypes' and type( value) is types.ListType:
##			import pdb
##			pdb.set_trace()
##			raise TypeError( "Invalid type for protoTypes attribute of sceneGraph %s"%(`value`) )
##		else:
##			self.__dict__[key] = value

DEFAULTFIELDVALUES ={
	"SFBool": 0,
	"SFString": "",
	"SFFloat": 0,
	"SFTime": 0,
	"SFVec3f": (0, 0,0),
	"SFVec2f": (0,0),
	"SFRotation": (0, 1,0, 0),
	"SFInt32": 0,
	"SFImage": (0,0,0),
	"SFColor": (0,0, 0),
	"SFNode": NULL,
	"MFString": [],
	"MFFloat": [],
	"MFTime": [],
	"MFVec3f": [],
	"MFVec2f": [],
	"MFRotation": [],
	"MFInt32": [],
	"MFColor": [],
	"MFNode": [],
}



