"======================================================================
|
|   Smalltalk Document Object Model
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright (c) 1998-1999 InDelv Inc.
| Written by InDelv Inc.
|
| This file is part of the InDelv Document Object Model.
|
| The InDelv Document Object Model for GNU Smalltalk is free software;
| you can redistribute it and/or modify it under the terms of the GNU
| Lesser General Public License as published by the Free Software
| Foundation; either version 2.1, or (at your option) any later version.
|
| The InDelv Document Object Model for GNU Smalltalk is distributed in
| the hope that it will be useful, but WITHOUT ANY WARRANTY; without
| even the implied warranty of MERCHANTABILITY or FITNESS FOR A
| PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
| more details.
|
| You should have received a copy of the GNU Lesser General Public
| License along with The InDelv Document Object Model for GNU
| Smalltalk; see the file COPYING.LESSER. If not, you can download it
| from http://www.gnu.org/copyleft/lgpl.txt
|
 ======================================================================"


IdObject subclass: #IdDOMImpl
	instanceVariableNames: ''
	classVariableNames: 'GlobalImplementation '
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdObject subclass: #IdNodeCollection
	instanceVariableNames: 'nodes '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdObject subclass: #IdNodeImpl
	instanceVariableNames: 'parent lock '
	classVariableNames: 'EmptyChildCollection '
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdObject subclass: #IdFilterIterator
	instanceVariableNames: 'nodeFilter '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Level 2'
!

IdObject subclass: #IdSearchFilter
	instanceVariableNames: 'pattern startIndex stopIndex '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Level 2'
!

IdObject subclass: #IdDOMConstants
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdException subclass: #IdDOMException
	instanceVariableNames: 'code message '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdObject subclass: #IdPlatformUtilities
	instanceVariableNames: ''
	classVariableNames: 'WorkingDirectory '
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdObject subclass: #IdDocumentAssembler
	instanceVariableNames: 'currentParent document '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdFunctionsDOM
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdHierarchyPoint
	instanceVariableNames: 'offset offsetTrail '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdIndexVector
	instanceVariableNames: 'indexList '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdNodeVisitorBase
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdRangeImpl
	instanceVariableNames: 'start end '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdSAXHandlerBase subclass: #IdXMLReader
	instanceVariableNames: 'assembler charCount wsStack wsOn wsPreserve entityResolver '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdSingleNodeList
	instanceVariableNames: 'childNode '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdXMLWriter
	instanceVariableNames: 'options writeStream currentIndent isInBody charCount lineNum lineNumStack '
	classVariableNames: 'AposChar ExclChar '
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdObject subclass: #IdXMLWriterOptions
	instanceVariableNames: 'indentSize emptyTagSpacing includingVersion maxLineLength isXhtml '
	classVariableNames: 'DefaultIndentSize '
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdNodeCollection subclass: #IdNamedNodeList
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdNodeCollection subclass: #IdNodeListImpl
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdNodeImpl subclass: #IdAttributeImpl
	instanceVariableNames: 'name value specified '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdNodeImpl subclass: #IdDocumentFragmentImpl
	instanceVariableNames: 'masterDocument childNodes '
	classVariableNames: 'DefaultListSize '
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdNodeImpl subclass: #IdElementNode
	instanceVariableNames: 'attributes tagName '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdNodeImpl subclass: #IdStringNode
	instanceVariableNames: 'data nodeType '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdFilterIterator subclass: #IdNodeSequenceIterator
	instanceVariableNames: 'gapPosition sequence '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Level 2'
!

IdDOMException subclass: #IdDOMExceptionImpl
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdHierarchyPoint subclass: #IdRangeEndPoint
	instanceVariableNames: 'parent '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdNodeVisitorBase subclass: #IdNodeQuery
	instanceVariableNames: 'resultNodes queryType matchString '
	classVariableNames: 'AllNames Ancestors ChildrenByName Descendants '
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdNodeVisitorBase subclass: #IdTextNodeVisitor
	instanceVariableNames: 'reverse '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!

IdAttributeImpl subclass: #IdQNameAttribute
	instanceVariableNames: 'localNameOffset '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdDocumentFragmentImpl subclass: #IdDocumentImpl
	instanceVariableNames: 'impl docElement '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdElementNode subclass: #IdCompositeNode
	instanceVariableNames: 'childNodes '
	classVariableNames: 'DefaultListSize '
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdStringNode subclass: #IdPINode
	instanceVariableNames: 'target '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Basic'
!

IdTextNodeVisitor subclass: #IdNodeSearch
	instanceVariableNames: 'matchString matchRange matchStart rangeDoc '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Utils'
!


! IdDOMImpl class methodsFor: 'convenience' !

createNewDocument
	"Convenience method. Return a new document instance
	which points to the global implementation object. This is
	the preferred way to create a new document."
"
	IdDOMImpl createNewDocument
"
	^IdDocumentImpl forImpl: self getGlobalImplementation! !


! IdDOMImpl class methodsFor: 'accessing' !

getGlobalImplementation
	"Return the single instance of the receiver which
	functions as a global implementation reference for
	all documents in the image. Create now if needed."

	GlobalImplementation == nil ifTrue: [
		GlobalImplementation := IdDOMImpl new ].
	^GlobalImplementation! !


! IdDOMImpl class methodsFor: 'exception constructors' !

newException: exceptionCode msg: msg
	"Return a new DOM exception for exceptionCode and msg."

	^IdDOMExceptionImpl forExceptionCode: exceptionCode msg: msg!

newExceptionHierarchyRequest
	"Return a new DOM exception with an exception code
	of HIERARCHY_REQUEST_ERR."

	^self newException: IdDOMException HIERARCHY_REQUEST_ERR
		msg: 'HIERARCHY_REQUEST_ERR'!

newExceptionIndexSize
	"Return a new DOM exception with an exception code
	of INDEX_SIZE_ERR."

	^self newException: IdDOMException INDEX_SIZE_ERR
		msg: 'INDEX_SIZE_ERR'!

newExceptionInuseAttribute
	"Return a new DOM exception with an exception code
	of INUSE_ATTRIBUTE_ERR."

	^self newException: IdDOMException INUSE_ATTRIBUTE_ERR
		msg: 'INUSE_ATTRIBUTE_ERR'!

newExceptionInvalidCharacter
	"Return a new DOM exception with an exception code
	of INVALID_CHARACTER_ERR."

	^self newException: IdDOMException INVALID_CHARACTER_ERR
		msg: 'INVALID_CHARACTER_ERR'!

newExceptionNoDataAllowed
	"Return a new DOM exception with an exception code
	of NO_DATA_ALLOWED_ERR."

	^self newException: IdDOMException NO_DATA_ALLOWED_ERR
		msg: 'NO_DATA_ALLOWED_ERR'!

newExceptionNoModificationAllowed
	"Return a new DOM exception with an exception code
	of NO_MODIFICATION_ALLOWED_ERR."

	^self newException: IdDOMException NO_MODIFICATION_ALLOWED_ERR
		msg: 'NO_MODIFICATION_ALLOWED_ERR'!

newExceptionNotFound
	"Return a new DOM exception with an exception code
	of NOT_FOUND_ERR."

	^self newException: IdDOMException NOT_FOUND_ERR
		msg: 'NOT_FOUND_ERR'!

newExceptionNotSupported
	"Return a new DOM exception with an exception code
	of NOT_SUPPORTED_ERR."

	^self newException: IdDOMException NOT_SUPPORTED_ERR
		msg: 'NOT_SUPPORTED_ERR'!

newExceptionStringSize
	"Return a new DOM exception with an exception code
	of DOMSTRING_SIZE_ERR."

	^self newException: IdDOMException DOMSTRING_SIZE_ERR
		msg: 'DOMSTRING_SIZE_ERR'!

newExceptionWrongDocument
	"Return a new DOM exception with an exception code
	of WRONG_DOCUMENT_ERR."

	^self newException: IdDOMException WRONG_DOCUMENT_ERR
		msg: 'WRONG_DOCUMENT_ERR'! !


! IdDOMImpl class methodsFor: 'deprecated' !

createNewXmlDocument
	"Deprecated method. Do not use. Kept for compatibility
	with previous releases where xml and html documents
	were handled differently."

	^self createNewDocument! !


IdDOMImpl comment: '
	This class implements the DOMImplementation interface. It also
	provides a set of convenience methods. In particular, methods
	used to create new documents and exceptions are defined. A
	single instance of this class is stored in the GlobalImplementation
	variable and is used to support all documents in the image.

	Comment from the DOM specification:

	The DOMImplementation interface provides a number of methods 
	for performing operations that are independent of any particular 
	instance of the document object model. 

	The DOM Level 1 does not specify a way of creating a document 
	instance, and hence document creation is an operation specific to 
	an implementation. Future Levels of the DOM specification are 
	expected to provide methods for creating documents directly.'!


! IdDOMImpl methodsFor: 'queries' !

hasFeature: feature version: version
	"Test if the DOM implementation implements a specific feature.
	The following is pasted from DOM[REC level-one-core Oct 1998]:

	Parameters 
		feature
			The package name of the feature to test. In Level 1, the legal 
			values are 'HTML' and 'XML' (case-insensitive).
		version
			This is the version number of the package name to test. In Level 1,
			this is the string '1.0'. If the version is not specified, supporting any
			version of the feature will cause the method to return true.

	Return true if the feature is implemented in the specified version, 
	false otherwise.

	This method returns true for 'xml' and false for everything else.
	No version checking is implemented  yet."

	| featureType |
	featureType := feature asLowercase.
	( featureType ideEquals: 'xml' ) ifTrue: [ ^true ].
	^false! !


IdNodeCollection comment: '
	This class implements the NodeList interface in DOM. It
	is designed as an abstract class: usage should be restricted
	to instantiating subclasses. This class should not be instantiated.

	Comment from the DOM specification:

	The NodeList interface provides the abstraction of an ordered 
	collection of nodes, without defining or constraining how this 
	collection is implemented.

	The items in the NodeList are accessible via an integral index, 
	starting from 0.'!


! IdNodeCollection methodsFor: 'convenience' !

getLastOffset
	"Convenience method. Return the last child offset in 
	the receiver, or -1 if no children are stored."

	^self getLength - 1!

positionOfChild: aNode

	0 to: self getLastOffset do: [ :i |
		( self itemImpl: i ) == aNode ifTrue: [ ^i + 1 ] ].
	^-1! !


! IdNodeCollection methodsFor: 'accessing' !

getLength
	"Return the number of nodes stored within the receiver."

	nodes == nil ifTrue: [ ^0 ].
	^nodes size!

item: anInteger
	"Return the node stored at the (zero-based) index anInteger.
	Valid indexes are in the range 0 through n-1 where n is the
	size of the node list. If anInteger is invalid return nil."

	anInteger >= self getLength ifTrue: [ ^nil ].
	^nodes ideAtIndex: anInteger + 1!

itemImpl: anInteger
	"Return the node stored at the (zero-based) index anInteger.
	Valid indexes are in the range 0 through n-1 where n is the
	size of the node list. If anInteger is invalid return nil.

	This method works exactly the same as item() except that
	it returns the NodeImpl type instead of the org.w3c.dom.Node  
	which prevents unnecessary type casting in Java."

	anInteger >= self getLength ifTrue: [ ^nil ].
	^nodes ideAtIndex: anInteger + 1! !


! IdNodeCollection methodsFor: 'internal' !

protectedNodes
	"Return the nodes collection. This method should
	only be called by members of a subclass. In Java it is 
	marked as protected to reinforce this."

	nodes == nil ifTrue: [ 
		nodes := OrderedCollection new ].  "should allow tuning of size"
	^nodes!

protectedNodes: aCollection
	nodes := aCollection! !


! IdNodeCollection methodsFor: 'creation' !

initialize

	super initialize! !


! IdNodeImpl class methodsFor: 'constants' !

nodeTypeUnknown
	"Return the integer -1 which represents an unknown 
	node type. See the class comment of StringNode for more 
	information on node types."

	^-1! !


! IdNodeImpl class methodsFor: 'class initialization' !

initializeAfterLoad
	"Initialize the shared empty node collection after
	loading the class. Note that methods which interact
	directly with EmptyChildCollection are guaranteed
	to not modify it, so it will always be truly empty."

	EmptyChildCollection := OrderedCollection new: 0! !


IdNodeImpl comment: '
	This class implements the DOM Node interface.
	This is an abstract class which implements the basic behavior
	of a Node object instance. It provides default implementations of 
	several instance methods common to all node types. Many
	methods are reimplemented from subclasses for performance
	reasons to avoid type casting in Java. As a result, this is an 
	unusually large class.

	Comment from the DOM specification:

	The Node interface is the primary datatype for the entire Document 
	Object Model. It represents a single node in the document tree. 
	While all objects implementing the Node interface expose methods 
	for dealing with children, not all objects implementing the Node interface 
	may have children. For example, Text nodes may not have children, 
	and adding children to such nodes results in a DOMException being raised. 

	The attributes nodeName, nodeValue and attributes are included as a 
	mechanism to get at node information without casting down to the specific 
	derived interface. In cases where there is no obvious mapping of these 
	attributes for a specific nodeType (e.g., nodeValue for an Element or 
	attributes for a Comment), this returns null. Note that the specialized 
	interfaces may contain additional and more convenient mechanisms to 
	get and set the relevant information.'!


! IdNodeImpl methodsFor: 'test node type' !

isTypeATTRIBUTE
	"Return true if the receiver's node type is ATTRIBUTE_NODE,
	else return false."

	^self getNodeType == IdDOMConstants ATTRIBUTE_NODE!

isTypeCDATA_SECTION
	"Return true if the receiver's node type is CDATA_SECTION_NODE,
	else return false."

	^self getNodeType == IdDOMConstants CDATA_SECTION_NODE!

isTypeCOMMENT
	"Return true if the receiver's node type is COMMENT_NODE,
	else return false."

	^self getNodeType == IdDOMConstants COMMENT_NODE!

isTypeDOCUMENT
	"Return true if the receiver's node type is DOCUMENT_NODE,
	else return false."

	^self getNodeType == IdDOMConstants DOCUMENT_NODE!

isTypeDOCUMENT_FRAGMENT
	"Return true if the receiver's node type is DOCUMENT_FRAGMENT_NODE,
	else return false."

	^self getNodeType == IdDOMConstants DOCUMENT_FRAGMENT_NODE!

isTypeDOCUMENT_TYPE
	"Return true if the receiver's node type is DOCUMENT_TYPE_NODE,
	else return false."

	^self getNodeType == IdDOMConstants DOCUMENT_TYPE_NODE!

isTypeELEMENT
	"Return true if the receiver's node type is ELEMENT_NODE,
	else return false."

	^self getNodeType == IdDOMConstants ELEMENT_NODE!

isTypeENTITY
	"Return true if the receiver's node type is ENTITY_NODE,
	else return false."

	^self getNodeType == IdDOMConstants ENTITY_NODE!

isTypeENTITY_REFERENCE
	"Return true if the receiver's node type is ENTITY_REFERENCE_NODE,
	else return false."

	^self getNodeType == IdDOMConstants ENTITY_REFERENCE_NODE!

isTypeNOTATION
	"Return true if the receiver's node type is NOTATION_NODE,
	else return false."

	^self getNodeType == IdDOMConstants NOTATION_NODE!

isTypePROCESSING_INSTRUCTION
	"Return true if the receiver's node type is PROCESSING_INSTRUCTION_NODE,
	else return false."

	^self getNodeType == IdDOMConstants PROCESSING_INSTRUCTION_NODE!

isTypeTEXT
	"Return true if the receiver's node type is TEXT_NODE,
	else return false."

	^self getNodeType == IdDOMConstants TEXT_NODE! !


! IdNodeImpl methodsFor: 'accessing' !

getAttributes
	"Return a W3 named node map containing all of the
	receiver's attributes, or nil if none are stored."

	^self getAttributesList!

getAttributesList
	"Return a named node list containing all of the
	receiver's attributes. Default is nil."

	^nil!

getChildNodes
	"Return the receiver typed as a W3 NodeList on 
	its own child nodes (if any exist)."

	^self ideYourself!

getDocElement
	"Return the document element if stored in the receiver.
	Default returns nil."

	^nil!

getLength
	"Return the length of the receiver. Default returns
	the number of child nodes. This is overridden by
	string nodes."

	^self getLiveNodeCollection size!

getLocalNameOffset
	"Return the offset (zero based) of the local name
	assuming the receiver has a namespace qualified
	name. Default for all nodes is zero."

	^0!

getNodeName
	"Return the name of this node. The default value
	for all nodes is nil. Subclasses override where needed."

	^nil!

getNodeType
	"Return an integer representing the receiver's
	node type. Node types are statically defined in
	this class and can be accessed via class methods.
	Default is return the 'unknown' node type (-1).
	This method is redefined by several of the 
	receiver's subclasses."

	^self class nodeTypeUnknown!

getNodeValue
	"Return the value of this node. The default value
	for all nodes is nil."

	^nil!

getParent
	"Return the receiver's parent node or nil if no 
	parent is found."

	^parent!

getParentNode
	"Return the receiver's parent node typed using
	the W3 Node interface."

	^self getParent!

item: anInteger
	"Return the node stored at the (zero-based) index anInteger.
	Valid indexes are in the range 0 through n-1 where n is the
	size of the node list. If anInteger is invalid return nil.

	This method is required by the W3 NodeList interface which
	the receiver implements. It is only relevent when used on
	subclasses that can store child nodes."

	^self itemImpl: anInteger!

itemImpl: anInteger
	"Return the node stored at the (zero-based) index anInteger.
	Valid indexes are in the range 0 through n-1 where n is the
	size of the node list. If anInteger is invalid return nil.

	This method works exactly the same as item() except that
	it returns the NodeImpl type instead of the org.w3c.dom.Node  
	which prevents unnecessary type casting in Java."

	| nodes |
	anInteger < 0 ifTrue: [ ^nil ].
	nodes := self getLiveNodeCollection.
	anInteger >= nodes size ifTrue: [ ^nil ].
	^nodes ideAtIndex: anInteger + 1!

setNodeValue: aString
	"Set the value of this node to aString.
	By default raise a NO_MODIFICATION_ALLOWED_ERR
	exception if used."

	IdDOMImpl newExceptionNoModificationAllowed signal!

setParent: aParent
	"Set the receiver's parent to aParent."

	parent := aParent! !


! IdNodeImpl methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver. Cloning an
	element copies all attributes, and optionally its
	children if deep is true. Cloning any other type
	of node returns a copy with no content. Default 
	is return nil. This method should be reimplemented
	by all subclasses."

	^nil!

cloneNode: deep
	"Return a duplicate of the receiver. Cast the
	result of clone(deep) to the w3 Node interface."

	^self clone: deep! !


! IdNodeImpl methodsFor: 'services' !

addAncestorOffsetsFrom: childNode into: indexVector
	"Add the index of childNode to indexVector and pass
	the request up to the top of the hierarchy."

	| index |
	self isTypeDOCUMENT ifTrue: [ ^self ].
	index := self positionOfChild: childNode.
	index == -1 ifTrue: [ ^self ].
	indexVector addIndex: index - 1.
	self getParent == nil ifTrue: [ ^self ].
	self getParent addAncestorOffsetsFrom: self into: indexVector!

factoryDocument
	"Return a document which can be used as a factory
	to create new text elements. Use the receiver's document
	if one has been stored, otherwise create a new document
	for temporary use."

	| myDoc |
	myDoc := self getCurrentDocument.
	myDoc ~~ nil ifTrue: [ ^myDoc ].
	^IdDOMImpl createNewDocument!

getElementsByTagName: aTagName
	"Returns a node list of all descendant Elements with aTagName,
	in the order in which they would be encountered in a preorder 
	traversal of the document tree. If aTagName is '*', this method will 
	return all descendant elements in the receiver. The result will not 
	include the receiver even if it has aTagName.
	Note that this is not a 'live' node list as specified in the DOM spec. 
	Liveness is very expensive in this case and is intended for a scripting 
	environment. Scripting is not supported in this implementation."

	^IdNodeQuery new queryChildrenByName: aTagName 
		in: self!

positionOfChild: aChild
	"Return the position (1-n) of aChild or -1 if it is not found."

	self hasChildNodes ifFalse: [ ^-1 ].
	0 to: self getLastOffset do: [ :i |
		( self itemImpl: i ) == aChild ifTrue: [ ^i + 1 ] ].
	^-1!

printOn: aStream
	"Print a string representation of the receiver to aStream.
	Call the receiver's version of #toString to implement. The
	method #toString is overridden in subclasses instead of 
	#printOn: to make it easier to translate to Java."

	aStream nextPutAll: self toString!

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting a node and return the
	result of processing this visit. This is the default visiting 
	method for all NodeImpl subclasses."

	^aVisitor visitNode: self!

resetLock
	"Reset the node lock boolean back to false. All edit
	requests which are otherwise valid will be allowed."

	lock := false!

setAttribute: aName value: aString
	"Adds a new attribute/value pair to the receiver. 
	This default method does nothing and exists to 
	avoid some type casting in Java."!

setLock
	"Set the node lock boolean to true. This will prevent
	all new edit requests until the lock is reset to false
	using the resetLock() method."

	lock := true!

toString
	"Return a string representation of the receiver.
	Default just prints the class name."

	^self ideGetClassName!

value
	"Return a string representation of the receiver
	which concatenates all descendant children which
	are Text nodes. This implements the XSL definition
	of node value."

	| buffer |
	buffer := IdStringBuffer new.
	self valueToBuffer: buffer.
	^buffer convertToString!

valueToBuffer: stringBuffer
	"Append a string representation of the receiver
	to stringBuffer which concatenates all descendant 
	children which are Text nodes. This implements the 
	XSL definition of node value."

	0 to: self getLastOffset do: [ :i |
		( self itemImpl: i ) valueToBuffer: stringBuffer ]! !


! IdNodeImpl methodsFor: 'queries' !

hasChildNodes
	"Return true if the receiver contains any child nodes,
	else return false."

	^self getLiveNodeCollection size > 0!

ideIsNodeImpl
	"Return true if the receiver is a NodeImpl instance,
	else return false."

	^true!

isLocked
	"Return true if the receiver is locked, else return false."

	^lock!

isWithinAncestor: aNode
	"Return true if the receiver is currently stored
	within aNode or one of its children, else return false."

	self getParent == nil ifTrue: [ ^false ].
	self getParent == aNode ifTrue: [ ^true ].
	^self getParent isWithinAncestor: aNode! !


! IdNodeImpl methodsFor: 'internal' !

confirmChildNotAncestor: aNode
	"Raise a HIERARCHY_REQUEST_ERR exception if aNode
	is an ancestor of the receiver."

	( self isWithinAncestor: aNode ) ifTrue: [
		IdDOMImpl newExceptionHierarchyRequest signal ]!

confirmSameOwnerDocument: aNode
	"Placeholder method - exception is NOT implemented. DOM indicates
	that the WRONG_DOCUMENT_ERR exception should be thrown if aNode
	has a different document than the one that created the receiver. That is
	not a requirement of this system and the exception is not raised. This
	means you can safely mix text from more than one document."!

getLiveNodeCollection
	"Return a 'live' collection of all the receiver's 
	child nodes. Default is return a shared empty 
	list. This method is private to ensure that the 
	returned collections are not modified directly by
	any calling method. Modification must be done
	with public Node methods. All implementations 
	of this method must never return nil."

	^EmptyChildCollection!

internalReplaceChildren: aCollection
	"Completely replace the children collection with
	aCollection (not a copy). Default is do nothing."! !


! IdNodeImpl methodsFor: 'convenience' !

asStringNode
	"Return nil for any class other than StringNode."

	^nil!

getAttribute: aName
	"Return the first attribute value (String) stored in 
	the receiver which is identified by aName, or nil 
	if none is found. Default for all nodes is nil. "

	^nil!

getCommonParent: aNode
	"Return the common parent between the receiver 
	and aNode. If none is found return nil."

	self == aNode ifTrue: [ ^self ideYourself ].
	( aNode isWithinAncestor: self ) ifTrue: [ ^self ideYourself ].
	self getParent == nil ifTrue: [ ^nil ].
	^self getParent getCommonParent: aNode!

getCurrentDocument
	"Return the document in which the receiver currently 
	resides. Relay to the parent."

	^self getParent == nil
		ifTrue: [ nil ]
		ifFalse: [ self getParent getCurrentDocument ]!

getFirstChild
	"Return the first child of this node. If there is no such node,
	this returns nil."

	^self item: 0!

getLastChild
	"Return the last child of this node. If there is no such node,
	this returns nil."

	^self item: self getLastOffset!

getLastOffset
	"Return the last child offset in the receiver,
	or -1 if no children are stored. This value is
	commonly used in node iteration loops."

	^self getLength - 1!

getNextSibling
	"Returns the node immediately following the receiver 
	in a breadth-first traversal, or nil if no such node exists."

	| max child |
	self getParent == nil ifTrue: [ ^nil ].
	self getParent hasChildNodes ifFalse: [ ^nil ].
	max := self getParent getLastOffset.
	0 to: max do: [ :i |
		child := self getParent item: i.
		child == self ifTrue: [
			i == max ifTrue: [ ^nil ].
			child := self getParent item: i + 1.
			^child ] ].
	^nil!

getOwnerDocument
	"Return the Document object associated with this node.
	This is also the Document object used to create new nodes.
	When the receiver is a Document this is nil."

	^self getCurrentDocument!

getPreviousSibling
	"Returns the node immediately preceding the receiver 
	in a breadth-first traversal, or nil if no such node exists."

	| child |
	self getParent == nil ifTrue: [ ^nil ].
	self getParent hasChildNodes ifFalse: [ ^nil ].
	0 to: self getParent getLastOffset do: [ :i |
		child := self getParent item: i.
		child == self ifTrue: [
			i == 0 ifTrue: [ ^nil ].
			child := self getParent item: i - 1.
			^child ] ].
	^nil! !


! IdNodeImpl methodsFor: 'editing' !

appendChild: aNode
	"Add aNode to the end of the receiver's childNodes.
	Convert from DOM to internal types if possible. See 
	appendNodeImpl() method comment for complete details."

	| typed |
	aNode ideIsNodeImpl ifTrue: [
		typed := aNode.
		^self appendNodeImpl: typed ].
	^nil!

appendDocFragment: aNode
	"Append a document fragment by adding all of its child
	nodes in order. Return aNode."

	^self appendDocFragment: aNode from: 0!

appendDocFragment: aNode from: startZeroIndex
	"Append a document fragment by adding all of its child
	nodes in order from startZeroIndex. Return aNode."

	| nodes |
	nodes := OrderedCollection new: aNode getLastOffset - startZeroIndex + 1.
	startZeroIndex to: aNode getLastOffset do: [ :i |
		nodes add: ( aNode itemImpl: i ) ].

	"must use node list because adding node removes it from old parent"
	IdNodeListImpl appendNodesAsList: nodes to: self.
	^aNode  "is this correct, or return last node in fragment?"!

appendNodeImpl: aNode
	"Adds the node newChild to the end of the list of children of 
	this node. If the newChild is already in the tree, it is first removed.
	Return aNode.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if newChild is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if newChild was created
	from a different document than the one that created the receiver.
	Raise a NO_MODIFICATION_ALLOWED_ERR exception if the receiver
	is read only.

	Default is raise a NO_MODIFICATION_ALLOWED_ERR exception."

	IdDOMImpl newExceptionNoModificationAllowed signal!

insertBefore: newChild dom: refChild
	"Insert newChild before the existing refChild and return
	newChild if successful. Convert from DOM to internal types 
	if possible. See internally typed insertBefore() method 
	comment for complete details."

	| typed1 typed2 |
	newChild ideIsNodeImpl ifTrue: [
		typed1 := newChild.
		typed2 := refChild.
		^self insertBefore: typed1 ref: typed2 ].
	^nil!

insertBefore: newChild ref: refChild
	"Insert newChild before the existing refChild and return
	newChild if successful. If refChild is nil, append newChild.
	If newChild is a DocumentFragment object, all of its children 
	are inserted, in the same order, before refChild. If the newChild 
	is already in the tree, it is first removed.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if newChild is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if newChild was created
	from a different document than the one that created the receiver.
	Raise a NO_MODIFICATION_ALLOWED_ERR exception if the receiver
	is read only.
	Raise a NOT_FOUND_ERR exception if refChild is not nil or a 
	current child.

	Default is raise a NO_MODIFICATION_ALLOWED_ERR exception."

	IdDOMImpl newExceptionNoModificationAllowed signal!

removeChild: oldChild
	"Remove oldChild from the list of children and return it.
	Convert from DOM to internal types if possible. See 
	internally typed removeChild() method comment for 
	complete details."

	| typed |
	oldChild ideIsNodeImpl ifTrue: [
		typed := oldChild.
		^self removeChildImpl: typed ].
	^nil!

removeChildImpl: oldChild
	"Remove oldChild from the list of children and return it.
	Raise a NO_MODIFICATION_ALLOWED_ERR exception if the receiver
	is read only.
	Raise a NOT_FOUND_ERR exception if oldChild is not a child.

	Default is raise a NO_MODIFICATION_ALLOWED_ERR exception."

	IdDOMImpl newExceptionNoModificationAllowed signal!

replaceChild: newChild dom: oldChild
	"Replace oldChild with newChild and return oldChild.
	Convert from DOM to internal types if possible. See 
	internally typed replaceChild() method comment for 
	complete details."

	| typedNew typedOld |
	( newChild ideIsNodeImpl and: [ oldChild ideIsNodeImpl ] ) ifTrue: [
		typedNew := newChild.
		typedOld := oldChild.
		^self replaceChild: typedNew old: typedOld ].
	^nil!

replaceChild: newChild old: oldChild
	"Replace oldChild with newChild and return oldChild.
	If newChild is a DocumentFragment object, all of its children 
	are inserted, in the same order, before refChild. If the newChild 
	is already in the tree, it is first removed.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if newChild is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if newChild was created
	from a different document than the one that created the receiver.
	Raise a NO_MODIFICATION_ALLOWED_ERR exception if the receiver
	is read only.
	Raise a NOT_FOUND_ERR exception if refChild is not a child.

	Default is raise a NO_MODIFICATION_ALLOWED_ERR exception."

	IdDOMImpl newExceptionNoModificationAllowed signal! !


! IdNodeImpl methodsFor: 'creation' !

initialize
	"Initialize the receiver after it has been created."

	super initialize.
	self resetLock! !


! IdNodeImpl methodsFor: 'deprecated' !

addChildrenByAttribute: attName to: allNodes
	"Deprecated method. Do not use."!

clonePartialEnd: offsetTrail pos: position cut: cut
	"Deprecated method. Do not use."

	^self clone: false!

clonePartialStart: offsetTrail pos: position cut: cut
	"Deprecated method. Do not use."

	^self clone: false!

getAttributesOrNil
	"Deprecated method. Do not use."

	^self getAttributesList!

getChildAtNodeName: aName
	"Returns the first child node which has a node name
	equal to aName, or nil if none is found."

	| child |
	self getLength == 0 ifTrue: [ ^nil ].
	0 to: self getLastOffset do: [ :i |
		child := self itemImpl: i.
		( child getNodeName ~~ nil
			and: [ aName ideEquals: child getNodeName ] ) ifTrue: [
				^child ] ].
	^nil! !


IdFilterIterator comment: '
	This is an abstract class used to support node iterators
	which interoperate with filters. Instances of any subclass
	can store filters and use them during the iteration.'!


! IdFilterIterator methodsFor: 'accessing' !

getNodeFilter
	^nodeFilter!

setNodeFilter: anObject
	nodeFilter := anObject! !


! IdFilterIterator methodsFor: 'internal' !

advance: forward
	"Default is return nil. Should be subclassed."

	^nil!

advanceFiltered: forward
	"Return the next node in sequence which satisfies
	the current filter if one is present, or nil if at the end."

	| nextCandidate |
	nextCandidate := self advance: forward.
	nextCandidate == nil ifTrue: [ ^nil ].
	self getNodeFilter == nil ifTrue: [ ^nextCandidate ].
	[ self getNodeFilter acceptNode: nextCandidate ] whileFalse: [
		nextCandidate := self advance: forward.
		nextCandidate == nil ifTrue: [ ^nil ] ].
	^nextCandidate! !


! IdFilterIterator methodsFor: 'services' !

nextNode
	"This method returns the next node in sequence,
	or nil if no others remain. Note that if the underlying
	node list is being constructed, this method may return
	nil before the loading is complete. This method returns
	the W3 node type."

	^self nextNodeImpl!

nextNodeImpl
	"This method returns the next node in sequence,
	or nil if no others remain. Note that if the underlying
	node list is being constructed, this method may return
	nil before the loading is complete. This method returns
	the internal node type."

	^self advanceFiltered: true!

prevNode
	"This method returns the previous node in sequence,
	or nil if no others remain. Note that if the underlying
	node list is being constructed, this method may return
	nil before the loading is complete. This method returns
	the W3 node type."

	^self prevNodeImpl!

prevNodeImpl
	"This method returns the previous node in sequence,
	or nil if no others remain. Note that if the underlying
	node list is being constructed, this method may return
	nil before the loading is complete. This method returns
	the internal node type."

	^self advanceFiltered: false! !


IdSearchFilter comment: '
	This is a sample filter class used to support text
	searching. It stores the start and stop indexes for each
	node that matches. 
	Do not use this class in your application. It will likely
	be deleted in a future release.'!


! IdSearchFilter methodsFor: 'accessing' !

getPattern
	^pattern!

getStartIndex
	^startIndex!

getStopIndex
	^stopIndex!

setPattern: anObject
	pattern := anObject! !


! IdSearchFilter methodsFor: 'services' !

acceptNode: aNode
	"Accept aNode by returning true if the current pattern 
	can be found, else return false."

	| position |
	startIndex := stopIndex := -1.
	aNode getNodeType == IdDOMConstants TEXT_NODE ifFalse: [ ^false ].
	position := IdKernelUtilities positionOfStr: self getPattern 
		in: aNode getNodeValue 
		from: 1.
	position == -1 ifTrue: [ ^false ].
	startIndex := position.
	stopIndex := position + self getPattern ideLength - 1.
	^true! !


! IdSearchFilter methodsFor: 'creation' !

initialize

	super initialize.
	pattern := String new.
	startIndex := stopIndex := -1! !


! IdDOMConstants class methodsFor: 'node types' !

ATTRIBUTE_NODE
	"Return the integer which represents the
	ATTRIBUTE_NODE node type."

	^2!

CDATA_SECTION_NODE
	"Return the integer which represents the
	CDATA_SECTION_NODE node type."

	^4!

COMMENT_NODE
	"Return the integer which represents the
	COMMENT_NODE node type."

	^8!

DOCUMENT_FRAGMENT_NODE
	"Return the integer which represents the
	DOCUMENT_FRAGMENT_NODE node type."

	^11!

DOCUMENT_NODE
	"Return the integer which represents the
	DOCUMENT_NODE node type."

	^9!

DOCUMENT_TYPE_NODE
	"Return the integer which represents the
	DOCUMENT_TYPE_NODE node type."

	^10!

ELEMENT_NODE
	"Return the integer which represents the
	ELEMENT_NODE node type."

	^1!

ENTITY_NODE
	"Return the integer which represents the
	ENTITY_NODE node type."

	^6!

ENTITY_REFERENCE_NODE
	"Return the integer which represents the
	ENTITY_REFERENCE_NODE node type."

	^5!

NOTATION_NODE
	"Return the integer which represents the
	NOTATION_NODE node type."

	^12!

PROCESSING_INSTRUCTION_NODE
	"Return the integer which represents the
	PROCESSING_INSTRUCTION_NODE node type."

	^7!

TEXT_NODE
	"Return the integer which represents the
	TEXT_NODE node type."

	^3! !


IdDOMConstants comment: '
	This is a static class which implements class methods 
	containing DOM Node constants. In Java, these methods
	are implemented by the DOM Node Interface. As a result,
	there is no need to include this class within a Java application.

	The constants in this class are node types which are integers 
	stored within StringNode instances to identify the purpose of 
	the instance (ie. Attribute, Comment, Text, etc.). Note that an 
	additional node type for unknown instances (-1) is defined 
	in a static method of NodeImpl.'!


! IdDOMException class methodsFor: 'exception codes' !

DOMSTRING_SIZE_ERR
	^2!

HIERARCHY_REQUEST_ERR
	^3!

INDEX_SIZE_ERR
	^1!

INUSE_ATTRIBUTE_ERR
	^10!

INVALID_CHARACTER_ERR
	^5!

NOT_FOUND_ERR
	^8!

NOT_SUPPORTED_ERR
	^9!

NO_DATA_ALLOWED_ERR
	^6!

NO_MODIFICATION_ALLOWED_ERR
	^7!

WRONG_DOCUMENT_ERR
	^4! !


IdDOMException comment: '
	This class implements the DOMException interface. Only exceptions
	defined by DOM are currently implemented. Instances of this class
	store an integer exception code to identify the reason for which it
	was created. All codes are stored in class methods of this class.

	Comment from the DOM specification:

	DOM operations only raise exceptions in ''exceptional'' circumstances, 
	i.e., when an operation is impossible to perform (either for logical reasons, 
	because data is lost, or because the implementation has become unstable). 
	In general, DOM methods return specific error values in ordinary processing 
	situation, such as out-of-bound errors when using NodeList.'!


! IdDOMException methodsFor: 'accessing' !

code
	"Return the exception code. The exception code is an
	integer which matches one of the standard error types.
	These types are defined in class methods of this class
	and match the naming standard of DOM."

	^code!

code: anInteger
	"Set the exception code to anInteger. See getter
	method comment for more."

	code := anInteger!

getMessage
	^message!

setMessage: aString
	message := aString! !


! IdPlatformUtilities class methodsFor: 'XML parsing' !

getNewSAXParser
	"Return a new SAX compliant XML parser instance,
	or nil if one can not be found."

	^IdKernelUtilities newInstanceOfClass: #IdXWalkerParser!

parseXMLDocument: aSystemId
	"Return the XML document located at aSystemId, or nil
	if it can not be located or parsed."

	^IdXMLReader parseDocumentAtURI: aSystemId!

parseXMLInLocal: directoryPath fileName: aFileName
	"Attempt to parse and return the XML document named
	aFileName at directoryPath, or nil if it can not be found."

	| stream inputSource document |
	inputSource := IdKernelUtilities getNewInputSource.
	inputSource == nil ifTrue: [ ^nil ].
	stream := self openFileTextReader: directoryPath fileName: aFileName.
	stream == nil ifTrue: [ ^nil ].
	inputSource setCharacterStream: stream.
	[ document := IdXMLReader parseDocument: inputSource ]
		ideEnsureCompletion: [ stream close ].
	^document!

transformWithXT: source style: stylesheet result: result
	"Do nothing in Smalltalk."! !


! IdPlatformUtilities class methodsFor: 'file access' !

combineIntoUri: directoryPath filename: fileName
	"Return a string containing the arguments provided
	combined into the form of a URI."

	| uri |
	fileName == nil ifTrue: [ ^directoryPath ].
	directoryPath == nil ifTrue: [ ^fileName ].
	uri := IdURI forBase: ( IdURI forUriString: directoryPath ) string: fileName.
	^uri toExternalForm!

convertLocalPathToURI: pathName
	"Return a URI string which represents pathName.
	Currently this does not include the protocol prefix."

	| buffer nextChar |
	buffer := IdStringBuffer new: pathName size.
	1 to: pathName size do: [ :i |
		nextChar := pathName ideCharAt: i.
		nextChar == $\
			ifTrue: [ buffer appendChar: $/ ]
			ifFalse: [ buffer appendChar: nextChar ] ].	
	^buffer convertToString!

convertURIToLocalPath: systemId
	^IdKernelUtilities convertURIToLocalPath: systemId!

createSystemUserDirURI: systemId
	"Return a URI combining the current user working
	directory with systemId."

	| dir |
	dir := self convertLocalPathToURI: self getWorkingDirectory.
	( dir size > 1 and: [ dir last ~~ $/ ] ) ifTrue: [
		dir := dir, '/' ].
	^self combineIntoUri: dir filename: systemId!

getWorkingDirectory
	"Return the current working directory. This is used
	in Smalltalk development to simulate the root directory
	in a runtime program."

	WorkingDirectory == nil ifTrue: [ ^'' ].
	^WorkingDirectory!

openBufferedFileReader: systemId
	"Return a ReadStream on the file stored at systemId.
	In Smalltalk this only works on local files. Note that the
	ReadStream is open on a file and must be closed
	when no longer required. That the is calling
	method's responsibility."

	^self openFileTextReader: (
		self convertURIToLocalPath: systemId )!

openFileTextReader: pathName
	^IdKernelUtilities openFileTextReader: pathName!

openFileTextReader: pathName fileName: aFileName
	"Return a ReadStream on the file stored locally at 
	pathName (the directory) and aFileName. This method
	will concatenate the two together into a single path."

	| buffer |
	buffer := IdStringBuffer new: pathName size + aFileName size + 1.
	buffer appendString: pathName.
	pathName last == $\ ifFalse: [
		buffer appendChar: $\ ].
	buffer appendString: aFileName.
	^self openFileTextReader: buffer convertToString!

openFileTextWriter: pathName
	^IdKernelUtilities openFileTextWriter: pathName!

openFileWriteStream: pathName
	^self openFileTextWriter: pathName!

openWriteStream: aCapacity
	"Return a WriteStream on a string of size qual to aCapacity."

	^WriteStream on: ( String new: aCapacity )!

setWorkingDirectory: directoryPath
	"Set the working directory to directoryPath. This is used
	in Smalltalk development to simulate the root directory
	in a runtime program."

	WorkingDirectory := directoryPath! !


IdPlatformUtilities comment: '
	A platform specific utility class which implements many
	useful functions such as reading and parsing files. Refer to
	the individual method comments for more information.
	Similar functions which are also required by the XML parser
	are defined in IdKernelUtilities.'!


IdDocumentAssembler comment: '
	This is a utility class used to create a documents
	by traversing the internal text hierarchy. It stores a
	document which is used to create all new text (and
	which takes the first element as its root element).
	It also stores a currentElement which is used to add
	text created by other convenience methods in this class.
	The hierarchy can be traversed using the ascendHierarchy()
	and addAndDescendInto() methods.'!


! IdDocumentAssembler methodsFor: 'accessing' !

getCurrentParent
	^currentParent!

getDocument
	^document! !


! IdDocumentAssembler methodsFor: 'add text' !

addAttribute: aName value: aValue
	"Add an attribute as specified to the current parent."

	currentParent == nil ifTrue: [ ^self ].
	currentParent setAttribute: aName value: aValue!

addBlockedText: aTagName content: aString
	"Add an element with type aTagName which contains a 
	single text node containing aString."

	| block |
	block := self addElement: aTagName.
	block == nil ifTrue: [ ^self ].
	block appendNodeImpl: (
		self getDocument createTextNodeImpl: aString )!

addElement: aTagName
	"Add an element with type aTagName. The result
	will be capable of holding children but will not have
	been set as the current element in the receiver.
	Return the new element or nil if the receiver is not 
	yet set up."

	| answer |
	currentParent == nil ifTrue: [ ^nil ].
	answer := self getDocument createComposite: aTagName.
	currentParent appendNodeImpl: answer.
	^answer!

addNode: aNode
	"Add aNode to the current parent."

	currentParent == nil ifTrue: [ ^self ].
	currentParent appendNodeImpl: aNode!

addParentElement: aTagName
	"Add a new element with type aTagName and set it
	as the new parent element. Return the new element"

	| answer |
	answer := self getDocument createComposite: aTagName.
	self addAndDescendInto: answer.
	^answer!

addText: aString
	"Add a text node containing aString."

	currentParent == nil ifTrue: [ ^self ].
	currentParent appendNodeImpl: (
		self getDocument createTextNodeImpl: aString )! !


! IdDocumentAssembler methodsFor: 'services' !

addAndDescendInto: anElement
	"Add and register anElement as the current parent 
	which will accept all children from now on. If this is the
	first element to be added, make it the root element.
	If no document has been stored do nothing."

	document == nil ifTrue: [ ^self ].
	currentParent == document
		ifTrue: [ document setDocElement: anElement ]
		ifFalse: [ self addNode: anElement ].
	currentParent := anElement!

ascendHierarchy
	"Move up to the current element's parent."

	currentParent == nil ifTrue: [ ^self ].
	currentParent := currentParent getParent!

storeDocument: aDocument
	"Set the current document to aDocument. This object
	will be responsible for creating all other text elements.
	Also store it as the current parent."

	document := aDocument.
	currentParent := aDocument!

storeEmptyDocument
	"Store a new document which is ready to hold text."

	self storeDocument: IdDOMImpl createNewDocument! !


! IdFunctionsDOM class methodsFor: 'parsing' !

parseAttributeList: dataString in: aDocument
	"Return an attribute list (NamedNodeList) created by
	parsing dataString for attribute names and values. The
	result can be queried for individual attributes."

	| i charPosition attName attList equals apos quote space nextChar att attValue quotChar endQuot |

	charPosition := 1.
	attName := nil.
	equals := 61 "$=".
	apos := 39 "$' ".
	quote := 34 "quote".
	space := 32 "$ ".
	attList := IdNamedNodeList new.
	i := 1.

	[ i <= dataString ideLength ] whileTrue: [
		nextChar := dataString ideCharValueAt: i.
		nextChar == equals ifTrue: [ 
			attName := dataString copyFrom: charPosition to: i - 1 ].
		quotChar := 0.
		nextChar == apos ifTrue: [ 
			quotChar := apos ].
		nextChar == quote ifTrue: [ 
			quotChar := quote ].
		attValue := nil.
		i := i + 1.
		quotChar ~~ 0 ifTrue: [
			endQuot := IdKernelUtilities positionOfChar: quotChar in: dataString from: i.
			endQuot ~~ -1 ifTrue: [
				attValue := dataString copyFrom: i to: endQuot - 1.
				i := endQuot + 1.
				att := aDocument createAttribute: attName.
				att setValue: attValue.
				attList setNamedItem: att ] ].
		nextChar == space ifTrue: [ 
			charPosition := i ] ].
	^attList!

stripWhitespace: charArray from: start length: length edges: stripEdges
	"Return a string after stripping whitespace from charArray.
	Begin at start (a zero based index) and continue for length.
	This converts multiple separator characters in charArray to
	a single space. If stripEdges is true, strip leading and trailing
	whitespace as well. If the entire string is separators return nil."

	| addSpace buffer nextChar space leading |
	length == 0 ifTrue: [ ^nil ].
	addSpace := false.
	leading := true.
	buffer := IdStringBuffer new: length.
	space := Character value: 32.
	start + 1 to: start + length do: [ :i |
		nextChar := charArray at: i.
		( IdKernelUtilities isSeparatorChar: nextChar )
			ifTrue: [ addSpace := true ]
			ifFalse: [
				addSpace ifTrue: [
					addSpace := false.
					( stripEdges not or: [ leading not ] ) ifTrue: [
						buffer appendChar: space ] ].
				leading := false.
				buffer appendChar: nextChar ] ].
	buffer length == 0 ifTrue: [ ^nil ].
	( addSpace and: [ stripEdges not ] ) ifTrue: [
		buffer appendChar: space ].
	^buffer convertToString! !


! IdFunctionsDOM class methodsFor: 'nodes' !

getProcessingInstruction: piName in: aNode
	"Return the processing instruction node named piName 
	in aNode, or nil if one can not be found."

	| childNode pi |
	0 to: aNode getLastOffset do: [ :i |
		childNode := aNode itemImpl: i.
		childNode isTypePROCESSING_INSTRUCTION ifTrue: [
			pi := childNode.
			( pi getTarget ideEquals: piName ) ifTrue: [ ^pi ] ] ].
	^nil! !


! IdFunctionsDOM class methodsFor: 'validating' !

validateXMLName: aString
	"Return true if aString is a valid name in the XML
	specification, else return false. This is used to validate
	PI targets, entity references and attribute names."

	| tagSize |
	tagSize := aString ideLength.
	tagSize < 1 ifTrue: [ ^false ].
	1 to: tagSize do: [ :i |
		( IdKernelUtilities isXMLNameChar: ( aString ideCharAt: i ) ) ifFalse: [ ^false ] ].
	^true!

validateXMLTagName: tagName
	"Return true if tagName is a valid XML tag name,
	else return false."

	| tagSize |
	tagSize := tagName ideLength.
	tagSize < 1 ifTrue: [ ^false ].
	( IdKernelUtilities isXMLNameFirstChar: ( tagName ideCharAt: 1 ) ) ifFalse: [ ^false ].
	tagSize == 1 ifTrue: [ ^true ].
	2 to: tagSize do: [ :i |
		( IdKernelUtilities isXMLNameChar: ( tagName ideCharAt: i ) ) ifFalse: [ ^false ] ].
	^true! !


! IdFunctionsDOM class methodsFor: 'stylesheets' !

styleSheetAssignXSL: styleURI doc: aDocument
	"Add a processing instruction to aDocument to represent
	an XSL stylesheet using styleURI. Update an existing PI if one
	exists. Remove any existing PI if styleURI is nil."

	| existing buffer piData piNode |
	existing := self styleSheetPI: aDocument.
	styleURI == nil ifTrue: [
		existing == nil ifTrue: [ ^self ].
		aDocument removeChildImpl: existing.
		^self ].
	buffer := IdStringBuffer new: 50.
	buffer
		appendString: 'type=';
		appendChar: $";
		appendString: 'text/xsl';
		appendChar: $";
		appendString: ' href=';
		appendChar: $";
		appendString: styleURI;
		appendChar: $".
	piData := buffer convertToString.
	existing ~~ nil ifTrue: [
		existing asStringNode setData: piData.
		^self ].
	piNode := aDocument createPIImpl: 'xml-stylesheet' data: piData.
"	aDocument getLength > 0 
		ifTrue: [ aDocument insertBefore: piNode ref: ( aDocument item: 0 ) ] - composite can not insert yet
		ifFalse: [ aDocument appendNodeImpl: piNode ]"
	aDocument appendNodeImpl: piNode!

styleSheetPI: aDocument
	"Return the PI containing the stylesheet reference for aDocument,
	or nil if none can be found."

	^self getProcessingInstruction: 'xml-stylesheet' in: aDocument!

styleSheetURIForXSL: aDocument
	"Return a URI string for the XSL stylesheet referenced in aDocument,
	or nil if none can be found."

	| piNode href type attList |
	piNode := self styleSheetPI: aDocument.
	piNode == nil ifTrue: [ ^nil ].
	attList := self parseAttributeList: piNode getNodeValue in: aDocument.
	href := attList getNamedItem: 'href'.
	href == nil ifTrue: [ ^nil ].
	type := attList getNamedItem: 'type'.
	( type getNodeValue ideEquals: 'text/xsl' ) ifFalse: [ ^nil ].
	^href getNodeValue! !


IdFunctionsDOM comment: '
	This class contains a number of convenience methods
	related to the supported DOM classes. There are no instance
	methods - i.e. this class is not designed to be instantiated.'!


IdHierarchyPoint comment: '
	This is an abstract class used to represent individual
	points within an object hierarchy. It implements the basic 
	design of DOM range end points, while allowing subclasses
	to support different parent objects.
	Instances store an offset which corresponds to the zero
	based offset of a child in its parent. An IndexVector is also
	stored as an offsetTrail which caches each offset of a child 
	object and its ancestors (within their respective parents).'!


! IdHierarchyPoint methodsFor: 'accessing' !

getOffset
	^offset!

getOffsetTrail
	"Return the offsetTrail. Recalculate if necessary."

	offsetTrail == nil ifTrue: [
		offsetTrail := self constructOffsetTrail ].
	^offsetTrail!

getParent
	^nil!

setOffset: anInteger

	self invalidateOffsetTrail.
	offset := anInteger! !


! IdHierarchyPoint methodsFor: 'internal' !

calculateReverseIndexes: indexVector
	"Abstract method - should be overridden.
	Default does nothing."!

constructOffsetTrail

	| trail revIndexes max |
	revIndexes := IdIndexVector new.
	self getOffset == -1 ifTrue: [ ^revIndexes ].
	revIndexes addIndex: self getOffset.
	self calculateReverseIndexes: revIndexes.
	trail := IdIndexVector new.  "could preset capacity"
	max := revIndexes getSize.
	0 to: max - 1 do: [ :i |
		trail addIndex: ( revIndexes getIndexAtPosition: max - i ) ].
	^trail!

invalidateOffsetTrail
	offsetTrail := nil!

isOffsetTrailLess: indexVector

	| myTrail count myIndex itsIndex |
	myTrail := self getOffsetTrail.
	count := myTrail getSize min: indexVector getSize.
	count < 1 ifTrue: [ 
		^myTrail getSize < indexVector getSize ].
	1 to: count do: [ :i |
		myIndex := myTrail getIndexAtPosition: i.
		itsIndex := indexVector getIndexAtPosition: i.
		myIndex < itsIndex ifTrue: [ ^true ].
		myIndex > itsIndex ifTrue: [ ^false ] ].
	^myTrail getSize < indexVector getSize!

sharesParent: anEndPoint
	"Abstract method - should be overridden.
	Default returns false."

	^false! !


! IdHierarchyPoint methodsFor: 'services' !

compareToEndPoint: anEndPoint
	"Return -1, 0 or 1 depending on whether the receiver is 
	less than, equal or greater than anEndPoint."

	( self equalsEndPoint: anEndPoint ) ifTrue: [ ^0 ].
	( self isLessThanEndPoint: anEndPoint ) ifTrue: [ ^-1 ].
	^1!

equalsEndPoint: anEndPoint

	( self sharesParent: anEndPoint ) ifFalse: [ ^false ].
	^self getOffset == anEndPoint getOffset!

isLessThanEndPoint: anEndPoint
	"If both end-points have the same parent node, then one 
	end-point is less than the other if its offset is less the offset 
	of the other end-point.

	If the end-points have different parent nodes, then there are three 
	cases to consider.

	Let A and B be the two end-points. The first case to consider is 
	when a child of the parent of A is the parent or an ancestor of the 
	parent of B. In this case, A is less than B if the offset of A is less 
	than or equal to the index of the child containing B.

	The second case is when a child of the parent of B is the parent 
	or an ancestor of the parent of A. In this case, A is less than B if 
	the index of the child containing A is less than the offset of B.

	The third case is when neither parent is an ancestor of the other 
	end-point's parent. In this case, let N be the common ancestor of 
	both A and B which has the greatest depth in the DOM tree. Then A 
	is less than B if the index of the child of N which is an ancestor of 
	the parent of A is less than the index of the child of N which is an 
	ancestor of the parent of B."

	( self sharesParent: anEndPoint ) ifTrue: [ 
		^self getOffset < anEndPoint getOffset ].
	^self isOffsetTrailLess: anEndPoint getOffsetTrail! !


! IdHierarchyPoint methodsFor: 'creation' !

initialize

	super initialize.
	offset := -1! !


IdIndexVector comment: '
	A simple class used to store integers in a vector
	(OrderedCollection in Smalltalk) which handles the
	required type casting to and from Integer in Java.
	This class may disappear in a future release.'!


! IdIndexVector methodsFor: 'services' !

addIndex: anIndex
	"Add anIndex to the index lsit and convert it to
	Integer in Java."

	indexList add: anIndex ideAsIntegerObject!

getIndexAtPosition: anIndex
	"Return the index stored at anIndex or -1 if
	none is found."

	| intObject |
	( anIndex < 1 or: [ anIndex > indexList size ] ) ifTrue: [ ^-1 ].
	intObject := indexList ideAtIndex: anIndex.
	^intObject ideAsPrimitiveInt!

getSize
	"Return the size of the underlying index list."

	^indexList size!

toString

	| answer |
	answer := self ideGetClassName, '('.
	1 to: self getSize do: [ :i |
		answer := answer, ( self getIndexAtPosition: i ) idePrintPrimitive.
		i < self getSize ifTrue: [
			answer := answer, ' ' ] ].
	^answer, ')'!


! IdIndexVector methodsFor: 'creation' !

initialize

	super initialize.
	indexList := OrderedCollection new! !


IdNodeVisitorBase comment: '
	This class serves as a superclass for classes which
	need to implement only a portion of the NodeVisitor 
	interface. All methods in the interface are implemented
	in this class but simply return nil.'!


! IdNodeVisitorBase methodsFor: 'convenience' !

visitAllChildren: aNode
	"Visit all children of aNode in document order.
	Terminate early if visitIsComplete() returns true."

	self visitChildren: aNode 
		from: 0 
		to: aNode getLastOffset!

visitAllChildrenRev: aNode
	"Visit all children of aNode in reverse document order.
	Terminate early if visitIsComplete() returns true."

	self visitChildrenRev: aNode 
		from: 0 
		to: aNode getLastOffset!

visitChildren: aNode from: startOffset to: endOffset
	"Visit all children of aNode from startOffset to endOffset
	in document order. Terminate early if visitIsComplete() 
	returns true."

	| child |
	startOffset to: endOffset do: [ :i |
		child := aNode itemImpl: i.
		child ~~ nil ifTrue: [ 
			child receiveVisitor: self.
			self visitIsComplete ifTrue: [ ^self ] ] ]!

visitChildrenRev: aNode from: startOffset to: endOffset
	"Visit all children of aNode from startOffset to endOffset
	in reverse document order. Terminate early if visitIsComplete() 
	returns true."

	| child dif |
	dif := endOffset - startOffset.
	0 to: dif do: [ :i |
		child := aNode itemImpl: endOffset - i.
		child ~~ nil ifTrue: [ 
			child receiveVisitor: self.
			self visitIsComplete ifTrue: [ ^self ] ] ]! !


! IdNodeVisitorBase methodsFor: 'visiting' !

visitComposite: aNode
	^nil!

visitDocument: aNode
	^nil!

visitDocumentFragment: aNode
	^nil!

visitElement: aNode
	^nil!

visitNode: aNode
	^nil!

visitPI: aNode
	^nil!

visitString: aNode
	^nil! !


! IdNodeVisitorBase methodsFor: 'internal' !

visitIsComplete
	"Return true if the current visit is complete. Default is
	return false. This method is used in conjunction with
	several convenience methods defined in this class."

	^false! !


! IdRangeImpl class methodsFor: 'constructors' !

forDocument: aDocument
	"Return a new Range which has both end-points positioned
	at the start of aDocument."

	| answer |
	answer := IdRangeImpl new.
	answer initDocument: aDocument.
	^answer! !


IdRangeImpl comment: '
	This is a concrete class used to support a range (text 
	selection) within a node hierarchy. It closely corresponds 
	to the DOM Range interface, however it has not yet been 
	compiled to enforce it. It is not clear how ranges in XML/XSL 
	will compare to DOM ranges which are designed for dynamic 
	HTML. In particular, script based access to ranges will likely
	not be supported in the same way within XSL browsers. As a
	result, this class may be subject to change.'!


! IdRangeImpl methodsFor: 'accessing' !

getCommonParent
	^self getCommonParentImpl!

getCommonParentImpl
	"Return the common parent of start and end or
	nil if one can't be found. Return nil if either end
	point is nil."

	( start == nil or: [ end == nil ] ) ifTrue: [ ^nil ].
	^start getParent getCommonParent: end getParent!

getEnd
	^end!

getEndOffset

	end == nil ifTrue: [ ^-1 ].
	^end getOffset!

getEndParent

	end == nil ifTrue: [ ^nil ].
	^end getParent!

getStart
	^start!

getStartOffset

	start == nil ifTrue: [ ^-1 ].
	^start getOffset!

getStartParent

	start == nil ifTrue: [ ^nil ].
	^start getParent!

setEnd: parent offset: offset
	"throws RangeException
	This will collapse the receiver to the new end if 
	parent is in a new document fragment, or if it is after
	the current end."

	| typedParent |
	parent ideIsNodeImpl ifFalse: [ ^self ].
	typedParent := parent.
	end 
		setParent: typedParent;
		setOffset: offset.
	self validateOrCollapse: start 
		node: typedParent 
		toStart: false!

setEndAfter: aNode
	"throws RangeException
	Position the end point immediately after aNode."

	self setRelative: aNode start: false adj: 1!

setEndBefore: aNode
	"throws RangeException
	Position the end point immediately before aNode."

	self setRelative: aNode start: false adj: -1!

setStart: parent offset: offset
	"throws RangeException.
	This will collapse the receiver to the new start if 
	parent is in a new document fragment, or if it is after
	the current end."

	| typedParent |
	parent ideIsNodeImpl ifFalse: [ ^self ].
	typedParent := parent.
	start 
		setParent: typedParent;
		setOffset: offset.
	self validateOrCollapse: end 
		node: typedParent 
		toStart: true!

setStartAfter: aNode
	"throws RangeException
	Position the start point immediately after aNode."

	self setRelative: aNode start: true adj: 1!

setStartBefore: aNode
	"throws RangeException
	Position the start point immediately before aNode."

	self setRelative: aNode start: true adj: -1! !


! IdRangeImpl methodsFor: 'cloning' !

cloneRange
	"Return a new range."

	| answer rangeDoc |
	self getStart == nil ifTrue: [ ^nil ].
	rangeDoc := self getStart getParent getCurrentDocument.
	rangeDoc == nil ifTrue: [ ^nil ].
	answer := rangeDoc createRangeImpl.
	answer
		setStart: self getStartParent offset: self getStartOffset;
		setEnd: self getEndParent offset: self getEndOffset.
	^answer! !


! IdRangeImpl methodsFor: 'internal' !

confirmDocOrCollapse: endPoint node: aNode toStart: toStart
	"throws RangeException
	This may be insufficient - it needs to catch different
	doc fragments as well, which may share the same owner doc."

	endPoint == nil ifTrue: [ ^self ].
	endPoint getParent == nil ifTrue: [ ^self ].  "at first use this happens"
	endPoint getParent getOwnerDocument == aNode getOwnerDocument ifTrue: [ ^self ].
	self collapse: toStart!

setRelative: aNode start: isStart adj: indexAdj
	"Make the parent of aNode the respective end point's parent 
	and position it according to indexAdj."

	| typedNode siblingParent index |
	aNode ideIsNodeImpl ifFalse: [ ^self ].
	typedNode := aNode.
	siblingParent := typedNode getParent.
	siblingParent == nil ifTrue: [ ^self ].
	index := siblingParent positionOfChild: typedNode.
	index == -1 ifTrue: [ ^self ].
	isStart
		ifTrue: [ self setStart: siblingParent offset: index + indexAdj ]
		ifFalse: [ self setEnd: siblingParent offset: index + indexAdj ]!

validateOrCollapse: endPoint node: aNode toStart: toStart
	"throws RangeException
	Ensure this is a valid range or collapse."

	self confirmDocOrCollapse: endPoint node: aNode toStart: toStart.
	( start compareToEndPoint: end ) > 0 ifTrue: [
		self collapse: toStart ]! !


! IdRangeImpl methodsFor: 'queries' !

isCollapsed
	^start equalsEndPoint: end! !


! IdRangeImpl methodsFor: 'pending' !

cloneContents
	"Pending. Return a document fragment."

	^nil!

extractContents
	"Pending. Returns a DocumentFragment. throws DOMException"

	^nil!

surroundContents: aNode
	"Pending. Same as insertNode except all currently selected
	content will become children of aNode (instead of being 
	deleted). Remove all current children in aNode."
	"throws DOMException, RangeException"!

toString
	"Pending. DOM requires that this returns a copy of all
	text currently selected converted to a string. For now this
	does a normal toString which is used for debugging."

	self isCollapsed ifTrue: [ 
		^'range gap(', self getStart toString, ')' ].
	^'range(', self getStart toString, ' - ', self getEnd toString, ')'!


! IdRangeImpl methodsFor: 'editing' !

deleteContents
	"throws DOMException"

	| parent count |
	parent := self getCommonParentImpl.
	parent == nil ifTrue: [ ^self ].
	parent isTypeTEXT ifFalse: [ ^self ].
	count := self getEndOffset - self getStartOffset.
	count <= 0 ifTrue: [ ^self ].
	parent asStringNode deleteData: self getStartOffset
		count: count!

insertNode: aNode
	"throws DOMException, RangeException"

	| parent |
	aNode getNodeType == IdDOMConstants TEXT_NODE ifFalse: [ ^self ].
	parent := self getCommonParentImpl.
	parent == nil ifTrue: [ ^self ].
	parent getNodeType == IdDOMConstants TEXT_NODE ifFalse: [ ^self ].
	parent asStringNode insertData: self getStartOffset
		string: aNode getNodeValue! !


! IdRangeImpl methodsFor: 'creation' !

initDocument: aDocument
	"Position both end-points at the beginning of aDocument."

	start := IdRangeEndPoint forParent: aDocument offset: 0.
	end := IdRangeEndPoint forParent: aDocument offset: 0! !


! IdRangeImpl methodsFor: 'services' !

collapse: toStart
	"Collapse at the start if toStart is true, otherwise 
	use the end."

	toStart
		ifTrue: [ self setEnd: self getStartParent offset: self getStartOffset ]
		ifFalse: [ self setStart: self getEndParent offset: self getEndOffset ]!

compareEndPoints: how with: sourceRange
	"The how parameter can be one of four strings defined
	in the Range interface: 
		StartToStart StartToEnd EndToEnd EndToStart
	Return -1, 0 or 1 depending on whether the corresponding 
	end-point of the receiver is less than, equal or greater than 
	the corresponding end-point of sourceRange."

	( how ideEquals: 'StartToStart' ) ifTrue: [
		^self getStart compareToEndPoint: sourceRange getStart ].
	( how ideEquals: 'StartToEnd' ) ifTrue: [
		^self getStart compareToEndPoint: sourceRange getEnd ].
	( how ideEquals: 'EndToEnd' ) ifTrue: [
		^self getEnd compareToEndPoint: sourceRange getEnd ].
	^self getEnd compareToEndPoint: sourceRange getStart  "EndToStart"!

selectNode: aNode
	"throws RangeException"

	| parent child index |
	aNode ideIsNodeImpl ifFalse: [ ^self ].
	child := aNode.
	parent := child getParent.
	parent == nil ifTrue: [ ^self ].  "RangeException"
	index := parent positionOfChild: child.
	index == -1 ifTrue: [ ^self ].  "RangeException"
	self 
		setStart: parent offset: index - 1;
		setEnd: parent offset: index!

selectNodeContents: aNode
	"throws RangeException"

	self 
		setStart: aNode offset: 0;
		setEnd: aNode offset: aNode getChildNodes getLength! !


IdXMLReader comment: '
	This is a utility class used to parse an XML encoding and
	create a new Document instance to represent it. It utilizes
	the SAX XML parsing interface which means it can be used
	with any SAX compliant XML parser. It preserves XML
	whitespace by maintaining a stack for elements which
	specify the xml:space attribute.

	The easiest way to use it is by invoking one of the class methods
	parseDocumentAtURI() or parseDocument() which accept a
	URI/URL path or a SAX InputSource respectively as parameters.
	The InputSource class provides several options for pointing
	the parser at XML resources including alternate public URIs
	and a wrapped character read stream. Note that you do not have
	to invoke these class methods to use this class, however you
	will have to properly initialize a SAX parser yourself if you do not.

	Standard normalization of the XML input is handled. This 
	includes replacing multiple separator characters (including line
	feeds) with a single space and stripping out ignorable whitespace.'!


! IdXMLReader class methodsFor: 'services' !

parseDocument: anInputSource
	"Parse the XML document described by anInputSource
	and return the result. Return nil if a SAX compliant parser
	can not be found or if anInputSource can not be resolved
	to a valid XML character stream."

	| parser |
	parser := IdPlatformUtilities getNewSAXParser.
	parser == nil ifTrue: [ ^nil ].
	^self parseDocument: anInputSource with: parser!

parseDocument: anInputSource with: aParser
	"Parse the XML document described by anInputSource
	using aParser and return the result. Return nil if anInputSource 
	can not be resolved to a valid XML character stream."

	^self parseDocument: anInputSource 
		with: aParser 
		reader: IdXMLReader new!

parseDocument: anInputSource with: aParser reader: aReader
	"Parse the XML document described by anInputSource using 
	aParser and aReader and return the result. Return nil if anInputSource 
	can not be resolved to a valid XML character stream."

	| parseError |
	aParser setDocumentHandler: aReader.
	parseError := false.
	[ aParser parse: anInputSource ] 
		ideOnException: IdException
		do: [ :ex |
			parseError := true.
			ex ideExitGracefully ].
	parseError ifTrue: [ ^nil ].
	^aReader getDocument!

parseDocumentAtURI: aSystemId
	"Parse and return the XML document stored at aSystemId 
	(which must observe the URI/URL syntax ie. 'file:/d:/dir/mydoc.xml').
	Return nil if a SAX compliant parser can not be found or if 
	aSystemId can not be located."

	| inputSource |
	inputSource := IdKernelUtilities getNewInputSource.
	inputSource == nil ifTrue: [ ^nil ].
	inputSource setSystemId: aSystemId.
	^self parseDocument: inputSource!

parseDocumentInHttpDir: directoryPath file: fileName
	"Attempt to parse and return the XML document named
	fileName stored within directoryPath. Assume that directoryPath
	is in http format. Similar methods for local file access are
	provided in the PlatformUtilities class. Return nil if the 
	document can not be found."

	^self parseDocumentAtURI: (
		IdPlatformUtilities combineIntoUri: directoryPath filename: fileName )!

parseDocumentInString: aString
	"Parse and return the XML document encoded within aString.
	Return nil if the required classes can not be found."

	| inputSource |
	aString == nil ifTrue: [ ^nil ].
	inputSource := IdKernelUtilities getNewInputSource.
	inputSource == nil ifTrue: [ ^nil ].
	inputSource setCharacterStream: ( 
		ReadStream ideStringReaderOn: aString ).
	^self parseDocument: inputSource! !


! IdXMLReader class methodsFor: 'instance creation' !

new
	"Answer an instance of the receiver and initialize it."

	^super new initialize! !


! IdXMLReader class methodsFor: 'examples' !

runSimpleXMLExample
	"Return a DOM document parsed from a simple XML String."
"
	IdXMLReader runSimpleXMLExample
"
	^self parseDocumentInString:
'<customers>
	<customer>
		<name>
			<first>Kelly</first>
			<last>Sandars</last>
		</name>
		<order>
			<number>6587</number>
			<date><day>13</day><month>6</month><year>1996</year></date>
			<item>
				<title>Number, the Language of Science</title>
				<author>Danzig</author>
				<price>5.95</price>
			</item>
			<item>
				<title>Tales of Grandpa Cat</title>
				<author>Wardlaw, Lee</author>
				<price>6.58</price>
			</item>
		</order>
	</customer>
	<customer>
		<name>
			<last>Higginbottom</last>
			<first>Amy</first>
		</name>
		<order>
			<number>1182</number>
			<date><month>6</month><day>15</day><year>1997</year></date>
			<item>
				<title>Evolution of Complexity in Animal Culture</title>
				<author>Bonner</author>
				<price>5.95</price>
			</item>
			<item>
				<title>When We Were Very Young</title>
				<author>Milne, A. A.</author>
				<price>12.50</price>
			</item>
		</order>
		<order>
			<number>1982</number>
			<date><year>1997</year><month>7</month><day>17</day></date>
			<item>
				<title>Learn Java Now</title>
				<author>Stephen R. Davis</author>
				<price>9.95</price>
			</item>
		</order>
	</customer>
</customers>'!

runXMLDiskExample
	"Return an XML document parsed from an XML file on disk. 
	This method does not execute, however you can modify the 
	path name shown below to refer to an XML file on your network 
	and execute the code fragment in a workspace. If nil is returned, 
	the path name may be incorrect."
"
	IdXMLReader parseDocumentAtURI: 'file:/c:/xmldocs/customers.xml'
"! !


! IdXMLReader methodsFor: 'accessing' !

getCharCount
	"Return the number of characters assigned to text nodes
	so far. This value can be used to help guage progress."

	^charCount!

getDocument
	^assembler getDocument!

getEntityResolver
	^entityResolver!

setDocument: aDocument
	"Set the target document to aDocument. This object
	will be responsible for creating all other text elements
	and will contain all the text added."

	assembler storeDocument: aDocument!

setEntityResolver: anObject
	entityResolver := anObject! !


! IdXMLReader methodsFor: 'entity resolver api' !

resolveEntity: publicId system: systemId
	"SAX method for EntityResolver interface. This will
	only be called if the receiver has been registered as
	entity resolver for this parse (which by default it does not).
	Relay the request to the entityResolver if one is stored."

	| entity |
	self getEntityResolver == nil ifTrue: [ ^nil ].
	entity := nil.
	[ entity := self getEntityResolver resolveEntity: publicId system: systemId ]
		ideOnException: IdException
		do: [ :ex |
			ex ideExitGracefully ].
	^entity! !


! IdXMLReader methodsFor: 'internal' !

addNodeToCurrent: aNode
	"Add aNode to the current parent element."

	assembler addNode: aNode!

whitespacePullElement: aNode
	"Only pull if the whitespace attribute is present
	in aNode."

	| attValue lastPref |
	attValue := aNode getAttribute: 'xml:space'.
	attValue == nil ifTrue: [ ^self ].
	( ( attValue ideEquals: 'default' ) 
		or: [ attValue ideEquals: 'preserve' ] ) ifFalse: [ ^self ].
	wsOn ifFalse: [ ^self ].
	wsStack size > 0 ifTrue: [
		wsStack removeAtIndex: wsStack size ].
	wsStack size == 0 ifTrue: [
		wsOn := false.
		wsStack := nil.
		wsPreserve := false.
		^self ].
	lastPref := wsStack last.
	wsPreserve := lastPref ideAsPrimitiveBoolean!

whitespacePushAtt: attValue
	"Add a new whitespace preference to the stack as required."

	( attValue ideEquals: 'default' ) ifTrue: [
		wsOn ifFalse: [ ^self ].
		wsStack add: false ideAsBooleanObject.
		wsPreserve := false.
		^self ].
	( attValue ideEquals: 'preserve' ) ifFalse: [ ^self ].
	wsOn ifFalse: [
		wsOn := true.
		wsStack := OrderedCollection new ].
	wsStack add: true ideAsBooleanObject.
	wsPreserve := true! !


! IdXMLReader methodsFor: 'doc handler api' !

characters: charArray start: start length: length
	"Convert the raw characters in charArray to a valid format.
	Add a text node to the current element as required. Strip white
	space if it is not being preserved."

	| trueString |
	wsPreserve
		ifTrue: [ trueString := charArray ideToStringFromZero: start length: length ]
		ifFalse: [ trueString := IdFunctionsDOM stripWhitespace: charArray from: start length: length edges: false ].
	trueString == nil ifTrue: [ ^self ].
	self addNodeToCurrent: (
		self getDocument createTextNodeImpl: trueString ).
	charCount := charCount + trueString ideLength!

endElement: name
	"Reset to the current parent node."

	self whitespacePullElement: assembler getCurrentParent.
	assembler ascendHierarchy!

processingInstruction: target data: data
	"Add a PI node to the current element."

	self addNodeToCurrent: (
		self getDocument createPIImpl: target data: data )!

startDocument
	"Create a new document if none already exists."

	self getDocument == nil ifTrue: [
		assembler storeEmptyDocument ].
	charCount := 0!

startElement: name atts: atts
	"Create a new composite element and add it using the
	document assmebler. Check for whitespace attributes
	and process accordingly."

	| newElement size attName |
	newElement := self getDocument createComposite: name.
	assembler addAndDescendInto: newElement.
	size := atts getLength.
	size == 0 ifTrue: [ ^self ].
	0 to: size - 1 do: [ :i |
		attName := atts getName: i.
		( attName ideEquals: 'xml:space' ) ifTrue: [
			self whitespacePushAtt: ( atts getValue: i ) ].
		newElement setAttribute: attName
			value: ( atts getValue: i ) ]! !


! IdXMLReader methodsFor: 'creation' !

initialize
	"Initialize the receiver with an empty document assembler."

	super initialize.
	assembler := IdDocumentAssembler new.
	wsOn := false.
	wsPreserve := false! !


! IdSingleNodeList class methodsFor: 'constructors' !

forNode: aNode

	| answer |
	answer := IdSingleNodeList new.
	answer initNode: aNode.
	^answer! !


IdSingleNodeList comment: '
	This is a concrete class used to implement the NodeList 
	and NodeSequence interfaces for a single node. This is more
	efficient than using a node list based on collections.'!


! IdSingleNodeList methodsFor: 'convenience' !

getLastOffset
	^0!

positionOfChild: aNode

	childNode == aNode ifTrue: [ ^1 ].
	^-1! !


! IdSingleNodeList methodsFor: 'accessing' !

getLength
	^1!

item: anInteger

	anInteger == 0 ifTrue: [ ^childNode ].
	^nil!

itemImpl: anInteger

	anInteger == 0 ifTrue: [ ^childNode ].
	^nil! !


! IdSingleNodeList methodsFor: 'creation' !

initNode: aNode

	childNode := aNode! !


! IdXMLWriter class methodsFor: 'examples' !

writeXMLDiskExample
	"Write an XML document created from an example method
	in IdXMLReader to a file on disk. This method does not 
	execute, however you can modify the path name shown below
	to refer a valid file name on your network and execute the code
	fragment in a workspace."
"
	IdXMLWriter encodeDocumentLocally: IdXMLReader runSimpleXMLExample
		to: 'c:\xmldocs\TestOutput.xml'
"!

writeXMLTranscriptExample
	"Write an XML document created from an example method
	in IdXMLReader to the Transcript."
"
	IdXMLWriter writeXMLTranscriptExample
"
	self new writeXMLFor: IdXMLReader runSimpleXMLExample
		to: Transcript.
	( Smalltalk at: #TextCollector ifAbsent: [nil] ) ~~ nil ifTrue: [
		Transcript endEntry ]  "required in VisualWorks"! !


! IdXMLWriter class methodsFor: 'class initialization' !

initializeAfterLoad

	ExclChar := Character value: 33.
	AposChar := Character value: 39.! !


! IdXMLWriter class methodsFor: 'services' !

encodeDocumentLocally: aDocument to: aPath
	"Encode an XML representation of aDocument to the local
	file stored at aPath. Overwrite the contents of an existing file
	or create a new one if required."

	self encodeDocumentLocally: aDocument 
		to: aPath 
		writer: IdXMLWriter new!

encodeDocumentLocally: aDocument to: aPath writer: aWriter
	"Encode an XML representation of aDocument to the local
	file stored at aPath using aWriter. Overwrite the contents of 
	an existing file or create a new one if required."

	| aStream |
	aStream := IdPlatformUtilities openFileWriteStream: aPath.
	aStream == nil ifTrue: [ ^self ].
	[ aWriter writeXMLFor: aDocument to: aStream ]
		ideEnsureCompletion: [ aStream close ]! !


IdXMLWriter comment: '
	This is a utility class used to emit an XML encoding of a
	document. It writes the character based output to a
	PrintWriter in Java or a WriteStream in Smalltalk which
	both can be used to direct output to files or other objects.
	It is invoked using the writeXMLFor() instance method
	which takes a node (usually a document) and a stream 
	as parameters.

	A utility method encodeDocumentLocally() is provided to
	write the XML output to a local disk file. This will only be
	functional in Java if the security manager allows.

	Output can be customized using the XMLWriterOptions
	class. You can access the options with getOptions()
	before a writing operation begins and make changes
	to the default values. See the class comment of
	XMLWriterOptions for more specific details.'!


! IdXMLWriter methodsFor: 'accessing' !

getOptions
	^options! !


! IdXMLWriter methodsFor: 'encoding' !

encodeCDATA: aNode

	self writeString: '<'.
	self writeChar: ExclChar.
	self writeString: '[CDATA['.
	self writeString: aNode getNodeValue.
	self writeString: ']]>'!

encodeComment: aNode

	self writeString: '<'.
	self writeChar: ExclChar.
	self writeString: '--'.
	self writeString: aNode getNodeValue.
	self writeString: '-->'!

encodeComposite: aNode

	| tagName startLine |
	tagName := aNode getNodeName.
	self writeStartTagOpen: tagName attributes: aNode getAttributesList.
	self writeString: '>'.
	lineNumStack add: lineNum ideAsIntegerObject.
	self increaseIndent.
	0 to: aNode getLastOffset do: [ :i |
		self encodeNode: ( aNode itemImpl: i ) ].
	self decreaseIndent.
	startLine := lineNumStack last.
	startLine ideAsPrimitiveInt < lineNum ifTrue: [
		lineNum := lineNum + 1.
		self startNewIndentedLine ].
	lineNumStack removeAtIndex: lineNumStack size.
	self writeEndTag: tagName!

encodeDocument: aDocument

	| childNode |
	self getOptions isIncludingVersion ifTrue: [
		self writeVersionInfo ].
	self getOptions isXhtml ifTrue: [
		self writeXHTMLDoctype ].
	0 to: aDocument getLastOffset do: [ :i |
		childNode := aDocument itemImpl: i.
		childNode isTypePROCESSING_INSTRUCTION ifTrue: [  "what else?"
			self encodePI: childNode ] ].
	self writeCr;writeCr.
	self encodeNode: aDocument getDocElement!

encodeEmpty: aNode
	"Including an extra space before the slash improves
	compatibility with older HTML browsers. This behavior
	can be changed using setEmptyTagSpacing()."

	self writeStartTagOpen: aNode getNodeName
		attributes: aNode getAttributesList.
	self getOptions isEmptyTagSpacing
		ifTrue: [ self writeString: ' />' ]
		ifFalse: [ self writeString: '/>' ]!

encodeNode: aNode
	"Still need to add Entity refs."

	| asDocument |
	aNode == nil ifTrue: [ ^self ].
	aNode isTypeTEXT ifTrue: [
		self encodeStringNode: aNode.
		^self ].
	aNode isTypeELEMENT ifTrue: [ 
		aNode hasChildNodes
			ifTrue: [ self encodeComposite: aNode ]
			ifFalse: [ self encodeEmpty: aNode ].
		^self ].
	aNode isTypeDOCUMENT ifTrue: [
		asDocument := aNode.
		self encodeDocument: asDocument.
		^self ].
	aNode isTypeCDATA_SECTION ifTrue: [
		self encodeCDATA: aNode.
		^self ].
	aNode isTypeCOMMENT ifTrue: [
		self encodeComment: aNode.
		^self ].
	aNode isTypePROCESSING_INSTRUCTION ifTrue: [
		self encodePI: aNode.
		^self ].!

encodePI: aNode

	| pi |
	pi := aNode.
	self 
		writeCr;
		writeString: '<?';
		writeString: pi getTarget;
		writeString: ' '.
	self writeString: pi getNodeValue.
	self writeString: '?>'!

encodeStringNode: aNode
	"Encode a text node. Break according to maxLineLength."

	| str newEnd newStart textLength lineSize |
	str := aNode getNodeValue.
	textLength := self getOptions getMaxLineLength - charCount.
	lineSize := textLength - 0.
	str ideLength <= lineSize ifTrue: [ 
		self writeString: ( self insertEntities: str ).
		^self ].
	newEnd := self calculateStringBreak: str from: 0 first: true.
	self writeString: ( 
		self insertEntities: ( str copyFrom: 1 to: newEnd ) ).
	self increaseIndent.
	[ newEnd < str ideLength ] whileTrue: [
		newStart := newEnd + 1.
		newEnd := self calculateStringBreak: str from: newEnd first: false.
		newEnd >= newStart ifTrue: [
			self startNewIndentedLine.
			self writeString: ( 
				self insertEntities: ( str copyFrom: newStart to: newEnd ) ) ] ].
	self decreaseIndent.! !


! IdXMLWriter methodsFor: 'internal' !

calculateStringBreak: aString from: prevEnd first: firstLine
	"Return a break index for aString which is close to the
	desired size and guaranteed to be on a space or the end."

	| suggested safeIndex newLineMax nextChar |
	firstLine
		ifTrue: [ newLineMax := self getOptions getMaxLineLength - charCount ]
		ifFalse: [ newLineMax := self getOptions getMaxLineLength - currentIndent ideLength ].
	suggested := aString ideLength min: ( prevEnd + newLineMax ).
	safeIndex := 0.
	( prevEnd + 1 ) to: aString ideLength do: [ :i |
		nextChar := aString ideCharValueAt: i.
		nextChar == 32 "space" ifTrue: [
			i > suggested ifTrue: [
				safeIndex ~~ 0 ifTrue: [ ^safeIndex ].
				^i ].
			safeIndex := i ] ].
	safeIndex ~~ 0 ifTrue: [ ^safeIndex ].
	^aString ideLength!

decreaseIndent

	currentIndent ideLength <= self getOptions getIndentSize ifTrue: [
		currentIndent := String new.
		^self ].
	currentIndent := currentIndent 
		copyFrom: 1
		to: currentIndent ideLength - self getOptions getIndentSize!

increaseIndent

	| buffer |
	buffer := IdStringBuffer new: self getOptions getIndentSize + currentIndent ideLength.
	buffer appendString: currentIndent.
	1 to: self getOptions getIndentSize do: [ :i |
		buffer appendChar: $  ].
	currentIndent := buffer convertToString!

insertEntities: aString

	| buffer nextChar entityString |
	buffer := IdStringBuffer new: aString ideLength.
	1 to: aString ideLength do: [ :i |
		entityString := nil.
		nextChar := aString ideCharAt: i.
		nextChar ideCharValue == 38  "$&" ifTrue: [ entityString := 'amp' ].
		nextChar ideCharValue == 60  "$<" ifTrue: [ entityString := 'lt' ].
		nextChar ideCharValue == 62  "$>" ifTrue: [ entityString := 'gt' ].
		nextChar ideCharValue == 39  "$'" ifTrue: [ entityString := 'apos' ].
		nextChar ideCharValue == 34          ifTrue: [ entityString := 'quot' ].
		entityString ~~ nil ifTrue: [
				buffer
					appendChar: $&;
					appendString: entityString;
					appendChar: $; ]
			ifFalse: [ buffer appendChar: nextChar ] ].
	^buffer convertToString!

startNewIndentedLine
	"Create a new line and start it with the current indent."

	self writeCr.
	self writeString: currentIndent!

writeEndTag: tagName

	self 
		writeString: '</';
		writeString: tagName;
		writeString: '>'!

writeStartTagOpen: tagName attributes: attributeMap

	| nextAtt count indentAtts |
	isInBody ifTrue: [
			lineNum := lineNum + 1.
			self writeCr ]
		ifFalse: [ isInBody := true ].
	self
		writeString: currentIndent;
		writeString: '<';
		writeString: tagName.
	attributeMap == nil ifTrue: [ ^self ].
	count := attributeMap getLength.
	count == 0 ifTrue: [ ^self ].
	indentAtts := count > 1.
	self increaseIndent.
	self increaseIndent.  "increase twice to stand out from children"
	0 to: count - 1 do: [ :i |
		nextAtt := attributeMap item: i.
		indentAtts 
			ifTrue: [ self startNewIndentedLine ]
			ifFalse: [ self writeString: ' ' ].
		self writeString: nextAtt getNodeName.
		self writeString: '='.
		self writeChar: AposChar.
		self writeString: nextAtt getNodeValue.
		self writeChar: AposChar ].
	self decreaseIndent.
	self decreaseIndent.!

writeVersionInfo
	"Write the XML version information (in a way which is 
	safe for Java and Smalltalk compilers)."

	self
		writeString: '<?xml version=';
		writeChar: AposChar;
		writeString: '1.0';
		writeChar: AposChar;
		writeString: '?>'!

writeXHTMLDoctype
	"Write the XHTML frameset DOCTYPE statement over three lines."

	self
		writeCr;
		writeString: '<';
		writeChar: ExclChar;
		writeString: 'DOCTYPE html PUBLIC ';
		writeChar: AposChar;
		writeString: '-//W3C//DTD XHTML 1.0 Frameset//EN';
		writeChar: AposChar;
		writeCr;
		writeString: '    ';
		writeChar: AposChar;
		writeString: 'http://www.w3.org/TR/xhtml1/DTD/frameset.dtd';
		writeChar: AposChar;
		writeString: '>'!


! IdXMLWriter methodsFor: 'write stream' !

writeChar: anObject

	charCount := charCount + 1.
	writeStream nextPut: anObject!

writeCr

	charCount := 0.
	writeStream cr!

writeString: anObject

	charCount := charCount + anObject ideLength.
	writeStream nextPutAll: anObject! !


! IdXMLWriter methodsFor: 'creation' !

initialize

	super initialize.
	options := IdXMLWriterOptions new.
	charCount := 0.
	isInBody := false! !


! IdXMLWriter methodsFor: 'services' !

writeXMLFor: aNode
	"Return a string containing the XML encoding of aNode."

	| stream |
	stream := IdPlatformUtilities openWriteStream: 500.
	self writeXMLFor: aNode to: stream.
	^stream contents!

writeXMLFor: aNode to: aStream
	"Write an XML encoding of aNode to aStream. Note
	that the calling method is responsible for closing aStream
	when done."

	writeStream := aStream.
	currentIndent := String new.
	lineNumStack := OrderedCollection new.
	lineNum := 1.
	self encodeNode: aNode! !


! IdXMLWriterOptions class methodsFor: 'accessing' !

getDefaultIndentSize
	^DefaultIndentSize!

setDefaultIndentSize: anInteger
	"Set the default indent size to anInteger. The indent
	size refers to the number of spaces added to the beginning
	of each output line on which a new XML element begins.
	The indent will also be used to position the end tags and
	attributes. Below is a sample of output using the default
	indent size of 2.

<ancestor-element>
  <parent-element>
	<child-element
		start-indent='6pt'
		end-indent='6pt'
		font-size='12pt'>Some plain text content</child-element>
  </parent-element>
</ancestor-element>

	The indent size can also be set directly on instances of this class."

	DefaultIndentSize := anInteger! !


! IdXMLWriterOptions class methodsFor: 'class init' !

initializeAfterLoad

	DefaultIndentSize := 2! !


IdXMLWriterOptions comment: '
	This class stores options for using XMLWriter.
	If none is specified a default instance is created using
	values specified in this class.

	Output can be customized by changing the indentSize
	variable which will alter the depth of the indented
	XML tag encoding. See the setDefaultIndentSize() class method
	comment for more. You can also turn on emptyTagSpacing
	which inserts a space before the slash character in empty
	element end tags (for HTML compatibility). Refer to 
	isEmptyTagSpacing() for more. The maxLineLength variable
	(default 72) can also be changed. This is used to break the 
	output into lines at recognized word breaks.

	The XML version processing instruction is normally written
	at the start of each file. It is ignored if the includingVersion flag
	is set to false using setIncludingVersion().

	The isXhtml flag is used to insert the XHTML DOCTYPE statement 
	in the output files. Default is false.'!


! IdXMLWriterOptions methodsFor: 'accessing' !

getIndentSize
	"Return the indent size. See class method comment 
	for setDefaultIndentSize() for more."

	^indentSize!

getMaxLineLength
	"Return the maximum line length (number of characters)."

	^maxLineLength!

isEmptyTagSpacing
	"Return true if spaces are to be inserted in empty tags 
	before the slash, else false. Default is false. Returning true
	provides compatibility with older HTML web browsers (as 
	recommended by the w3). This value can be set to true 
	using the setEmptyTagSpacing() method."

	^emptyTagSpacing!

isIncludingVersion
	"Return true if the '<?xml version='1.0'?>' processing instruction
	should be written at the start of the file. Default is true."

	^includingVersion!

isXhtml
	"Return true if an XHTML DOCTYPE statement should be 
	written at the start of the file. Default is false."

	^isXhtml!

setEmptyTagSpacing: aBoolean
	"See method comment for isEmptyTagSpacing()."

	emptyTagSpacing := aBoolean!

setIncludingVersion: aBoolean
	"See method comment for isIncludingVersion()."

	includingVersion := aBoolean!

setIndentSize: anInteger
	"See class method comment for setDefaultIndentSize()."

	indentSize := anInteger!

setMaxLineLength: anInteger
	"Set the maximum line length to anInteger number
	of characters. Default is 72."

	maxLineLength := anInteger!

setXhtml: aBoolean
	"See method comment for isXhtml()."

	isXhtml := aBoolean! !


! IdXMLWriterOptions methodsFor: 'creation' !

initialize

	indentSize := self class getDefaultIndentSize.
	emptyTagSpacing := false.
	includingVersion := true.
	maxLineLength := 72.
	isXhtml := false! !


IdNamedNodeList comment: '
	This class implements the NamedNodeMap interface in DOM.

	Comment from the DOM specification:

	Objects implementing the NamedNodeMap interface are used to represent 
	collections of nodes that can be accessed by name. Note that NamedNodeMap 
	does not inherit from NodeList; NamedNodeMaps are not maintained in any
	particular order. Objects contained in an object implementing NamedNodeMap 
	may also be accessed by an ordinal index, but this is simply to allow convenient 
	enumeration of the contents of a NamedNodeMap, and does not imply that the 
	DOM specifies an order to these Nodes.'!


! IdNamedNodeList methodsFor: 'services' !

cloneSelfAndNodes: deep
	"Return a copy of the receiver which includes copies
	of all the contained nodes. Use a deep clone on the nodes
	only if deep is true."

	| answer aNode |
	answer := IdNamedNodeList new.
	answer prepChildNodes: self getLength.
	0 to: self getLastOffset do: [ :i |
		aNode := self item: i.
		answer setNamedItem: ( aNode cloneNode: deep ) ].
	^answer!

getNamedItem: aName
	"Return a Node (of any type) with the specified name, 
	or nil if aName did not identify any node in the receiver."

	| aNode |
	1 to: self getLength do: [ :i |
		aNode := self protectedNodes ideAtIndex: i.
		( aNode getNodeName ideEquals: aName ) ifTrue: [ ^aNode ] ].
	^nil!

prepChildNodes: anInteger
	"Initialize the child nodes collections to a new
	collection anInteger in size. This method lets you
	ensure that the receiver has enough space for
	adding all planned nodes without growing the
	child nodes collection."

	self protectedNodes: ( OrderedCollection new: anInteger )!

removeNamedItem: aName
	"Remove the node stored under the specified name.
	Return the removed node if successful, or nil if aName 
	did not identify any node in the receiver."

	| aNode |
	1 to: self getLength do: [ :i |
		aNode := self protectedNodes ideAtIndex: i.
		( aNode getNodeName ideEquals: aName ) ifTrue: [ 
			self protectedNodes remove: aNode.
			^aNode ] ].
	^nil!

setNamedItem: aNode
	"Store aNode in the receiver at its node name.
	This will replace any existing node at the same name,
	although its position in nodes would likely change.
	If the new Node replaces an existing node with the 
	same name the previously existing Node is returned, 
	otherwise nil is returned."

	| old |
	old := self removeNamedItem: aNode getNodeName.
	self protectedNodes add: aNode.
	^old! !


! IdNodeListImpl class methodsFor: 'services' !

appendNodesAsList: aCollection to: aNode
	"Create a new instance using aCollection and add
	its contents to aNode."

	| workList |
	workList := IdNodeListImpl forNodes: aCollection.
	0 to: workList getLastOffset do: [ :i |
		aNode appendNodeImpl: ( workList itemImpl: i ) ]! !


! IdNodeListImpl class methodsFor: 'constructors' !

forNodes: aCollection

	| answer |
	answer := IdNodeListImpl new.
	answer initNodes: aCollection.
	^answer! !


IdNodeListImpl comment: '
	This class implements the NodeList interface in DOM. It
	subclasses NodeCollection to provide a single argument
	constructor (containing a collection of nodes) in Java. 
	Refer to the NodeCollection class comment for more. It also 
	contains a convenience class method appendNodesAsList()
	which is useful during editing operations.'!


! IdNodeListImpl methodsFor: 'creation' !

initNodes: aCollection
	"Implement a single argument constructor to store
	aCollection in the new instance. It is provided in this
	subclass to prevent it from restricting named node
	lists from being created with no nodes."

	self protectedNodes: aCollection! !


! IdAttributeImpl class methodsFor: 'constructors' !

forName: aName
	"Return a new instance for aName."

	| newAtt |
	newAtt := IdAttributeImpl new.
	newAtt initName: aName.
	^newAtt! !


IdAttributeImpl comment: '
	This class implements the DOM Attr interface.

	Comment from the DOM specification:

	The Attr interface represents an attribute in an Element object. Typically 
	the allowable values for the attribute are defined in a document type definition.

	Attr objects inherit the Node interface, but since they are not actually child 
	nodes of the element they describe, the DOM does not consider them part of 
	the document tree. Thus, the Node attributes parentNode, previousSibling, 
	and nextSibling have a null value for Attr objects. The DOM takes the view 
	that attributes are properties of elements rather than having a separate identity 
	from the elements they are associated with; this should make it more efficient 
	to implement such features as default attributes associated with all elements 
	of a given type. Furthermore, Attr nodes may not be immediate children of a 
	DocumentFragment. However, they can be associated with Element nodes 
	contained within a DocumentFragment. In short, users and implementors of the 
	DOM need to be aware that Attr nodes have some things in common with other 
	objects inheriting the Node interface, but they also are quite distinct.

	The attribute''s effective value is determined as follows: if this attribute has been 
	explicitly assigned any value, that value is the attribute''s effective value; otherwise, 
	if there is a declaration for this attribute, and that declaration includes a default 
	value, then that default value is the attribute''s effective value; otherwise, the 
	attribute does not exist on this element in the structure model until it has been 
	explicitly added. Note that the nodeValue attribute on the Attr instance can also be 
	used to retrieve the string version of the attribute''s value(s). 

	In XML, where the value of an attribute can contain entity references, the child 
	nodes of the Attr node provide a representation in which entity references are not 
	expanded. These child nodes may be either Text or EntityReference nodes. Because 
	the attribute type may be unknown, there are no tokenized attribute values.'!


! IdAttributeImpl methodsFor: 'accessing' !

getName
	"Return the name of this attribute."

	^name!

getNodeName
	^self getName!

getNodeType
	"Return an integer representing the receiver's
	node type. All instances share the same node
	type: ATTRIBUTE_NODE."

	^IdDOMConstants ATTRIBUTE_NODE!

getNodeValue
	^self getValue!

getSpecified
	"Return true if the receiver was explicitly assigned
	for the Element in which it occurs, else return false.
	Default is false. Note that false is returned for default
	attribute values derived from the DocumentType."

	^specified!

getValue
	"Returns the value of this attribute as a string. Currently
	this assumes only TEXT values. Need to support entity references.

	Note that an effective value (see class comment) of the null string
	would be returned as a zero length string. If the attribute has no 
	effective value, then this method will return nil."

	value == nil ifTrue: [ ^nil ].
	value isTypeTEXT ifTrue: [ ^value getNodeValue ].
	^nil!

setNodeValue: aString
	"Set the value of this node to aString."

	self setValue: aString!

setSpecified: aBoolean
	"Set the receiver's specified variable to aBoolean.
	This should only be set to true for attributes which
	are explicitly set for the Element in which they occur."

	specified := aBoolean!

setValue: aString
	"Set the receiver's value to aString. This gets stored
	internally as a Text node in accordance with DOM."

	value ~~ nil ifTrue: [
		value setParent: nil ].
	value := ( IdStringNode asTEXT: aString ).
	value setParent: self! !


! IdAttributeImpl methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver. Specify the
	same attribute name and value."

	| answer |
	answer := IdAttributeImpl forName: self getNodeName.
	answer
		setSpecified: self getSpecified;
		setValue: self getValue.
	^answer! !


! IdAttributeImpl methodsFor: 'services' !

toString
	"Return a string representation of the receiver.
	Only the attribute value will be included as required
	by the DOM spec. Note that if the getNodeValue method
	returns nil (no specified or default value) then this
	method will return an empty string."

	| asString |
	asString := self getNodeValue.
	^asString == nil
		ifTrue: [ String new ]
		ifFalse: [ asString ]!

value
	"Return a string representation of the receiver.
	This implements the XSL definition of value."

	^self toString! !


! IdAttributeImpl methodsFor: 'creation' !

initName: aName

	self setSpecified: false.
	name := aName! !


! IdDocumentFragmentImpl class methodsFor: 'accessing' !

getDefaultListSize
	"Return the default size used to initialize new
	childNodes collections."

	^DefaultListSize!

setDefaultListSize: anInteger
	"Set the default size used to initialize new
	childNodes collections to anInteger."

	DefaultListSize := anInteger! !


! IdDocumentFragmentImpl class methodsFor: 'class initialization' !

initializeAfterLoad
	"Initialize the default collection size variable after
	loading the class."

	DefaultListSize := 8! !


! IdDocumentFragmentImpl class methodsFor: 'constructors' !

forMaster: aDocument
	"Return a new document fragment which references
	aDocument as its master document."

	| answer |
	answer := IdDocumentFragmentImpl new.
	answer initMaster: aDocument.
	^answer! !


IdDocumentFragmentImpl comment: '
	This class implements the DocumentFragment interface. It also
	serves as the superclass of the DocumentImpl class and implements
	behavior common to both classes.

	Comment from the DOM specification:

	DocumentFragment is a ''lightweight'' or ''minimal'' Document object. It is 
	very common to want to be able to extract a portion of a document''s tree or 
	to create a new fragment of a document. Imagine implementing a user 
	command like cut or rearranging a document by moving fragments around. 
	It is desirable to have an object which can hold such fragments and it is quite 
	natural to use a Node for this purpose. While it is true that a Document object 
	could fulfil this role, a Document object can potentially be a heavyweight object, 
	depending on the underlying implementation. What is really needed for this is 
	a very lightweight object. DocumentFragment is such an object.

	Furthermore, various operations -- such as inserting nodes as children of another 
	Node -- may take DocumentFragment objects as arguments; this results in all the 
	child nodes of the DocumentFragment being moved to the child list of this node.

	The children of a DocumentFragment node are zero or more nodes representing 
	the tops of any sub-trees defining the structure of the document. DocumentFragment 
	nodes do not need to be well-formed XML documents (although they do need to follow 
	the rules imposed upon well-formed XML parsed entities, which can have multiple 
	top nodes). For example, a DocumentFragment might have only one child and that 
	child node could be a Text node. Such a structure model represents neither an HTML 
	document nor a well-formed XML document. 

	When a DocumentFragment is inserted into a Document (or indeed any other Node 
	that may take children) the children of the DocumentFragment and not the DocumentFragment 
	itself are inserted into the Node. This makes the DocumentFragment very useful when 
	the user wishes to create nodes that are siblings; the DocumentFragment acts as the 
	parent of these nodes so that the user can use the standard methods from the Node 
	interface, such as insertBefore() and appendChild().'!


! IdDocumentFragmentImpl methodsFor: 'accessing' !

getCurrentDocument
	"Return the master document."

	^self getMasterDocument!

getMasterDocument
	"Return the master document. For documents
	this will return a reference to themselves."

	^masterDocument!

getNodeName
	"Return the node name used for all instances:
	document-fragment."

	^'document-fragment'!

getNodeType
	"Return an integer representing the receiver's
	node type. All instances share the same node
	type: DOCUMENT_FRAGMENT_NODE."

	^IdDOMConstants DOCUMENT_FRAGMENT_NODE!

setMasterDocument: aDocument
	"Set the master document to aDocument."

	masterDocument := aDocument!

setParentNode: aParent
	"Set the receiver's parent node. Override method 
	defined in superclass to prevent this variable from 
	being set for documents and document fragments.
	The getParentNode() method will always return nil."! !


! IdDocumentFragmentImpl methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver which
	contains no content."

	^IdDocumentFragmentImpl forMaster: self getMasterDocument! !


! IdDocumentFragmentImpl methodsFor: 'internal' !

confirmChildNodeType: aNode
	"Raise a HIERARCHY_REQUEST_ERR exception if aNode
	is not a valid child node type."

	( aNode isTypeDOCUMENT_FRAGMENT 
		or: [ aNode isTypeATTRIBUTE
		or: [ aNode isTypeDOCUMENT
		or: [ aNode isTypeDOCUMENT_TYPE
		or: [ aNode isTypeENTITY ]]]] ) ifTrue: [
			IdDOMImpl newExceptionHierarchyRequest signal ]!

getLiveNodeCollection
	"Return a 'live' collection of all the receiver's 
	child nodes. This method is private to ensure 
	that the returned collections are not modified 
	directly by any calling method. Lazy initialize
	to the default list size if no collection exists yet."

	childNodes == nil ifTrue: [
		self allocateChildNodes: DefaultListSize ].
	^childNodes!

internalReplaceChildren: aCollection
	"Completely replace the children collection with
	aCollection (not a copy). Assume all children already
	reference the receiver as parent."

	childNodes := aCollection! !


! IdDocumentFragmentImpl methodsFor: 'editing' !

appendNodeImpl: aNode
	"Adds aNode to the end of the list of children of this node. 
	If aNode is already in the tree, it is first removed. Return aNode.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if aNode is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if aNode was created
	from a different document than the one that created the receiver."

	aNode == nil ifTrue: [ ^aNode ].
	aNode isTypeDOCUMENT_FRAGMENT ifTrue: [
		^self appendDocFragment: aNode ].
	self confirmChildNodeType: aNode.
	self confirmChildNotAncestor: aNode.
	self confirmSameOwnerDocument: aNode.
	aNode getParent ~~ nil ifTrue: [
		aNode getParent removeChild: aNode ].

	self getLiveNodeCollection add: aNode.
	aNode setParent: self.
	^aNode!

removeChildImpl: oldChild
	"Remove oldChild from the list of children and return it.
	Raise a NOT_FOUND_ERR exception if oldChild is not a child."

	self == oldChild getParent ifFalse: [
		IdDOMImpl newExceptionNotFound signal ].
	oldChild setParent: nil.
	childNodes ~~ nil ifTrue: [
		childNodes remove: oldChild ].
	^oldChild! !


! IdDocumentFragmentImpl methodsFor: 'creation' !

initialize
	"Explicitly define the zero argument constructor to keep
	Java compiler happy for subclasses. Instances of this class
	should be created using the single argument constructor 
	containing the master document."

	super initialize!

initMaster: aDocument
	"Initialize the master document to aDocument."

	self setMasterDocument: aDocument! !


! IdDocumentFragmentImpl methodsFor: 'services' !

allocateChildNodes: anInteger
	"Initialize the receiver's childNodes collection to
	a size equal to anInteger."

	childNodes := OrderedCollection new: anInteger!

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting a document fragment and 
	return the result of processing this visit."

	^aVisitor visitDocumentFragment: self! !


IdElementNode comment: '
	This abstract class implements the DOM Element interface.
	It should not be used as a concrete class as it will likely be
	phased out in a future release. Use CompositeNode instead.

	Comment from the DOM specification:

	By far the vast majority of objects (apart from text) that authors 
	encounter when traversing a document are Element nodes. Assume 
	the following XML document: 

		<elementExample id=''demo''>
  			<subelement1/>
  			<subelement2>
				<subsubelement/>
			</subelement2>
		</elementExample>

	When represented using DOM, the top node is an Element node for 
	''elementExample'', which contains two child Element nodes, one for 
	''subelement1'' and one for ''subelement2''. ''subelement1'' contains no 
	child nodes.

	Elements may have attributes associated with them; since the Element 
	interface inherits from Node, the generic Node interface method getAttributes 
	may be used to retrieve the set of all attributes for an element. There are 
	methods on the Element interface to retrieve either an Attr object by name 
	or an attribute value by name. In XML, where an attribute value may contain 
	entity references, an Attr object should be retrieved to examine the possibly 
	fairly complex sub-tree representing the attribute value. On the other hand, 
	in HTML, where all attributes have simple string values, methods to directly 
	access an attribute value can safely be used as a convenience.'!


! IdElementNode methodsFor: 'accessing' !

getAttributesList
	"Return a named node list containing all of the
	receiver's attributes, or nil if none are stored.
	This method returns the NamedNodeList type
	rather than the W3 interface."

	^attributes!

getNodeName
	"Return the receiver's tag name."

	^self getTagName!

getNodeType
	"Return an integer representing the receiver's
	node type. All instances share the same node
	type: ELEMENT_NODE."

	^IdDOMConstants ELEMENT_NODE!

getTagName
	"Return the string which is the receiver's type, 
	or name. Note that all operations on a tag name 
	are case sensitive."

	^tagName!

setTagName: aTagName
	tagName := aTagName! !


! IdElementNode methodsFor: 'deprecated' !

addChildrenByAttribute: attName to: allNodes
	"Deprecated method. Do not use."

	( self getAttribute: attName ) ~~ nil ifTrue: [
		allNodes add: self ].
	0 to: self getLastOffset do: [ :i |
		( self itemImpl: i ) addChildrenByAttribute: attName to: allNodes ]!

getElementsByAttribute: attName
	"Deprecated method. Do not use."

	| allNodes |
	attName == nil ifTrue: [ ^IdNodeListImpl forNodes: nil ].
	allNodes := OrderedCollection new.
	self addChildrenByAttribute: attName to: allNodes.
	^IdNodeListImpl forNodes: allNodes! !


! IdElementNode methodsFor: 'queries' !

ideIsElementNode
	"Return true if the receiver is an element node,
	else return false."

	^true! !


! IdElementNode methodsFor: 'services' !

cloneAttributesList: anAttributesList
	"Set the receiver's attribute list to a clone of
	anAttributesList. Note that this will replace all 
	current attributes."

	anAttributesList == nil ifTrue: [ ^self ].
	self replaceAllAttributes: ( anAttributesList cloneSelfAndNodes: true )!

getAttribute: aName
	"Return the first attribute value (String) stored in 
	the receiver which is identified by aName, or nil 
	if none is found. Return an empty string if that 
	attribute does not have a specified or default value. "

	| attr |
	attr := self getAttributeNode: aName.
	attr == nil ifTrue: [ ^nil ].
	^attr getValue!

getAttributeNode: aName
	"Return the first attribute node stored in the receiver 
	which is identified by aName. or nil if none is found."

	attributes == nil ifTrue: [ ^nil ].
	^attributes getNamedItem: aName!

normalize
	"Puts all Text nodes in the sub-tree underneath the
	receiver into a 'normal' form where only markup 
	(e.g., tags, comments, PIs, CDATASections, etc.) and 
	entity references separates Text nodes."

	| child newList lastText childElement |
	self getLength > 0 ifFalse: [ ^self ].
	newList := OrderedCollection new: self getLength.
	lastText := nil.
	0 to: self getLastOffset do: [ :i |
		child := self itemImpl: i.
		child isTypeELEMENT ifTrue: [ 
			childElement := child.
			childElement normalize ].
		child isTypeTEXT ifTrue: [ 
				lastText == nil ifTrue: [ 
						lastText := child.
						newList add: child ]
					ifFalse: [
						lastText appendData: child getNodeValue ] ]
			ifFalse: [ 
				lastText := nil.
				newList add: child ] ].
	self internalReplaceChildren: newList!

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting an element node and 
	return the result of processing this visit."

	^aVisitor visitElement: self!

removeAttribute: aName
	"Removes the specified attribute by aName. If the
	attribute is not found do nothing."

	attributes == nil ifTrue: [ ^self ].
	attributes removeNamedItem: aName!

removeAttributeNode: anAttribute
	"Removes anAttribute from the receiver.
	Return anAttribute.
	Raise the NOT_FOUND_ERR exception if the
	attribute is not found."

	| existing |
	existing := self getAttributeNode: anAttribute getName.
	( existing == nil or: [ existing ~~ anAttribute ] ) ifTrue: [
		IdDOMImpl newExceptionNotFound signal ].
	self removeAttribute: anAttribute getNodeName.
	^anAttribute!

replaceAllAttributes: anAttributesList
	"Replace the receiver's attributes with anAttributesList."

	attributes := anAttributesList!

setAttribute: aName value: aString
	"Adds a new attribute/value pair to the receiver. 
	If an attribute called aName is already present, 
	it's value is changed to be that of aString.
	Raise the INVALID_CHARACTER_ERR exception if an
	invalid character is specified in aName."

	| att |
	att := self factoryDocument createAttributeImpl: aName.
	att setValue: aString.
	self setAttributeNode: att!

setAttributeNode: anAttribute
	"Adds anAttribute to the receiver. If an attribute by
	the same name is already present, replace it.
	If the newAttr attribute replaces an existing attribute with 
	the same name, the previously existing Attr node is returned, 
	otherwise nil is returned.
	Need to implement exceptions."

	| existing |
	existing := self getAttributeNode: anAttribute getName.
	attributes == nil ifTrue: [
		attributes := IdNamedNodeList new ].
	attributes setNamedItem: anAttribute.
	^existing!

toString
	"Return a string representation of the receiver."

	| valueString |
	valueString := self value.
	valueString ideLength > 50 ifTrue: [
		valueString := ( valueString copyFrom: 1 to: 50 ), '...' ].
	^'<', self getTagName idePrintPrimitive, '>', valueString, 
		'</', self getTagName idePrintPrimitive, '>'!


! IdStringNode class methodsFor: 'convenience' !

asCDATA: aString
	"Return a new instance formatted to represent a
	CDATA node which contains aString."

	^self forNodeType: IdDOMConstants CDATA_SECTION_NODE
		string: aString!

asCOMMENT: aString
	"Return a new instance formatted to represent a
	COMMENT node which contains aString."

	^self forNodeType: IdDOMConstants COMMENT_NODE
		string: aString!

asTEXT: aString
	"Return a new instance formatted to represent a
	Text node which contains aString."

	^self forNodeType: IdDOMConstants TEXT_NODE
		string: aString! !


! IdStringNode class methodsFor: 'constructors' !

forNodeType: anInteger string: aString
	"Return a new instance formatted to represent
	aString with a node type of anInteger."

	| newNode |
	newNode := IdStringNode new.
	newNode initNodeType: anInteger string: aString.
	^newNode! !


IdStringNode comment: '
	This class provides support for nodes which contain simple
	string content. It defines an instance variable ''data'' which stores
	this String value and provides methods to access and edit the data. 
	It implements the DOM CharacterData, Text, Comment and CDATASection
	interfaces as explained below.

	This class supports multiple node types (ie. Coment, Text, CDATA, etc.)
	using the nodeType variable. Valid types are defined by the DOM Node 
	interface (or IdDOMConstants in Smalltalk). NodeImpl also provides a 
	static method nodeTypeUnknown() which returns a valid node type (-1) 
	for any node which is otherwise unrecognized.'!


! IdStringNode methodsFor: 'services' !

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting a string node and 
	return the result of processing this visit."

	^aVisitor visitString: self!

splitText: offset
	"Breaks this Text node into two Text nodes at the specified 
	offset, keeping both in the tree as siblings. This node then only 
	contains all the content up to the offset point. And a new Text 
	node, which is inserted as the next sibling of this node, contains 
	all the content at and after the offset point.
	Raise an INDEX_SIZE_ERR exception if the specified offset
	is negative or greater than the number of characters in data.
	Not yet implemented."

	| mySize |
	mySize := self getLength.
	( offset < 0 or: [ offset > mySize ] ) ifTrue: [
		IdDOMImpl newExceptionIndexSize signal ].
	^nil!

toString
	"Return the data string stored in the receiver."

	^self getNodeValue!

valueToBuffer: stringBuffer
	"Append the receiver's data to stringBuffer as required
	by the XSL definition of value."

	( self isTypeTEXT 
		or: [ self isTypeCOMMENT
		or: [ self isTypePROCESSING_INSTRUCTION ]] ) ifTrue: [
			stringBuffer appendString: self getData ]! !


! IdStringNode methodsFor: 'accessing' !

getData
	"Return the data string stored by the receiver."

	^data!

getLength
	"Return the length of the receiver's data string."

	self getData == nil ifTrue: [ ^0 ].
	^self getData ideLength!

getNodeName
	"Return the node name used for the receiver. It will
	be either comment, text or cdata-section."

	self isTypeTEXT ifTrue: [ ^'text' ].
	self isTypeCOMMENT ifTrue: [ ^'comment' ].
	self isTypeCDATA_SECTION ifTrue: [ ^'cdata-section' ].
	^nil!

getNodeType
	"Return an integer representing the receiver's
	node type. Node types are statically defined in the
	Node class and can be accessed via class methods.
	Instances of this class can have node types equal
	to TEXT, COMMENT or CDATA."

	^nodeType!

getNodeValue
	"Returns the value of the receiver as a string. Return
	the data string for all instances."

	^self getData!

setData: aString
	"Set the data string stored by the receiver to aString.
	Raise a NO_MODIFICATION_ALLOWED_ERR DOMException 
	if the receiver is read only (not yet implemented)."

	data := aString!

setNodeValue: aString
	"Set the value of this node to aString."

	self setData: aString! !


! IdStringNode methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver. Specify the
	same node type and data."

	^IdStringNode forNodeType: self getNodeType string: self getData! !


! IdStringNode methodsFor: 'convenience' !

asStringNode
	"Return the receiver."

	^self ideYourself! !


! IdStringNode methodsFor: 'editing' !

appendData: aString
	"Append aString to the end of the receiver's data string.
	Raise a NO_MODIFICATION_ALLOWED_ERR DOMException 
	if the receiver is read only (not yet implemented)."

	| buffer mySize |
	self setLock.
	mySize := self getLength.
	buffer := IdStringBuffer new: mySize + aString ideLength.
	buffer appendString: self getData.
	buffer appendString: aString.
	self setData: buffer convertToString.
	self resetLock.!

deleteData: offset count: count
	"Delete a count number of characters immediately after 
	the character specified by offset. An offset of zero will delete 
	at the beginning of the data string.
	Raise an INDEX_SIZE_ERR exception if the specified offset
	is negative or greater than the number of characters in data."

	| buffer mySize end |
	count == 0 ifTrue: [ ^self ].
	mySize := self getLength.
	( offset < 0 or: [ offset > mySize or: [ count < 0 ]] ) ifTrue: [
		IdDOMImpl newExceptionIndexSize signal ].
	offset == mySize ifTrue: [ ^self ].
	self setLock.
	buffer := IdStringBuffer new: mySize - count.
	offset > 0 ifTrue: [
		buffer appendString: ( self getData copyFrom: 1 to: offset ) ].
	end := offset + count.
	end < mySize ifTrue: [
		buffer appendString: ( self getData copyFrom: end + 1 to: mySize ) ].
	self setData: buffer convertToString.
	self resetLock!

insertData: offset string: aString
	"Insert aString immediately after the character specified 
	by offset. An offset of zero will insert at the beginning of the
	data string.
	Raise an INDEX_SIZE_ERR exception if the specified offset
	is negative or greater than the number of characters in data."

	| buffer mySize |
	mySize := self getLength.
	( offset < 0 or: [ offset > mySize ] ) ifTrue: [
		IdDOMImpl newExceptionIndexSize signal ].
	offset == mySize ifTrue: [
		self appendData: aString.
		^self ].
	self setLock.
	buffer := IdStringBuffer new: mySize + aString ideLength.
	offset > 0 ifTrue: [
		buffer appendString: ( self getData copyFrom: 1 to: offset ) ].
	buffer appendString: aString.
	buffer appendString: ( self getData copyFrom: offset + 1 to: mySize ).
	self setData: buffer convertToString.
	self resetLock.!

replaceData: offset count: count string: aString
	"Replace the characters starting at the specified 
	character offset with the specified string.
	Raise an INDEX_SIZE_ERR exception if the specified offset
	is negative or greater than the number of characters in data, or if 
	the specified count is negative."

	self deleteData: offset count: count.
	self insertData: offset string: aString!

substringData: offset count: count 
	"Return a copy of the characters from offset (a zero-based
	index) for a length of count. An offset of zero will copy from
	the beginning of the data string. 
	Raise an INDEX_SIZE_ERR exception if the specified offset
	is negative or greater than the number of characters in data, or if 
	the specified count is negative."

	| mySize |
	mySize := self getLength.
	( offset < 0 or: [ offset > mySize or: [ count < 0 ]] ) ifTrue: [
		IdDOMImpl newExceptionIndexSize signal ].
	offset == mySize ifTrue: [ ^String new ].
	^self getData copyFrom: offset + 1 to: offset + count! !


! IdStringNode methodsFor: 'creation' !

initialize
	"Explicitly define the zero argument constructor to keep
	Java compiler happy for subclasses. Instances of this class
	should be created using the two argument constructor 
	containing the node type and string content."

	super initialize!

initNodeType: aNodeType string: aString
	"Initialize the receiver to represent aString as 
	a aNodeType (as defined by class variables in the 
	Node class)."

	nodeType := aNodeType.
	self setData: aString! !


! IdStringNode methodsFor: 'deprecated' !

clonePartialEnd: offsetTrail pos: position cut: cut
	"Deprecated method. Do not use."

	| offset max |
	offset := offsetTrail getIndexAtPosition: position.
	offset <= 0 ifTrue: [ ^nil ].
	max := self getData ideLength min: offset.
	^IdStringNode forNodeType: self getNodeType 
		string: ( self getData copyFrom: 1 to: max )!

clonePartialStart: offsetTrail pos: position cut: cut
	"Deprecated method. Do not use."

	| offset |
	offset := offsetTrail getIndexAtPosition: position.
	offset < 0 ifTrue: [ ^nil ].
	^IdStringNode forNodeType: self getNodeType 
		string: ( self getData copyFrom: offset + 1 to: self getData ideLength )! !


! IdNodeSequenceIterator class methodsFor: 'constructors' !

forNode: aNode

	| answer |
	answer := IdNodeSequenceIterator new.
	answer initNode: aNode.
	^answer!

forNodes: aNodeSequence

	| answer |
	answer := IdNodeSequenceIterator new.
	answer initNodes: aNodeSequence.
	^answer! !


IdNodeSequenceIterator comment: '
	This is a concrete class used to iterate over node
	sequences. It can be used on nodes or node sequences.'!


! IdNodeSequenceIterator methodsFor: 'internal' !

advance: forward

	forward
		ifTrue: [ gapPosition := gapPosition + 1 ]
		ifFalse: [ 
			gapPosition <= 0 ifTrue: [ ^nil ].
			gapPosition := gapPosition - 1 ].
	^sequence itemImpl: gapPosition!

prepareSequence: aNodeSequence

	gapPosition := -1.
	sequence := aNodeSequence! !


! IdNodeSequenceIterator methodsFor: 'creation' !

initNode: aNode
	"Initialize the receiver on aNode by treating it
	as a node sequence."

	| childSequence |
	childSequence := aNode.
	self prepareSequence: childSequence!

initNodes: aNodeSequence
	self prepareSequence: aNodeSequence! !


! IdDOMExceptionImpl class methodsFor: 'constructors' !

forExceptionCode: exceptionCode msg: msg
	"Return a new DOM exception for exceptionCode and msg."

	^IdDOMExceptionImpl new
		code: exceptionCode;
		setMessage: msg;
		yourself! !


IdDOMExceptionImpl comment: '
	A concrete DOM exception class.'!


! IdRangeEndPoint class methodsFor: 'constructors' !

forParent: aParent offset: anOffset

	| answer |
	answer := IdRangeEndPoint new.
	answer initParent: aParent offset: anOffset.
	^answer! !


IdRangeEndPoint comment: '
	This is a concrete class used to represent DOM range 
	end points. It subclasses the HierarchyPoint superclass
	to support Node parent objects. Instances of this class are 
	used by RangeImpl to model each end point of a range.'!


! IdRangeEndPoint methodsFor: 'accessing' !

getParent
	^parent!

setParent: aNode

	self invalidateOffsetTrail.
	parent := aNode! !


! IdRangeEndPoint methodsFor: 'internal' !

calculateReverseIndexes: indexVector

	self getParent == nil ifTrue: [ ^self ].
	self getParent getParent == nil ifTrue: [ ^self ].
	self getParent getParent addAncestorOffsetsFrom: self getParent
		into: indexVector!

sharesParent: anEndPoint
	^self getParent == anEndPoint getParent! !


! IdRangeEndPoint methodsFor: 'services' !

toString

	| parentString |
	parentString := parent toString.
	parentString ideLength > 35 ifTrue: [
		parentString := ( parentString copyFrom: 1 to: 35 ), '...' ].
	^'end-point(', self getOffset idePrintPrimitive, ' ', parentString, ')'!


! IdRangeEndPoint methodsFor: 'creation' !

initParent: aParent offset: anOffset

	self
		setParent: aParent;
		setOffset: anOffset! !


! IdNodeQuery class methodsFor: 'class init' !

initializeAfterLoad

	AllNames := 0.
	Descendants := AllNames + 1.
	Ancestors := Descendants + 1.
	ChildrenByName := Ancestors + 1.! !


IdNodeQuery comment: '
	This is a concrete class used to consolidate several
	common node queries into a single class. Most of the
	queries use the visitor pattern to query a node hierarchy
	which is why this is a subclass of NodeVisitorBase.
	The supported queries are all launched using methods
	named query*.'!


! IdNodeQuery methodsFor: 'internal' !

addChildrenByName: aNode

	| child |
	0 to: aNode getLastOffset do: [ :i |
		child := aNode itemImpl: i.
		matchString ~~ nil ifTrue: [
			( ( matchString ideEquals: '*' ) or: [ matchString ideEquals: child getNodeName ] ) ifTrue: [
				resultNodes add: child ] ].
		child receiveVisitor: self ]!

addDescendantChildren: aNode

	| child |
	0 to: aNode getLastOffset do: [ :i |
		child := aNode itemImpl: i.
		resultNodes add: child.
		child receiveVisitor: self ]!

testAllNamesContains: aName
	"Return true if a node containing aName has already
	been stored, else return false."

	resultNodes ideEnumeration do: [ :each |
		( each getNodeName ideEquals: aName ) ifTrue: [ ^true ] ].
	^false! !


! IdNodeQuery methodsFor: 'visiting' !

visitComposite: aNode

	queryType == Ancestors ifTrue: [
		resultNodes add: aNode.
		aNode getParent ~~ nil ifTrue: [
			aNode getParent receiveVisitor: self ].
		^nil ].
	queryType == Descendants ifTrue: [
		self addDescendantChildren: aNode.
		^nil ].
	queryType == ChildrenByName ifTrue: [
		self addChildrenByName: aNode.
		^nil ].
	self visitElement: aNode.
	self visitAllChildren: aNode.
	^nil!

visitDocument: aNode

	queryType == Ancestors ifTrue: [ ^nil ].
	queryType == Descendants ifTrue: [
		self addDescendantChildren: aNode.
		^nil ].
	queryType == ChildrenByName ifTrue: [
		self addChildrenByName: aNode.
		^nil ].
	self visitAllChildren: aNode.
	^nil!

visitDocumentFragment: aNode

	queryType == Ancestors ifTrue: [ ^nil ].
	queryType == Descendants ifTrue: [
		self addDescendantChildren: aNode.
		^nil ].
	queryType == ChildrenByName ifTrue: [
		self addChildrenByName: aNode.
		^nil ].
	self visitAllChildren: aNode.
	^nil!

visitElement: aNode

	queryType == AllNames ifTrue: [
		( self testAllNamesContains: aNode getNodeName ) ifFalse: [
			resultNodes add: aNode ].
		^nil ].
	^nil! !


! IdNodeQuery methodsFor: 'services' !

queryAllNames: aNode
	"Return a collection of strings containing all unique names
	of elements stored withing aNode (including aNode)."

	| answer |
	queryType := AllNames.
	aNode receiveVisitor: self.
	answer := OrderedCollection new: resultNodes size.
	resultNodes ideEnumeration do: [ :each |
		answer add: each getNodeName ].
	^answer!

queryAncestors: aNode
	"Return a node list containing aNode's ancestors
	in reverse document order. aNode's parent is the first
	in the list, the parent's parent is the second node, etc."

	queryType := Ancestors.
	aNode getParent ~~ nil ifTrue: [
		aNode getParent receiveVisitor: self ].
	^IdNodeListImpl forNodes: resultNodes!

queryChildrenByName: aTagName in: aNode
	"Return a node list containing aNode's descendants
	in document order (as start tags are stored) which have
	a tag name matching aTagName. If aTagName is '*' all
	descendants will be returned."

	queryType := ChildrenByName.
	matchString := aTagName.
	aNode receiveVisitor: self.
	^IdNodeListImpl forNodes: resultNodes!

queryDescendants: aNode
	"Return a node list containing aNode's descendants
	in document order (as start tags are stored). This includes 
	its children and all of their descendants."

	queryType := Descendants.
	aNode receiveVisitor: self.
	^IdNodeListImpl forNodes: resultNodes!

queryFollowingSiblings: aNode
	"Return a node list containing the following siblings of aNode
	in document order. Return nil if an error is encountered.
	Note that this method does not perform a visit. It is stored
	here because of its similarity to other supported queries."

	| working index |
	aNode getParent == nil ifTrue: [ ^nil ].
	index := aNode getParent positionOfChild: aNode.
	index == -1 ifTrue: [ ^nil ].
	working := OrderedCollection new.
	index < aNode getParent getLength ifTrue: [
		index to: aNode getParent getLastOffset do: [ :i |
			working add: ( aNode getParent itemImpl: i ) ] ].
	^IdNodeListImpl forNodes: working!

queryPrecedingSiblings: aNode
	"Return a node list containing the preceding siblings of aNode
	in reverse document order; the first preceding sibling is first; 
	the sibling preceding that node is second and so on.
	Return nil if an error is encountered.
	Note that this method does not perform a visit. It is stored
	here because of its similarity to other supported queries."

	| working index last |
	aNode getParent == nil ifTrue: [ ^nil ].
	index := aNode getParent positionOfChild: aNode.
	index == -1 ifTrue: [ ^nil ].
	working := OrderedCollection new.
	index > 1 ifTrue: [
		last := index - 2.
		0 to: last do: [ :i |
			working add: ( aNode getParent itemImpl: last - i ) ] ].
	^IdNodeListImpl forNodes: working! !


! IdNodeQuery methodsFor: 'creation' !

initialize

	super initialize.
	queryType := -1.
	resultNodes := OrderedCollection new! !


IdTextNodeVisitor comment: '
	This is an abstract class used to support node visitors
	which are only concerned with Text nodes. The default behavior
	descends into elements to process all children. Subclasses
	should override processTextNode() as required.'!


! IdTextNodeVisitor methodsFor: 'accessing' !

isReverse
	^reverse!

setReverse: anObject
	reverse := anObject! !


! IdTextNodeVisitor methodsFor: 'internal' !

continueFrom: start
	"Continue the visit from the node represented by start."

	| startNode |
	startNode := start getParent.
	startNode isTypeTEXT ifTrue: [
		self isReverse
			ifTrue: [ self processTextNode: startNode from: 0 to: start getOffset ]
			ifFalse: [ self processTextNode: startNode from: start getOffset to: startNode getLastOffset ].
		^self ].
	self isReverse
		ifTrue: [ self visitPartial: startNode from: 0 to: start getOffset ]
		ifFalse: [ self visitPartial: startNode from: start getOffset to: startNode getLastOffset ]!

processTextNode: aNode from: startOffset to: endOffset
	"Process the current text node. Default is do nothing."!

visitAll: aNode

	self isReverse
		ifTrue: [ self visitAllChildrenRev: aNode ]
		ifFalse: [ self visitAllChildren: aNode ].
	^nil!

visitIsComplete
	"Default is return true. This should be overridden by
	subclasses as required."

	^true!

visitPartial: aNode from: startOffset to: endOffset

	self isReverse
		ifTrue: [ self visitChildrenRev: aNode from: startOffset to: endOffset ]
		ifFalse: [ self visitChildren: aNode from: startOffset to: endOffset ]! !


! IdTextNodeVisitor methodsFor: 'visiting' !

visitComposite: aNode
	^self visitAll: aNode!

visitDocument: aNode
	^self visitAll: aNode!

visitDocumentFragment: aNode
	^self visitAll: aNode!

visitString: aNode

	aNode isTypeTEXT ifFalse: [ ^nil ].
	self processTextNode: aNode 
		from: 0 
		to: aNode getNodeValue ideLength - 1.
	^nil! !


! IdTextNodeVisitor methodsFor: 'creation' !

initialize

	super initialize.
	reverse := false! !


IdQNameAttribute comment: '
	A subclass of AttributeImpl which supports fast queries of 
	attributes with QNames (as defined in the w3 namespace spec). 
	Instances store an offset which is where their local name starts 
	within their respective node name. By default, documents scan 
	for QNames when creating attributes and choose this class 
	automatically. Failure to use this class may cause the attribute 
	to not be recognized.'!

! IdQNameAttribute methodsFor: 'instance creation' !

forName: aName offset: anOffset

	| newAtt |
	newAtt := IdQNameAttribute new.
	newAtt initName: aName offset: anOffset.
	^newAtt! !


! IdQNameAttribute methodsFor: 'accessing' !

getLocalNameOffset
	"Return the offset (zero based) of the local name."

	^localNameOffset!

setLocalNameOffset: anOffset
	localNameOffset := anOffset! !


! IdQNameAttribute methodsFor: 'creation' !

initName: aName offset: anOffset

	super initName: aName.
	localNameOffset := anOffset! !


! IdDocumentImpl class methodsFor: 'constructors' !

forImpl: anObject
	"Return a new document which will maintain a
	reference to anObject as its implementation."

	| answer |
	answer := IdDocumentImpl new.
	answer initImpl: anObject.
	^answer! !


IdDocumentImpl comment: '
	This class implements the Document interface.

	Comment from the DOM specification:

	The Document interface represents the entire HTML or XML document. 
	Conceptually, it is the root of the document tree, and provides the primary 
	access to the document''s data.

	Since elements, text nodes, comments, processing instructions, etc. 
	cannot exist outside the context of a Document, the Document interface 
	also contains the factory methods needed to create these objects. The 
	Node objects created have a ownerDocument attribute which associates 
	them with the Document within whose context they were created.'! !


! IdDocumentImpl methodsFor: 'services' !

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting a document and 
	return the result of processing this visit."

	^aVisitor visitDocument: self! !


! IdDocumentImpl methodsFor: 'accessing' !

getCurrentDocument
	"Return the receiver."

	^self ideYourself!

getDocElement
	"Return the document element."

	^docElement!

getDoctype
	"Return the receiver's document type if one 
	is stored, else return nil. Currently return nil."

	^nil!

getDocumentElement
	"Return the W3 document element (root) of this document."

	^self getDocElement!

getImpl
	^impl!

getImplementation
	"Return the DOM implementation which was used to
	create the receiver."

	^self getImpl!

getNodeName
	"Return the node name used for all instances:
	document."

	^'document'!

getNodeType
	"Return an integer representing the receiver's
	node type. All instances share the same node
	type: DOCUMENT_NODE."

	^IdDOMConstants DOCUMENT_NODE!

getOwnerDocument
	"Return the Document object associated with this node.
	This is also the Document object used to create new nodes.
	When the receiver is a Document this is nil."

	^nil!

setDocElement: anElement
	"Set the document element to anElement. Add it to the childNodes
	collection and store a direct reference in this class. If a document
	element already exists remove it from the child nodes."

	docElement ~~ nil ifTrue: [
		self removeChildImpl: docElement ].
	docElement := anElement.
	self appendNodeImpl: anElement! !


! IdDocumentImpl methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver which
	contains no content."

	^IdDocumentImpl forImpl: self getImpl! !


! IdDocumentImpl methodsFor: 'internal' !

isHTMLDocument
	"Return true if the receiver is an HTML document,
	else return false. Always return false as the HTML
	DOM functionality is not supported."

	^false! !


! IdDocumentImpl methodsFor: 'factory - DOM' !

createAttribute: aName
	"Return a new Attribute for aName.
	Raise the INVALID_CHARACTER_ERR exception if an
	invalid character is specified in aName."

	^self createAttributeImpl: aName!

createCDATASection: aString
	"Return a new CDATA node containing aString.
	Raise the NOT_SUPPORTED_ERR exception if this
	is an HTML document."

	self isHTMLDocument ifTrue: [
		IdDOMImpl newExceptionNotSupported signal ].
	^IdStringNode asCDATA: aString!

createComment: aString
	"Return a new Comment node containing aString."

	^IdStringNode asCOMMENT: aString!

createDocumentFragment
	"Return a new document fragment which references the
	receiver as its master document."

	^self createDocFragment!

createElement: tagName
	"Return a new element node with a type equal to tagName.
	Raise the INVALID_CHARACTER_ERR exception if tagName
	contains an invalid character.

	This will always return a composite node so that
	it can add more child nodes if required. Some efficiency
	might be gained by using ElementNode for elements
	which you know will be empty if there are large numbers
	of them."

	^self createComposite: tagName!

createEntityReference: aName
	"Return a new entity reference for aName.
	Not yet implemented.
	Raise the INVALID_CHARACTER_ERR exception if an
	invalid character is specified in aName.
	Raise the NOT_SUPPORTED_ERR exception if this
	is an HTML document."

	( IdFunctionsDOM validateXMLName: aName ) ifFalse: [
		IdDOMImpl newExceptionInvalidCharacter signal ].
	self isHTMLDocument ifTrue: [
		IdDOMImpl newExceptionNotSupported signal ].
	^nil!

createProcessingInstruction: aTarget data: data
	"Return a new ProcessingInstruction node for aTarget
	and contents data.
	Raise the INVALID_CHARACTER_ERR exception if an
	invalid character is specified in aTarget.
	Raise the NOT_SUPPORTED_ERR exception if this
	is an HTML document."

	^self createPIImpl: aTarget data: data!

createTextNode: aString
	"Return a new Text node containing aString."

	^self createTextNodeImpl: aString! !


! IdDocumentImpl methodsFor: 'editing' !

replaceChild: newChild old: oldChild
	"Only check for root element for now. Return oldChild."

	| asElement |
	oldChild == self getDocElement ifFalse: [ ^oldChild ].
	newChild isTypeELEMENT ifFalse: [ ^oldChild ].
	asElement := newChild.
	self setDocElement: asElement.
	^oldChild! !


! IdDocumentImpl methodsFor: 'factory' !

createAttributeImpl: aName
	"Return a new Attribute for aName. Check for a QName
	and preserve it using a QNameAttribute instance.
	Raise the INVALID_CHARACTER_ERR exception if an
	invalid character is specified in aName."

	| colon |
	( IdFunctionsDOM validateXMLName: aName ) ifFalse: [
		IdDOMImpl newExceptionInvalidCharacter signal ].
	colon := IdKernelUtilities positionOfChar: 58 "$:" in: aName.
	colon == -1 ifTrue: [
		^IdAttributeImpl forName: aName ].
	^IdQNameAttribute forName: aName offset: colon!

createComposite: tagName
	"Return a new composite node with a type equal to tagName.
	Raise the INVALID_CHARACTER_ERR exception if tagName
	contains an invalid character."

	( IdFunctionsDOM validateXMLTagName: tagName ) ifFalse: [
		IdDOMImpl newExceptionInvalidCharacter signal ].
	^IdCompositeNode forCompositeTag: tagName!

createDocFragment
	"Return a new document fragment which references the
	receiver as its master document."

	^IdDocumentFragmentImpl forMaster: self!

createPIImpl: aTarget data: data
	"Return a new ProcessingInstruction node for aTarget
	and contents data.
	Raise the INVALID_CHARACTER_ERR exception if an
	invalid character is specified in aTarget.
	Raise the NOT_SUPPORTED_ERR exception if this
	is an HTML document."

	( IdFunctionsDOM validateXMLName: aTarget ) ifFalse: [
		IdDOMImpl newExceptionInvalidCharacter signal ].
	self isHTMLDocument ifTrue: [
		IdDOMImpl newExceptionNotSupported signal ].
	^IdPINode forTarget: aTarget data: data!

createRangeImpl
	"Return a new Range which has both end-points positioned
	at the start of the receiver."

	^IdRangeImpl forDocument: self!

createTextNodeImpl: aString
	"Return a new Text node containing aString typed
	using internal classes."

	^IdStringNode asTEXT: aString! !


! IdDocumentImpl methodsFor: 'creation' !

initImpl: anObject
	"Initialize the receiver by storing a reference to 
	itself as the masterDoc. This ensures that the 
	getMasterDoc() method defined in the superclass
	will always return the receiver."

	impl := anObject.
	self setMasterDocument: self! !


! IdDocumentImpl methodsFor: 'deprecated' !

createEmpty: tagName
	"Deprecated method. Do not use."

	^self createComposite: tagName!

getElementsByAttribute: attName
	"Deprecated method. Do not use."

	^self getDocElement getElementsByAttribute: attName!

getRootElement
	"Deprecated method. Use getDocElement instead - root
	elements are not precisely the same thing according to
	the W3 path language."

	Transcript cr;show: 'Using deprecated method: #getRootElement'.
	^self getDocElement!

setRootElement: anElement
	"Deprecated method. Use setDocElement instead - root
	elements are not precisely the same thing according to
	the W3 path language."

	Transcript cr;show: 'Using deprecated method: #setRootElement:'.
	self setDocElement: anElement! !


! IdCompositeNode class methodsFor: 'accessing' !

getDefaultListSize
	"Return the default size used to initialize new
	childNodes collections."

	^DefaultListSize!

setDefaultListSize: anInteger
	"Set the default size used to initialize new
	childNodes collections to anInteger."

	DefaultListSize := anInteger! !


! IdCompositeNode class methodsFor: 'class initialization' !

initializeAfterLoad
	"Initialize the default collection size variable after
	loading the class."

	DefaultListSize := 8! !


! IdCompositeNode class methodsFor: 'constructors' !

forCompositeTag: aTagName
	"Return a new instance formatted with aTagName."

	| newNode |
	newNode := IdCompositeNode new.
	newNode initTagName: aTagName.
	^newNode! !


IdCompositeNode comment: '
	This subclass represents a parent element which stores
	its children in an indexed collection. This is the concrete
	class used to represent DOM Elements.'!


! IdCompositeNode methodsFor: 'services' !

allocateChildNodes: anInteger
	"Initialize the receiver's childNodes collection to
	a size equal to anInteger."

	childNodes := OrderedCollection new: anInteger!

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting a composite node and 
	return the result of processing this visit."

	^aVisitor visitComposite: self! !


! IdCompositeNode methodsFor: 'accessing' !

getLength
	"Return the number of nodes stored within the receiver.
	This method overrides the superclass version to return
	zero if the collection has not been initialized yet."

	childNodes == nil ifTrue: [ ^0 ].
	^childNodes size! !


! IdCompositeNode methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver with copies of
	all attributes (including defaults). Only add copies
	of its children if deep is true."

	| answer childNode |
	answer := IdCompositeNode forCompositeTag: self getTagName.
	self getAttributesList ~~ nil ifTrue: [
		answer cloneAttributesList: self getAttributesList ].
	deep ifFalse: [ ^answer ].
	0 to: self getLastOffset do: [ :i |
		childNode := self itemImpl: i.
		answer appendNodeImpl: ( childNode clone: true ) ].
	^answer! !


! IdCompositeNode methodsFor: 'internal' !

confirmChildNodeType: aNode
	"Raise a HIERARCHY_REQUEST_ERR exception if aNode
	is not a valid child node type."

	( aNode isTypeELEMENT 
		or: [ aNode isTypeTEXT
		or: [ aNode isTypeENTITY_REFERENCE
		or: [ aNode isTypePROCESSING_INSTRUCTION
		or: [ aNode isTypeCDATA_SECTION ]]]] ) ifFalse: [
			IdDOMImpl newExceptionHierarchyRequest signal ]!

getLiveNodeCollection
	"Return a 'live' collection of all the receiver's 
	child nodes. This method is private to ensure 
	that the returned collections are not modified 
	directly by any calling method. Lazy initialize
	to the default list size if no collection exists yet."

	childNodes == nil ifTrue: [
		self allocateChildNodes: DefaultListSize ].
	^childNodes!

internalAddByOffset: startOffset to: stopOffset into: aCollection

	| start stop |
	start := startOffset max: 0.
	stop := stopOffset min: self getLastOffset.
	start to: stop do: [ :i |
		aCollection add: ( self itemImpl: i ) ]!

internalInsert: newChild before: refChild
	"Insert newChild before the existing refChild. Return a
	new collection of child nodes if refChild is identified, else
	return nil. If refChild is nil return nil."

	| match newNodes child |
	self hasChildNodes ifFalse: [ ^nil ].
	newChild setParent: self.
	newNodes := OrderedCollection new: childNodes size + 1.
	match := false.
	1 to: childNodes size do: [ :i |
		child := childNodes ideAtIndex: i.
		child == refChild ifTrue: [ 
			match := true.
			newNodes add: newChild ].
		newNodes add: child ].
	^match
		ifTrue: [ newNodes ]
		ifFalse: [ nil ]!

internalReplaceChildren: aCollection
	"Completely replace the children collection with
	aCollection (not a copy). Assume all children already
	reference the receiver as parent."

	childNodes := aCollection! !


! IdCompositeNode methodsFor: 'queries' !

ideIsCompositeNode
	"Return true if the receiver is a composite node,
	else return false."

	^true! !


! IdCompositeNode methodsFor: 'editing' !

appendNodeImpl: aNode
	"Adds aNode to the end of the list of children of this node. 
	If aNode is already in the tree, it is first removed. Return aNode.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if aNode is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if aNode was created
	from a different document than the one that created the receiver."

	aNode == nil ifTrue: [ ^aNode ].
	aNode isTypeDOCUMENT_FRAGMENT ifTrue: [
		^self appendDocFragment: aNode ].
	self confirmChildNodeType: aNode.
	self confirmChildNotAncestor: aNode.
	self confirmSameOwnerDocument: aNode.
	aNode getParent ~~ nil ifTrue: [
		aNode getParent removeChild: aNode ].

	self setLock.
	self getLiveNodeCollection add: aNode.
	aNode setParent: self.
	self resetLock.
	^aNode!

insertBefore: newChild ref: refChild
	"Insert newChild before the existing refChild and return
	newChild if successful. If refChild is nil, append newChild.
	If newChild is a DocumentFragment object, all of its children 
	are inserted, in the same order, before refChild. If the newChild 
	is already in the tree, it is first removed.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if newChild is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if newChild was created
	from a different document than the one that created the receiver.
	Raise a NOT_FOUND_ERR exception if refChild is not nil or a 
	current child."

	| newNodes |

	newChild isTypeDOCUMENT_FRAGMENT ifTrue: [
		0 to: newChild getLastOffset do: [ :i |
			self insertBefore: ( newChild itemImpl: i ) ref: refChild ].
		^newChild ].
	refChild == nil ifTrue: [
		^self appendNodeImpl: newChild ].

	self confirmChildNodeType: newChild.
	self confirmChildNotAncestor: newChild.
	self confirmSameOwnerDocument: newChild.
	newChild getParent ~~ nil ifTrue: [
		newChild getParent removeChild: newChild ].

	self setLock.
	newNodes := self internalInsert: newChild before: refChild.
	newNodes == nil ifTrue: [ 
		IdDOMImpl newExceptionNotFound signal ].
	childNodes := newNodes.
	self resetLock.
	^newChild!

removeByOffsets: startOffset to: stopOffset
	"Remove the child nodes at startOffset and stopOffset
	including all in between."

	| newNodes dif mySize deletedNodes deletedList |
	dif := stopOffset - startOffset.
	mySize := self getLength.
	( startOffset >= 0
		and: [ stopOffset < mySize
		and: [ dif >= 0 ]] ) ifFalse: [ ^self ].
	self setLock.
	newNodes := OrderedCollection new: mySize - dif.
	deletedNodes := OrderedCollection new: dif + 1.
	self
		internalAddByOffset: startOffset to: stopOffset into: deletedNodes;
		internalAddByOffset: 0 to: startOffset - 1 into: newNodes;
		internalAddByOffset: stopOffset + 1 to: mySize - 1 into: newNodes.
	childNodes := newNodes.
	deletedList := IdNodeListImpl forNodes: deletedNodes.
	0 to: deletedList getLastOffset do: [ :i |
		( deletedList itemImpl: i ) setParent: nil ].
	self resetLock!

removeChildImpl: oldChild
	"Remove oldChild from the list of children and return it.
	Raise a NOT_FOUND_ERR exception if oldChild is not a child."

	| answer newNodes child |
	( self == oldChild getParent and: [ self hasChildNodes ] ) ifFalse: [ 
		IdDOMImpl newExceptionNotFound signal ].
	self setLock.
	oldChild setParent: nil.
	newNodes := OrderedCollection new: childNodes size.
	answer := nil.
	1 to: childNodes size do: [ :i |
		child := childNodes ideAtIndex: i.
		child == oldChild
			ifTrue: [ answer := child ]
			ifFalse: [ newNodes add: child ] ].
	childNodes := newNodes.
	self resetLock.
	answer ~~ nil ifTrue: [ ^answer ].
	IdDOMImpl newExceptionNotFound signal!

replaceChild: newChild old: oldChild
	"Replace oldChild with newChild and return oldChild.
	If newChild is a DocumentFragment object, all of its children 
	are inserted in the same order. If the newChild is already in 
	the tree, it is first removed.

	Raise a HIERARCHY_REQUEST_ERR exception if the receiver
	does not allow children of this type, or if newChild is an ancestor.
	Raise a WRONG_DOCUMENT_ERR exception if newChild was created
	from a different document than the one that created the receiver.
	Raise a NOT_FOUND_ERR exception if refChild is not a child."

	| index |
	index := self positionOfChild: oldChild.
	index == -1 ifTrue: [ IdDOMImpl newExceptionNotFound signal ].
	self insertBefore: newChild ref: oldChild.
	self removeChildImpl: oldChild.
	^oldChild! !


! IdCompositeNode methodsFor: 'creation' !

initTagName: aTagName
	self setTagName: aTagName! !


! IdCompositeNode methodsFor: 'deprecated' !

cloneChildrenInto: anElement from: fromOffset to: toOffset cut: cut
	"Deprecated method. Do not use."

	| from to addedNodes |
	from := fromOffset max: 0.
	to := toOffset min: self getLastOffset.
	addedNodes := OrderedCollection new: to - from + 1.
	from to: to do: [ :i |
		cut
			ifTrue: [ addedNodes add: ( self itemImpl: i ) ]
			ifFalse: [ addedNodes add: ( ( self itemImpl: i ) clone: true ) ] ].
	IdNodeListImpl appendNodesAsList: addedNodes to: anElement!

clonePartialEnd: offsetTrail pos: position cut: cut
	"Deprecated method. Do not use."

	| offset max myCopy partialNode |
	offset := offsetTrail getIndexAtPosition: position.
	partialNode := self itemImpl: offset.
	partialNode == nil ifTrue: [ ^nil ].  "could raise exception for coding error"
	myCopy := self clone: false.

	max := offset - 1.
	max >= 0 ifTrue: [
		self cloneChildrenInto: myCopy 
			from: 0
			to: max
			cut: cut ].

	position < offsetTrail getSize ifTrue: [ 
		myCopy appendNodeImpl: (
			partialNode clonePartialEnd: offsetTrail 
				pos: position + 1
				cut: cut ) ].
	^myCopy!

clonePartialStart: offsetTrail pos: position cut: cut
	"Deprecated method. Do not use."

	| offset max myCopy partialNode |
	offset := offsetTrail getIndexAtPosition: position.
	partialNode := self itemImpl: offset.
	partialNode == nil ifTrue: [ ^nil ].  "could raise exception for coding error"
	myCopy := self clone: false.
	position < offsetTrail getSize ifTrue: [ 
		myCopy appendNodeImpl: (
			partialNode clonePartialStart: offsetTrail 
				pos: position + 1
				cut: cut ) ].

	max := self getLastOffset.
	offset < max ifTrue: [
		self cloneChildrenInto: myCopy 
			from: offset + 1
			to: max
			cut: cut ].
	^myCopy! !


! IdPINode class methodsFor: 'constructors' !

forTarget: aTarget data: aString
	"Return a new instance formatted to represent
	aString as a processing instruction for aTarget."

	| newNode |
	newNode := IdPINode new.
	newNode initTarget: aTarget data: aString.
	^newNode! !


IdPINode comment: '
	This subclass supports PI nodes by storing a target
	instance variable.'!


! IdPINode methodsFor: 'accessing' !

getNodeName
	"Return the node name used for all instances:
	processing-instruction."

	^'processing-instruction'!

getNodeType
	"Return an integer representing the receiver's
	node type. All instances share the same node
	type: PROCESSING_INSTRUCTION_NODE."

	^IdDOMConstants PROCESSING_INSTRUCTION_NODE!

getTarget
	"Return the target of the receiver."

	^target!

setTarget: aString
	"Set the target of the receiver to aString."

	target := aString! !


! IdPINode methodsFor: 'cloning' !

clone: deep
	"Return a duplicate of the receiver. Specify the
	same target and data."

	^IdPINode forTarget: self getTarget data: self getData! !


! IdPINode methodsFor: 'services' !

receiveVisitor: aVisitor
	"Inform aVisitor that it is visiting a processing instruction and 
	return the result of processing this visit."

	^aVisitor visitPI: self! !


! IdPINode methodsFor: 'creation' !

initTarget: aTarget data: aString
	"Initialize the receiver to represent aString as 
	a aNodeType (as defined by class variables in the 
	Node class)."

	target := aTarget.
	self setData: aString! !


IdNodeSearch comment: '
	This is a concrete class used to support character
	searches within node hierarchies. This class is used to
	support the XPointer string:: axis. Note that it is still
	under development.'!


! IdNodeSearch methodsFor: 'accessing' !

getMatchRange
	^matchRange!

getMatchString
	^matchString!

setMatchString: anObject
	matchString := anObject! !


! IdNodeSearch methodsFor: 'internal' !

completeSearch: aNode offset: anOffset
	"Complete the search by saving the end range point and
	return true. This method can return false if no rangeDoc has
	been stored (used to create the range result)."

	rangeDoc == nil ifTrue: [ ^false ].
	matchRange := rangeDoc createRangeImpl.
	matchRange
		setStart: matchStart getParent offset: matchStart getOffset;
		setEnd: aNode offset: anOffset + 1.
	^true!

processTextNode: aNode from: startOffset to: endOffset

	| str testChar lastOffset |
	( self getMatchString == nil or: [ self getMatchString ideLength == 0 ] ) ifTrue: [ ^self ].
	str := aNode getNodeValue.
	lastOffset := endOffset min: str ideLength - 1.
	lastOffset < 0 ifTrue: [ ^self ].
	testChar := self getMatchString ideCharAt: 1.
	( 0 max: startOffset ) to: lastOffset do: [ :i |
		( str ideCharAt: i + 1 ) == testChar ifTrue: [
			matchStart := IdRangeEndPoint forParent: aNode offset: i.
			( self testRemaining: str in: aNode from: i + 1 to: lastOffset ) ifTrue: [ ^self ].
			matchStart := nil ] ]!

testRemaining: str in: aNode from: startOffset to: endOffset

	| testChar testOffset |
	testOffset := 1.
	startOffset to: endOffset do: [ :i |
		testChar := self getMatchString ideCharAt: testOffset + 1.
		( str ideCharAt: i + 1 ) == testChar ifFalse: [ ^false ].
		testOffset := testOffset + 1.
		testOffset >= self getMatchString ideLength ifTrue: [   "success"
			^self completeSearch: aNode offset: i ] ].
	^true!

visitIsComplete
	"Return true if the current visit is complete."

	^self getMatchRange ~~ nil! !


! IdNodeSearch methodsFor: 'visiting' !

visitPI: aNode
	"Pending to support XPath."

	^nil! !


! IdNodeSearch methodsFor: 'services' !

search: testString from: start doc: aDocument
	"Search for testString beginning with the node represented by start.
	Return a range describing the match if one is found, else return nil."

	aDocument == nil ifTrue: [ ^nil ].
	rangeDoc := aDocument.
	self setMatchString: testString.
	self continueFrom: start.
	^self getMatchRange! !


! Behavior methodsFor: 'ID extensions' !

ideNewInstance
	"Return a new instance of the receiver. This maps
	to newInstance() in Java."

	^self new! !


! Object methodsFor: 'ID extensions' !

ideIsCompositeNode
	"Return true if the receiver is a composite node,
	else return false."

	^false!

ideIsElementNode
	"Return true if the receiver is an element node,
	else return false."

	^false!

ideIsNodeImpl
	"Return true if the receiver is a NodeImpl instance,
	else return false."

	^false! !


! IdException class methodsFor: 'examples' !

runPortableMultiExample
	"Run an exception handling example which demonstrates
	the portable handling syntax for two exceptions. Note that
	when using more than one exception, you should always
	list the more specific exception first (ie. list an exception
	subclass before its superclass) in Smalltalk or in Java.

	In this example only the first handler block will be executed.
	Specifying 3 or 4 handler blocks is also legal using the
	#ideOnException:do:when:do:when:do: and
	#ideOnException:do:when:do:when:do:when:do: variants."
"
	IdException runPortableMultiExample
"
	[ IdDOMImpl newExceptionNotFound signal ]
		ideOnException: IdDOMException
		do: [ :ex |
			Transcript cr;show: 'IdDOMException was caught in IdDOMException handler block'.
			Transcript cr;show: '  query for DOM code: ', ex ideAsExceptionInstance code printString.
			Transcript cr;show: '  query for message: ', ex ideAsExceptionInstance getMessage printString.
			ex ideExitGracefully ]
		when: IdException
		do: [ :ex |
			Transcript cr;show: 'IdException was caught in IdException handler block'.
			ex ideExitGracefully ].
	Transcript cr;show: 'Processing can continue after handling an IdException'.! !

IdNodeImpl initializeAfterLoad!
IdDocumentFragmentImpl initializeAfterLoad!
IdCompositeNode initializeAfterLoad!
IdNodeQuery initializeAfterLoad!
IdXMLWriter initializeAfterLoad!
IdXMLWriterOptions initializeAfterLoad!