"======================================================================
|
|   Smalltalk XML Parser
|
|   $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
|
 ======================================================================"


OrderedCollection variableSubclass: #IdStringBuffer
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

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

WriteStream subclass: #IdWriteStreamSB
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

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

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

IdObject subclass: #IdInputSource
	instanceVariableNames: 'characterStream systemId publicId '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdObject subclass: #IdKernelUtilities
	instanceVariableNames: ''
	classVariableNames: 'Cr Lf Space Tab '
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

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

IdObject subclass: #IdURI
	instanceVariableNames: 'protocol path '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdObject subclass: #IdStreamParser
	instanceVariableNames: 'lastChar streamReader crLfFlag baseURI streamStack '
	classVariableNames: 'CR EofChar GT LF LT SEMI SPACE TAB '
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdObject subclass: #IdStreamParserCache
	instanceVariableNames: 'stream lastChar '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdObject subclass: #IdXWalkerAttribute
	instanceVariableNames: 'name value '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdObject subclass: #IdXWalkerAttributeList
	instanceVariableNames: 'names values types attCount '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdObject subclass: #IdXWalkerDTD
	instanceVariableNames: 'entities defaultAttributes '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdObject subclass: #IdXWalkerParserEvent
	instanceVariableNames: 'tagName declaredAttributes isStart '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdException subclass: #IdGeneralException
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdException subclass: #IdIOException
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-Kernel'
!

IdException subclass: #IdSAXException
	instanceVariableNames: 'errorMessage '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdSAXHandlerBase subclass: #IdXWalkerParseAppExample
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdStreamParser subclass: #IdXWalkerParser
	instanceVariableNames: 'errHandler docHandler dtdHandler locale lineNum linePos entityResolver dtd cdata cdataIndex currentTag tagStack '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdXWalkerDTD subclass: #IdXWalkerHTMLDTD
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdSAXException subclass: #IdSAXParseException
	instanceVariableNames: 'publicId systemId lineNumber columnNumber '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdXWalkerParser subclass: #IdXWalkerDoctypeParser
	instanceVariableNames: 'isDoneDoctype '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!

IdXWalkerParser subclass: #IdXWalkerHTMLParser
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'InDelv DOM-XWalker'
!


! IdStringBuffer methodsFor: 'queries' !

length
	^self size! !


! IdStringBuffer methodsFor: 'services' !

appendChar: aCharacter
	"Add aCharacter to the receiver. Will convert to 
	StringBuffer.append() in Java."

	self add: aCharacter!

appendString: aString
	"Add aString to the receiver. Will convert to 
	StringBuffer.append() in Java."

	self addAll: aString!

convertToString
	"Return a string containing all the characters
	stored in the receiver. Will convert to 
	StringBuffer.toString() in Java.
	Only supports single byte strings for now."

	| answer nextChar |
	answer := String new: self size.
	1 to: self size do: [ :i |
		nextChar := self at: i.
		nextChar ideCharValue > 255 
			ifTrue: [ answer at: i put: ( Character value: 127 ) ]
			ifFalse: [ answer at: i put: nextChar ] ].
	^answer! !


! IdObject class methodsFor: 'class init' !

initializeAfterLoad! !


! IdObject class methodsFor: 'instance creation' !

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

	^super new initialize! !


! IdObject methodsFor: 'services' !

getClassName
	"Return the name of the receiver's class."

	^self class name!

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!

toString
	"Return a string representation of the receiver.
	This is overridden instead of #printOn: in Smalltalk
	to make it easier to translate to Java. It is called
	by a copy of #printOn: in the receiver."

	^self class name first isVowel
		ifTrue: [ 'an ', self class name ]
		ifFalse: [ 'a ', self class name ]! !


! IdObject methodsFor: 'creation' !

initialize
	"Initialize the receiver immediately after creation.
	This should be overidden by subclasses as needed.
	Default is do nothing."! !


! IdWriteStreamSB methodsFor: 'writing' !

nextPut: anObject
	"Override to ensure only single byte characters are written
	to the stream."

	anObject ideCharValue > 255 ifTrue: [ ^self ].
	super nextPut: anObject! !


IdArrayOfArrays comment: '
	This is a concrete class used to simulate the behavior
	of arrays of arrays in Java.'!


! IdArrayOfArrays class methodsFor: 'instance creation' !

ideNew: arrayCount commonSize: arraySize
	"Return a new instance which will have arrayCount
	arrays each of length arraySize. If arraySize is zero
	no content arrays (child arrays) will be created."

	| answer |
	answer := IdArrayOfArrays new.
	answer initArrayCount: arrayCount sameSize: arraySize.
	^answer! !


! IdArrayOfArrays methodsFor: 'accessing' !

at: yIndex
	"This can return nil if no array has been
	allocated yet."

	yIndex > arrays ideArrayLength ifTrue: [ ^nil ].  "raise exception instead?"
	^arrays at: yIndex!

at: yIndex put: array
	"This will replace an existing array at yIndex
	if one exists."

	yIndex > arrays ideArrayLength ifTrue: [ ^self ].  "raise exception instead?"
	^arrays at: yIndex put: array!

copyToLargerGrid: anObject
	"Assume anObject is larger than the receiver."

	IdKernelUtilities copyArray: arrays 
		start: 0
		dst: anObject 
		length: arrays size!

getEntry: yOffset x: xOffset
	"This can return nil if no row array has been
	allocated or if the offsets are incorrect."

	| row |
	row := self at: yOffset + 1.
	row == nil ifTrue: [ ^nil ].
	xOffset >= row ideArrayLength ifTrue: [ ^nil ].  "raise exception instead?"
	^row at: xOffset + 1!

ideArrayLength
	^arrays size! !


! IdArrayOfArrays methodsFor: 'creation' !

initArrayCount: arrayCount sameSize: arraySize

	arrays := Array new: arrayCount.
	arraySize > 0 ifFalse: [ ^self ].
	1 to: arrayCount do: [ :i |
		arrays at: i put: ( Array new: arraySize ) ]! !


! IdException class methodsFor: 'examples' !

runPortableExample
	"Run an exception handling example which demonstrates
	the portable handling syntax for a single exception."
"
	IdException runPortableExample
"
	[ IdException signalNew ]
		ideOnException: IdException
		do: [ :ex |
			Transcript cr;show: 'IdException was caught using portable syntax'.
			ex ideExitGracefully ].
	Transcript cr;show: 'Processing can continue after handling an IdException'.!

runGnuSmalltalkExample
	"Run an exception handling example which demonstrates
	the GNU Smalltalk syntax for a single exception."
"
	IdException runGnuSmalltalkExample
"
		[ IdException throwNew ]
		        on: IdException getException
			do: [ :sig |
				Transcript cr;show: 'IdException was caught using GNU Smalltalk syntax'.
				sig return ].

	Transcript cr;show: 'Processing can continue after handling a IdException'.! !


! IdException class methodsFor: 'class initialization' !

initializeAfterLoad

	self prepareHostExLibrary!

prepareHostExLibrary
	"Create a dictionary to hold host exception event signals and 
	store it in a class variable after loading the class. Store one
	Object errorSignal exception for IdException as a default for
	all subclasses. Do not allow this method to
	execute for any other subclass."

	| ex |
	self == IdException ifFalse: [ ^self ].
	HostExceptionLibrary := IdentityDictionary new.
	HostExceptionLibrary at: self put: ( ex := ExError newChild ).
	ex description: '(IdException) An error has occurred.'! !


! IdException class methodsFor: 'host exceptions' !

getException
	"Return the host exception object for this class."

	^self privateGetExceptionFor: self!

getSignal
	"Convenience method for VisualWorks."

	^self privateGetExceptionFor: self!

privateGetExceptionFor: aClass
	"Return the host exception for aClass in GNU Smalltalk.
	Add a new entry to HostExceptionLibrary if needed."

	| ex |
	HostExceptionLibrary == nil ifTrue: [
		^self error: 'IdException class was not properly initialized after loading.' ].
	^HostExceptionLibrary at: aClass ifAbsent: [
		ex := aClass superclass getException newChild.
		ex description: '(', aClass name, ') An error has occurred.'.
		HostExceptionLibrary at: aClass put: ex ]! !


! IdException class methodsFor: 'throw' !

signalNew
	"Create a new exception instance and signal it using
	the host platform exception mechanism."

	self new signal!

throwNew
	"Create a new exception instance and signal it (throw in Java)
	using the host platform exception mechanism."

	self new signal! !


IdException comment: '
	Smalltalk implementation of a portable exception
	mechanism. Host exception events are stored in a class
	variable dictionary keyed by the class (subclass of this
	class) itself. A signal method is defined in this class.

	In all systems you can register handler blocks using the 
	following syntax (where IdExceptionSubclass is the specific
	IdException subclass being handled). Note that the method
	ideExitGracefully must be sent to the exception to ensure the
	host exception handler block exits without raising another 
	exception. This requirement is dropped under the platform
	specific syntax''s shown below.  See the #runPortableExample
	class method for a working example.

		[ code which can raise exception ] 
			ideOnException: IdException*
			do: [ :ex |
				optional exception handling code...
				ex ideExitGracefully ]

	Specifying multiple handler blocks is also legal using the
	#ideOnException:do:when:do: and
	#ideOnException:do:when:do:when:do: and
	#ideOnException:do:when:do:when:do:when:do: variants.
	See the #runPortableMultiExample class method for an example.

	In IBM Smalltalk you can also register handler blocks using 
	the following syntax:

		[ code which can raise exception ] 
			when: IdException* getException
			do: [ :ex |
				IBM specific exception handling code ]

	In GNU Smalltalk you can also register handler blocks using 
	the following syntax:

		[ code which can raise exception ] 
			on: IdException* getException
			do: [ :ex |
				GNU Smalltalk specific exception handling code ]

	In VisualWorks you can also register handler blocks using 
	the following syntax:

		IdExceptionSubclass getSignal
			handle: [ :ex |
				VisualWorks specific exception handling code ]
			do: [ code which can raise exception ]'!


! IdException methodsFor: 'signalling' !

privateSignalHost: anException
	"Signal anException (the underlying host exception
	for this class) using VisualWorks syntax."

	anException signalWith: self!

signal
	"Signal the underlying host exception for this class."

	self privateSignalHost: self class getException! !


! IdInputSource class methodsFor: 'constructors' !

forStream: aStream
	"Create a new input source with a character stream. 
	Application writers may use #setSystemId to provide a 
	base for resolving relative URIs, and #setPublicId to 
	include a public identifier."

	^self new
		setCharacterStream: aStream;
		yourself!

forSystemId: aSystemId
	"Create a new input source with a system identifier.
	Applications may use #setPublicId to include a public
	identifier as well. If the system identifier is a URI/URL, 
	it must be fully resolved."

	^self new
		setSystemId: aSystemId;
		yourself! !


IdInputSource comment: '
	This class is used as a single input source for an XML entity.
	It allows a SAX application to encapsulate information about an 
	input source in a single object, which may include a public 
	identifier, a system identifier, and/or a character stream. Note
	that a public identifier serves as an alternate URI/URl and will
	only be utilized if a SAX application explicitly uses it to
	resolve entities.

	There are two places that the application will deliver this input 
	source to the parser: as the argument to the Parser>>parse method, 
	or as the return value of the EntityResolver>>resolveEntity method.

	The SAX parser will use the InputSource object to determine how to 
	read XML input. If there is a character stream available, the parser 
	will read that stream directly; if not, the parser will attempt to open a 
	URI/URL connection to the resource identified by the system identifier.

	Note that this Smalltalk version of this SAX class does not support byte 
	streams and different encodings. It relies on the host platform to convert 
	various encodings to standard character streams.

	An InputSource object belongs to the application: the SAX parser shall 
	never modify it in any way (it may modify a copy if necessary).'!


! IdInputSource methodsFor: 'accessing' !

getByteStream
	"Get the byte stream for this input source.
	Return nil in Smalltalk."

	^nil!

getCharacterStream
	"Get the character stream for this input source.
	If none was provided return nil."

	^characterStream!

getPublicId
	"Get the public identifier for this input source."

	^publicId!

getSystemId
	"Get the system identifier for this input source.
	If the systemId is a URI/URL, it will be fully resolved."

	^systemId!

setCharacterStream: aStream
	"Set the character stream for this input source
	to aStream. If there is a character stream specified, 
	the SAX parser will not attempt to open a URI/URL
	connection to the system identifier."

	characterStream := aStream!

setPublicId: aPublicId
	"Set the public identifier for this input source. 
	The public identifier is always optional: if the application 
	writer includes one, it will be provided as part of the 
	location information. The application can then use it to
	resolve entities if so desired."

	publicId := aPublicId!

setSystemId: aSystemId
	"Set the system identifier for this input source. 
	The system identifier is optional if there is a character 
	stream, but it is still useful to provide one, since the 
	application can use it to resolve relative URIs and can 
	include it in error messages and warnings (the parser 
	will attempt to open a connection to the URI only if there
	is no character stream specified).
	If the system ID is a URL, it must be fully resolved."

	systemId := aSystemId! !


! IdKernelUtilities class methodsFor: 'file access' !

convertURIToLocalPath: systemId
	"Return a local file system path created from systemId
	which is assumed to be a URI."

	| start buffer nextChar |
	start := 1.
	( systemId size > 7 and: [ ( systemId copyFrom: 1 to: 6 ) = 'file:/' ] ) ifTrue: [
		start := 7 ].
	buffer := IdStringBuffer new: systemId size.
	start to: systemId size do: [ :i |
		nextChar := systemId ideCharAt: i.
		nextChar == $/
			ifTrue: [ buffer appendChar: $\ ]
			ifFalse: [ buffer appendChar: nextChar ] ].	
	^buffer convertToString!

directoryExistsAt: pathName
	"Return true if a directory at local pathName exists, 
	else return false."

	pathName isNil ifTrue: [ ^false ].
	^(File name: pathName) isDirectory!

fileExistsAt: pathName
	"Return true if the file stored locally at pathName exists, 
	else return false."

	pathName isNil ifTrue: [ ^false ].
	^(File name: pathName) isFile!

fileRemoveAt: pathName
	"Remove the file stored locally at pathName if it exists, 
	otherwise do nothing."

	(File name: pathName) isFile ifTrue: [
		File remove: pathName ]!

openFileTextReader: pathName
	"Return a ReadStream on the file stored locally at 
	pathName. If no file exists return nil. 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 fileExistsAt: pathName ) ifFalse: [ ^nil ].
	^FileStream open: pathName mode: FileStream read!

openFileTextWriter: pathName
	"Return a WriteStream on the file stored locally at 
	pathName. Completely replace an existing file of the
	same name if one exists. If no file exists, create a new 
	one. If the file can not be created or written to return nil."

	^FileStream open: pathName mode: FileStream write ifFail: [ nil ]!

! IdKernelUtilities class methodsFor: 'instance creation' !

newInstanceOfClass: className
	"Return a new instance of the class at className,
	or nil if it can not be found."

	| class |
	^( class := Smalltalk at: className asSymbol ifAbsent: [nil] ) == nil
		ifTrue: [ nil ]
		ifFalse: [ class new ]! !


! IdKernelUtilities class methodsFor: 'XML parsing' !

getNewInputSource
	"Return a new InputSource instance used for XML parsing."

	^IdInputSource new!

isSeparatorChar: aChar
	"Return true if aChar is a Space, Lf, Cr or Tab,
	else return false."

	( aChar == Space 
		or: [ aChar == Lf
		or: [ aChar == Tab 
		or: [ aChar == Cr ]]] ) ifTrue: [ ^true ].
	^false!

isXMLNameChar: aChar
	"Return true if aChar is a valid character for an XML name,
	else return false."

	^( self isXMLNameFirstChar: aChar )
		or: [ aChar == $. 
		or: [ aChar == $- ]]!

isXMLNameFirstChar: aChar
	"Return true if aChar is a valid character to start an XML name,
	else return false."

	^aChar isAlphaNumeric or: [ aChar == $_ or: [ aChar == $: ]]! !


! IdKernelUtilities class methodsFor: 'string utilities' !

positionOfChar: charValue in: aString
	"Return the position of the first occurence of a Character in
	aString with an ascii value of charValue, or -1 if it is not found."

	^self positionOfChar: charValue in: aString from: 1!

positionOfChar: charValue in: aString from: startIndex
	"Return the position of the first occurence of a Character in
	aString with an ascii value of charValue, or -1 if it is not found.
	Start searching at startIndex."

	| str |
	str := String new: 1.
	str at: 1 put: ( Character value: charValue ).
	^self positionOfStr: str in: aString from: startIndex!

positionOfStr: subString in: aString
	"Return the position of the first character in aString where 
	that character and subsequent ones are equivalent to those in 
	subString, or -1 if it is not found."

	^self positionOfStr: subString in: aString from: 1!

positionOfStr: subString in: aString from: startIndex
	"Return the position of the first character in aString where 
	that character and subsequent ones are equivalent to those in 
	subString, or -1 if it is not found. Start at startIndex."

	| last base limit i |
	last := subString size.
	limit := aString size - last.
	( base := startIndex - 1 ) > limit ifTrue: [ ^-1 ].
	i := 1.
	[ i <= last ] whileTrue: [
		( aString at: i + base ) = ( subString at: i )
			ifTrue: [ i := i + 1 ]
			ifFalse: [
				i := 1.
				( base := base + 1 ) > limit ifTrue: [ ^-1 ] ] ].
	^i > 1
		ifTrue: [ base + 1 ]
		ifFalse: [ -1 ]! !


IdKernelUtilities comment: '
	A general utility class which supports core system
	functions. All utilities are stored in class methods.
	Similar functions which are required by DOM related
	classes are defined in IdPlatformUtilities.'!


! IdKernelUtilities class methodsFor: 'type conversion' !

validateAsBoolean: anObject default: defaultAnswer
	"Validate anObject as a boolean and return a properly cast
	reference to, or copy of, it if successful. Return defaultAnswer
	if it is not successful."

	| lowercase |
	anObject == true ifTrue: [ ^true ].
	anObject == false ifTrue: [ ^false ].
	anObject isString ifTrue: [
		( anObject ideEquals: 'true' ) ifTrue: [ ^true ].
		( anObject ideEquals: 'false' ) ifTrue: [ ^false ].
		lowercase := anObject asLowercase.
		( lowercase ideEquals: 'true' ) ifTrue: [ ^true ].
		( lowercase ideEquals: 'false' ) ifTrue: [ ^false ] ].
	^defaultAnswer!

validateAsDouble: anObject default: defaultAnswer
	"Validate anObject as a double and return a properly cast
	reference to, or copy of, it if successful. Return defaultAnswer
	if it is not successful."

	| char |
	anObject isString ifTrue: [
		^( anObject isEmpty or: [ ( ( char := anObject first ) = $- ) not and: [ char isDigit not ] ] )
			ifTrue: [ 0.0 ]
			ifFalse: [ anObject ideAsFloat "asDecimal asFloat" ] ].
	^defaultAnswer!

validateAsInteger: anObject default: defaultAnswer
	"Validate anObject as an integer and return a properly cast
	reference to, or copy of, it if successful. Return defaultAnswer
	if it is not successful."

	anObject isString ifTrue: [
		^anObject asInteger ].
	^defaultAnswer! !


! IdKernelUtilities class methodsFor: 'arrays' !

copyArray: src start: startOffset dst: dst length: length
	"Copy from src array to dst array as specified.
	The start offset is relative to src. Copy to the
	start of dst. Raise an exception if any errors."

	dst replaceFrom: 1 
		to: length
		with: src 
		startingAt: startOffset + 1!

createZeroFilledArray: arraySize
	"Return an integer array of arraySize and fill it with zeros."

	^Array new: arraySize withAll: 0! !


! IdKernelUtilities class methodsFor: 'class init' !

initializeAfterLoad

	Cr := Character value: 13.
	Lf := Character value: 10.
	Space := Character value: 32.
	Tab := Character value: 9! !


IdSAXHandlerBase comment: '
	A default implementation of SAX handlers which does nothing.
	Subclasses only need to overide the methods they use.'!


! IdSAXHandlerBase methodsFor: 'doc handler api' !

characters: charArray start: start length: length
	"Receive notification of character data.

	The Parser will call this method to report each chunk of 
	character data. SAX parsers may return all contiguous character 
	data in a single chunk, or they may split it into several chunks; 
	however, all of the characters in any single event must come
	from the same external entity, so that the Locator provides 
	useful information.

	The application must not attempt to read from the array
	outside of the specified range."!

endDocument
	"Receive notification of the end of a document.
	The SAX parser will invoke this method only once, and 
	it will be the last method invoked during the parse."!

endElement: name
	"Receive notification of the end of an element.
	The Parser will invoke this method at the end of every 
	element in the XML document; there will be a corresponding 
	startElement() event for every endElement() event (even when 
	the element is empty)."!

ignorableWhitespace: charArray start: start length: length
	"Receive notification of ignorable whitespace in element content.

	Validating Parsers must use this method to report each chunk of 
	ignorable whitespace (see the W3C XML 1.0 recommendation, 
	section 2.10): non-validating parsers may also use this method if 
	they are capable of parsing and using content models.

	SAX parsers may return all contiguous whitespace in a single chunk, 
	or they may split it into several chunks; however, all of the characters 
	in any single event must come from the same external entity, so that 
	the Locator provides useful information.

	The application must not attempt to read from the array outside of 
	the specified range."!

processingInstruction: target data: data
	"Receive notification of a processing instruction. 

	The Parser will invoke this method once for each processing
	instruction found: note that processing instructions may occur 
	before or after the main document element.

	A SAX parser should never report an XML declaration or a text 
	declaration using this method."!

setDocumentLocator: locator
	"Receive an object for locating the origin of SAX document events.

	SAX parsers are strongly encouraged (though not absolutely required) 
	to supply a locator: if it does so, it must supply the locator to the 
	application by invoking this method before invoking any of the other 
	methods in the DocumentHandler interface.

	The locator allows the application to determine the end position of 
	any document-related event, even if the parser is not reporting an error. 
	Typically, the application will use this information for reporting its own 
	errors (such as character content that does not match an application's 
	business rules).  The information returned by the locator is probably not 
	sufficient for use with a search engine.

	Note that the locator will return correct information only during the 
	invocation of the events in this interface.  The application should not 
	attempt to use it at any other time."!

startDocument
	"Receive notification of the beginning of a document. 
	The SAX parser will invoke this method only once, before 
	any other methods in this interface or in
	DTDHandler (except for setDocumentLocator)."!

startElement: name atts: atts
	"Receive notification of the beginning of an element.
	The Parser will invoke this method at the beginning of every 
	element in the XML document; there will be a corresponding 
	endElement() event for every startElement() event (even when 
	the element is empty). All of the element's content will be reported, 
	in order, before the corresponding endElement() event.

	If the element name has a namespace prefix, the prefix will 
	still be attached. Note that the attribute list provided will contain 
	only attributes with explicit values (specified or defaulted): 
	#IMPLIED attributes will be omitted."! !


! IdURI class methodsFor: 'internal' !

expandPath: aPath baseURI: baseURI
	"Return a fully expanded version of aPath based
	on baseURI. If baseURI is nil return aPath, otherwise
	fill in relative path names accordingly.
	This method assumes a drive name is the first letter
	immediately followed by a colon (:). If a drive is not
	specified, assume aPath is a relative path and append
	it to the first part of baseURI's path (up to the last path
	separator). If a drive is specified return aPath."

	| lastSep goodSeps nextChar goodSep sepsOK basePath fileName fileNameStart fBase |
	baseURI == nil ifTrue: [ ^aPath ].
	aPath size = 0 ifTrue: [ ^aPath ].
	( aPath size > 2 and: [ ( aPath at: 2 ) == $: ] ) ifTrue: [ ^aPath ].
	basePath := baseURI getPath.
	( basePath == nil or: [ basePath isEmpty ] ) ifTrue: [ ^aPath ].
	lastSep := 0.
	goodSeps := OrderedCollection new.
	sepsOK := true.
	1 to: basePath size do: [ :i |
		nextChar := basePath at: i.
		nextChar == $# ifTrue: [  "/ can occur in XPointer fragment - must ignore"
			sepsOK := false ].
		( sepsOK and: [ nextChar == $/ ] ) ifTrue: [
			goodSeps add: lastSep.
			lastSep := i ] ].
	fileName := aPath.
	fileNameStart := 1.
	( lastSep > 0 
		and: [ fileName size > 2
		and: [ ( ( fileName copyFrom: 1 to: 2 ) = '..' ) ]] ) ifTrue: [
			goodSeps size == 0 ifTrue: [ ^aPath ].
			goodSep := goodSeps last.
			fBase := 3.
			( fileName size > 5
				and: [ goodSeps size > 1
				and: [ ( ( fileName copyFrom: 1 to: 5 ) = '../..' ) ]] ) ifTrue: [
					fBase := 6.
					goodSep := goodSeps at: goodSeps size - 1 ].
			( fileName size > 8
				and: [ goodSeps size > 2
				and: [ ( ( fileName copyFrom: 1 to: 8 ) = '../../..' ) ]] ) ifTrue: [
					fBase := 9.
					goodSep := goodSeps at: goodSeps size - 2 ].
			( fileName size > 11
				and: [ goodSeps size > 3
				and: [ ( ( fileName copyFrom: 1 to: 11 ) = '../../../..' ) ]] ) ifTrue: [
					fBase := 12.
					goodSep := goodSeps at: goodSeps size - 3 ].
			basePath := basePath copyFrom: 1 to: goodSep.
			lastSep := basePath size.
			( fileName at: fBase ) = $/
				ifTrue: [ fileNameStart := fBase + 1 ]
				ifFalse: [ fileNameStart := fBase ].
			fileName := fileName copyFrom: fileNameStart to: fileName size ].
	fileName first == $/ ifTrue: [
		lastSep := lastSep - 1 ].
	lastSep < 1 ifTrue: [ ^fileName ].
	^( basePath copyFrom: 1 to: lastSep ), fileName! !


! IdURI class methodsFor: 'constructors' !

forBase: baseURI string: aString
	"Return a new URI instance from aString within the
	context of baseURI. If any of the URI components are
	missing from aString, they will be inherited from baseURI
	if it is not nil.
	The argument aString is parsed for a protocol (file, http, etc.)
	which is everything before a colon (:) which occurs before
	the first slash character (/).
	In this class, everything after the protocol (if one exists) is
	considered the URI path."

	| aProtocol aPath looking |
	aProtocol := nil.
	aPath := aString.
	looking := true.
	1 to: aString size do: [ :i |
		( i > 1 and: [ ( ( i + 1 ) < aString size ) and: [ ( aString at: i ) == $: ] ] ) ifTrue: [
			( looking and: [ ( aString at: i + 1 ) == $/ ] ) ifTrue: [
				looking := false.
				aProtocol := aString copyFrom: 1 to: i - 1.
				aPath := aString copyFrom: i + 2 to: aString size ] ] ].
	( aProtocol == nil and: [ baseURI ~~ nil ] ) ifTrue: [
		aProtocol := baseURI getProtocol.
		aPath := self expandPath: aPath baseURI: baseURI ].
	^self new
		initProtocol: aProtocol path: aPath;
		yourself!

forUriString: aString
	"Return a new URI instance from aString. This is
	equivalent to calling #forBase:string: with nil as the
	first argument."

	^self forBase: nil string: aString! !


IdURI comment: '
	This is a simplified implementation of URI in Java. It
	is used to resolve single and multiple URI/URL identifiers
	for local paths (http is not supported in Smalltalk).'!


! IdURI methodsFor: 'accessing' !

getPath
	"Return the path if one is defined, else return nil."

	^path!

getProtocol
	"Return the protocol (file, http, etc.) as a String if
	one is defined, else return the default 'file'. In this 
	class, only the file protocol is supported for 
	retrieving files."

	^protocol == nil
		ifTrue: [ 'file' ]
		ifFalse: [ protocol ]! !


! IdURI methodsFor: 'internal' !

stripFragment: aPath
	"Return aPath after stripping any fragment which might
	be included. Fragments (such as XPointers in XML or IDREFs
	in HTML) are specified following the # character which is
	called a fragment identifier."

	| fragmentIndex |
	aPath == nil ifTrue: [ ^nil ].
	fragmentIndex := IdKernelUtilities positionOfChar: 35 "$#" in: aPath.
	fragmentIndex == -1 ifTrue: [ ^aPath ].
	^aPath copyFrom: 1 to: fragmentIndex - 1! !


! IdURI methodsFor: 'services' !

openStream
	"Return a read only file stream on the receiver if
	possible, else return nil. If a stream is returned it is
	the calling object's responsibility to close it when done."

	| stream |
	self getPath == nil ifTrue: [ ^nil ].
	stream := IdKernelUtilities openFileTextReader: (
		IdKernelUtilities convertURIToLocalPath: (
			self stripFragment: self getPath ) ).
	stream ~~ nil ifTrue: [ ^stream ].
	^IdKernelUtilities openFileTextReader: (
		self stripFragment: self toExternalForm )!

toExternalForm
	"Return a string representation of this URI." 

	| buffer |
	self getPath == nil ifTrue: [ ^String new ].
	buffer := IdStringBuffer new: self getPath ideLength + 10.
	buffer appendString: self getProtocol.
	buffer appendString: ':/'.
	buffer appendString: self getPath.
	^buffer convertToString! !


! IdURI methodsFor: 'creation' !

initProtocol: aProtocol path: aPath

	protocol := aProtocol.
	path := aPath! !


! IdStreamParser class methodsFor: 'class init' !

initializeAfterLoad
	"Initialize class variables after loading the class."

	EofChar := -1.
	CR := Character value: 13.
	LF := Character value: 10.
	SPACE := Character value: 32.
	TAB := Character value: 9.
	SEMI := Character value: 59.
	GT := Character value: 62.
	LT := Character value: 60.! !


! IdStreamParser methodsFor: 'accessing' !

getBaseURI
	^baseURI!

getLastChar

	lastChar == EofChar ifTrue: [ ^EofChar ].  "later stored with Character"
	^lastChar ideCharValue! !


! IdStreamParser methodsFor: 'testing' !

atEnd
	^lastChar == EofChar!

atEndNot
	^self atEnd not!

charIsCrOrLf: aChar
	"Return true if aChar is a Cr of Lf."

	| answer |
	answer := aChar == CR or: [ aChar == LF ].
	^answer!

lastCharIsNormalizedLineFeed
	"Return true if lastChar is a Cr of Lf. Normalize CR-LF
	combinations to a single line feed occurance by setting
	the crLfFlag as needed. The method #nextPutRe takes care
	of actually writing the Lf character."

	lastChar == LF ifTrue: [ ^true ].
	lastChar == CR ifFalse: [ ^false ].
	crLfFlag := true.
	^true!

lastCharIsQuoteOrApos

	| answer |
	answer := lastChar ideCharValue == 34 "quote" 
		or: [ lastChar ideCharValue == 39 "apos" ].
	^answer!

lastCharIsSeparator
	"Return true if lastChar is a Space, Cr, Lf or Tab."

	| answer |
	( self charIsCrOrLf: lastChar ) ifTrue: [ ^true ].
	answer := lastChar == SPACE or: [ lastChar == TAB ].
	^answer!

streamStackIsEmpty
	^streamStack size == 0! !


! IdStreamParser methodsFor: 'routines' !

captureUpTo: aChar
	"Advance up to aChar. Return the content between
	the current position and aChar as a string."

	| buffer |
	buffer := IdStringBuffer new: 20.
	[ self atEndNot and: [ self getLastChar ~~ aChar ] ]
		whileTrue: [
			buffer appendChar: self getLastCharContent.
			self nextChar ].
	^buffer convertToString!

captureUpToSemicolon: aStream
	"Advance aStream up to the next semi colon. Return the 
	content between the current position and there as a string."

	| buffer nextChar |
	nextChar := aStream ideReadNext.
	buffer := IdStringBuffer new: 30.
	[ nextChar ~~ -1 and: [ nextChar ~~ SEMI ] ]
		whileTrue: [
			buffer appendChar: ( self getCharContent: nextChar ).
			nextChar := aStream ideReadNext ].
	^buffer convertToString!

skipToCharacter: aChar
	"Skip to the next occurance of aChar or the end
	of the stream. The receiver will be positioned over
	the matching character."

	[ lastChar ~~ -1 and: [ self getLastChar ~~ aChar ] ]
		whileTrue: [ self nextChar ]!

skipUpToSeparator

	[ lastChar ~~ -1 and: [ self lastCharIsSeparator not ] ]
		whileTrue: [ self nextChar ]!

upToSeparators

	| buffer |
	buffer := IdStringBuffer new: 30.
	[ self atEndNot and: [ self lastCharIsSeparator ] ]
		whileTrue: [
			buffer appendChar: self getLastCharContent.
			self nextChar ].
	^buffer convertToString! !


! IdStreamParser methodsFor: 'errors' !

fatalError: aString
	"Raise a Fatal Error containing aString.
	Default is do nothing."!

fatalException: anException
	"Raise a new SAX exception to represent anException.
	Include the message or a descriptive string for debugging."

	| msg |
	msg := anException getMessage.
	msg == nil ifTrue: [
		msg := anException idePrintString ].
	self fatalError: msg! !


! IdStreamParser methodsFor: 'internal' !

getStreamReader
	^streamReader!

nextChar
	"Advance the stream and return the new lastChar.
	Detect the LF in a CR/LF combination and skip it."

	lastChar := self getStreamReader ideReadNext.
	crLfFlag ifTrue: [
		lastChar == LF ifTrue: [ self nextChar ].
		crLfFlag := false ].
	^lastChar ideCharValue!

popStream

	| cache |
	streamStack size == 0 ifTrue: [
		self fatalError: 'too many end tags'.
		^self ].
	cache := streamStack first.
	streamStack removeAtIndex: 1.
	streamReader := cache getStream.
	lastChar := cache getLastChar!

privateParseStream
	"Default is do nothing."!

pushStream: aStream

	streamReader == nil ifTrue: [
		self fatalError: 'parse not correctly started'.
		^self ].
	streamStack 
		ideInsertElement: ( IdStreamParserCache forStream: streamReader char: lastChar ) 
		offset: 0.
	streamReader := aStream.
	self nextChar.!

resetBaseURI: anObject
	baseURI := anObject!

resetLastChar: aChar
	lastChar := aChar!

retrieveInputStream: anInputSource
	"Attempt to retrieve the XML resource represented by
	anInputSource and return a read-only file stream which
	contains it. If the systemId can not be resolved try again
	using the publicId. Return nil if a stream can not be opened."

	| aStream inputURI |
	inputURI := self retrieveURI: baseURI systemId: anInputSource getSystemId.
	aStream := self retrieveURIStreamReader: inputURI.
	aStream ~~ nil ifTrue: [ ^aStream ].
	inputURI := self retrieveURI: baseURI systemId: anInputSource getPublicId.
	^self retrieveURIStreamReader: inputURI!

retrieveOpenReader: anInputSource
	"Look for character and byte streams in anInputSource.
	Return a reader if one is found, else return false."

	| reader byteStream |
	reader := anInputSource getCharacterStream.
	reader ~~ nil ifTrue: [ ^reader ].
	byteStream := anInputSource getByteStream.
	byteStream == nil ifTrue: [ ^nil ].
	^byteStream ideAsStreamReader!

retrieveURI: aBaseURI systemId: systemId

	| inputURI |
	inputURI := nil.
	[ inputURI := IdURI forBase: aBaseURI string: systemId ] 
		ideOnException: IdException 
		do: [ :ex | ex ideExitGracefully ].
	^inputURI!

retrieveURIStreamReader: inputURI

	| reader |
	reader := nil.
	[ reader := inputURI openStream ideAsStreamReader ] 
		ideOnException: IdException 
		do: [ :ex | ex ideExitGracefully ].
	^reader!

setStreamReader: anObject
	streamReader := anObject!

setStreamToEnd
	"Can't do this in Java. Just set lastChar to -1."

	"stream setToEnd"
	lastChar := EofChar! !


! IdStreamParser methodsFor: 'creation' !

initialize

	super initialize.
	streamStack := OrderedCollection new.
	crLfFlag := false! !


! IdStreamParser methodsFor: 'type casting' !

getCharContent: aChar
	"In Java return a char version of aChar.
	In Smalltalk return aChar."

	^aChar!

getCharContentLF
	^self getCharContent: LF!

getLastCharContent
	"Return the last char as content. In Smalltalk this
	returns a Character object. In Java the char value."

	^lastChar! !


! IdStreamParser methodsFor: 'services' !

parse: anInputSource
	"Complete a parse of the resource represented
	by anInputSource. If the input source can not be resolved
	to a valid character stream, throw a SAXException."

	anInputSource getSystemId ~~ nil ifTrue: [
		self resetBaseURI: (
			IdURI forUriString: anInputSource getSystemId ) ].
	streamReader := self retrieveOpenReader: anInputSource.
	streamReader == nil ifTrue: [
		streamReader := self retrieveInputStream: anInputSource ].
	streamReader == nil ifTrue: [
		self fatalError: 'document not found'.  "should send IO error"
		^self ].
	[ self privateParseStream ] 
		ideOnException: IdException 
		do: [ :ex | 
			self fatalException: ex ideAsExceptionInstance ]
		finally: [ 
			streamReader close ]!

parseSystemId: aSystemId
	"Complete a parse of the resource represented
	by aSystemId. If the input source can not be resolved
	to a valid character stream, throw a SAXException."
	"This method is renamed to parse() in Java to
	satisfy the SAX Parser interface."

	self parse: (
		IdInputSource forSystemId: aSystemId )! !


! IdStreamParserCache class methodsFor: 'constructors' !

forStream: anObject char: charValue

	| answer |
	answer := IdStreamParserCache new.
	answer initStream: anObject char: charValue.
	^answer! !


! IdStreamParserCache methodsFor: 'accessing' !

getLastChar
	^lastChar!

getStream
	^stream! !


! IdStreamParserCache methodsFor: 'creation' !

initStream: anObject char: charValue

	stream := anObject.
	lastChar := charValue! !


! IdXWalkerAttribute class methodsFor: 'constructors' !

forNameValue: aName pair: aValue

	| answer |
	answer := IdXWalkerAttribute new.
	answer initNameValue: aName pair: aValue.
	^answer! !


! IdXWalkerAttribute methodsFor: 'accessing' !

getName
	^name!

getValue
	^value!

setName: anObject
	name := anObject!

setValue: anObject
	value := anObject! !


! IdXWalkerAttribute methodsFor: 'creation' !

initNameValue: aName pair: aValue

	name := aName.
	value := aValue! !


IdXWalkerAttributeList comment: '
	A Smalltalk implementation of SAX AttributeList.

	Instances of this class contain a list of attributes for a parsed 
	element, maintaining the order in which they were encoded. The 
	parser passes an instance to the SAX application as the second
	argument of each startElement event.

	The instance provided will return valid results only during the 
	scope of the startElement invocation (to save it for future use, the 
	application must make a copy).

	An AttributeList includes only attributes that have been specified 
	or defaulted: #IMPLIED attributes will not be included.

	There are two ways for the SAX application to obtain information 
	from an AttributeList. First, it can iterate through the entire list
	accessing each attribute by index position using the #getName:
	and #getValue: methods. This is comparable to the Java SAX
	interface. As an alternative, the application can request the value 
	of attributes by name using #getValueAtName: (this differs
	slightly from the Java implementation because Smalltalk can
	not have two methods which share the same name.'!


! IdXWalkerAttributeList methodsFor: 'internal' !

enlargeStringArray: anArray

	| answer |
	answer := Array new: anArray ideArrayLength + 10.
	IdKernelUtilities copyArray: anArray 
		start: 0
		dst: answer 
		length: anArray ideArrayLength.
	^answer! !


! IdXWalkerAttributeList methodsFor: 'services' !

getLength
	"Return the number of attributes in this list."

	^attCount!

getName: zeroBasedIndex
	"Return the name of the attribute stored at zeroBasedIndex.
	Return nil if it is not found."

	| index |
	index := zeroBasedIndex + 1.
	names ideArrayLength < index ifTrue: [ ^nil ].
	^names at: index!

getType: zeroBasedIndex
	"Return the type of the attribute stored at zeroBasedIndex.
	Return nil if it is not found."

	| index |
	index := zeroBasedIndex + 1.
	types ideArrayLength < index ifTrue: [ ^nil ].
	^types at: index!

getTypeAtName: aName
	"Return the type of the first attribute named aName 
	in this list. Return nil if none is found."
	"Note that in Java this method is named getType() as
	required by the SAX interface AttributeList. In Smalltalk
	a class can not have two methods with the same name."

	1 to: names ideArrayLength do: [ :i |
		( ( names at: i ) ideEquals: aName ) ifTrue: [
			^types at: i ] ].
	^nil!

getValue: zeroBasedIndex
	"Return the value of the attribute stored at zeroBasedIndex.
	Return nil if it is not found."

	| index |
	index := zeroBasedIndex + 1.
	values ideArrayLength < index ifTrue: [ ^nil ].
	^values at: index!

getValueAtName: aName
	"Return the value of the first attribute named aName 
	in this list. Return nil if none is found."
	"Note that in Java this method is named getValue() as
	required by the SAX interface AttributeList. In Smalltalk
	a class can not have two methods with the same name."

	1 to: names ideArrayLength do: [ :i |
		( ( names at: i ) ideEquals: aName ) ifTrue: [
			^values at: i ] ].
	^nil!

storeAttribute: aName value: aValue type: aType
	"Add an attribute for aName, aValue and aType."

	attCount >= names ideArrayLength ifTrue: [
		names := self enlargeStringArray: names.
		values := self enlargeStringArray: values.
		types := self enlargeStringArray: types ].
	attCount := attCount + 1.
	names at: attCount put: aName.
	values at: attCount put: aValue.
	types at: attCount put: aType! !


! IdXWalkerAttributeList methodsFor: 'creation' !

initialize

	attCount := 0.
	names := Array new: 10.
	values := Array new: 10.
	types := Array new: 10! !


IdXWalkerDTD comment: '
	This class provides a minimal representation of DTD''s
	as they are parsed by a non-validating parser.'!


! IdXWalkerDTD methodsFor: 'setup' !

addXMLCharRefs
	"Add the 5 standard XML character entities."

	self
		addEntityName: 'quot' value: '"';
		addEntityName: 'amp' value: '&';
		addEntityName: 'apos' value: '''';
		addEntityName: 'lt' value: '<';
		addEntityName: 'gt' value: '>'! !


! IdXWalkerDTD methodsFor: 'elements' !

elementDeclared: tagName content: content
	"A declaration for tagName has just been read.
	Default is do nothing."! !


! IdXWalkerDTD methodsFor: 'queries' !

elementTypeIsEmpty: aTagName
	^false!

isHTMLAware
	^false!

typeHasOptEndTag: aTagName preceding: nextType
	^false! !


! IdXWalkerDTD methodsFor: 'entities' !

addEntityName: aName value: aValue
	entities ideAtKey: aName put: aValue!

getEntities
	"Return the entire entities dictionary. This is useful
	for debugging - do not change the dictionary's contents."

	^entities!

getEntityValueAt: aString
	"Return the String value stored as an entity at
	a name equal to aString, or nil if none is found."

	^entities ideAtKeyOrNil: aString! !


! IdXWalkerDTD methodsFor: 'creation' !

initialize
	"Initialize the receiver and add the standard XML
	character entities."

	super initialize.
	defaultAttributes := Dictionary new.
	entities := Dictionary new.
	self addXMLCharRefs! !


! IdXWalkerDTD methodsFor: 'default attributes' !

addDefaultAttribute: aName value: aValue for: tagName

	| attList |
	attList := defaultAttributes ideAtKeyOrNil: tagName.
	attList == nil ifTrue: [
		attList := OrderedCollection new.
		defaultAttributes ideAtKey: tagName put: attList ].
	attList add: (
		IdXWalkerAttribute forNameValue: aName pair: aValue )!

processDefaultAtts: anEvent
	"Add copies of all default attributes to anEvent for
	the element type represented."

	| attList att |
	attList := defaultAttributes ideAtKeyOrNil: anEvent getTagName.
	attList == nil ifTrue: [ ^self ].
	1 to: attList size do: [ :i |
		att := attList ideAtIndex: i.
		anEvent attributeAtName: att getName put: att getValue ]! !


! IdXWalkerParserEvent class methodsFor: 'constructors' !

endTag: aTagName
	^IdXWalkerParserEvent new setTagName: aTagName isStart: false!

startTag: aTagName
	^IdXWalkerParserEvent new setTagName: aTagName isStart: true! !


IdXWalkerParserEvent comment: '
	A class used to wrap information about a parse event.
	There should be no need to access this class directly by
	applications using the parser.'!


! IdXWalkerParserEvent methodsFor: 'accessing' !

getAttributeList
	^declaredAttributes!

getTagName
	^tagName!

isStart
	^isStart! !


! IdXWalkerParserEvent methodsFor: 'convenience' !

isEnd
	^isStart not! !


! IdXWalkerParserEvent methodsFor: 'services' !

attributeAtName: aName put: aValue
	"Store an attribute as specified. Note that only CDATA
	attribute types are supported."

	declaredAttributes storeAttribute: aName 
		value: aValue 
		type: 'CDATA'!

printOn: aStream
	super printOn: aStream.
	aStream
		space;
		nextPut: $(;
		space;
		nextPutAll: self getTagName;
		space;
		nextPut: $)!

setTagName: aName isStart: aBoolean
	"Return the receiver."

	tagName := aName.
	isStart := aBoolean.
	declaredAttributes := IdXWalkerAttributeList new.
	^self ideYourself! !


IdGeneralException comment: '
	This class maps to Error in Java. The code fragment
	''IdGeneralException signalNew'' will translate to 
	''throw new Error'' in Java.'!


IdIOException comment: '
	Simulate an IO exception in Java.'!


! IdSAXException class methodsFor: 'constructors' !

forSAXMessage: aMsg

	| answer |
	answer := IdSAXException new.
	answer setMessage: aMsg.
	^answer! !


IdSAXException comment: '
	Simulate a SAX exception in Java.'!


! IdSAXException methodsFor: 'accessing' !

getMessage

	errorMessage == nil ifTrue: [  "need to tie into parser"
		errorMessage := 'SAX error' ].
	^errorMessage!

setMessage: aString
	errorMessage := aString! !


! IdSAXException methodsFor: 'queries' !

isParseException
	^false! !


! IdXWalkerParseAppExample class methodsFor: 'examples' !

runAttributesExample
"
	IdXWalkerParseAppExample runAttributesExample
"
	"Test element attributes specified with single and double 
	quotation marks."

	self runExampleOnString: 
'<Customers>
	<Customer custID=', $" asSymbol, '001', $" asSymbol, ' lastMod=', $' asSymbol, '1998.05.22', $' asSymbol, ' >
		<Name>
			<First>Bradley</First>
			<Last>Bismark</Last>
		</Name>
	</Customer>
</Customers>'!

runCharRefsCommentPIExample
"
	IdXWalkerParseAppExample runCharRefsCommentPIExample
"
	"Test common xml character references, comments and
	processing instructions."

	self runExampleOnString: 
'<?xml version=''1.0''?>
<Customers>
	<Customer>
		<?examplePI contents=''test''?>
		<Name>
			<First>Bradley &lt;Brad&gt;</First>
			<Last>Bismark</Last>
		</Name>
	</Customer>
</Customers>'!

runCustomerExample
"
	IdXWalkerParseAppExample runCustomerExample
"
	"Test a simple well formed document which contains
	several occurences of the same element types."

	self runExampleOnString: 
'<Customers>
	<Customer>
		<Name>
			<First>Bradley</First>
			<Last>Bismark</Last>
		</Name>
		<Order>
			<Number>16273</Number>
			<Date><Day>11</Day><Month>6</Month><Year>1997</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>
		<Credit-Limit>5000</Credit-Limit>
		<Order>
			<Number>16182</Number>
			<Date><Month>6</Month><Day>15</Day><Year>1997</Year></Date>
			<Item>
				<Title>Evolution of Complexity in Animal Culture</Title>
				<Publisher>Associated Press</Publisher>
				<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>19182</Number>
			<Date><Year>1997</Year><Month>7</Month><Day>17</Day></Date>
			<Item>
				<Title>Learn Java Now</Title>
				<Author>Stephen R. Davis</Author>
				<Publisher>Microsoft Corporation</Publisher>
				<Price>9.95</Price>
			</Item>
		</Order>
	</Customer>
</Customers>'!

runExampleOn: anInputSource
	"Main method for running examples. anInputSource is
	used to represent an XML resource which is either a stream
	or a systemId (path). Input sources are modelled by the
	class IdInputSource. See that class' comment for more."

	| parser |
	parser := IdXWalkerParser new.
	parser setDocumentHandler: IdXWalkerParseAppExample new.
	[ parser parse: anInputSource ] 
		ideOnException: IdException
		do: [ :ex | 
			ex ideExitGracefully ]!

runExampleOnString: aString
	"Create an input source containing a character stream 
	over aString and run the example."

	self runExampleOn: (
		IdInputSource forStream: ( ReadStream ideReadStreamOn: aString ) )!

runExampleOnSystemId: aSystemId
	"Run an example by reading the file from aSystemId (path).
	The path must be provided using a URI/URL syntax such as
	'file:/c:/example.xml'. Note that only the 'file' protocol is supported
	in the Smalltalk implementation, not 'http', 'ftp', etc."
"
	IdXWalkerParseAppExample runExampleOnSystemId: 'file:/c:/example.xml'
"
	^self runExampleOn: (
		IdInputSource forSystemId: aSystemId )! !


IdXWalkerParseAppExample comment: '
	This class contains several examples of how to use
	the parser. These are stored in class methods.'!


! IdXWalkerParseAppExample methodsFor: 'sax api' !

characters: charArray start: start length: length
	"CHANGED PB"
	"Transcript cr;show: 'char data: ', charArray"
	Transcript cr;show: 'char data: ',
	    (charArray copyFrom: start + 1 to: start + length)!

endDocument
	Transcript cr;show: 'end document'!

endElement: name
	Transcript cr;show: 'end element: ', name!

processingInstruction: target data: data
	Transcript cr;show: 'PI target: ', target;cr;show: '     data: ', data!

startDocument
	Transcript cr;show: 'start document'!

startElement: name atts: atts
	Transcript cr;show: 'start element: ', name, '   ', atts idePrintString! !


IdXWalkerParser comment: '
	The main class used to perform a parse. It accepts a single
	input source parameter in the #parse: method which will parse
	the document (if possible) and inform handlers as needed.
	Nothing is returned. To gather information during a parse
	you must provide a document handler. See IdXWalkerParseAppExample
	for usage examples. Errors are optionally sent to an error handler
	registered using the #setErrorHandler: method.'!


! IdXWalkerParser methodsFor: 'accessing' !

getCurrentTag
	^currentTag!

getDocumentHandler
	"This is guaranteed not to return nil."

	docHandler == nil ifTrue: [
		docHandler := IdSAXHandlerBase new ].
	^docHandler!

getDtd
	"Return the current DTD and lazy initialize it with
	an empty one if necessary."

	dtd == nil ifTrue: [ dtd := self createDtd ].
	^dtd!

getEntityResolver
	^entityResolver!

setDocumentHandler: aHandler
	docHandler := aHandler!

setDtd: aDTD
	dtd := aDTD!

setDTDHandler: aHandler
	dtdHandler := aHandler!

setEntityResolver: anEntityResolver
	entityResolver := anEntityResolver!

setErrorHandler: aHandler
	errHandler := aHandler!

setLocale: anObject
	locale := anObject! !


! IdXWalkerParser methodsFor: 'testing' !

hasPreloadedXHTMLSchemas
	^false!

ideIsXWalkerParser
	^true!

isPITargetXml: target
	"Return true if target equals 'xml' using any combination
	of upper and lower case characters."

	| aChar |
	target ideLength == 3 ifFalse: [ ^false ].
	aChar := target ideCharAt: 1.
	( aChar ideCharValue == 120 "$x" or: [ aChar ideCharValue == 88 "$X" ] ) ifFalse: [ ^false ].
	aChar := target ideCharAt: 2.
	( aChar ideCharValue == 109 "$m" or: [ aChar ideCharValue == 77 "$M" ] ) ifFalse: [ ^false ].
	aChar := target ideCharAt: 3.
	( aChar ideCharValue == 108 "$l" or: [ aChar ideCharValue == 76 "$L" ] ) ifFalse: [ ^false ].
	^true!

lastCharIsAnyCaseP

	self getLastChar == 80 "$P" ifTrue: [ ^true ].
	^self getLastChar == 112 "$p"!

lastCharIsAnyCaseS

	self getLastChar == 83 "$S" ifTrue: [ ^true ].
	^self getLastChar == 115 "$s"!

lastCharIsName
	^IdKernelUtilities isXMLNameChar: self getLastCharContent!

lastCharIsNameFirst
	^IdKernelUtilities isXMLNameFirstChar: self getLastCharContent! !


! IdXWalkerParser methodsFor: 'running' !

nextAction

	self getLastChar == 38 "$&" ifTrue: [
		self nextChar == 35 "$#"
			ifTrue: [self nextChar. self cro. ^self]
			ifFalse: [self ero. ^self]].
	self getLastChar == 60 "$<" ifTrue:
		[self nextChar == 47 "$/"
			ifTrue: [self nextChar. self etago. ^self]
			ifFalse: [
			  self getLastChar == 33 "excl point"
				ifTrue: [self nextChar. self mdo. ^self]
				ifFalse: [
				  self getLastChar == 63 "$?"
					ifTrue: [self nextChar. self pio. ^self]
					ifFalse: [self stago. ^self]]]].
	(self lastCharIsNormalizedLineFeed)
		ifTrue: [self nextChar. self re. ^self].
	self fatalError: 'unexpected character ', self getLastChar printString.!

nextAttributeAssignToken

	self skipSeparators.
	self getLastChar == 61 "$=" ifTrue: [ self nextChar. ^'=' ].
	^nil!

nextAttributeValueToken

	| value |
	value := self nextLiteralOrToken.
	value == nil ifTrue: [ ^nil ].
	false "if contains $<" ifTrue: [
		self fatalError: 'No < in Attribute Values' ].
	^value "normalized"!

nextCDATA

	[ self nextCDATATest ] whileFalse: [
		self cdataAppendChar: self getLastCharContent.
		self nextChar ]!

nextCDATATest

	| done |
	done := self atEnd
		or: [ self getLastChar == 38 "$&" 
		or: [ self getLastChar == 60 "$<" 
		or: [ self lastCharIsNormalizedLineFeed ]]].
	^done!

nextLiteralOrToken

	| buffer |
	self skipSeparators.
	self atEnd ifTrue: [ ^nil ].
	self lastCharIsQuoteOrApos ifTrue: [ 
		^self nextStringInQuotesOrApos ].
	buffer := IdStringBuffer new: 30.
	[ self nextLiteralOrTokenTest ] whileFalse: [
		buffer appendChar: self getLastCharContent.
		self nextChar ].
	^buffer convertToString!

nextLiteralOrTokenTest

	| answer |
	answer := self atEnd
		or: [ self lastCharIsSeparator 
		or: [ self getLastChar == 62 "$>" 
		or: [ self getLastChar == 47 "$/" ]]].
	^answer!

nextPutCdataAtEnd: elementEnd

	| saxIndex |
	saxIndex := cdataIndex.
	cdataIndex == 0 ifFalse: [ self resetCdata ].
	( elementEnd
		and: [ saxIndex > 0
		and: [ self charIsCrOrLf: ( cdata at: saxIndex ) ]] ) ifTrue: [
			saxIndex := saxIndex - 1 ].
	saxIndex == 0 ifFalse: [
		self getDocumentHandler characters: cdata
			start: 0 
			length: saxIndex ]!

nextStringInQuotesOrApos
	"Assume the receiver is positioned over a quote or
	apostrophe character. Return the surrounded string
	and advance beyond the matching quote or apos."

	| buffer ch |
	buffer := IdStringBuffer new: 50.
	ch := self getLastChar.
	self nextChar.
	[ self atEndNot and: [ self getLastChar ~~ ch and: [ self getLastChar ~~ 62 "$>" ] ] ]
		whileTrue: [
			buffer appendChar: self getLastCharContent.
			self nextChar ].
	self getLastChar == ch ifTrue: [ self nextChar ].
	^buffer convertToString!

nextXMLToken
	self skipSeparators.
	^self nextXMLTokenNoSkip!

nextXMLTokenNoSkip

	| buffer |
	self getLastChar == 37 "$%" ifTrue: [  "allows % in parameter entity decl."
		self nextChar.
		self skipSeparators ].
	self atEnd ifTrue: [ ^nil ].
	self lastCharIsNameFirst ifFalse: [ ^nil ].
	buffer := IdStringBuffer new: 20.
	buffer appendChar: self getLastCharContent.
	self nextChar.
	[ self atEnd not and: [ self lastCharIsName ] ]
		whileTrue: [
			buffer appendChar: self getLastCharContent.
			self nextChar ].
	^buffer convertToString!

parseCDATASection
	"The receiver is positioned over the [ in a CDATA section.
	Advance past the last > character."

	| buffer errString |
	buffer := IdStringBuffer new: 6.
	1 to: 6 do: [ :i |
		self nextChar.
		buffer appendChar: self getLastCharContent ].
	( buffer convertToString ideEquals: 'CDATA[' ) ifFalse: [
		self fatalError: 'invalid start to CDATA section'. 
		^self ].
	errString := 'CDATA section must end with ]]> characters'.
	self nextChar.
	[ self atEnd ] whileFalse: [
		self writeCDATASegmentToBuffer.  "ends on ] char"
		self atEnd ifTrue: [ self fatalError: errString. ^self ].
		self nextChar.
		self getLastChar == 93 "$]" ifTrue: [
				self nextChar.
				self atEnd ifTrue: [ self fatalError: errString. ^self ].
				self getLastChar == 62 "$>" ifTrue: [
						self nextChar.                 "successful CDATA parse"
						^self ]
					ifFalse: [
						self cdataAppendString: ']]' ] ]
			ifFalse: [ 
				self cdataAppendString: ']' ] ].
	self fatalError: errString!

parseDOCTYPE
	"The receiver is positioned over the D in a DOCTYPE
	declaration. The next token must be
	a tag name which corresponds to the root element (which
	is not validated by this parser). Process an internal or
	external DTD as required."

	| dtdInputSource |
	self skipUpToSeparator.
	self skipSeparators.
	self lastCharIsNameFirst ifFalse: [
		self fatalError: 'invalid DOCTYPE Root Element Type'. ^self ].
	self skipUpToSeparator.
	self skipSeparators.
	self getLastChar == 91 "$[" ifTrue: [  "Internal DTD"
		self resetLastChar: (
			IdXWalkerDoctypeParser new parseInternalDTD: self stream: self getStreamReader ).
		self atEnd ifTrue: [
			self fatalError: 'invalid DOCTYPE - can not parse internal DTD'. ^self ].
		self skipSeparators.
		self getLastChar == 62 "$>" ifTrue: [ self nextChar. ^self ].
		self fatalError: 'invalid DOCTYPE for internal DTD'. ^self ].

	( self lastCharIsAnyCaseS or: [ self lastCharIsAnyCaseP ] ) ifFalse: [
		self fatalError: 'invalid DOCTYPE - can not locate DTD system identifier'. ^self ].

	"External DOCTYPE"
	dtdInputSource := self recognizeExternalEntity.
	dtdInputSource == nil ifTrue: [
		self fatalError: 'invalid DOCTYPE - can not parse DTD external entity'. ^self ].
	IdXWalkerDoctypeParser new parseExternalDTD: dtdInputSource for: self.
	self skipSeparators.
	self getLastChar == 62 "$>" ifTrue: [ self nextChar. ^self ].
	self fatalError: 'invalid DOCTYPE for external DTD'!

parseElementAttributes: anEvent
	"Add copies of all default attributes and then replace
	them with any that are parsed. Return true if this is recognized
	as an empty tag, else return false."

	| key value |
	self getDtd processDefaultAtts: anEvent.
	key := self nextXMLToken.
	[ key ~~ nil ] whileTrue: [
		self nextAttributeAssignToken == nil
			ifTrue: [ value := String new ]
			ifFalse: [ 
				value := self nextAttributeValueToken.
				value == nil ifTrue: [
					self fatalError: 'illegal attribute format'.
					self skipToTagc.
					^false ] ].
		anEvent attributeAtName: key put: (
			self normalizeAttValue: value ).
		key := self nextXMLToken ].
	self skipSeparators.
	self peekEtagSlash ifTrue: [ ^true ].  "this indicates an empty tag"
	self peekTagc
		ifTrue: [ self nextChar ]
		ifFalse: [ self fatalError: 'tag close expected during attribute parse' ].
	^false!

parseEndTag: aTagName

	self skipSeparators.
	self peekTagc
		ifTrue: [ self nextChar ]
		ifFalse: [
			self fatalError: 'tag close expected'.
			self skipToTagc ].
	self endTagCloseAction: aTagName!

parseStartTag: aTagName

	| event isEmptyTag |
	event := IdXWalkerParserEvent startTag: aTagName.
	isEmptyTag := self parseElementAttributes: event.
	self nextPutCdataAtEnd: false.
	( isEmptyTag and: [ self processEmptyTag: aTagName event: event ] ) ifFalse: [
		self pushElement: aTagName event: event ]!

popElementWithEvent: anEvent

	| stackTag |
	self nextPutCdataAtEnd: true.
	self sendEndElementEvent: anEvent.
	tagStack isEmpty
		ifTrue: [ currentTag := nil ]
		ifFalse: [ 
			stackTag := tagStack first.
			currentTag := stackTag.
			tagStack ideRemoveFirst ]!

privateParseStream
	"This method assumes that the stream has already
	been properly stored."

	self getStreamReader == nil ifTrue: [ ^self ].
	lineNum := linePos := 0.
	self startParse.
	self endParse!

pushElement: aTagName event: anEvent

	self sendStartElementEvent: anEvent.
	currentTag ~~ nil ifTrue: [
		tagStack ideInsertElement: currentTag offset: 0 ].
	currentTag := aTagName!

recognizeExternalEntity
	"Return an input source or nil. XML requires either a
	SYSTEM, or a PUBLIC and SYSTEM idetifier for external
	entities. However, many HTML docs use only the PUBLIC
	identifier for the DOCTYPE which is an error in XML. In this 
	method the HTML technique is allowed."

	| sysId pubId |
	self lastCharIsAnyCaseS ifTrue: [
		self skipUpToSeparator.
		self skipSeparators.
		self lastCharIsQuoteOrApos ifFalse: [
			self fatalError: 'requires SYSTEM identifier'. ^nil ].
		sysId := self nextStringInQuotesOrApos.
		sysId ideLength == 0 ifTrue: [
			self fatalError: 'invalid SYSTEM identifier'. ^nil ].
		^self resolveEntity: nil system: sysId ].
	self lastCharIsAnyCaseP ifFalse: [
		self fatalError: 'external entity requires SYSTEM or PUBLIC identifier'. ^nil ].
	self skipUpToSeparator.
	self skipSeparators.
	self lastCharIsQuoteOrApos ifFalse: [
		self fatalError: 'requires PUBLIC identifier'. ^nil ].
	pubId := self nextStringInQuotesOrApos.
	pubId ideLength == 0 ifTrue: [
		self fatalError: 'invalid PUBLIC identifier'. ^nil ].
	self skipSeparators.
	sysId := ''.
	self lastCharIsQuoteOrApos ifTrue: [
		sysId := self nextStringInQuotesOrApos.
		sysId ideLength == 0 ifTrue: [
			self fatalError: 'invalid SYSTEM portion of external entity reference'. ^nil ] ].
	^self resolveEntity: pubId system: sysId!

resetCdata
	cdataIndex := 0!

resolveEntity: publicId system: systemId

	| answer |
	self getEntityResolver ~~ nil ifTrue: [
		answer := self getEntityResolver resolveEntity: publicId system: systemId.
		answer ~~ nil ifTrue: [ ^answer ] ].
	answer := IdInputSource forSystemId: systemId.
	answer setPublicId: publicId.
	^answer!

runBasicParseLoop

	| done |
	done := false.
	[ done ] whileFalse: [
		self runInternalParseLoop.
		done := self streamStackIsEmpty.
		done ifFalse: [ 
			self popStream ] ]!

runInternalParseLoop

	| done |
	done := false.
	[ done ] whileFalse: [
		self nextCDATA.
		done := self atEnd.
		done ifFalse: [ 
			self nextAction ] ]!

skipComment
	"The receiver is positioned over the first - in the comment.
	Advance the receiver to the character immediately after the last >."

	| twoDash |
	self nextChar.
	self getLastChar == 45 "$-" ifFalse: [
		self fatalError: 'invalid comment start'. ^self ].
	self nextChar.
	twoDash := false.
	[ self atEndNot ] whileTrue: [
		self getLastChar == 45 "$-" ifTrue: [ 
			"	Transcript cr;show: '$- ', stream position printString, '  ', stream size printString,
						(stream copyFrom: stream position to: stream position + 10) printString."
				self nextChar.
				( twoDash and: [ self getLastChar == 62 "$>" ] ) ifTrue: [ self nextChar. ^self ].
				self getLastChar == 45 "$-" ifTrue: [ 
						twoDash := true.
						self nextChar.
						self getLastChar == 62 "$>" ifTrue: [ self nextChar. ^self ] ] ]  "normal exit"
			ifFalse: [ 
				twoDash := false.
				self nextChar ] ].
	self fatalError: 'invalid comment - could not find termination'!

skipSeparators

	[ self atEndNot and: [ self lastCharIsSeparator ] ]
		whileTrue: [
			self nextChar ]!

skipToTagc

	self skipToCharacter: 62 "$>".
	self getLastChar == 62 "$>" ifTrue: [ self nextChar ]!

writeCDATASegmentToBuffer
	"Write all characters to the cdata buffer without trying
	to recognize any markup. Continue until the ] character
	or the end of the stream."

	[ self atEndNot and: [ self getLastChar ~~ 93 "$]" ] ] 
		whileTrue: [
			self cdataAppendChar: self getLastCharContent.
			self nextChar ]! !


! IdXWalkerParser methodsFor: 'routines' !

appendUpToTagClose: stringBuffer
	"Append all characters which are not the tag close '>'
	character to stringBuffer. This will leave the receiver positioned
	over the > char which will also be stored in lastChar."

	| looping |
	looping := self atEndNot and: [ self getLastChar ~~ 62 "$>" ].
	[ looping ] whileTrue: [
		stringBuffer appendChar: self getLastCharContent.
		self nextChar.
		looping := self atEndNot and: [ self getLastChar ~~ 62 "$>" ] ]!

normalizeAttValue: aString
	"Return a copy of aString which replaces entity
	references as needed."

	| readStream attChar buffer eRef eValue |
	readStream := ReadStream on: aString.
	buffer := IdStringBuffer new: aString ideLength.
	attChar := readStream ideReadNext.
	[ attChar ~~ -1 ] whileTrue: [
		attChar ideCharValue == 38 "$&" ifTrue: [
				eRef := self captureUpToSemicolon: readStream.
				eValue := self getDtd getEntityValueAt: eRef.
				eValue ~~ nil ifTrue: [ buffer appendString: eValue ] ]
			ifFalse: [ buffer appendChar: ( self getCharContent: attChar ) ].
		attChar := readStream ideReadNext. ].
	^buffer convertToString! !


! IdXWalkerParser methodsFor: 'errors' !

fatalError: aString
	"Raise a Fatal Error containing aString."

	self raiseFatalError: aString!

raiseFatalError: aString
	"Raise an error containing aString using the platform's
	default mechanism. A fatal error can be handled using
	the host platform's exception handling.
	This raises an exception even if an error handler is
	provided which is not precisely what SAX specifies.
	For now record -1 as the line and column numbers.
	These are not easy to calculate precisely without adding
	too much overhead. If anyone has time to solve this problem,
	please send it in to the support team."

	| parseException uriString |
	uriString := nil.
	self getBaseURI ~~ nil ifTrue: [
		uriString := self getBaseURI toExternalForm ].
	parseException := IdSAXParseException forException: aString 
		pub: uriString
		sys: nil
		line: -1 "lineNum"
		col: -1 "linePos".
	errHandler ~~ nil ifTrue: [
		errHandler fatalError: parseException ].
	parseException signal! !


! IdXWalkerParser methodsFor: 'internal' !

cdataAppendChar: aCharacter

	self cdataConfirmCapacity: 1.
	cdataIndex := cdataIndex + 1.
	cdata at: cdataIndex put: aCharacter!

cdataAppendString: aString

	| nextChar |
	self cdataConfirmCapacity: aString ideLength.
	1 to: aString ideLength do: [ :i |
		nextChar := aString ideCharAt: i.
		cdataIndex := cdataIndex + 1.
		cdata at: cdataIndex put: nextChar ]!

cdataConfirmCapacity: anInteger

	| newSize newArray |
	newSize := cdataIndex + anInteger.
	newSize > cdata ideArrayLength ifTrue: [
		newArray := Array new: cdataIndex + cdataIndex + anInteger.
		IdKernelUtilities copyArray: cdata 
			start: 0
			dst: newArray 
			length: cdataIndex.
		cdata := newArray ]!

syncCoreParserVariables: docParser

	self resetCdata.
	self resetBaseURI: docParser getBaseURI.
	docHandler := docParser getDocumentHandler.
	entityResolver := docParser getEntityResolver.
	dtd := docParser getDtd!

syncCoreParserVariables: docParser stream: aStream

	self syncCoreParserVariables: docParser.
	self setStreamReader: aStream! !


! IdXWalkerParser methodsFor: 'peeking' !

peekEtagSlash
	^self getLastChar == 47 "$/"!

peekRe
	^self lastCharIsNormalizedLineFeed!

peekRefc
	^self getLastChar == 59 "$;"!

peekTagc
	^self getLastChar == 62 "$>"!

peekTago
	^self getLastChar == 60 "$<"!

peekXmlEmptyTag: aTagName enforce: enforce

	self skipSeparators.
	self peekEtagSlash ifFalse: [ ^false ].
	self nextChar.
	self peekTagc ifTrue: [ ^true ].
	enforce ifTrue: [
		self fatalError: 'invalid empty tag: ', aTagName ].
	^false! !


! IdXWalkerParser methodsFor: 'parse events' !

cro
	"Character reference open. This method contains UNICODE
	validation logic. Only characters from 1-65535 are legal. Note
	that this is not as precise as required by XML. All illegal
	characters are replaced by the entity string itself."

	| num validNum charValue |
	num := self nextXMLTokenNoSkip.
	num == nil ifTrue: [ 
		self cdataAppendString: '&#'. 
		^self ].
	self peekRefc ifTrue: [ self nextChar ].
	validNum := IdKernelUtilities validateAsInteger: num default: -999.
	validNum == -999 ifTrue: [ ^self ].
	( validNum < 1 or: [ validNum > 65535 ] ) ifTrue: [      "Maximum UNICODE character"
		self cdataAppendString: '&#', num, ';'.
		^self ].
	charValue := validNum.
	self cdataAppendChar: ( Character value: charValue )!

endParse

	[ self getCurrentTag ~~ nil ] whileTrue: [
		self popElementWithEvent: (
			IdXWalkerParserEvent endTag: self getCurrentTag ) ].
	self getDocumentHandler endDocument!

endTagCloseAction: aTagName

	( ( self getCurrentTag ideEquals: aTagName ) or: [ tagStack includes: aTagName ] )
		ifFalse: [ ^self ].
	[ self getCurrentTag ~~ nil and: [ ( self getCurrentTag ideEquals: aTagName ) not  ]]
		whileTrue: [
			self popElementWithEvent: (
				IdXWalkerParserEvent endTag: self getCurrentTag ) ].
	( self getCurrentTag ideEquals: aTagName ) ifFalse: [
		self fatalError: 'end tag does not match: ', aTagName. ^self ].
	self popElementWithEvent: (
		IdXWalkerParserEvent endTag: aTagName )!

ero
	| ename entityValue |
	ename := self nextXMLTokenNoSkip.
	ename == nil ifTrue: [ 
		self cdataAppendString: '&'. 
		^self ].
	entityValue := self getDtd getEntityValueAt: ename.
	entityValue == nil ifTrue: [
		self fatalError: 'entity ', ename, ' does not exist'.
		^self ].
	self peekRefc ifTrue: [ self nextChar ].
	self pushStream: ( ReadStream ideReadStreamOn: entityValue )!

etago
	| name |
	name := self nextXMLTokenNoSkip.
	name == nil ifTrue: [ self cdataAppendString: '</'. ^self ].
	self parseEndTag: name!

mdo
	"Process a markup declaration open. Receiver is 
	positioned just after the excl point and this can be a comment, 
	CDATA section or some kind of markup declaration.
	Note that DOCTYPE specific delarations are handled
	by the IdXWalkerDoctypeParser subclass."

	self skipSeparators.  "tolerate sloppy HTML syntax"
	self getLastChar == 45 "$-" ifTrue: [ self skipComment. ^self ].
	self getLastChar == 91 "$[" ifTrue: [ self parseCDATASection. ^self ].
	self getLastChar == 68 "$D" ifTrue: [ self parseDOCTYPE. ^self ].
	self getLastChar == 100 "$d" ifTrue: [ self parseDOCTYPE. ^self ].
	self getLastChar == 37 "$%" ifTrue: [ "self parseConditionalSection." ^self ].
	self getLastChar == 78 "$N" ifTrue: [ "self parseNotationDeclaration." ^self ].
	self fatalError: 'unrecognized Markup Declaration Open'!

pio
	"The receiver is positioned just after <?. Handle the
	<?xml declaration here if it is in lower case, otherwise
	report an error. Pass all other PI's to the document handler."

	| target dataBuf targetBuf isLooping dSize |
	targetBuf := IdStringBuffer new: 20.
	dataBuf := IdStringBuffer new: 50.
	target := nil.
	isLooping := true.
	[ isLooping and: [ self atEndNot ] ] whileTrue: [
		self lastCharIsSeparator ifTrue: [
			target := targetBuf convertToString.
			self nextChar.
			self appendUpToTagClose: dataBuf.
			self nextChar.
			isLooping := false ].
		targetBuf appendChar: self getLastCharContent.
		self nextChar ].
	isLooping ifTrue: [
		self fatalError: 'invalid PI - no space detected'. ^self ].
	dSize := dataBuf convertToString ideLength - 1.
	dSize == -1 ifTrue: [
		self fatalError: 'invalid PI - no data detected'. ^self ].
	dSize := dSize max: 1.
	( self isPITargetXml: target ) ifTrue: [
		( target ideEquals: 'xml' ) ifFalse: [
			self fatalError: 'invalid PI - ?xml declaration must be lower case'. ^self ].
		self processXmlDeclaration: (
			dataBuf convertToString copyFrom: 1 to: dSize ). ^self ].
	self getDocumentHandler processingInstruction: target data: (
		dataBuf convertToString copyFrom: 1 to: dSize )!

processEmptyTag: aTagName event: anEvent
	"Process an XML empty tag (ie. <element/>) if required 
	and return true, else return false."

	( self peekXmlEmptyTag: aTagName enforce: false ) ifFalse: [ ^false ].
	self processEmptyTagEvent: anEvent.
	self peekTagc ifTrue: [ 
		self nextChar.
		^true ].
	self fatalError: 'empty XML start tag must end with /> characters'.
	^true!

processEmptyTagEvent: anEvent
	self sendStartElementEvent: anEvent.
	self sendEndElementEvent: anEvent!

processXmlDeclaration: aString
	"Process a legal ?xml declaration which contains aString
	up to the tag close character. Currently do nothing."!

re
	"Encode a return character in cdata. Use a Lf character
	as required by the XML spec."

	self cdataAppendChar: self getCharContentLF!

sendEndElementEvent: event
	self getDocumentHandler endElement: event getTagName!

sendStartElementEvent: event
	self getDocumentHandler startElement: event getTagName
		atts: event getAttributeList!

stago

	| name |
	name := self nextXMLTokenNoSkip.
	name == nil ifTrue: [ self cdataAppendString: '<'. ^self ].
	self parseStartTag: name!

startParse

	self getDocumentHandler startDocument.
	self resetCdata.
	self nextChar.
	self runBasicParseLoop! !


! IdXWalkerParser methodsFor: 'creation' !

createDtd
	^IdXWalkerDTD new!

initialize

	super initialize.
	tagStack := OrderedCollection new.
	cdata := Array new: 512.
	self resetCdata! !


IdXWalkerHTMLDTD comment: '
	An extension of the DTD class which handles some
	HTML specific variations. It includes logic which adds
	all the required internal entities for XHTML (HTML 4). It 
	also defines mechanisms for handling empty tags and
	optional end tags.'!


! IdXWalkerHTMLDTD methodsFor: 'queries' !

elementTypeIsEmpty: tagName
	"Return true if elements of tagName are always
	considered empty, else return false."

	( self checkEmptyType: tagName ) ifTrue: [ ^true ].
	^self checkEmptyType: tagName asLowercase   "supports non xhtml content"!

isHTMLAware
	^true!

typeHasOptEndTag: tagName preceding: nextType

	tagName == nil ifTrue: [ ^false ].
	( ( tagName ideEquals: 'p' ) or: [ tagName ideEquals: 'P' ] ) ifTrue: [
		( self checkOptEndTagP: nextType ) ifTrue: [ ^true ].
		^self checkOptEndTagP: nextType asLowercase ].
	( self checkOptEndTag: tagName preceding: nextType ) ifTrue: [ ^true ].
	^self checkOptEndTag: tagName asLowercase  "non xhtml content"
		preceding: nextType asLowercase! !


! IdXWalkerHTMLDTD methodsFor: 'setup' !

addHTMLEntitiesDTDs
	"Parsed from XHTML frameset.dtd."

	self 
		addEntityName: 'a.content' value: '(#PCDATA | %special; | %fontstyle; | %phrase; | %inline.forms; | %misc;)*';
		addEntityName: 'attrs' value: '%coreattrs; %i18n; %events;';
		addEntityName: 'Block' value: '(%block; | form | %misc;)*';
		addEntityName: 'block' value: 'p | %heading; | div | %lists; | %blocktext; | fieldset | table';
		addEntityName: 'blocktext' value: 'pre | hr | blockquote | address | center';
		addEntityName: 'button.content' value: '(#PCDATA | p | %heading; | div | %lists; | %blocktext; |
	  table | br | span | bdo | object | applet | img | map |
	  %fontstyle; | %phrase; | %misc;)*';
		addEntityName: 'Character' value: 'CDATA';
		addEntityName: 'Charset' value: 'CDATA';
		addEntityName: 'Charsets' value: 'CDATA';
		addEntityName: 'Color' value: 'CDATA';
		addEntityName: 'ContentType' value: 'CDATA';
		addEntityName: 'ContentTypes' value: 'CDATA';
		addEntityName: 'Coords' value: 'CDATA';
		addEntityName: 'coreattrs' value: 'id          ID             #IMPLIED
  class       CDATA          #IMPLIED
  style       %StyleSheet;   #IMPLIED
  title       %Text;         #IMPLIED';
		addEntityName: 'Datetime' value: 'CDATA';
		addEntityName: 'events' value: 'onclick     %Script;       #IMPLIED
  ondblclick  %Script;       #IMPLIED
  onmousedown %Script;       #IMPLIED
  onmouseup   %Script;       #IMPLIED
  onmouseover %Script;       #IMPLIED
  onmousemove %Script;       #IMPLIED
  onmouseout  %Script;       #IMPLIED
  onkeypress  %Script;       #IMPLIED
  onkeydown   %Script;       #IMPLIED
  onkeyup     %Script;       #IMPLIED';
		addEntityName: 'Flow' value: '(#PCDATA | %block; | form | %inline; | %misc;)*';
		addEntityName: 'focus' value: 'accesskey   %Character;    #IMPLIED
  tabindex    %Number;       #IMPLIED
  onfocus     %Script;       #IMPLIED
  onblur      %Script;       #IMPLIED';
		addEntityName: 'fontstyle' value: 'tt | i | b | big | small | u | s | font | basefont';
		addEntityName: 'form.content' value: '(#PCDATA | %block; | %inline; | %misc;)*';
		addEntityName: 'FrameTarget' value: 'CDATA';
		addEntityName: 'heading' value: 'h1|h2|h3|h4|h5|h6';
		addEntityName: 'i18n' value: 'lang        %LanguageCode; #IMPLIED
  xml:lang    %LanguageCode; #IMPLIED
  dir         (ltr|rtl)      #IMPLIED';
		addEntityName: 'ImgAlign' value: '(top|middle|bottom|left|right)';
		addEntityName: 'Inline' value: '(#PCDATA | %inline; | %misc;)*';
		addEntityName: 'inline' value: 'a | %special; | %fontstyle; | %phrase; | %inline.forms;';
		addEntityName: 'inline.forms' value: 'input | select | textarea | label | button';
		addEntityName: 'LanguageCode' value: 'CDATA';
		addEntityName: 'Length' value: 'CDATA';
		addEntityName: 'LinkTypes' value: 'CDATA';
		addEntityName: 'lists' value: 'ul | ol | dl';
		addEntityName: 'MediaDesc' value: 'CDATA';
		addEntityName: 'misc' value: 'ins | del | script | noscript';
		addEntityName: 'MultiLength' value: 'CDATA';
		addEntityName: 'MultiLengths' value: 'CDATA';
		addEntityName: 'Number' value: 'CDATA';
		addEntityName: 'phrase' value: 'em | strong | dfn | code | q | sub | sup |
				   samp | kbd | var | cite | abbr | acronym';
		addEntityName: 'Pixels' value: 'CDATA';
		addEntityName: 'pre.content' value: '(#PCDATA | a | br | span | bdo | map | tt | i | b | u | s |
	  %phrase; | %inline.forms;)*';
		addEntityName: 'Script' value: 'CDATA';
		addEntityName: 'Shape' value: '(rect|circle|poly|default)';
		addEntityName: 'special' value: 'br | span | bdo |object | applet | img | map | iframe';
		addEntityName: 'StyleSheet' value: 'CDATA';
		addEntityName: 'Text' value: 'CDATA';
		addEntityName: 'TextAlign' value: 'align (left|center|right) #IMPLIED';
		addEntityName: 'URI' value: 'CDATA';
		addEntityName: 'UriList' value: 'CDATA'!

addHTMLEntitiesLat1
	"Parsed from XHTML HTMLlat1x.ent."

	self 
		addEntityName: 'Aacute' value: '&#193;';
		addEntityName: 'aacute' value: '&#225;';
		addEntityName: 'Acirc' value: '&#194;';
		addEntityName: 'acirc' value: '&#226;';
		addEntityName: 'acute' value: '&#180;';
		addEntityName: 'aelig' value: '&#230;';
		addEntityName: 'AElig' value: '&#198;';
		addEntityName: 'agrave' value: '&#224;';
		addEntityName: 'Agrave' value: '&#192;';
		addEntityName: 'aring' value: '&#229;';
		addEntityName: 'Aring' value: '&#197;';
		addEntityName: 'Atilde' value: '&#195;';
		addEntityName: 'atilde' value: '&#227;';
		addEntityName: 'Auml' value: '&#196;';
		addEntityName: 'auml' value: '&#228;';
		addEntityName: 'brvbar' value: '&#166;';
		addEntityName: 'Ccedil' value: '&#199;';
		addEntityName: 'ccedil' value: '&#231;';
		addEntityName: 'cedil' value: '&#184;';
		addEntityName: 'cent' value: '&#162;';
		addEntityName: 'copy' value: '&#169;';
		addEntityName: 'curren' value: '&#164;';
		addEntityName: 'deg' value: '&#176;';
		addEntityName: 'divide' value: '&#247;';
		addEntityName: 'Eacute' value: '&#201;';
		addEntityName: 'eacute' value: '&#233;';
		addEntityName: 'ecirc' value: '&#234;';
		addEntityName: 'Ecirc' value: '&#202;';
		addEntityName: 'Egrave' value: '&#200;';
		addEntityName: 'egrave' value: '&#232;';
		addEntityName: 'eth' value: '&#240;';
		addEntityName: 'ETH' value: '&#208;';
		addEntityName: 'Euml' value: '&#203;';
		addEntityName: 'euml' value: '&#235;';
		addEntityName: 'frac12' value: '&#189;';
		addEntityName: 'frac14' value: '&#188;';
		addEntityName: 'frac34' value: '&#190;';
		addEntityName: 'Iacute' value: '&#205;';
		addEntityName: 'iacute' value: '&#237;';
		addEntityName: 'Icirc' value: '&#206;';
		addEntityName: 'icirc' value: '&#238;';
		addEntityName: 'iexcl' value: '&#161;';
		addEntityName: 'Igrave' value: '&#204;';
		addEntityName: 'igrave' value: '&#236;';
		addEntityName: 'iquest' value: '&#191;';
		addEntityName: 'iuml' value: '&#239;';
		addEntityName: 'Iuml' value: '&#207;';
		addEntityName: 'laquo' value: '&#171;';
		addEntityName: 'macr' value: '&#175;';
		addEntityName: 'micro' value: '&#181;';
		addEntityName: 'middot' value: '&#183;';
		addEntityName: 'nbsp' value: '&#160;';
		addEntityName: 'not' value: '&#172;';
		addEntityName: 'Ntilde' value: '&#209;';
		addEntityName: 'ntilde' value: '&#241;';
		addEntityName: 'oacute' value: '&#243;';
		addEntityName: 'Oacute' value: '&#211;';
		addEntityName: 'ocirc' value: '&#244;';
		addEntityName: 'Ocirc' value: '&#212;';
		addEntityName: 'ograve' value: '&#242;';
		addEntityName: 'Ograve' value: '&#210;';
		addEntityName: 'ordf' value: '&#170;';
		addEntityName: 'ordm' value: '&#186;';
		addEntityName: 'oslash' value: '&#248;';
		addEntityName: 'Oslash' value: '&#216;';
		addEntityName: 'Otilde' value: '&#213;';
		addEntityName: 'otilde' value: '&#245;';
		addEntityName: 'ouml' value: '&#246;';
		addEntityName: 'Ouml' value: '&#214;';
		addEntityName: 'para' value: '&#182;';
		addEntityName: 'plusmn' value: '&#177;';
		addEntityName: 'pound' value: '&#163;';
		addEntityName: 'raquo' value: '&#187;';
		addEntityName: 'reg' value: '&#174;';
		addEntityName: 'sect' value: '&#167;';
		addEntityName: 'shy' value: '&#173;';
		addEntityName: 'sup1' value: '&#185;';
		addEntityName: 'sup2' value: '&#178;';
		addEntityName: 'sup3' value: '&#179;';
		addEntityName: 'szlig' value: '&#223;';
		addEntityName: 'THORN' value: '&#222;';
		addEntityName: 'thorn' value: '&#254;';
		addEntityName: 'times' value: '&#215;';
		addEntityName: 'Uacute' value: '&#218;';
		addEntityName: 'uacute' value: '&#250;';
		addEntityName: 'Ucirc' value: '&#219;';
		addEntityName: 'ucirc' value: '&#251;';
		addEntityName: 'Ugrave' value: '&#217;';
		addEntityName: 'ugrave' value: '&#249;';
		addEntityName: 'uml' value: '&#168;';
		addEntityName: 'Uuml' value: '&#220;';
		addEntityName: 'uuml' value: '&#252;';
		addEntityName: 'Yacute' value: '&#221;';
		addEntityName: 'yacute' value: '&#253;';
		addEntityName: 'yen' value: '&#165;';
		addEntityName: 'yuml' value: '&#255;'!

addHTMLEntitiesSpecial
	"Parsed from XHTML HTMLspecialx.ent."

	self 
		addEntityName: 'bdquo' value: '&#8222;';
		addEntityName: 'circ' value: '&#710;';
		addEntityName: 'dagger' value: '&#8224;';
		addEntityName: 'Dagger' value: '&#8225;';
		addEntityName: 'emsp' value: '&#8195;';
		addEntityName: 'ensp' value: '&#8194;';
		addEntityName: 'euro' value: '&#8364;';
		addEntityName: 'ldquo' value: '&#8220;';
		addEntityName: 'lrm' value: '&#8206;';
		addEntityName: 'lsaquo' value: '&#8249;';
		addEntityName: 'lsquo' value: '&#8216;';
		addEntityName: 'mdash' value: '&#8212;';
		addEntityName: 'ndash' value: '&#8211;';
		addEntityName: 'oelig' value: '&#339;';
		addEntityName: 'OElig' value: '&#338;';
		addEntityName: 'permil' value: '&#8240;';
		addEntityName: 'rdquo' value: '&#8221;';
		addEntityName: 'rlm' value: '&#8207;';
		addEntityName: 'rsaquo' value: '&#8250;';
		addEntityName: 'rsquo' value: '&#8217;';
		addEntityName: 'sbquo' value: '&#8218;';
		addEntityName: 'scaron' value: '&#353;';
		addEntityName: 'Scaron' value: '&#352;';
		addEntityName: 'thinsp' value: '&#8201;';
		addEntityName: 'tilde' value: '&#732;';
		addEntityName: 'Yuml' value: '&#376;';
		addEntityName: 'zwj' value: '&#8205;';
		addEntityName: 'zwnj' value: '&#8204;'!

addHTMLEntitiesSymbol
	"Parsed from XHTML HTMLsymbolx.ent."

	self 
		addEntityName: 'alefsym' value: '&#8501;';
		addEntityName: 'Alpha' value: '&#913;';
		addEntityName: 'alpha' value: '&#945;';
		addEntityName: 'and' value: '&#8743;';
		addEntityName: 'ang' value: '&#8736;';
		addEntityName: 'asymp' value: '&#8776;';
		addEntityName: 'Beta' value: '&#914;';
		addEntityName: 'beta' value: '&#946;';
		addEntityName: 'bull' value: '&#8226;';
		addEntityName: 'cap' value: '&#8745;';
		addEntityName: 'chi' value: '&#967;';
		addEntityName: 'Chi' value: '&#935;';
		addEntityName: 'clubs' value: '&#9827;';
		addEntityName: 'cong' value: '&#8773;';
		addEntityName: 'crarr' value: '&#8629;';
		addEntityName: 'cup' value: '&#8746;';
		addEntityName: 'dArr' value: '&#8659;';
		addEntityName: 'darr' value: '&#8595;';
		addEntityName: 'Delta' value: '&#916;';
		addEntityName: 'delta' value: '&#948;';
		addEntityName: 'diams' value: '&#9830;';
		addEntityName: 'empty' value: '&#8709;';
		addEntityName: 'epsilon' value: '&#949;';
		addEntityName: 'Epsilon' value: '&#917;';
		addEntityName: 'equiv' value: '&#8801;';
		addEntityName: 'Eta' value: '&#919;';
		addEntityName: 'eta' value: '&#951;';
		addEntityName: 'exist' value: '&#8707;';
		addEntityName: 'fnof' value: '&#402;';
		addEntityName: 'forall' value: '&#8704;';
		addEntityName: 'frasl' value: '&#8260;';
		addEntityName: 'gamma' value: '&#947;';
		addEntityName: 'Gamma' value: '&#915;';
		addEntityName: 'ge' value: '&#8805;';
		addEntityName: 'harr' value: '&#8596;';
		addEntityName: 'hArr' value: '&#8660;';
		addEntityName: 'hearts' value: '&#9829;';
		addEntityName: 'hellip' value: '&#8230;';
		addEntityName: 'image' value: '&#8465;';
		addEntityName: 'infin' value: '&#8734;';
		addEntityName: 'int' value: '&#8747;';
		addEntityName: 'Iota' value: '&#921;';
		addEntityName: 'iota' value: '&#953;';
		addEntityName: 'isin' value: '&#8712;';
		addEntityName: 'kappa' value: '&#954;';
		addEntityName: 'Kappa' value: '&#922;';
		addEntityName: 'lambda' value: '&#955;';
		addEntityName: 'Lambda' value: '&#923;';
		addEntityName: 'lang' value: '&#9001;';
		addEntityName: 'lArr' value: '&#8656;';
		addEntityName: 'larr' value: '&#8592;';
		addEntityName: 'lceil' value: '&#8968;';
		addEntityName: 'le' value: '&#8804;';
		addEntityName: 'lfloor' value: '&#8970;';
		addEntityName: 'lowast' value: '&#8727;';
		addEntityName: 'loz' value: '&#9674;';
		addEntityName: 'minus' value: '&#8722;';
		addEntityName: 'mu' value: '&#956;';
		addEntityName: 'Mu' value: '&#924;';
		addEntityName: 'nabla' value: '&#8711;';
		addEntityName: 'ne' value: '&#8800;';
		addEntityName: 'ni' value: '&#8715;';
		addEntityName: 'notin' value: '&#8713;';
		addEntityName: 'nsub' value: '&#8836;';
		addEntityName: 'nu' value: '&#957;';
		addEntityName: 'Nu' value: '&#925;';
		addEntityName: 'oline' value: '&#8254;';
		addEntityName: 'omega' value: '&#969;';
		addEntityName: 'Omega' value: '&#937;';
		addEntityName: 'omicron' value: '&#959;';
		addEntityName: 'Omicron' value: '&#927;';
		addEntityName: 'oplus' value: '&#8853;';
		addEntityName: 'or' value: '&#8744;';
		addEntityName: 'otimes' value: '&#8855;';
		addEntityName: 'part' value: '&#8706;';
		addEntityName: 'perp' value: '&#8869;';
		addEntityName: 'Phi' value: '&#934;';
		addEntityName: 'phi' value: '&#966;';
		addEntityName: 'Pi' value: '&#928;';
		addEntityName: 'pi' value: '&#960;';
		addEntityName: 'piv' value: '&#982;';
		addEntityName: 'Prime' value: '&#8243;';
		addEntityName: 'prime' value: '&#8242;';
		addEntityName: 'prod' value: '&#8719;';
		addEntityName: 'prop' value: '&#8733;';
		addEntityName: 'psi' value: '&#968;';
		addEntityName: 'Psi' value: '&#936;';
		addEntityName: 'radic' value: '&#8730;';
		addEntityName: 'rang' value: '&#9002;';
		addEntityName: 'rArr' value: '&#8658;';
		addEntityName: 'rarr' value: '&#8594;';
		addEntityName: 'rceil' value: '&#8969;';
		addEntityName: 'real' value: '&#8476;';
		addEntityName: 'rfloor' value: '&#8971;';
		addEntityName: 'Rho' value: '&#929;';
		addEntityName: 'rho' value: '&#961;';
		addEntityName: 'sdot' value: '&#8901;';
		addEntityName: 'sigma' value: '&#963;';
		addEntityName: 'Sigma' value: '&#931;';
		addEntityName: 'sigmaf' value: '&#962;';
		addEntityName: 'sim' value: '&#8764;';
		addEntityName: 'spades' value: '&#9824;';
		addEntityName: 'sub' value: '&#8834;';
		addEntityName: 'sube' value: '&#8838;';
		addEntityName: 'sum' value: '&#8721;';
		addEntityName: 'sup' value: '&#8835;';
		addEntityName: 'supe' value: '&#8839;';
		addEntityName: 'tau' value: '&#964;';
		addEntityName: 'Tau' value: '&#932;';
		addEntityName: 'there4' value: '&#8756;';
		addEntityName: 'Theta' value: '&#920;';
		addEntityName: 'theta' value: '&#952;';
		addEntityName: 'thetasym' value: '&#977;';
		addEntityName: 'trade' value: '&#8482;';
		addEntityName: 'uarr' value: '&#8593;';
		addEntityName: 'uArr' value: '&#8657;';
		addEntityName: 'upsih' value: '&#978;';
		addEntityName: 'upsilon' value: '&#965;';
		addEntityName: 'Upsilon' value: '&#933;';
		addEntityName: 'weierp' value: '&#8472;';
		addEntityName: 'xi' value: '&#958;';
		addEntityName: 'Xi' value: '&#926;';
		addEntityName: 'zeta' value: '&#950;';
		addEntityName: 'Zeta' value: '&#918;'! !


! IdXWalkerHTMLDTD methodsFor: 'elements' !

elementDeclared: tagName content: content
	"A declaration for tagName has just been read.
	Ignore for now - no validation is happening."

	"elements ideAtKey: tagName put: content"! !


! IdXWalkerHTMLDTD methodsFor: 'internal' !

checkEmptyType: tagName

	( tagName ideEquals: 'basefont' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'base' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'meta' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'link' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'frame' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'hr' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'br' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'param' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'img' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'area' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'input' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'col' ) ifTrue: [ ^true ].
	^false!

checkOptEndTag: tagName preceding: nextType
	"Return true if tagName can have an optional end tag 
	before nextType."

	( tagName ideEquals: 'head' ) ifTrue: [
		( nextType ideEquals: 'body' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'dl' ) ifTrue: [
		( nextType ideEquals: 'dl' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'dt' ) ifTrue: [
		( nextType ideEquals: 'dt' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'dd' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'dl' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'dd' ) ifTrue: [
		( nextType ideEquals: 'dt' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'dd' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'dl' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'li' ) ifTrue: [
		( nextType ideEquals: 'li' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'option' ) ifTrue: [
		( nextType ideEquals: 'option' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'thead' ) ifTrue: [
		( nextType ideEquals: 'tbody' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'tr' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'td' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'th' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'tbody' ) ifTrue: [
		( nextType ideEquals: 'tfoot' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'tr' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'td' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'th' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'colgroup' ) ifTrue: [
		( nextType ideEquals: 'colgroup' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'col' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'tr' ) ifTrue: [
		( nextType ideEquals: 'tr' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'th' ) ifTrue: [
		( nextType ideEquals: 'td' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'th' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'tr' ) ifTrue: [ ^true ].
		^false ].
	( tagName ideEquals: 'td' ) ifTrue: [
		( nextType ideEquals: 'td' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'th' ) ifTrue: [ ^true ].
		( nextType ideEquals: 'tr' ) ifTrue: [ ^true ].
		^false ].
	^false!

checkOptEndTagP: tagName
	"Return true if p can have an optional end tag 
	before tagName. Assume any block text is valid 
	to follow a p element with no end tag."

	( tagName ideEquals: 'p' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'body' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'h1' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'h2' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'h3' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'h4' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'h5' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'h6' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'ol' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'ul' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'dl' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'pre' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'div' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'noscript' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'blockquote' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'form' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'hr' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'table' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'fieldset' ) ifTrue: [ ^true ].
	( tagName ideEquals: 'address' ) ifTrue: [ ^true ].
	^false! !


! IdXWalkerHTMLDTD methodsFor: 'creation' !

initialize
	"Initialize the receiver and add the standard HTML
	internal entities."

	super initialize.
	self 
		addHTMLEntitiesDTDs;
		addHTMLEntitiesLat1;
		addHTMLEntitiesSpecial;
		addHTMLEntitiesSymbol! !


! IdSAXParseException class methodsFor: 'constructors' !

forException: message pub: aPublicId sys: aSystemId line: aLine col: aColumn
	"This approximates the SAXParseException constructor
	used in Java."

	| answer |
	answer := IdSAXParseException new.
	answer initException: message pub: aPublicId sys: aSystemId line: aLine col: aColumn.
	^answer! !


IdSAXParseException comment: '
	This class implements the SAXParseException class in Smalltalk.
	It stores additional information about a parsing error. Note that
	in this implementation all parse exceptions are raised using 
	this class, even those unrelated to SAX.
	The line and column numbers return -1 to indicate that they are 
	not supported (as defined in SAX). If anyone can find an efficient
	and accurate mechanism to calculate these send it in. For
	now no attempt is made to calculate them properly.'!


! IdSAXParseException methodsFor: 'accessing' !

getColumnNumber
	^columnNumber!

getLineNumber
	^lineNumber!

getPublicId
	^publicId!

getSystemId
	^systemId! !


! IdSAXParseException methodsFor: 'queries' !

isParseException
	^true! !


! IdSAXParseException methodsFor: 'creation' !

initException: message pub: aPublicId sys: aSystemId line: aLine col: aColumn

	self setMessage: message.
	publicId := aPublicId.
	systemId := aSystemId.
	lineNumber := aLine.
	columnNumber := aColumn! !


IdXWalkerDoctypeParser comment: '
	An extension of the parser class which handles some
	Doctype specific variations.'!


! IdXWalkerDoctypeParser methodsFor: 'internal' !

internalParseDoctype
	"Return the lastChar when done or nil if an error
	is encountered."

	self nextChar.
	self nextDoctypeCDATA.
	[ self atEnd ] whileFalse: [ 
		self nextDoctypeAction.
		isDoneDoctype ifTrue: [ ^self getLastChar ].
		self nextDoctypeCDATA ].
	^-1!

mdo
	"Process a markup declaration open. Handle all
	DOCTYPE specific declarations here."

	self getLastChar == 69 "$E" ifTrue: [
		self nextChar.
		self getLastChar == 76 "$L" ifTrue: [ self parseElementDeclaration. ^self ].
		self getLastChar == 78 "$N" ifTrue: [ self parseEntityDeclaration. ^self ].
		self fatalError: 'markup declaration must be ENTITY or ELEMENT' ].
	self getLastChar == 65 "$A" ifTrue: [ self parseAttlistDeclaration. ^self ].
	super mdo!

nextDoctypeAction

	self getLastChar == 93 "$]" ifTrue: [
		self nextChar.
		isDoneDoctype := true.
		^self ].
	self nextAction!

nextDoctypeCDATA
	"Identify the ] char which should indicate the end of
	the DOCTYPE. We may need to do some more work if
	that char can legally exist in CDATA within a DOCTYPE."

	( self atEnd or: [ self getLastChar == 93 "$]" ] ) ifTrue: [ ^self ].
	self nextCDATA!

parseAttlistDeclaration
	"Parse the current attribute declaration. Only worry 
	about default attributes because this is not a validating
	parser. Default attributes are stored in the DTD."

	| elementType |
	self skipUpToSeparator.
	elementType := self nextXMLToken.
	elementType == nil ifTrue: [
		self fatalError: 'ATTLIST declaration has no element type'. ^self ].
	[ self atEndNot and: [ self getLastChar ~~ 62 "$>" ] ]
		whileTrue: [
			self skipSeparators.
			self getLastChar == 37 "$%"
				ifTrue: [ self skipUpToSeparator ]   "need to resolve entity and parse content"
				ifFalse: [ self parseAttlistEntry: elementType ].
			self skipSeparators ]!

parseAttlistEntry: elementType
	"Not working when entities are used."

	| attName attType defaultDec defaultValue |

	attName := self nextXMLToken.
	attName == nil ifTrue: [
		self fatalError: 'ATTLIST declaration has no attribute name'. ^self ].
	self skipSeparators.

	self getLastChar == 37 "$%" ifTrue: [   "need to resolve entity and parse content"
		self skipToCharacter: 62 "$>".
		^self ].

	self getLastChar == 40 "$(" ifTrue: [
			attType := 'ENUMERATED'.
			self skipToCharacter: 41 "$)".
			self getLastChar == 41 "$)" 
				ifTrue: [ self nextChar ]
				ifFalse: [ self fatalError: 'ATTLIST declaration has invalid enumerated types'. ^self ] ]
		ifFalse: [ attType := self nextXMLToken ].
	attType == nil ifTrue: [
		self fatalError: 'ATTLIST declaration has no attribute type'. ^self ].
	defaultDec := self nextLiteralOrToken.
	defaultDec == nil ifTrue: [
		self fatalError: 'ATTLIST declaration has no default declaration'. ^self ].
	( ( defaultDec ideEquals: '#REQUIRED' ) or: [ defaultDec ideEquals: '#IMPLIED' ] ) ifFalse: [
		( defaultDec ideEquals: '#FIXED' )
			ifTrue: [ defaultValue := self nextLiteralOrToken ]
			ifFalse: [ defaultValue := defaultDec ].
		defaultValue == nil ifTrue: [
			self fatalError: 'ATTLIST declaration has no required default value'. ^self ].
		self getDtd addDefaultAttribute: attName
			value: defaultValue
			for: elementType ]!

parseElementDeclaration

	| name content |
	self skipUpToSeparator.
	name := self nextXMLToken.
	name == nil ifTrue: [
		self fatalError: 'ENTITY declaration contains no entity name'. ^self ].
	content := self captureUpTo: 62. "$>"
	self getDtd elementDeclared: name content: content.
	self nextChar!

parseEntityDeclaration
	"reading external entities not yet supported"

	| name value |
	self skipUpToSeparator.
	name := self nextXMLToken.
	name == nil ifTrue: [
		self fatalError: 'ENTITY declaration contains no entity name'. ^self ].
	value := self nextAttributeValueToken.
	value == nil ifTrue: [
		self fatalError: 'ENTITY declaration contains no entity value'. ^self ].
	( value ideEquals: 'PUBLIC' ) ifFalse: [
		self getDtd addEntityName: name value: value.
		self skipSeparators.
		^self ].
	"reading external entities not yet supported
	examples are HTMLlat1x.ent (and other 2) in XHTML dtd"!

syncCoreParserVariables: docParser

	isDoneDoctype := false.
	super syncCoreParserVariables: docParser! !


! IdXWalkerDoctypeParser methodsFor: 'services' !

parseExternalDTD: anInputSource for: docParser
	"Should give entityResolver a chance to change input."

	| newStream |
	newStream := anInputSource getCharacterStream.
	newStream ~~ nil ifTrue: [
		self syncCoreParserVariables: docParser   "sync these"
			stream: newStream.
		self internalParseDoctype. ^self ].
	newStream := self retrieveInputStream: anInputSource.
	newStream == nil ifTrue: [ ^self ].
	self syncCoreParserVariables: docParser 
		stream: newStream.
	[ self internalParseDoctype ] 
		ideOnException: IdException 
		do: [ :ex | 
			self fatalException: ex ideAsExceptionInstance ]
		finally: [ 
			newStream close ]!

parseInternalDTD: docParser stream: aStream
	"The receiver is continuing a DOCTYPE parse for docParser.
	Synchronize the receiver to use the same aStream, etc. It should 
	be positioned on top of the [ char which marks a DOCTYPE and 
	will end just after the ] char if successful. Return the current lastChar
	when done or nil if an error is encountered."

	self syncCoreParserVariables: docParser stream: aStream.
	^self internalParseDoctype! !


IdXWalkerHTMLParser comment: '
	An extension of the parser class which handles some
	HTML specific variations.'!


! IdXWalkerHTMLParser methodsFor: 'testing' !

hasPreloadedXHTMLSchemas
	"Return true because the XHTML schemas are
	prelaoded in the HTML DTD."

	^true! !


! IdXWalkerHTMLParser methodsFor: 'services' !

parseStartTag: aTagName
	"Recognize and process optional end tags."

	[ self getDtd typeHasOptEndTag: self getCurrentTag preceding: aTagName ] whileTrue: [
		self endTagCloseAction: self getCurrentTag ].
	super parseStartTag: aTagName!

processEmptyTag: aTagName event: anEvent
	"Process an HTML empty tag if aTagName has been
	specified as EMPTY and return true, else return false."

	( self getDtd elementTypeIsEmpty: aTagName )
		ifTrue: [ self peekXmlEmptyTag: aTagName enforce: true ]  "needed for XHTML which has XML style empty elements"
		ifFalse: [ ( self peekXmlEmptyTag: aTagName enforce: false ) ifFalse: [ ^false ] ].
	self processEmptyTagEvent: anEvent.
	self peekTagc ifTrue: [
		self nextChar ].
	^true!

syncCoreParserVariables: docParser

	super syncCoreParserVariables: docParser.
	self getDtd isHTMLAware ifTrue: [ ^self ].
	self setDtd: IdXWalkerHTMLDTD new! !


! IdXWalkerHTMLParser methodsFor: 'creation' !

createDtd
	"Should use shared instance."

	^IdXWalkerHTMLDTD new! !


! Array methodsFor: 'ID extensions' !

ideToStringFromZero: start length: length
	"Return a String which contains the receiver's contents
	from start (zero based index) for length.
	It is assumed the receiver is an array of characters. This will
	translate to String.valueof() in Java."

	| max buffer nextChar |
	self size == 0 ifTrue: [ ^nil ].
	max := start + length.
	buffer := IdStringBuffer new: length.
	start + 1 to: max do: [ :i |
		nextChar := self at: i.
		nextChar == nil ifTrue: [
			buffer length == 0 ifTrue: [ ^nil ].
			^buffer convertToString ].
		buffer appendChar: nextChar ].
	buffer length == 0 ifTrue: [ ^nil ].
	^buffer convertToString! !


! BlockClosure methodsFor: 'ID extensions' !

ideEnsureCompletion: completionBlock
	"Evaluate the receiver normally and ensure that 
	completionBlock executes no matter what happens."

	self ensure: completionBlock!

ideOnException: exceptionClass do: handlerBlock
	"Evaluate the receiver in the context of an exception
	represented by exceptionClass, which is a subclass
	of IdException."

	self
		on: exceptionClass getException
		do: handlerBlock!

ideOnException: exceptionClass do: handlerBlock finally: completionBlock
	"Evaluate the receiver as a normal #ideOnException:do:
	and ensure that completionBlock executes no matter
	what happens."

	[
	    self ideOnException: exceptionClass do: handlerBlock
	] ensure: completionBlock!

ideOnException: exClass1 do: handler1 when: exClass2 do: handler2
	"Evaluate the receiver in the context of multiple exceptions
	and handler blocks."

	self
		on: exClass2 getException do: handler2
		on: exClass1 getException do: handler1!

ideOnException: exClass1 do: handler1 
	when: exClass2 do: handler2 
	when: exClass3 do: handler3
		"Evaluate the receiver in the context of multiple exceptions
		and handler blocks."

	self
		on: exClass3 getException do: handler2
		on: exClass2 getException do: handler2
		on: exClass1 getException do: handler1!

ideOnException: exClass1 do: handler1 
	when: exClass2 do: handler2 
	when: exClass3 do: handler3
	when: exClass4 do: handler4
		"Evaluate the receiver in the context of multiple exceptions
		and handler blocks."

	self
		on: exClass4 getException do: handler2
		on: exClass3 getException do: handler2
		on: exClass2 getException do: handler2
		on: exClass1 getException do: handler1!


! Boolean methodsFor: 'ID extensions' !

ideAsBooleanObject
	"Return the receiver. This method is used to
	convert the primitive boolean in Java to an instance of
	the Boolean Object which can then be stored in
	Vectors, etc."

	^self!

ideAsPrimitiveBoolean
	"Return the receiver. This method is used to
	convert an Boolean instance in Java to a primitive
	boolean which can then be used in control 
	statements, etc\."

	^self! !


! Character methodsFor: 'ID extensions' !

ideCharValue
	"Return the integer char value of the receiver."

	^self asInteger!

idePrintPrimitive
	"Return a string representation of the receiver. This
	calls #asString in Smalltalk and nothing in Java (see
	implementation in Object)."

	^self asString! !


! Collection methodsFor: 'ID extensions' !

ideArrayLength
	"Return the receiver's length. Will convert to
	length in Java for char arrays."

	^self size! !


! Dictionary methodsFor: 'ID extensions' !

ideAtKey: aKey put: aValue
	"Store aValue at aKey. No return. Will convert 
	to put() in Java. Use of the normal #at:put: will
	convert to setElementAt() in Java (for collections)."

	self at: aKey put: aValue!

ideAtKeyOrNil: aKey
	"Return the value at aKey or nil. Will convert to
	get() in Java which also returns null if not found."

	^self at: aKey ifAbsent: [nil]!

ideIsDictionary
	^true! !


!Signal methodsFor: 'ID extensions' !

ideAsExceptionInstance
	"Return the IdException instance represented by the receiver."

	^self argument!

ideExitGracefully
	"The receiver is an exception being handled by
	portable ID application code. Have it exit with nil
	as an argument."

	self return! !


! Float methodsFor: 'ID extensions' !

idePrintPrimitive
	^self ideFloatToString! !


! IdentityDictionary methodsFor: 'ID extensions' !

ideAtKey: aKey put: aValue
	"Store aValue at aKey. No return. Will convert 
	to put() in Java. Use of the normal #at:put: will
	convert to setElementAt() in Java (for collections)."

	self at: aKey put: aValue!

ideAtKeyOrNil: aKey
	"Return the value at aKey or nil. Will convert to
	get() in Java which also returns null if not found."

	^self at: aKey ifAbsent: [nil]! !


! Integer methodsFor: 'ID extensions' !

ideAsIntegerObject
	"Return the receiver. This method is used to
	convert the primitive int in Java to an instance of
	the Integer Object which can then be stored in
	Vectors, etc."

	^self!

ideAsPrimitiveInt
	"Return the receiver. This method is used to
	convert an Integer instance in Java to a primitive
	int which can then be used for mathmatical
	operations, etc\."

	^self!

ideCharValue
	"Return the receiver."

	^self! !


! Number methodsFor: 'ID extensions' !

ideAsDoubleObject
	"Return the receiver. This method is used to
	convert the primitive double in Java to an instance of
	the Double Object which can then be stored in
	Vectors, etc."

	^self!

ideAsPrimitiveDouble
	"Return the receiver. This method is used to
	convert an Double instance in Java to a primitive
	double which can then be used for mathmatical
	operations, etc\."

	^self!

ideDivideToFloat: aNumber
	"Return the result of dividing the receiver by aNumber
	which is an accurate float value.
	This casts the receiver to double first in Java. If this is
	not used and the receiver of / is an integer, the result
	will be rounded to an integer as well."

	^self / aNumber!

ideNumToJavaChar
	"Return a Java char representation of the receiver.
	This maps to (char)aNumber in Java. In Smalltalk
	return the receiver rounded to an integer."

	^self asInteger!

ideNumToString
	"Return a String representation of the receiver.
	This maps to String.valueOf() in Java."

	^self printString!

ideTruncateToInteger
	"Return the integer nearest to the receiver 
	truncated toward zero.
	This is the same as casting to int in Java."

	^self truncated! !


! Object methodsFor: 'ID extensions' !

ideAsExceptionInstance
	"Return the exception represented by the receiver. 
	By default return the receiver itself."

	^self!

ideAsStreamReader
	"Converts an InputStream to an InputStreamReader.
	Return the receiver in Smalltalk."

	^self!

ideEqualsFloat: aNumber
	"This uses = in Smalltalk and maps to
	Float.equals() in Java."

	^self = aNumber!

ideFloatToString
	"Return the receiver (a Float) as a string. Default
	simply does a printString, however on some platforms
	we need to strip out scaling information."

	^self idePrintPrimitive!

ideGetClassName
	"Return the name of the receiver's class."

	^self class name!

ideIsDictionary
	"Return default false for all objects. Will convert to 
	'instanceof java.util.Hashtable' in Java."

	^false!

ideIsOrderedCollection
	"Return default false for all objects. Will convert to 
	'instanceof java.util.Vector' in Java."

	^false!

ideIsXWalkerParser
	^false!

idePrintPrimitive
	"Return a string representation of the receiver which
	is assumed to be a primitive data type in Java. This
	calls #printString in Smalltalk and nothing in Java.
	In Java primitives can not reply to toString() but they
	are automatically converted to strings during common
	string operations like concatenation (+). Note that this
	method also works well for strings as it is safe for
	values of nil."

	^self printString!

idePrintString
	"Return a string representation of the receiver. This
	calls #printString in Smalltalk and toString() in Java.
	Use for reference objects - for primitive data types
	and strings use #idePrintPrimitive."

	^self printString!

ideYourself
	"Return the receiver. This is used to generate an 
	explict 'return this' in java. Note that a ^self in Smalltalk
	generates a 'return' in java which is only a valid exit
	for a void return type."

	^self! !


! OrderedCollection methodsFor: 'ID extensions' !

ideAtIndex: anIndex
	"Return the object stored at anIndex. Will convert to
	Vector.elementAt() in Java which throws an exception
	when the index is wrong."

	^self at: anIndex!

ideAtIndex: anIndex put: anObject
	"Store anObject at anIndex. Will convert to
	Vector.setElementAt() in Java."

	self at: anIndex put: anObject!

ideCopyIntoArray: anArray
	"Copy the receiver's elements into anArray. Will convert to
	copyInto() in Java."

	anArray replaceFrom: 1
		to: self size
		with: self
		startingAt: 1!

ideEnumeration
	"Return the receiver. Will convert to
	elements() in Java."

	^self!

ideInsertElement: anObject offset: anOffset
	"Insert anObject at anOffset (0 - self size). Will convert to
	Vector.insertElementAt() in Java."

	anOffset == 0 ifTrue: [
		^self addFirst: anObject ].  "bug in VA"
	self replaceFrom: anOffset + 1
		to: anOffset
		with: ( Array with: anObject )
		startingAt: 1!

ideIsOrderedCollection
	"Return true for all instances. Will convert to 
	'instanceof java.util.Vector' in Java."

	^true!

ideOffsetOf: anObject
	"Return the offset (zero based index) of anObject in the 
	receiver. Return -1 when anObject is not found. Will 
	convert to Vector.indexOf() in Java."

	^( self indexOf: anObject ifAbsent: [0] ) - 1!

ideRemoveFirst
	"Remove the first object. Will convert to
	Vector.removeElementAt(0) in Java."

	self removeFirst! !


! ReadStream methodsFor: 'ID extensions' !

ideReadNext
	"Return the next character read, or -1 if at the end
	of the stream. This will map to StringReader read() 
	in Java."

	self atEnd ifTrue: [ ^-1 ].
	^self next!

ideReadNextLine
	"Return a string containing everything on the current line, 
	or nil if at the end. This maps to BufferedReader readLine() 
	in Java."

	self atEnd ifTrue: [ ^nil ].
	^self nextLine! !


! String methodsFor: 'ID extensions' !

ideAsFloat
	^self asNumber!

ideCharAt: anInteger
	"Return the character stored at anInteger. Will convert to
	charAt() in Java."

	^self at: anInteger!

ideCharValueAt: anIndex
	"Answer the integer char value at index position anIndex.
	Will transform to normal array access in Java which stores
	characters as integers in a String."

	^( self at: anIndex ) ideCharValue!

ideCompareTo: aString
"	 * @return  the value <code>0</code> if the argument string is equal to
	 *          this string; a value less than <code>0</code> if this string
	 *          is lexicographically less than the string argument; and a
	 *          value greater than <code>0</code> if this string is
	 *          lexicographically greater than the string argument.
	This translates to compareTo() in Java.
"

	self < aString ifTrue: [ ^-1 ].
	( self ideEquals: aString ) ifTrue: [ ^0 ].
	^1!

ideContentsAsCharArray
	"In Smalltalk simply return the receiver. In Java the
	string is converted to a char array."

	^self!

ideEquals: aString
	"Return true if the receiver equals aString,
	else return false. This translates to equals() in 
	Java. Identity testing two symbols is 50% faster 
	on IBM Smalltalk than this method, however
	sending asSymbol once is 850 times slower than
	sending = once.

	Use == if you know both are Symbols, or this method 
	if you know both are Strings. This method is usually 
	faster overall and provides better unicode string 
	matching support in Java."

	^self = aString asString!

ideLastOffsetOfChar: aCharacter
	"Returns the offset (zero based) within this string of the last occurrence 
	of the specified character. Return -1 if the character does not exist.
	Will convert to lastIndexOf() in Java which takes an int argument."

	| answer next |
	answer := -1.
	next := 0.
	[ next ~~ -1 ] whileTrue: [
		next := self indexOf: aCharacter startingAt: next + 1.
		next == 0 
			ifTrue: [ next := -1 ]
			ifFalse: [ answer := next - 1 ] ].
	^answer!

ideLength
	"Return the receiver's length. Will convert to
	length() in Java."

	^self size!

idePrintPrimitive
	"Return a string representation of the receiver. This
	calls #asString in Smalltalk (to strip out the single quote
	characters) and nothing in Java (see implementation 
	in Object)."

	^self asString!

ideStartsWith: aString
	"Return true if the recevier starts with aString.
	Maps to startsWith() in Java."

	self size < aString size ifTrue: [ ^false ].
	self size = aString size ifTrue: [ ^self = aString ].
	^( self copyFrom: 1 to: aString size ) = aString! !


! Symbol methodsFor: 'ID extensions' !

ideEquals: aString
	"Return true if the receiver equals aString,
	else return false. This translates to equals() in 
	Java. This version is very inefficient because it
	must create a new string to test. Use == if you
	are sure both are Symbols, or this method if
	you are sure both are Strings. See comment of 
	String version of this method for more."

	^self asString ideEquals: aString!

idePrintPrimitive
	"Return a string representation of the receiver. This
	calls #asString in Smalltalk (to strip out the # character)
	and nothing in Java (see implementation in Object)."

	^self asString! !


! WriteStream methodsFor: 'ID extensions' !

ideContentsAsCharArray
	"In Smalltalk simply return the contents. In Java the
	result is returned as a char array."

	^self contents! !


! Float class methodsFor: 'ID extensions' !

NaN
	"Can not return true value of 0.0d / 0.0. Instead return
	an unlikely and wierd number.
	Could use exceptions somehow to catch NaN."

	^-6836296635248.778346244966707!

NEGATIVE_INFINITY
	"Can not return true value of -1.0 / 0.0. Instead return
	an unlikely very low number.
	Could use min Double value 4.94065645841246544e-324
	in some fashion."

	^-9999999999999999998.9999999999997!

POSITIVE_INFINITY
	"Can not return true value of 1.0 / 0.0. Instead return
	an unlikely very high number.
	Could use max Double value 1.79769313486231570e+308
	in some fashion."

	^999999999999999998.99999999999997! !


! ReadStream class methodsFor: 'ID extensions' !

ideReadStreamOn: aString
	"In Java this maps to a BufferedReader wrapped
	around a StringReader. BufferedReader know how to
	return the next line."

	^self on: aString!

ideStringReaderOn: aString
	"In Java this maps to a simple StringReader.
	Note that string readers do not understand the
	concept of next line. Buffered readers do."

	^self on: aString! !


! WriteStream class methodsFor: 'ID extensions' !

ideWriteStreamOfSize: anInteger
	"In Java this maps to a WriteStream opening
	with an initial capacity of anInteger. In Smalltalk
	use a single byte write stream."

	^IdWriteStreamSB on: ( String new: anInteger )! !

IdObject initializeAfterLoad!
IdException initializeAfterLoad!
IdKernelUtilities initializeAfterLoad!
IdStreamParser initializeAfterLoad!