<?xml version="1.0"?>

<st-source>
<!-- 
Name: XPath
Notice: Copyright © 2008 Cincom Systems, Inc.  All Rights Reserved.
Comment: XPath is an enabler technology for XSLT, but is also useful for searching an XML document.


DbIdentifier: bear73
DbTrace: 47276
DevelopmentPrerequisites: #(#(#any 'AT MetaNumerics' '') #(#any 'AT Parser Compiler' '') #(#any 'XML' ''))
PackageName: XPath
Parcel: #('XPath')
ParcelName: XPath
PrerequisiteParcels: #(#('AT MetaNumerics' '') #('AT Parser Compiler' '') #('XML' ''))
PrintStringCache: (7.2.1 - 1.0,bobw)
Version: 7.6
Date: 12:28:18 pm February 1, 2008
 -->
<time-stamp>From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:28:18 pm</time-stamp>


<do-it>(Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned.  Choose terminate or close.']</do-it>

<class>
<name>XPathExpression</name>
<environment>XML</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>predicates child </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathFunction</name>
<environment>XML</environment>
<super>XML.XPathExpression</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>name arguments valueBlock requiresSort requiresNodeSet </inst-vars>
<class-inst-vars>functions </class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathParser</name>
<environment>XML</environment>
<super>Kernel.GeneralParser</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>xTypes xmlNode functions </inst-vars>
<class-inst-vars>nodeTypes </class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathStep</name>
<environment>XML</environment>
<super>XML.XPathExpression</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>axisName baseTest namespace type parent </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathParentNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathNodeTest</name>
<environment>XML</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathTypedNodeTest</name>
<environment>XML</environment>
<super>XML.XPathNodeTest</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>typeName value </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathBinaryExpression</name>
<environment>XML</environment>
<super>XML.XPathExpression</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>operator argument1 argument2 valueBlock </inst-vars>
<class-inst-vars>operators </class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathFollowingSiblingNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XmlScannerTable</name>
<environment>XML</environment>
<super>Kernel.GeneralParser</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>value0 defaultValue letterValue digitValue separatorValue </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathAncestorNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathPrecedingSiblingNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathChildNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathDescendantNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathVariable</name>
<environment>XML</environment>
<super>XML.XPathExpression</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>name </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathTerminator</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathTaggedNodeTest</name>
<environment>XML</environment>
<super>XML.XPathNodeTest</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>namespace qualifier type </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathPrecedingNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathUnion</name>
<environment>XML</environment>
<super>XML.XPathExpression</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>arguments </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathSortingVector</name>
<environment>XML</environment>
<super>Core.Array</super>
<private>false</private>
<indexed-type>objects</indexed-type>
<inst-vars>value </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathNodeContext</name>
<environment>XML</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>documentOrder nodes index node baseNode variables </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathCurrentNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathRoot</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathFollowingNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathReadStream</name>
<environment>XML</environment>
<super>Core.ReadStream</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<class>
<name>XPathAttributeNode</name>
<environment>XML</environment>
<super>XML.XPathStep</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XPath</category>
<attributes>
<package>XPath</package>
</attributes>
</class>

<methods>
<class-id>XML.XPathExpression</class-id> <category>accessing</category>

<body package="XPath" selector="addPredicate:">addPredicate: aPredicate

	predicates := predicates copyWith: aPredicate</body>

<body package="XPath" selector="asUnion">asUnion

	^XPathUnion new add: self</body>

<body package="XPath" selector="child">child

	^child</body>

<body package="XPath" selector="child:">child: aStep

	child := aStep</body>

<body package="XPath" selector="enumerate:">enumerate: aBlock

	aBlock value: self.
	predicates do: [:p | self for: p enumerate: aBlock].
	self for: child enumerate: aBlock.</body>

<body package="XPath" selector="predicates">predicates

	^predicates</body>

<body package="XPath" selector="xpathUsedVarNames">xpathUsedVarNames

	| list |
	list := OrderedCollection new.
	self enumerate: [:exp |
		(exp isKindOf: XPathVariable)
			ifTrue: [list add: exp name]].
	^list</body>
</methods>

<methods>
<class-id>XML.XPathExpression</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	self subclassResponsibility</body>

<body package="XPath" selector="isMatchFor:">isMatchFor: anXmlNode

	^self isLocalMatchFor: anXmlNode</body>

<body package="XPath" selector="match:">match: aNodeContext

	| base |
	base := self.
	[base child == nil]
		whileFalse: [base := base child].
	^base simpleMatchFor: aNodeContext node isComplex: false do:
		[:root :complex || ns found |
		complex not or:
			[ns := aNodeContext copy add: root; index: 1.
			found := false.
			self valueIn: ns do: [:nd | nd == aNodeContext node ifTrue: [found := true]].
			found]]</body>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	^self subclassResponsibility</body>

<body package="XPath" selector="valueIn:do:">valueIn: aNodeContext do: aBlock

	| result |
	result := self baseValueIn: aNodeContext.
	result xpathIsNodeSet
		ifTrue:
			[1 to: predicates size do: [:i |
				result := result select: (predicates at: i)].
			result reset.
			[result atEnd]
				whileFalse: [child valueIn: result next do: aBlock]]
		ifFalse: [(predicates isEmpty and: [child isTerminator])
			ifTrue: [aBlock value: result]
			ifFalse: [self error: 'The expression &lt;1s&gt; does not represent a node set']].</body>

<body package="XPath" selector="xpathEvalIn:">xpathEvalIn: aNodeContext
	"This is private protocol--see #xpathValueIn: for the client protocol"

	| nc |
	nc := Association new.
	self valueIn: aNodeContext
		do: [:x | x addToXPathHolder: nc for: aNodeContext].
	^nc value == nil
		ifTrue: [aNodeContext copy]
		ifFalse: [nc value]</body>

<body package="XPath" selector="xpathValueFor:variables:">xpathValueFor: anXmlNode variables: vars

	^self xpathValueIn: (XPathNodeContext new add: anXmlNode; index: 1; variables: vars)</body>

<body package="XPath" selector="xpathValueIn:">xpathValueIn: aNodeContext
	"This is public protocol only--see #xpathEvalIn: for internal clients"

	aNodeContext baseNode: aNodeContext node.
	^[self xpathEvalIn: aNodeContext]
		on: Number errorSignal
		do: [:x |
			(Number divisionByZeroSignal handles: x)
				ifTrue: [x resume: (Infinity negative: x dividend negative)].
			(Number divisionByZeroSignal handles: x)
				ifTrue: [x resume: NotANumber positive].
			(Number overflowSignal handles: x)
				ifTrue: [x resume: (Number computeOverflow: x parameter)].
			(Number underflowSignal handles: x)
				ifTrue: [x resume: x parameter receiver class zero].
			x pass]</body>
</methods>

<methods>
<class-id>XML.XPathExpression</class-id> <category>printing</category>

<body package="XPath" selector="completeChildPrintOn:">completeChildPrintOn: aStream

	self completePrintOn: aStream.</body>

<body package="XPath" selector="completePrintOn:">completePrintOn: aStream

	self printTestOn: aStream.
	predicates do: [:p | self printPredicate: p on: aStream].
	self child isTerminator
		ifFalse: [aStream nextPut: $/].
	self child completeChildPrintOn: aStream.</body>

<body package="XPath" selector="printOn:">printOn: aStream

	self completePrintOn: aStream</body>

<body package="XPath" selector="printPredicate:on:">printPredicate: p on: aStream

	aStream nextPutAll: '['.
	aStream print: p.
	aStream nextPutAll: ']'</body>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	self subclassResponsibility</body>
</methods>

<methods>
<class-id>XML.XPathExpression</class-id> <category>initialize</category>

<body package="XPath" selector="initialize">initialize

	predicates := #().
	child := XPathTerminator new.
	child parent: self</body>
</methods>

<methods>
<class-id>XML.XPathExpression</class-id> <category>testing</category>

<body package="XPath" selector="xpathMayRequireNodeSet">xpathMayRequireNodeSet

	^self subclassResponsibility</body>

<body package="XPath" selector="xpathMayRequireNodeSetTopLevel">xpathMayRequireNodeSetTopLevel

	^self subclassResponsibility</body>

<body package="XPath" selector="xpathMayRequireSort">xpathMayRequireSort

	^self subclassResponsibility</body>

<body package="XPath" selector="xpathMayRequireSortTopLevel">xpathMayRequireSortTopLevel

	^self subclassResponsibility</body>
</methods>

<methods>
<class-id>XML.XPathExpression</class-id> <category>private</category>

<body package="XPath" selector="for:enumerate:">for: anExpression enumerate: aBlock

	(anExpression isKindOf: XPathExpression)
		ifTrue: [anExpression enumerate: aBlock]
		ifFalse: [aBlock value: anExpression]</body>
</methods>

<methods>
<class-id>XML.XPathExpression class</class-id> <category>instance creation</category>

<body package="XPath" selector="new">new

	^super new initialize</body>
</methods>

<methods>
<class-id>XML.XPathExpression class</class-id> <category>coercing</category>

<body package="XPath" selector="notANumber">notANumber

	^NotANumber new</body>

<body package="XPath" selector="stringToNumber:">stringToNumber: aString

	| s foundDigit numerator denominator ch |
	s := aString readStream.
	s skipSeparators.
	foundDigit := false.
	numerator := 0.
	denominator := 1.
	[(ch := s next) notNil and: [ch isDigit]]
		whileTrue:
			[numerator := numerator * 10 + ch digitValue.
			foundDigit := true].
	ch = $.
		ifTrue: [[(ch := s next) notNil and: [ch isDigit]]
			whileTrue:
				[numerator := numerator * 10 + ch digitValue.
				denominator := denominator * 10.
				foundDigit := true]].
	(ch == nil or: [ch isSeparator]) ifFalse: [^self notANumber].
	s skipSeparators.
	s atEnd ifFalse: [^self notANumber].
	foundDigit ifFalse: [^self notANumber].
	^(numerator / denominator) + 0.0d</body>
</methods>

<methods>
<class-id>XML.XPathFunction</class-id> <category>accessing</category>

<body package="XPath" selector="addArgument:">addArgument: arg

	arguments := arguments copyWith: arg</body>

<body package="XPath" selector="arguments">arguments

	^arguments</body>

<body package="XPath" selector="enumerate:">enumerate: aBlock

	super enumerate: aBlock.
	self arguments do: [:i | self for: i enumerate: aBlock].</body>

<body package="XPath" selector="name">name

	^name</body>

<body package="XPath" selector="name:">name: nm

	name := nm</body>

<body package="XPath" selector="requiresNodeSet">requiresNodeSet

	^requiresNodeSet</body>

<body package="XPath" selector="requiresNodeSet:">requiresNodeSet: bool

	requiresNodeSet := bool</body>

<body package="XPath" selector="requiresSort">requiresSort

	^requiresSort</body>

<body package="XPath" selector="requiresSort:">requiresSort: bool

	requiresSort := bool</body>
</methods>

<methods>
<class-id>XML.XPathFunction</class-id> <category>testing</category>

<body package="XPath" selector="answersNumber">answersNumber

	^#('sum' 'round' 'count' 'last' 'position' 'string-length' 'floor' 'ceiling') includes: self name</body>

<body package="XPath" selector="xpathMayRequireNodeSet">xpathMayRequireNodeSet

	^requiresNodeSet
		or: [arguments inject: false into: [:b :arg | b or: [arg xpathMayRequireNodeSet]]]</body>

<body package="XPath" selector="xpathMayRequireNodeSetTopLevel">xpathMayRequireNodeSetTopLevel

	^self answersNumber or: [self xpathMayRequireNodeSet]</body>

<body package="XPath" selector="xpathMayRequireSort">xpathMayRequireSort

	^requiresSort
		or: [arguments inject: false into: [:b :arg | b or: [arg xpathMayRequireSort]]]</body>

<body package="XPath" selector="xpathMayRequireSortTopLevel">xpathMayRequireSortTopLevel

	^self answersNumber or: [self xpathMayRequireSort]</body>
</methods>

<methods>
<class-id>XML.XPathFunction</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	^valueBlock value: self value: aNodeContext</body>
</methods>

<methods>
<class-id>XML.XPathFunction</class-id> <category>initialize</category>

<body package="XPath" selector="initialize">initialize

	super initialize.
	arguments := #().
	requiresSort := false.
	requiresNodeSet := false</body>

<body package="XPath" selector="valueBlock:">valueBlock: aBlock

	valueBlock := aBlock</body>
</methods>

<methods>
<class-id>XML.XPathFunction</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	aStream nextPutAll: name, '('.
	arguments do: [:a | aStream print: a] separatedBy: [aStream nextPutAll: ','].
	aStream nextPutAll: ')'.</body>
</methods>

<methods>
<class-id>XML.XPathFunction class</class-id> <category>class initialization</category>

<body package="XPath" selector="baseFunctions">baseFunctions

	^functions</body>

<body package="XPath" selector="initialize">initialize
	"XPathFunction initialize"

	functions == nil
		ifTrue: [functions := Dictionary new]
		ifFalse: [functions keys do: [:k | functions removeKey: k]].
	self initializeBoolean.
	self initializeStrings.
	self initializeNodeSets.
	self initializeNumeric.</body>

<body package="XPath" selector="initializeBoolean">initializeBoolean

	functions at: 'boolean' put: (self new
			name: 'boolean';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'boolean() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathAsBoolean]).
	functions at: 'not' put: (self new
			name: 'not';
			valueBlock: [:fn :ns |
				(fn arguments first xpathEvalIn: ns) xpathAsBoolean not]).
	functions at: 'true' put: (true).
	functions at: 'false' put: (false).</body>

<body package="XPath" selector="initializeNodeSets">initializeNodeSets

	functions at: 'count' put: (self new
			requiresNodeSet: true;
			name: 'count';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'count() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathIsNodeSet ifFalse: [self error: 'count() requires a nodeset as an argument'].
					ns2 size]).
	functions at: 'position' put: (self new
			requiresNodeSet: true;
			requiresSort: true;
			name: 'position';
			valueBlock: [:fn :ns |
					fn arguments size &gt; 0 ifTrue: [self error: 'position() cannot take any arguments'].
					ns index]).
	functions at: 'last' put: (self new
			requiresNodeSet: true;
			name: 'last';
			valueBlock: [:fn :ns |
					fn arguments size &gt; 0 ifTrue: [self error: 'last() cannot take any arguments'].
					ns size]).
	functions at: 'local-name' put: (self new
			name: 'local-name';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'local-name() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathIsNodeSet ifFalse: [self error: 'local-name() requires a nodeset as an argument'].
					ns2 documentOrder; index: 1.
					(ns2 node isElement or: [ns2 node isAttribute])
						ifTrue: [ns2 node tag type]
						ifFalse: ['']]).
	functions at: 'namespace-uri' put: (self new
			name: 'namespace-uri';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'namespace-uri() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathIsNodeSet ifFalse: [self error: 'namespace-uri() requires a nodeset as an argument'].
					ns2 documentOrder; index: 1.
					(ns2 node isElement or: [ns2 node isAttribute])
						ifTrue: [ns2 node tag namespace]
						ifFalse: ['']]).
	functions at: 'name' put: (self new
			name: 'name';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'name() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathIsNodeSet ifFalse: [self error: 'name() requires a nodeset as an argument'].
					ns2 documentOrder; index: 1.
					(ns2 node isElement or: [ns2 node isAttribute])
						ifTrue: [ns2 node tag asString]
						ifFalse: ['']]).
	functions at: 'id' put: (self new
			name: 'id';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size ~= 1 ifTrue: [self error: 'id() only takes one argument'].
					ns2 := fn arguments first xpathEvalIn: ns.
					ns2 := ns2 xpathAsString.
					ns copy add: (ns node document atID: ns2 ifAbsent: [self error: 'ID "', ns2, '" not found'])]).
	functions at: 'current' put: (self new
			requiresNodeSet: true;
			name: 'current';
			valueBlock: [:fn :ns |
					fn arguments size &gt; 0 ifTrue: [self error: 'current() takes no arguments'].
					ns copy add: ns baseNode; index: 1]).</body>

<body package="XPath" selector="initializeNumeric">initializeNumeric

	functions at: 'number' put: (self new
			name: 'number';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'number() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathAsNumber]).
	functions at: 'round' put: (self new
			name: 'round';
			valueBlock: [:fn :ns |
					fn arguments size ~= 1 ifTrue: [self error: 'round() only takes one argument'].
					(fn arguments first xpathEvalIn: ns) xpathAsNumber rounded]).
	functions at: 'floor' put: (self new
			name: 'floor';
			valueBlock: [:fn :ns |
					fn arguments size ~= 1 ifTrue: [self error: 'floor() only takes one argument'].
					(fn arguments first xpathEvalIn: ns) xpathAsNumber floor]).
	functions at: 'ceiling' put: (self new
			name: 'ceiling';
			valueBlock: [:fn :ns |
					fn arguments size ~= 1 ifTrue: [self error: 'ceiling() only takes one argument'].
					(fn arguments first xpathEvalIn: ns) xpathAsNumber ceiling]).
	functions at: 'sum' put: (self new
			name: 'sum';
			valueBlock: [:fn :ns |
					fn arguments size ~= 1 ifTrue: [self error: 'sum() only takes one argument'].
					(fn arguments first xpathEvalIn: ns) sum]).</body>

<body package="XPath" selector="initializeStrings">initializeStrings

	functions at: 'string' put: (self new
			name: 'string';
			valueBlock: [:fn :ns || ns2 |
					fn arguments size &gt; 1 ifTrue: [self error: 'string() only takes one argument'].
					ns2 := fn arguments size = 1
						ifTrue: [fn arguments first xpathEvalIn: ns]
						ifFalse: [ns asSingleNode].
					ns2 xpathAsString]).
	functions at: 'concat' put: (self new
			name: 'concat';
			valueBlock: [:fn :ns || s |
				s := ''.
				fn arguments do: [:exp | s := s, (exp xpathEvalIn: ns) xpathAsString].
				s]).
	functions at: 'contains' put: (self new
			name: 'contains';
			valueBlock: [:fn :ns || s1 s2 i |
				fn arguments size = 2 ifFalse: [self error: 'contains() takes two arguments'].
				s1 := (fn arguments at: 1) xpathEvalIn: ns.
				s2 := (fn arguments at: 2) xpathEvalIn: ns.
				i := s1 xpathAsString findString: s2 xpathAsString startingAt: 1.
				i &gt; 0]).
	functions at: 'translate' put: (self new
			name: 'translate';
			valueBlock: [:fn :ns || s1 s2 s3 |
				fn arguments size = 3 ifFalse: [self error: 'translate() takes three arguments'].
				s1 := (fn arguments at: 1) xpathEvalIn: ns.
				s2 := (fn arguments at: 2) xpathEvalIn: ns.
				s3 := (fn arguments at: 3) xpathEvalIn: ns.
				(self
						translate: s1 xpathAsString
						from: s2 xpathAsString
						to: s3 xpathAsString)]).
	functions at: 'string-length' put: (self new
			name: 'string-length';
			valueBlock: [:fn :ns || s1 |
				fn arguments size &lt; 2 ifFalse: [self error: 'string-length() takes no more than 1 argument'].
				s1 := (fn arguments size = 0
					ifTrue: [ns asSingleNode]
					ifFalse: [fn arguments first xpathEvalIn: ns]) xpathAsString.
				s1 size]).
	functions at: 'substring' put: (self new
			name: 'substring';
			valueBlock: [:fn :ns || s1 i1 i2 |
				(fn arguments size between: 2 and: 3) ifFalse: [self error: 'substring() takes two or three arguments'].
				s1 := ((fn arguments at: 1) xpathEvalIn: ns) xpathAsString.
				i1 := ((fn arguments at: 2) xpathEvalIn: ns) xpathAsNumber.
				i2 := fn arguments size = 2
					ifTrue: [1.0d10]
					ifFalse: [((fn arguments at: 3) xpathEvalIn: ns) xpathAsNumber].
				i2 := (i1 + i2 - 1) rounded min: s1 size.
				i1 := i1 rounded max: 1.
				(s1 copyFrom: i1 to: i2)]).
	functions at: 'starts-with' put: (self new
			name: 'starts-with';
			valueBlock: [:fn :ns || s1 s2 |
				fn arguments size = 2 ifFalse: [self error: 'starts-with() takes two arguments'].
				s1 := ((fn arguments at: 1) xpathEvalIn: ns) xpathAsString.
				s2 := ((fn arguments at: 2) xpathEvalIn: ns) xpathAsString.
				(s1 findString: s2 startingAt: 1) = 1]).
	functions at: 'substring-before' put: (self new
			name: 'substring-before';
			valueBlock: [:fn :ns || s1 s2 i |
				fn arguments size = 2 ifFalse: [self error: 'substring-before() takes two arguments'].
				s1 := ((fn arguments at: 1) xpathEvalIn: ns) xpathAsString.
				s2 := ((fn arguments at: 2) xpathEvalIn: ns) xpathAsString.
				i := s1 findString: s2 startingAt: 1.
				(i = 0 ifTrue: [''] ifFalse: [s1 copyFrom: 1 to: i-1])]).
	functions at: 'substring-after' put: (self new
			name: 'substring-after';
			valueBlock: [:fn :ns || s1 s2 i |
				fn arguments size = 2 ifFalse: [self error: 'substring-after() takes two arguments'].
				s1 := ((fn arguments at: 1) xpathEvalIn: ns) xpathAsString.
				s2 := ((fn arguments at: 2) xpathEvalIn: ns) xpathAsString.
				i := s1 findString: s2 startingAt: 1.
				(i = 0 ifTrue: [''] ifFalse: [s1 copyFrom: i+s2 size to: s1 size])]).
	functions at: 'normalize-space' put: (self new
			name: 'normalize-space';
			valueBlock: [:fn :ns || ns2 |
					ns2 := fn arguments first xpathEvalIn: ns.
					ns2 := ns2 xpathAsString.
					self normalizeWhitespace: ns2]).</body>
</methods>

<methods>
<class-id>XML.XPathFunction class</class-id> <category>function implementations</category>

<body package="XPath" selector="normalizeWhitespace:">normalizeWhitespace: aString

	| ch str buffer space |
	str := aString readStream.
	buffer := String new writeStream.
	space := false.
	[str skipSeparators; atEnd]
		whileFalse:
			[space ifTrue: [buffer space].
			[(ch := str next) notNil and: [ch isSeparator not]]
				whileTrue: [buffer nextPut: ch].
			space := true].
	^buffer contents</body>

<body package="XPath" selector="translate:from:to:">translate: base from: src to: dest

	| dir result c |
	dir := IdentityDictionary new.
	src size to: 1 by: -1 do: [:i |
		dir at: (src at: i) put: (i &gt; dest size ifTrue: [nil] ifFalse: [dest at: i])].
	result := (String new: base size) writeStream.
	1 to: base size do: [:i |
		c := base at: i.
		c := dir at: c ifAbsent: [c].
		c == nil ifFalse: [result nextPut: c]].
	^result contents</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>construction</category>

<body package="XPath" selector="abbreviatedDescendant">abbreviatedDescendant

	token = #'/'
		ifTrue: 
			[stack add: (XPathDescendantNode new
						axisName: 'descendant-or-self';
						baseTest: (XPathTypedNodeTest new type: 'node')).
			^true].
	^false</body>

<body package="XPath" selector="arg:op:arg:">arg: a1 op: operator arg: a2

	operator isString ifFalse: [self error: 'Parser construction error, should not happen'].
	^XPathBinaryExpression
		operator: operator with: a1 with: a2</body>

<body package="XPath" selector="axis:test:">axis: axisName test: test

	| stepClass step |
	stepClass := self class nodeTypes
				at: axisName
				ifAbsent: [self error: ('&lt;1s&gt; is not an axis' expandMacrosWith: axisName)].
	step := stepClass new.
	step axisName: axisName.		"Some classes represent multiple axes, and must be told which"
	step baseTest: test.
	^step</body>

<body package="XPath" selector="connectParent:child:">connectParent: parent child: child

	| p |
	p := parent.
	[p child isTerminator] whileFalse: [p := p child].
	p child: child.
	child parent: p.
	^parent</body>

<body package="XPath" selector="function:">function: aName

	^self functionNamed: aName</body>

<body package="XPath" selector="function:arg:">function: aFunction arg: anArgument

	^aFunction addArgument: anArgument</body>

<body package="XPath" selector="isNodeType">isNodeType

	| typeName argument ok |
	typeName := stack at: stack size-1.
	argument := stack at: stack size.
	ok := typeName = 'processing-instruction'
		ifTrue: [true]
		ifFalse: [(#('node' 'text' 'comment') includes: typeName)
			ifTrue: [argument == nil]
			ifFalse: [false]].
"	ok ifFalse: [stack removeLast]."
	^ok</body>

<body package="XPath" selector="nodeTestQualifier:type:">nodeTestQualifier: qualifier type: typeName

	| ns |
	ns := self namespaceAt: qualifier.
	^XPathTaggedNodeTest new
		namespace: ns;
		type: typeName</body>

<body package="XPath" selector="nodeTestType:">nodeTestType: typeName

	^XPathTaggedNodeTest new
		type: typeName</body>

<body package="XPath" selector="nodeTypeTest:arg:">nodeTypeTest: typeName arg: argument

	^XPathTypedNodeTest new
		type: typeName;
		value: argument</body>

<body package="XPath" selector="number:">number: aValue

	^aValue</body>

<body package="XPath" selector="selfOrParent">selfOrParent

	token = #'.'
		ifTrue: 
			[self scanToken.
			stack add: (XPathCurrentNode new
						axisName: 'self';
						baseTest: (XPathTypedNodeTest new type: 'node')).
			^true].
	token = #'..'
		ifTrue: 
			[self scanToken.
			stack add: (XPathParentNode new
						axisName: 'parent';
						baseTest: (XPathTypedNodeTest new type: 'node')).
			^true].
	^false</body>

<body package="XPath" selector="step:predicate:">step: step predicate: predicate

	^step addPredicate: predicate; yourself</body>

<body package="XPath" selector="string:">string: aValue

	^aValue</body>

<body package="XPath" selector="union:with:">union: path1 with: path2

	^path1 asUnion add: path2</body>

<body package="XPath" selector="variable:">variable: aName

	^XPathVariable new name: aName</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>paths</category>

<body package="XPath" selector="absoluteLocationPath">absoluteLocationPath =

	#/ [XPathRoot new] ( @ relativeLocationPath [connectParent:child:] | )</body>

<body package="XPath" selector="anyStep">anyStep =

	selfOrParent
	| abbreviatedDescendant
	| stepWithPredicates</body>

<body package="XPath" selector="locationPath">locationPath =

	absoluteLocationPath
	| relativeLocationPath</body>

<body package="XPath" selector="nodeTest">nodeTest =

	word ( @ $( ( literal | [nil] ) $) [self isNodeType]? [nodeTypeTest:arg:]
			| [peekFor: #':']? word [nodeTestQualifier:type:]
			| [peekFor: #':']? #* [nodeTestQualifier:]
			| [self verifyNotFunction]? [nodeTestType:] )
	| #* [#*] [nodeTestType:]</body>

<body package="XPath" selector="predicate">predicate =

	$[ expression $]</body>

<body package="XPath" selector="relativeLocationPath">relativeLocationPath =

	anyStep ( #/ anyStep [connectParent:child:] )* .</body>

<body package="XPath" selector="stepWithPredicates">stepWithPredicates =

	( @ word [peekFor: #'::']?
	| #@ ['attribute']
	| ['child'] )
		nodeTest [axis:test:]
		( predicate [step:predicate:] )* .</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>expressions</category>

<body package="XPath" selector="additiveExpr">additiveExpr =

	multiplicativeExpr
		(( #+ [#+]
		| #- [#-] ) multiplicativeExpr [arg:op:arg:] )* .</body>

<body package="XPath" selector="andExpr">andExpr =

	equalityExpr ( word=#and [#and] equalityExpr [arg:op:arg:] )* .</body>

<body package="XPath" selector="equalityExpr">equalityExpr =

	relationalExpr
		(( #= [#=]
		| #!= [#!=] ) relationalExpr [arg:op:arg:] )* .</body>

<body package="XPath" selector="expression">expression =

	orExpr</body>

<body package="XPath" selector="filterExpr">filterExpr =

	primaryExpr ( predicate [step:predicate:] )* .</body>

<body package="XPath" selector="multiplicativeExpr">multiplicativeExpr =

	unaryExpr
		(( #* [#*]
		| word=#div [#div]
		| word=#mod [#mod] ) unaryExpr [arg:op:arg:] )* .</body>

<body package="XPath" selector="orExpr">orExpr =

	andExpr ( word=#or [#or] andExpr [arg:op:arg:] )* .</body>

<body package="XPath" selector="pathExpr">pathExpr =

	@ locationPath
	| filterExpr ( #// relativeLocationPath halt
				| #/ relativeLocationPath [connectParent:child:]
				| )</body>

<body package="XPath" selector="primaryExpr">primaryExpr =

	$$ word [variable:]
	| $( expression $) [Transcript show: ' ?'].
	| string [string:]
	| number [number:]
	| word [function:] $( ( @ expression [function:arg:] ( $, expression [function:arg:] )* . | ) $)</body>

<body package="XPath" selector="relationalExpr">relationalExpr =

	additiveExpr
		(( #&lt; [#&lt;]
		| #&gt; [#&gt;]
		| #&lt;= [#&lt;=]
		| #&gt;= [#&gt;=] ) additiveExpr [arg:op:arg:] )* .</body>

<body package="XPath" selector="unaryExpr">unaryExpr =

	#- unaryExpr [negated:]
	| unionExpr</body>

<body package="XPath" selector="unionExpr">unionExpr =

	pathExpr ( [peekFor: #|]? pathExpr [union:with:] )* .</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>public</category>

<body package="XPath" selector="atEndOfExpression">atEndOfExpression

	^tokenType == #endOfExpression</body>

<body package="XPath" selector="parse:as:">parse: string as: construct

	self
		initScanner;
		init: string notifying: nil failBlock: nil;
		perform: construct.
	stack size = 1 ifFalse: [self error: 'Incomplete translation of the source code'].
	tokenType == #doIt ifFalse: [self error: 'Extra characters which could not be translated at end of stream'].
	^stack first</body>

<body package="XPath" selector="pastEnd">pastEnd

	^hereChar == nil</body>

<body package="XPath" selector="result">result

	stack size = 1 ifFalse: [self error: 'Parsing logic error, incorrect number of values on the stack'].
	^stack first</body>

<body package="XPath" selector="xmlNode:">xmlNode: aNode

	xmlNode := aNode</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>private</category>

<body package="XPath" selector="functionNamed:">functionNamed: fName

	functions == nil ifTrue: [functions := XPathFunction baseFunctions].
	^(functions at: fName ifAbsent: [self error: ('Not implemented yet &lt;1s&gt;()' expandMacrosWith: fName)]) copy</body>

<body package="XPath" selector="namespaceAt:">namespaceAt: aQualifier

	| elm ns |
	elm := xmlNode.
	[elm isDocument]
		whileFalse:
			[ns := elm namespaceAt: aQualifier.
			ns == nil ifFalse: [^ns].
			elm := elm parent].
	aQualifier = 'xml' ifTrue: [^XML_URI].
	self error: ('No namespace binding found for namespace qualifier "&lt;1s&gt;".'
			expandMacrosWith: aQualifier)</body>

<body package="XPath" selector="peekFor:">peekFor: trialValue 
	"Test to see if tokenType matches aType and token equals trialValue. If so, 
	advance to the next token"

	^token = trialValue
		ifTrue: 
			[self scanToken.
			true]
		ifFalse: [false]</body>

<body package="XPath" selector="unexpectedError">unexpectedError

	^self halt; notify: 'syntax error'</body>

<body package="XPath" selector="verifyNotFunction">verifyNotFunction

	^token ~= $(</body>

<body package="XPath" selector="xBinary">xBinary
	| char test |
	tokenType := #binary.
	char := hereChar.
	hereChar := source next.
	char = $. ifTrue: [self halt].
	hereChar == nil
		ifTrue: [token := Symbol internCharacter: char]
		ifFalse:
			[test := String with: char with: hereChar.
			(#('::' '&lt;=' '&gt;=' '!=') includes: test)
				ifTrue: [self step. token := Symbol intern: test]
				ifFalse: [token := Symbol internCharacter: char]]</body>

<body package="XPath" selector="xDigit">xDigit
	"form a number"

	| numerator denominator |
	tokenType := #number.
	numerator := 0.
	denominator := 1.
	[hereChar notNil and: [hereChar isDigit]]
		whileTrue:
			[numerator := numerator * 10 + hereChar digitValue.
			self step].
	hereChar = $. ifFalse: [^token := numerator + 0.0d0].
	self step.
	[hereChar notNil and: [hereChar isDigit]]
		whileTrue:
			[numerator := numerator * 10 + hereChar digitValue.
			denominator := denominator * 10.
			self step].
	token := (numerator / denominator) + 0.0d0.</body>

<body package="XPath" selector="xDoubleQuote">xDoubleQuote
	"collect string"

	| char |
	buffer reset.
	[(char := source next) == $"]
		whileFalse:
			[char == nil ifTrue: [^self offEnd: 'Unmatched comment quote'].
			buffer nextPut: char].
	"Shorten the buffer if it got unreasonably large."
	buffer position &gt; 200 ifTrue: [buffer := WriteStream on: (String new: 40)].
	tokenType := #string.
	token := buffer contents.
	hereChar := source next.</body>

<body package="XPath" selector="xLetter">xLetter
	"form a word, keyword, or reserved word"

	| char |
	buffer reset.
	buffer nextPut: hereChar.
	[char := source next.
	char notNil and:
		[((xTypes at: char asInteger+1) bitAnd: 2) = 2]]
		whileTrue:
			[buffer nextPut: char].
	tokenType := #word.
	hereChar := char.
	token := buffer contents.
	reservedWords == false ifFalse:
		[(reservedWords includes: token)
			ifTrue: [tokenType := token asSymbol]]</body>

<body package="XPath" selector="xPeriod">xPeriod
	"form a number"

	| numerator denominator |
	self step.
	hereChar = $.
		ifTrue:
			[self step.
			token := #'..'.
			tokenType := #binary.
			^self].
	(hereChar notNil and: [hereChar isDigit])
		ifFalse:
			[token := #'.'.
			tokenType := #binary.
			^self].
	tokenType := #number.
	numerator := 0.
	denominator := 1.
	[hereChar notNil and: [hereChar isDigit]]
		whileTrue:
			[numerator := numerator * 10 + hereChar digitValue.
			denominator := denominator * 10.
			self step].
	token := (numerator / denominator) + 0.0d0.</body>

<body package="XPath" selector="xSingleQuote">xSingleQuote
	"collect string"

	| char |
	buffer reset.
	[(char := source next) == $']
		whileFalse:
			[char == nil ifTrue: [^self offEnd: 'Unmatched comment quote'].
			buffer nextPut: char].
	"Shorten the buffer if it got unreasonably large."
	buffer position &gt; 200 ifTrue: [buffer := WriteStream on: (String new: 40)].
	tokenType := #string.
	token := buffer contents.
	hereChar := source next.</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>initialize</category>

<body package="XPath" selector="functions:">functions: aDictionary

	functions := aDictionary</body>

<body package="XPath" selector="init:notifying:failBlock:">init: streamOrString notifying: req failBlock: aBlock
	"Initialize the scanner.  Return the initial position
	in the source stream, so that we can back up
	over an initial scan if necessary."

	| sourceStream startPos |
	(streamOrString isCharacters)
		ifFalse: [startPos := streamOrString position.
				sourceStream := streamOrString]
		ifTrue: [startPos := 1.
				sourceStream := XPathReadStream on: streamOrString asString].
	requestor := req.
	failBlock := aBlock.
	correctionDelta := 0.
	self scan: sourceStream.
	^startPos</body>

<body package="XPath" selector="on:">on: inputStream 
	"Bind the input stream, fill the character buffer"

	xTypes := CharacterClasses.
	super on: inputStream.</body>
</methods>

<methods>
<class-id>XML.XPathParser</class-id> <category>basic items</category>

<body package="XPath" selector="literal">literal =

	string</body>
</methods>

<methods>
<class-id>XML.XPathParser class</class-id> <category>private</category>

<body package="XPath" selector="baseTable">baseTable
	"XPathParser nilTypeTable"

	| newTable |
	newTable := ScannerTable new: 255 withAll: #xDefault. "default"
	newTable atAllSeparatorsPut: #xDelimiter.
	newTable atAllDigitsPut: #xDigit.
	newTable atAllLettersPut: #xLetter.
	newTable at: $_ asInteger put: #xLetter.
	newTable at: $. asInteger put: #xPeriod.
	"Other multi-character tokens"
	newTable at: $" asInteger put: #xDoubleQuote.
	newTable at: $' asInteger put: #xSingleQuote.
	':*/+-@=!&lt;&gt;|' do: [:c | newTable at: c asInteger put: #xBinary].
	'[]()' do: [:c |
		newTable at: c asInteger
			put: (TypeTable at: c asInteger)].
	newTable at: $} asInteger put: #endOfExpression.

	^newTable</body>

<body package="XPath" selector="computeTables">computeTables
	"Compute the character type, reserved word tables, and
	keyword flag from the information associated with each method."
	| words types kwds info typeInfo |
	words := Set new.
	types := self baseTable.
	kwds := false. 
	self methodInfo keys do:
		[:selector |
		(methodDict includesKey: selector)
			ifTrue:
				[info := methodInfo at: selector.
				words addAll: (info at: 1).
				typeInfo := info at: 2.
				typeInfo isEmpty ifFalse:
					[typeInfo associationsDo:
						[:char |
						types at: char key asInteger
							put: char value]].
				kwds := kwds | (info at: 3)]
			ifFalse:
				[methodInfo removeKey: selector]].
	reservedWords := words isEmpty ifTrue: [false] ifFalse: [words].
	typeTable := types.
	hasKeywords := kwds</body>

<body package="XPath" selector="nilTypeTable">nilTypeTable

	super nilTypeTable.
	nodeTypes := nil</body>

<body package="XPath" selector="nodeTypes">nodeTypes

	"nodeTypes := nil."
	nodeTypes == nil
		ifTrue:
			[nodeTypes := Dictionary new.
			XPathStep allSubclassesDo: [:cls |
				cls axisNames do: [:nm | nodeTypes at: nm put: cls]]].
	^nodeTypes</body>
</methods>

<methods>
<class-id>XML.XPathParser class</class-id> <category>examples</category>

<body package="XPath" selector="examples">examples
	"XPathParser2 examples"

	| samples |
	samples := #('child::para' 'child::*' 'child::text()' 'child::node()'
			'attribute::name' 'attribute::*' 'descendant::para' 'ancestor::div'
			'ancestor-or-self::div' 'descendant-or-self::para' 'self::para'
			'child::chapter/descendant::para' 'child::*/child::para' '/'
			'/descendant::para' '/descendant::olist/child::item'
			'child::para[position()=1]' 'child::para[position()=last()]'
			'child::para[position()=last()-1]' 'child::para[position()&gt;1]'
			'following-sibling::chapter[position()=1]'
			'preceding-sibling::chapter[position()=1]'
			'/descendant::figure[position()=42]'
			'/child::doc/child::chapter[position()=5]/child::section[position()=2]'
			'child::para[attribute::type="warning"]'
			'child::para[attribute::type=''warning''][position()=5]'
			'child::para[position()=5][attribute::type="warning"]'
			'child::chapter[child::title=''Introduction'']'
			'child::chapter[child::title]' 'child::*[self::chapter or self::appendix]'
			'child::*[self::chapter or self::appendix][position()=last()]').
	^samples collect: [:str | self new parse: str as: #locationPath]</body>
</methods>

<methods>
<class-id>XML.XPathStep</class-id> <category>accessing</category>

<body package="XPath" selector="axisName">axisName

	^axisName</body>

<body package="XPath" selector="axisName:">axisName: aName

	axisName := aName</body>

<body package="XPath" selector="baseTest">baseTest

	^baseTest</body>

<body package="XPath" selector="baseTest:">baseTest: aNodeTest

	baseTest := aNodeTest</body>

<body package="XPath" selector="child">child

	^child</body>

<body package="XPath" selector="child:">child: aStep

	child := aStep</body>

<body package="XPath" selector="parent">parent

	^parent</body>

<body package="XPath" selector="parent:">parent: aStep

	parent := aStep</body>

<body package="XPath" selector="startOfPath">startOfPath

	| p |
	p := self.
	[p isStartOfPath]
		whileFalse: [p := p parent].
	^p</body>
</methods>

<methods>
<class-id>XML.XPathStep</class-id> <category>testing</category>

<body package="XPath" selector="hasComplexPredicate">hasComplexPredicate

	^predicates inject: false into: [:b :exp | b or: [exp xpathMayRequireNodeSetTopLevel]]</body>

<body package="XPath" selector="isStartOfPath">isStartOfPath

	^parent == nil</body>

<body package="XPath" selector="isTerminator">isTerminator

	^false</body>

<body package="XPath" selector="xpathMayRequireNodeSet">xpathMayRequireNodeSet

	^false</body>

<body package="XPath" selector="xpathMayRequireNodeSetTopLevel">xpathMayRequireNodeSetTopLevel

	^false</body>

<body package="XPath" selector="xpathMayRequireSort">xpathMayRequireSort

	^false</body>

<body package="XPath" selector="xpathMayRequireSortTopLevel">xpathMayRequireSortTopLevel

	^false</body>
</methods>

<methods>
<class-id>XML.XPathStep</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	aStream nextPutAll: self axisName, '::'.
	baseTest printOn: aStream</body>
</methods>

<methods>
<class-id>XML.XPathStep class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#()</body>
</methods>

<methods>
<class-id>XML.XPathParentNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| result |
	result := aNodeContext copy documentOrder.
	(baseTest match: aNodeContext node parent)
		ifTrue: [result add: aNodeContext node parent].
	^result</body>
</methods>

<methods>
<class-id>XML.XPathParentNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('parent')</body>
</methods>

<methods>
<class-id>XML.XPathNodeTest</class-id> <category>testing</category>

<body package="XPath" selector="isTrivial">isTrivial

	^false</body>
</methods>

<methods>
<class-id>XML.XPathTypedNodeTest</class-id> <category>testing</category>

<body package="XPath" selector="isTrivial">isTrivial

	^typeName = 'node'</body>
</methods>

<methods>
<class-id>XML.XPathTypedNodeTest</class-id> <category>matching</category>

<body package="XPath" selector="match:">match: anXmlNode

	typeName = 'node' ifTrue: [^true].
	typeName = 'text' ifTrue: [^anXmlNode isText].
	typeName = 'comment' ifTrue: [^anXmlNode isComment].
	typeName = 'processing-instruction'
		ifTrue: [^anXmlNode isProcessingInstruction
					and: [value == nil
					or: [value = anXmlNode name]]].
	self notYetImplementedError</body>
</methods>

<methods>
<class-id>XML.XPathTypedNodeTest</class-id> <category>printing</category>

<body package="XPath" selector="printOn:">printOn: aStream

	aStream nextPutAll: typeName, '('.
	value == nil ifFalse: [aStream nextPutAll: value].
	aStream nextPutAll: ')'.</body>
</methods>

<methods>
<class-id>XML.XPathTypedNodeTest</class-id> <category>accessing</category>

<body package="XPath" selector="type:">type: aString

	typeName := aString.
	(#('comment' 'text' 'node' 'processing-instruction') includes: typeName)
		ifFalse: [self error: 'A node test must be one of comment, text, node, or propcessing-instruction'].</body>

<body package="XPath" selector="value:">value: aString

	value := aString</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression</class-id> <category>accessing</category>

<body package="XPath" selector="arg1">arg1

	^argument1</body>

<body package="XPath" selector="arg2">arg2

	^argument2</body>

<body package="XPath" selector="enumerate:">enumerate: aBlock

	super enumerate: aBlock.
	self for: self arg1 enumerate: aBlock.
	self for: self arg2 enumerate: aBlock.</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	^valueBlock value: self value: aNodeContext</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression</class-id> <category>initialize</category>

<body package="XPath" selector="operator:with:with:">operator: op with: arg1 with: arg2

	operator := op.
	argument1 := arg1.
	argument2 := arg2.</body>

<body package="XPath" selector="valueBlock:">valueBlock: aBlock

	valueBlock := aBlock</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	argument1 printOn: aStream.
	aStream space; nextPutAll: operator; space.
	argument2 printOn: aStream.</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression</class-id> <category>testing</category>

<body package="XPath" selector="xpathMayRequireNodeSet">xpathMayRequireNodeSet

	^self arg1 xpathMayRequireNodeSet or: [self arg2 xpathMayRequireNodeSet]</body>

<body package="XPath" selector="xpathMayRequireNodeSetTopLevel">xpathMayRequireNodeSetTopLevel

	(#(+ - * div mod) includes: operator)
		ifTrue: [^true].
	(#(= != &lt; &gt; &lt;= &gt;= | and or) includes: operator)
		ifTrue: [^self arg1 xpathMayRequireNodeSet or: [self arg2 xpathMayRequireNodeSet]].
	self notYetImplementedError</body>

<body package="XPath" selector="xpathMayRequireSort">xpathMayRequireSort

	^self arg1 xpathMayRequireSort or: [self arg2 xpathMayRequireSort]</body>

<body package="XPath" selector="xpathMayRequireSortTopLevel">xpathMayRequireSortTopLevel

	(#(+ - * div mod) includes: operator)
		ifTrue: [^true].
	(#(= != &lt; &gt; &lt;= &gt;= | and or) includes: operator)
		ifTrue: [^self arg1 xpathMayRequireSort or: [self arg2 xpathMayRequireSort]].
	self notYetImplementedError</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression class</class-id> <category>class initialization</category>

<body package="XPath" selector="initialize">initialize
	"XPathBinaryExpression initialize"

	operators := Dictionary new.
	self initializeBoolean.
	self initializeComparison.
	self initializeNumeric</body>

<body package="XPath" selector="initializeBoolean">initializeBoolean

	operators at: #and put: (self new
				operator: #and with: nil with: nil;
				valueBlock: [:exp :ns || b1 b2 |
						b1 := (exp arg1 xpathEvalIn: ns) xpathAsBoolean.
						b2 := (exp arg2 xpathEvalIn: ns) xpathAsBoolean.
						b1 &amp; b2]).
	operators at: #or put: (self new
				operator: #or with: nil with: nil;
				valueBlock: [:exp :ns || b1 b2 |
						b1 := (exp arg1 xpathEvalIn: ns) xpathAsBoolean.
						b2 := (exp arg2 xpathEvalIn: ns) xpathAsBoolean.
						b1 | b2]).</body>

<body package="XPath" selector="initializeComparison">initializeComparison

	operators at: #= put: (self new
				operator: #= with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns)
							xpathCompareEquality: (exp arg2 xpathEvalIn: ns)
							using: [:v1 :v2 | v1 = v2]]).
	operators at: #!= put: (self new
				operator: #!= with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns)
							xpathCompareEquality: (exp arg2 xpathEvalIn: ns)
							using: [:v1 :v2 | v1 ~= v2]]).
	operators at: #&lt; put: (self new
				operator: #&lt; with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns)
							xpathCompareOrder: (exp arg2 xpathEvalIn: ns)
							using: [:v1 :v2 | v1 &lt; v2]]).
	operators at: #&gt; put: (self new
				operator: #&gt; with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns)
							xpathCompareOrder: (exp arg2 xpathEvalIn: ns)
							using: [:v1 :v2 | v1 &gt; v2]]).
	operators at: #&lt;= put: (self new
				operator: #&lt;= with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns)
							xpathCompareOrder: (exp arg2 xpathEvalIn: ns)
							using: [:v1 :v2 | v1 &lt;= v2]]).
	operators at: #&gt;= put: (self new
				operator: #&gt;= with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns)
							xpathCompareOrder: (exp arg2 xpathEvalIn: ns)
							using: [:v1 :v2 | v1 &gt;= v2]]).</body>

<body package="XPath" selector="initializeNumeric">initializeNumeric

	operators at: #+ put: (self new
				operator: #+ with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns) xpathAsNumber +
							(exp arg2 xpathEvalIn: ns) xpathAsNumber]).
	operators at: #- put: (self new
				operator: #- with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns) xpathAsNumber -
							(exp arg2 xpathEvalIn: ns) xpathAsNumber]).
	operators at: #* put: (self new
				operator: #* with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns) xpathAsNumber *
							(exp arg2 xpathEvalIn: ns) xpathAsNumber]).
	operators at: #div put: (self new
				operator: #div with: nil with: nil;
				valueBlock: [:exp :ns |
						(exp arg1 xpathEvalIn: ns) xpathAsNumber /
							(exp arg2 xpathEvalIn: ns) xpathAsNumber]).
	operators at: #mod put: (self new
				operator: #mod with: nil with: nil;
				valueBlock: [:exp :ns |
						((exp arg1 xpathEvalIn: ns) xpathAsNumber rem:
							(exp arg2 xpathEvalIn: ns) xpathAsNumber)]).</body>
</methods>

<methods>
<class-id>XML.XPathBinaryExpression class</class-id> <category>instance creation</category>

<body package="XPath" selector="operator:with:with:">operator: op with: arg1 with: arg2

	^(operators at: op ifAbsent: [self error: ('Not implemented yet &lt;1s&gt;' expandMacrosWith: op)]) copy
		operator: op with: arg1 with: arg2</body>
</methods>

<methods>
<class-id>XML.XPathFollowingSiblingNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc list i |
	nd := aNodeContext node.
	nc := aNodeContext copy documentOrder.
	list := nd parent children.
	i := list identityIndexOf: nd.
	(list copyFrom: i+1 to: list size) do: [:childNode |
		(baseTest match: childNode)
			ifTrue: [nc add: childNode]].
	^nc</body>
</methods>

<methods>
<class-id>XML.XPathFollowingSiblingNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('following-sibling')</body>
</methods>

<methods>
<class-id>XML.XmlScannerTable</class-id> <category>accessing</category>

<body package="XPath" selector="at:put:">at: index put: value
	" See the class comment for an explanation
	of why we have to reimplement this message. "

	&lt;primitive: 61&gt;
	index isInteger
		ifFalse: [^super at: index put: value].  "may fail again"
	(index &gt;= 1 and: [index &lt;= self basicSize])
		ifTrue: [^self improperStoreError].
	" Handle legitimate stores out of range "
	index = 0 ifTrue: [^value0 := value].
	value = (self at: index)
		ifTrue: [^value].  "value being stored is same as default"
	^self improperStoreError</body>

<body package="XPath" selector="basicAt:put:">basicAt: index put: value
	" See the class comment for an explanation
	of why we have to reimplement this message. "

	&lt;primitive: 61&gt;
	index isInteger
		ifFalse: [^super basicAt: index put: value].  "may fail again"
	(index &gt;= 1 and: [index &lt;= self basicSize])
		ifTrue: [^self improperStoreError].
	^super subscriptBoundsError: index  "super, not self!"</body>
</methods>

<methods>
<class-id>XML.XmlScannerTable</class-id> <category>accessing-defaults</category>

<body package="XPath" selector="atAllDigitsPut:">atAllDigitsPut: value
	" Set the value for all Characters
	that identify themselves as digits
	and that are outside the range of
	explicitly stored indices. "

	digitValue := value.
	0 to: self size do: [:index |
		(Character value: index) isDigit
			ifTrue: [self at: index put: value]]</body>

<body package="XPath" selector="atAllLettersPut:">atAllLettersPut: value
	" Set the value for all Characters
	that identify themselves as letters
	and that are outside the range of
	explicitly stored indices. "

	letterValue := value.
	0 to: self size do: [:index |
		((CharacterClasses at: index+1) bitAnd: 4) = 4
			ifTrue: [self at: index put: value]]</body>

<body package="XPath" selector="atAllPut:">atAllPut: value
	value0 := defaultValue := letterValue := digitValue := separatorValue := value.
	^super atAllPut: value</body>

<body package="XPath" selector="atAllSeparatorsPut:">atAllSeparatorsPut: value
	" Set the value for all Characters
	that identify themselves as separators
	and that are outside the range of
	explicitly stored indices. "

	separatorValue := value.
	0 to: self size do: [:index |
		(Character value: index) isSeparator
			ifTrue: [self at: index put: value]]</body>

<body package="XPath" selector="atDefaultPut:">atDefaultPut: value
	" Set the value for all Characters
	that are not letters and that are
	outside the range of explicitly stored
	indices. "

	defaultValue := value</body>
</methods>

<methods>
<class-id>XML.XmlScannerTable</class-id> <category>error handling</category>

<body package="XPath" selector="subscriptBoundsError:">subscriptBoundsError: index
	"This method is invoked when a subscript is
	an integer but is out of range.  Answer an
	appropriate default value."
	" Note that we have to override at: and at:put:
	to make this work properly, because at: and at:put:
	both send subscriptBoundsError:.  This is a design bug
	in the implementation of at: and at:put:. "

	| char |
	((index between: self size + 1 and: Character maxValue) or: [index = 0])
		ifFalse: [^super subscriptBoundsError: index].
	index = 0 ifTrue: [^value0].
	((CharacterClasses at: index+1) bitAnd: 4) = 4 ifTrue: [^letterValue].
	char := Character value: index.
	char isSeparator ifTrue: [^separatorValue].
	char isDigit ifTrue: [^digitValue].
	^defaultValue</body>
</methods>

<methods>
<class-id>XML.XPathAncestorNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc nextNode |
	nd := aNodeContext node.
	nc := aNodeContext copy inverseDocumentOrder.
	self axisName = 'ancestor-or-self'
		ifTrue: [nextNode := nd]
		ifFalse: [nextNode := nd parent].
	[nextNode == nil]
		whileFalse:
			[(baseTest match: nextNode)
				ifTrue: [nc add: nextNode].
			nextNode := nextNode parent].
	^nc</body>
</methods>

<methods>
<class-id>XML.XPathAncestorNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('ancestor' 'ancestor-or-self')</body>
</methods>

<methods>
<class-id>XML.XPathPrecedingSiblingNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc list i |
	nd := aNodeContext node.
	nc := aNodeContext copy inverseDocumentOrder.
	list := nd parent children.
	i := list identityIndexOf: nd.
	(list copyFrom: 1 to: i-1) do: [:childNode |
		(baseTest match: childNode)
			ifTrue: [nc add: childNode]].
	^nc</body>
</methods>

<methods>
<class-id>XML.XPathPrecedingSiblingNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('preceding-sibling')</body>
</methods>

<methods>
<class-id>XML.XPathChildNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc |
	nd := aNodeContext node.
	nc := aNodeContext copy documentOrder.
	(nd isElement or: [nd isDocument])
		ifFalse: [^nc].
	aNodeContext node children do: [:childNode |
		(baseTest match: childNode)
			ifTrue: [nc add: childNode]].
	^nc</body>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	| hasCP set |
	anXmlNode isAttribute ifTrue: [^false].
	(baseTest match: anXmlNode) ifFalse: [^false].
	(hasCP := self hasComplexPredicate)
		ifFalse:
			[set := XPathNodeContext new add: anXmlNode.
			1 to: predicates size do: [:i |
				set := set select: (predicates at: i)].
			set size = 0 ifTrue: [^false]].
	parent == nil ifTrue: [^aBlock value: anXmlNode parent value: complex | hasCP].
	^parent simpleMatchFor: anXmlNode parent
		isComplex: complex | hasCP
		do: aBlock</body>
</methods>

<methods>
<class-id>XML.XPathChildNode</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	axisName == nil
		ifTrue: [baseTest printOn: aStream]
		ifFalse: [super printTestOn: aStream]</body>
</methods>

<methods>
<class-id>XML.XPathChildNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('child')</body>
</methods>

<methods>
<class-id>XML.XPathDescendantNode</class-id> <category>testing</category>

<body package="XPath" selector="axisName">axisName

	^axisName == nil
		ifTrue: ['descendant-or-self']
		ifFalse: [axisName]</body>
</methods>

<methods>
<class-id>XML.XPathDescendantNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc queue nextNode |
	nd := aNodeContext node.
	nc := aNodeContext copy documentOrder.
	queue := OrderedCollection new.
	self axisName = 'descendant-or-self'
		ifTrue: [queue add: nd]
		ifFalse: [nd isElement
			ifTrue: [queue addAll: nd children]].
	nd isDocument
		ifTrue: [queue add: nd root].
	[queue isEmpty]
		whileFalse:
			[nextNode := queue removeFirst.
			(baseTest match: nextNode)
				ifTrue: [nc add: nextNode].
			nextNode isElement
				ifTrue: [queue addAll: nextNode children]].
	^nc</body>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	| startNode hasCP set |
	anXmlNode isAttribute ifTrue: [^false].
	(baseTest match: anXmlNode) ifFalse: [^false].
	(hasCP := self hasComplexPredicate)
		ifFalse:
			[set := XPathNodeContext new add: anXmlNode.
			1 to: predicates size do: [:i |
				set := set select: (predicates at: i)].
			set size = 0 ifTrue: [^false halt]].
	startNode := self axisName = 'descendant'
		ifTrue: [anXmlNode parent]
		ifFalse: [anXmlNode].
	[parent == nil
		ifTrue: [aBlock value: anXmlNode value: complex | hasCP]
		ifFalse: [parent simpleMatchFor: startNode
				isComplex: complex | hasCP
				do: aBlock]]
		whileFalse:
			[startNode := startNode parent.
			startNode == nil ifTrue: [^false]].
	^true</body>
</methods>

<methods>
<class-id>XML.XPathDescendantNode</class-id> <category>printing</category>

<body package="XPath" selector="completeChildPrintOn:">completeChildPrintOn: aStream

	(baseTest isTrivial
			and: [predicates isEmpty
			and: [axisName = 'descendant-or-self']])
		ifFalse: [^super completePrintOn: aStream].
	aStream nextPut: $/.
	self child completeChildPrintOn: aStream.</body>
</methods>

<methods>
<class-id>XML.XPathDescendantNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('descendant' 'descendant-or-self')</body>
</methods>

<methods>
<class-id>XML.XPathVariable</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| var |
	var := aNodeContext variables
		at: self name
		ifAbsent: [self error: ('No binding found for the variable $&lt;1s&gt;' expandMacrosWith: self name)].
	^var</body>
</methods>

<methods>
<class-id>XML.XPathVariable</class-id> <category>accessing</category>

<body package="XPath" selector="name">name

	^name</body>

<body package="XPath" selector="name:">name: aName

	name := aName</body>
</methods>

<methods>
<class-id>XML.XPathVariable</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	aStream nextPutAll: '$', name</body>
</methods>

<methods>
<class-id>XML.XPathVariable</class-id> <category>testing</category>

<body package="XPath" selector="xpathMayRequireSort">xpathMayRequireSort

	^false</body>
</methods>

<methods>
<class-id>XML.XPathTerminator</class-id> <category>printing</category>

<body package="XPath" selector="completePrintOn:">completePrintOn: aStream

	^self</body>

<body package="XPath" selector="printOn:">printOn: aStream

	self basicPrintOn: aStream</body>
</methods>

<methods>
<class-id>XML.XPathTerminator</class-id> <category>accessing</category>

<body package="XPath" selector="enumerate:">enumerate: aBlock

	aBlock value: self</body>
</methods>

<methods>
<class-id>XML.XPathTerminator</class-id> <category>initialize</category>

<body package="XPath" selector="initialize">initialize

	predicates := #().</body>
</methods>

<methods>
<class-id>XML.XPathTerminator</class-id> <category>testing</category>

<body package="XPath" selector="isTerminator">isTerminator

	^true</body>
</methods>

<methods>
<class-id>XML.XPathTerminator</class-id> <category>matching</category>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	^parent simpleMatchFor: anXmlNode isComplex: complex do: aBlock</body>

<body package="XPath" selector="valueIn:do:">valueIn: aNodeContext do: aBlock

	aBlock value: aNodeContext node</body>

<body package="XPath" selector="valueOfAllIn:">valueOfAllIn: aNodeContext

	^aNodeContext</body>
</methods>

<methods>
<class-id>XML.XPathTaggedNodeTest</class-id> <category>matching</category>

<body package="XPath" selector="match:">match: anXmlNode

	(anXmlNode isElement or: [anXmlNode isAttribute]) ifFalse: [^false].
	namespace == nil
		ifFalse: [namespace = anXmlNode tag namespace ifFalse: [^false]].
	^type = #* or: [type = anXmlNode tag type]</body>
</methods>

<methods>
<class-id>XML.XPathTaggedNodeTest</class-id> <category>accessing</category>

<body package="XPath" selector="namespace">namespace

	^namespace</body>

<body package="XPath" selector="namespace:">namespace: ns

	namespace := ns = '' ifTrue: [nil] ifFalse: [ns].</body>

<body package="XPath" selector="type">type

	^type</body>

<body package="XPath" selector="type:">type: aString

	type := aString</body>
</methods>

<methods>
<class-id>XML.XPathTaggedNodeTest</class-id> <category>printing</category>

<body package="XPath" selector="printOn:">printOn: aStream

	qualifier == nil ifFalse: [aStream nextPutAll: qualifier, ':'].
	aStream nextPutAll: type</body>
</methods>

<methods>
<class-id>XML.XPathPrecedingNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc |
	nd := aNodeContext node.
	nc := aNodeContext copy inverseDocumentOrder.
	self from: nd do: [:nd1 |
		(baseTest match: nd1)
			ifTrue: [nc add: nd1]].
	^nc</body>

<body package="XPath" selector="from:do:">from: aNode do: aBlock

	| current stack ignoreAll |
	ignoreAll := IdentitySet new.
	stack := OrderedCollection new.
	current := aNode.
	[ignoreAll add: current.
	current isDocument not] whileTrue:
		[stack addFirst: current parent -&gt; (current parent children indexOf: current).
		current := current parent].
	current := aNode.
	[current isDocument
		ifTrue: [^self]
		ifFalse: [stack last value = 1
			ifTrue: [current := stack removeLast key]
			ifFalse:
				[stack last value: stack last value-1.
				current := stack last key children at: stack last value.
				[current isElement and: [current children isEmpty not]]
					whileTrue:
						[stack add: current -&gt; current children size.
						current := current children last]]].
	(ignoreAll includes: current) ifFalse: [aBlock value: current]] repeat</body>
</methods>

<methods>
<class-id>XML.XPathPrecedingNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('preceding')</body>
</methods>

<methods>
<class-id>XML.XPathUnion</class-id> <category>accessing</category>

<body package="XPath" selector="add:">add: aNode

	arguments := arguments copyWith: aNode</body>

<body package="XPath" selector="arguments">arguments

	^arguments</body>

<body package="XPath" selector="asUnion">asUnion

	^self</body>

<body package="XPath" selector="enumerate:">enumerate: aBlock

	super enumerate: aBlock.
	arguments do: [:a | self for: a enumerate: aBlock].</body>
</methods>

<methods>
<class-id>XML.XPathUnion</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nc |
	nc := aNodeContext copy documentOrder.
	1 to: arguments size do: [:a |
		nc addNodeSet: ((arguments at: a) xpathEvalIn: aNodeContext)].
	^nc</body>

<body package="XPath" selector="match:">match: aNodeContext

	1 to: arguments size do: [:a |
		((arguments at: a) match: aNodeContext) ifTrue: [^true]].
	^false</body>
</methods>

<methods>
<class-id>XML.XPathUnion</class-id> <category>initialize</category>

<body package="XPath" selector="initialize">initialize

	super initialize.
	arguments := #()</body>
</methods>

<methods>
<class-id>XML.XPathUnion</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	arguments do: [:a | aStream print: a] separatedBy: [aStream nextPutAll: '|'].</body>
</methods>

<methods>
<class-id>XML.XPathSortingVector</class-id> <category>sorting</category>

<body package="XPath" selector="&lt;=">&lt;= aVector

	| min v1 v2 |
	min := self size min: aVector size.
	1 to: min do: [:i |
		v1 := self at: i.
		v2 := aVector at: i.
		v1 = v2 ifFalse: [^v1 &lt; v2]].
	^self size &lt;= aVector size</body>
</methods>

<methods>
<class-id>XML.XPathSortingVector</class-id> <category>accessing</category>

<body package="XPath" selector="value">value

	^value</body>

<body package="XPath" selector="value:">value: aNode

	value := aNode</body>
</methods>

<methods>
<class-id>XML.XPathSortingVector class</class-id> <category>instance creation</category>

<body package="XPath" selector="fromXmlNode:">fromXmlNode: aNode

	| list node |
	list := OrderedCollection new.
	node := aNode.
	[node isDocument]
		whileFalse:
			[list addFirst: (node parent children identityIndexOf: node ifAbsent: [0]).
			node := node parent].
	^(self withAll: list) value: aNode</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>adding</category>

<body package="XPath" selector="add:">add: aNode

	nodes add: aNode</body>

<body package="XPath" selector="addAll:">addAll: collection

	nodes addAll: collection</body>

<body package="XPath" selector="addNodeSet:">addNodeSet: nodeSet

	nodes addAll: nodeSet unsortedNodes</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>enumerating</category>

<body package="XPath" selector="addToXPathHolder:for:">addToXPathHolder: anAssociation for: aNodeContext

	self error: 'Should not happen--a NodeSet is being processed as if it were a single XML node'</body>

<body package="XPath" selector="select:">select: aPattern

	| result val |
	aPattern xpathMayRequireSortTopLevel
		ifTrue: [self checkSorted]
		ifFalse: [self checkOrdered].
	result := self copy.
	self reset.
	[self atEnd]
		whileFalse:
			[val := aPattern xpathEvalIn: self next.
			(val xpathIsNumber
					ifTrue: [val = self index]
					ifFalse: [val xpathAsBoolean])
				ifTrue: [result add: self node]].
	^result</body>

<body package="XPath" selector="selectMatch:">selectMatch: aPattern

	| result |
	result := self copy.
	self reset.
	[self atEnd]
		whileFalse:
			[(aPattern match: self next)
				ifTrue: [result add: self node]].
	^result</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>copying</category>

<body package="XPath" selector="asSingleNode">asSingleNode

	^self copy
		add: self node;
		yourself</body>

<body package="XPath" selector="copy">copy

	^self shallowCopy postCopy</body>

<body package="XPath" selector="postCopy">postCopy

	nodes := IdentitySet new.
	index := 0.
	node := nil.
	documentOrder := true.</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>streaming</category>

<body package="XPath" selector="atEnd">atEnd

	^index = nodes size</body>

<body package="XPath" selector="next">next

	index = nodes size ifTrue: [^nil].
	self index: index+1.
	node := nodes at: index.
	^self</body>

<body package="XPath" selector="reset">reset

	index := 0.
	node := nil</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>accessing</category>

<body package="XPath" selector="baseNode">baseNode

	^baseNode</body>

<body package="XPath" selector="baseNode:">baseNode: aNode

	baseNode := aNode</body>

<body package="XPath" selector="documentOrder">documentOrder

	documentOrder := true</body>

<body package="XPath" selector="index">index

	^index</body>

<body package="XPath" selector="index:">index: n

	self checkSorted.
	(n &lt; 1 or: [n &gt; nodes size])
		ifTrue: [self error: 'Index out of bounds'].
	index := n.
	node := nodes at: n</body>

<body package="XPath" selector="indexForNode:">indexForNode: aNode

	self checkSorted.
	index := nodes identityIndexOf: aNode.
	index = 0 ifTrue: [self error: 'No such node found in the list'].
	node := aNode</body>

<body package="XPath" selector="inverseDocumentOrder">inverseDocumentOrder

	documentOrder := false</body>

<body package="XPath" selector="node">node

	^node</body>

<body package="XPath" selector="size">size

	^nodes size</body>

<body package="XPath" selector="sort:">sort: aBlock

	nodes := nodes asSortedCollection: aBlock</body>

<body package="XPath" selector="sortedNodes">sortedNodes

	^nodes asSortedCollection: [:n1 :n2 | n1 precedes: n2]</body>

<body package="XPath" selector="unsortedNodes">unsortedNodes

	^nodes</body>

<body package="XPath" selector="variables">variables

	^variables</body>

<body package="XPath" selector="variables:">variables: aDictionary

	variables := aDictionary</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>initialize</category>

<body package="XPath" selector="checkOrdered">checkOrdered

	nodes class == IdentitySet
		ifTrue: [nodes := nodes asArray].</body>

<body package="XPath" selector="checkSorted">checkSorted

	nodes class == IdentitySet
		ifTrue:
			[nodes size &lt; 4
				ifTrue: [nodes := documentOrder
					ifTrue: [nodes asSortedCollection: [:n1 :n2 | n1 precedes: n2]]
					ifFalse: [nodes asSortedCollection: [:n1 :n2 | n2 precedes: n1]]]
				ifFalse:
					[nodes := nodes asArray collect: [:nd | XPathSortingVector fromXmlNode: nd].
					nodes := documentOrder
						ifTrue: [nodes asSortedCollection: [:n1 :n2 | n1 &lt;= n2]]
						ifFalse: [nodes asSortedCollection: [:n1 :n2 | n2 &lt;= n1]].
					nodes := nodes collect: [:nd | nd value]]].</body>

<body package="XPath" selector="ensureSorted">ensureSorted

	nodes class == IdentitySet
		ifFalse: [self error: 'This collection was already sorted once and may not be in correct sort order'].
	self checkSorted</body>

<body package="XPath" selector="initialize">initialize

	nodes := IdentitySet new.
	documentOrder := true</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>testing</category>

<body package="XPath" selector="contains:">contains: aBlock

	| match |
	match := nodes detect: aBlock ifNone: [].
	^match notNil</body>

<body package="XPath" selector="xpathIsNodeSet">xpathIsNodeSet

	^true</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>printing</category>

<body package="XPath" selector="printOn:">printOn: aStream

	self basicPrintOn: aStream</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>functions</category>

<body package="XPath" selector="sum">sum

	^(nodes inject: 0.0d into: [:i :nd | i + nd xpathStringData xpathAsNumber])</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>coercing</category>

<body package="XPath" selector="xpathAsBoolean">xpathAsBoolean

	^self size &gt; 0</body>

<body package="XPath" selector="xpathAsNumber">xpathAsNumber

	^self xpathAsString xpathAsNumber</body>

<body package="XPath" selector="xpathAsString">xpathAsString

	| list |
	nodes size = 0 ifTrue: [^''].
	list := nodes asSortedCollection: [:n1 :n2 | n1 precedes: n2].
	^list first xpathStringData</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext</class-id> <category>comparing</category>

<body package="XPath" selector="xpathCompareEquality:using:">xpathCompareEquality: aData using: aBlock

	aData isString
		ifTrue: [^nodes contains: [:nd | aBlock value: nd xpathStringData value: aData]].
	aData xpathIsNumber
		ifTrue: [^nodes contains: [:nd | aBlock value: nd xpathStringData xpathAsNumber value: aData]].
	aData xpathIsBoolean
		ifTrue: [^nodes contains: [:nd | aBlock value: nd xpathStringData xpathAsBoolean value: aData]].
	aData xpathIsNodeSet
		ifTrue: [^nodes contains: [:nd1 |
				aData unsortedNodes contains: [:nd2 |
					aBlock value: nd1 xpathStringData value: nd2 xpathStringData]]].
	self error: ('Can''t compare a &lt;1s&gt; with a node set' expandMacrosWith: aData class printString)</body>

<body package="XPath" selector="xpathCompareOrder:using:">xpathCompareOrder: aData using: aBlock

	^aData xpathIsNodeSet
		ifTrue: [self unsortedNodes contains: [:nd1 || v |
					v := nd1 xpathStringData xpathAsNumber.
					aData unsortedNodes contains: [:nd2 |
						aBlock value: v value: nd2 xpathStringData xpathAsNumber]]]
		ifFalse: [| v |
				v := aData xpathAsNumber.
				self unsortedNodes contains: [:nd |
					aBlock value: nd xpathStringData xpathAsNumber value: v]]</body>
</methods>

<methods>
<class-id>XML.XPathNodeContext class</class-id> <category>instance creation</category>

<body package="XPath" selector="new">new

	^super new initialize</body>
</methods>

<methods>
<class-id>XML.XPathCurrentNode</class-id> <category>accessing</category>

<body package="XPath" selector="axisName">axisName

	axisName == nil
		ifTrue: [^'self']
		ifFalse: [^axisName]</body>
</methods>

<methods>
<class-id>XML.XPathCurrentNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	^(baseTest match: aNodeContext node)
		ifTrue: [aNodeContext copy documentOrder; add: aNodeContext node]
		ifFalse: [aNodeContext copy documentOrder]</body>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	| hasCP set |
	(baseTest match: anXmlNode) ifFalse: [^false].
	(hasCP := self hasComplexPredicate)
		ifFalse:
			[set := XPathNodeContext new add: anXmlNode.
			1 to: predicates size do: [:i |
				set := set select: (predicates at: i)].
			set size = 0 ifTrue: [^false halt]].
	parent == nil ifTrue: [^aBlock value: anXmlNode value: complex | hasCP].
	^parent simpleMatchFor: anXmlNode
		isComplex: complex | hasCP
		do: aBlock</body>
</methods>

<methods>
<class-id>XML.XPathCurrentNode</class-id> <category>printing</category>

<body package="XPath" selector="completePrintOn:">completePrintOn: aStream

	(baseTest isTrivial
			and: [predicates isEmpty])
		ifTrue: [self child isTerminator
			ifTrue: [aStream nextPutAll: '.']
			ifFalse:
				[aStream nextPutAll: './'.
				self child completeChildPrintOn: aStream]]
		ifFalse: [super completePrintOn: aStream]</body>
</methods>

<methods>
<class-id>XML.XPathCurrentNode</class-id> <category>initialize</category>

<body package="XPath" selector="initialize">initialize

	super initialize.
	baseTest := XPathTypedNodeTest new type: 'node'</body>
</methods>

<methods>
<class-id>XML.XPathCurrentNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('self')</body>
</methods>

<methods>
<class-id>XML.XPathRoot</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	^aNodeContext copy documentOrder; add: aNodeContext node document</body>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	^anXmlNode isDocument
		and: [aBlock value: anXmlNode value: complex].</body>
</methods>

<methods>
<class-id>XML.XPathRoot</class-id> <category>printing</category>

<body package="XPath" selector="completePrintOn:">completePrintOn: aStream

	aStream nextPut: $/.
	self child completeChildPrintOn: aStream.</body>
</methods>

<methods>
<class-id>XML.XPathRoot</class-id> <category>testing</category>

<body package="XPath" selector="isStartOfPath">isStartOfPath

	^true</body>
</methods>

<methods>
<class-id>XML.XPathFollowingNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc |
	nd := aNodeContext node.
	nc := aNodeContext copy documentOrder.
	self from: nd do: [:nd1 |
		(baseTest match: nd1)
			ifTrue: [nc add: nd1]].
	^nc</body>

<body package="XPath" selector="from:do:">from: aNode do: aBlock

	| current stack idx followChildren |
	current := aNode.
	stack := OrderedCollection new.
	current isDocument ifFalse:
		[[current parent isDocument not] whileTrue:
			[stack addFirst: current parent -&gt; (current parent children indexOf: current).
			current := current parent]].
	current := aNode.
	"By setting followChildren to false the first time only, we ignore
	all descendents of aNode."
	followChildren := false.
	[(followChildren and: [(current isElement or: [current isDocument]) and: [current children size &gt; 0]])
		ifTrue: [stack add: current-&gt;1. current := current children at: 1.]
		ifFalse:
			[[stack isEmpty ifTrue: [^self].
			stack last key children size &gt; stack last value]
				whileFalse:
					[current := stack removeLast key].
			stack last value: (idx := stack last value+1).
			current := stack last key children at: idx].
	followChildren := true.
	aBlock value: current.] repeat</body>
</methods>

<methods>
<class-id>XML.XPathFollowingNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('following')</body>
</methods>

<methods>
<class-id>XML.XPathReadStream</class-id> <category>private</category>

<body package="XPath" selector="pastEnd">pastEnd
	"The receiver has attempted to read past the end, answer nil."

	^nil</body>
</methods>

<methods>
<class-id>XML.XPathAttributeNode</class-id> <category>matching</category>

<body package="XPath" selector="baseValueIn:">baseValueIn: aNodeContext

	| nd nc |
	nd := aNodeContext node.
	nc := aNodeContext copy documentOrder.
	nd isElement
		ifFalse: [^nc].
	nd attributes do: [:childNode |
		(baseTest match: childNode)
			ifTrue: [nc add: childNode]].
	^nc</body>

<body package="XPath" selector="simpleMatchFor:isComplex:do:">simpleMatchFor: anXmlNode isComplex: complex do: aBlock

	| hasCP set |
	anXmlNode isAttribute ifFalse: [^false].
	(baseTest match: anXmlNode) ifFalse: [^false].
	(hasCP := self hasComplexPredicate)
		ifFalse:
			[set := XPathNodeContext new add: anXmlNode.
			1 to: predicates size do: [:i |
				set := set select: (predicates at: i)].
			set size = 0 ifTrue: [^false halt]].
	parent == nil ifTrue: [^aBlock value: anXmlNode parent value: complex | hasCP].
	^parent simpleMatchFor: anXmlNode parent
		isComplex: complex | hasCP
		do: aBlock</body>
</methods>

<methods>
<class-id>XML.XPathAttributeNode</class-id> <category>printing</category>

<body package="XPath" selector="printTestOn:">printTestOn: aStream

	axisName == nil
		ifTrue: [aStream nextPutAll: '@'; print: baseTest]
		ifFalse: [super printTestOn: aStream]</body>
</methods>

<methods>
<class-id>XML.XPathAttributeNode class</class-id> <category>private</category>

<body package="XPath" selector="axisNames">axisNames

	^#('attribute')</body>
</methods>

<methods>
<class-id>Core.String</class-id> <category>converting</category>

<body package="XPath" selector="xpathAsBoolean">xpathAsBoolean

	^self size &gt; 0</body>

<body package="XPath" selector="xpathAsNumber">xpathAsNumber

	| s foundDigit numerator denominator ch |
	s := self readStream.
	s skipSeparators.
	foundDigit := false.
	numerator := 0.
	denominator := (s peekFor: $-) ifTrue: [-1] ifFalse: [1].
	[(ch := s next) notNil and: [ch isDigit]]
		whileTrue:
			[numerator := numerator * 10 + ch digitValue.
			foundDigit := true].
	ch = $.
		ifTrue: [[(ch := s next) notNil and: [ch isDigit]]
			whileTrue:
				[numerator := numerator * 10 + ch digitValue.
				denominator := denominator * 10.
				foundDigit := true]].
	(ch == nil or: [ch isSeparator]) ifFalse: [^NotANumber new].
	s skipSeparators.
	s atEnd ifFalse: [^NotANumber new].
	foundDigit ifFalse: [^NotANumber new].
	^(numerator / denominator) + 0.0d0</body>

<body package="XPath" selector="xpathAsString">xpathAsString

	^self</body>
</methods>

<methods>
<class-id>Core.String</class-id> <category>xml support</category>

<body package="XPath" selector="xpathCompareEquality:using:">xpathCompareEquality: aData using: aBlock

	aData isString
		ifTrue: [^aBlock value: self value: aData].
	aData xpathIsNumber
		ifTrue: [^aBlock value: self xpathAsNumber value: aData].
	aData xpathIsBoolean
		ifTrue: [^aBlock value: self xpathAsBoolean value: aData].
	aData xpathIsNodeSet
		ifTrue: [^aData unsortedNodes contains: [:nd2 |
					aBlock value: self value: nd2 xpathStringData]].
	self error: ('Can''t compare a &lt;1s&gt; with a string' expandMacrosWith: aData class printString)</body>

<body package="XPath" selector="xpathCompareOrder:using:">xpathCompareOrder: aData using: aBlock

	| v |
	v := self xpathAsNumber.
	^aData xpathIsNodeSet
		ifTrue: [aData unsortedNodes contains: [:nd2 |
					aBlock value: v value: nd2 xpathStringData xpathAsNumber]]
		ifFalse: [aBlock value: v value: aData xpathAsNumber]</body>
</methods>

<methods>
<class-id>Core.Number</class-id> <category>converting</category>

<body package="XPath" selector="xpathAsBoolean">xpathAsBoolean

	^self ~= 0</body>

<body package="XPath" selector="xpathAsNumber">xpathAsNumber

	^self</body>

<body package="XPath" selector="xpathAsString">xpathAsString

	| str n num delta n2 found |
"	self isZero ifTrue: [^'0']."
	(self class == NotANumber or: [self class == Infinity])
		ifTrue: [^self printString].
	str := (String new: 8) writeStream.
	self &lt; 0 ifTrue: [str nextPut: $-].
	n := self abs + 0.0d0.
	num := n truncated.
	str print: num.
	num + 0.0d = n ifTrue:
		[^str contents].
	delta := 1/10.
	found := false.
	[[n2 := num + delta.
	n2 &lt; n] whileTrue:
		[num := n2].
	num = n
		ifTrue: [found := true]
		ifFalse: [n2 = n
			ifTrue: [num := n2. found := true]].
	found]
		whileFalse: [delta := delta / 10].
	num = n ifFalse: [self halt].
	str nextPut: $..
	num := num - num truncated.
	[num = 0]
		whileFalse:
			[num := num * 10.
			str print: num truncated.
			num := num - num truncated].
	^str contents</body>
</methods>

<methods>
<class-id>Core.Number</class-id> <category>xml support</category>

<body package="XPath" selector="xpathCompareEquality:using:">xpathCompareEquality: aData using: aBlock

	aData isString
		ifTrue: [^aBlock value: self value: aData xpathAsNumber].
	aData xpathIsNumber
		ifTrue: [^aBlock value: self value: aData].
	aData xpathIsBoolean
		ifTrue: [^aBlock value: self xpathAsBoolean value: aData].
	aData xpathIsNodeSet
		ifTrue: [^aData unsortedNodes contains: [:nd2 |
					aBlock value: self value: nd2 xpathStringData xpathAsNumber]].
	self error: ('Can''t compare a &lt;1s&gt; with a number' expandMacrosWith: aData class printString)</body>

<body package="XPath" selector="xpathCompareOrder:using:">xpathCompareOrder: aData using: aBlock

	| v |
	v := self xpathAsNumber.
	^aData xpathIsNodeSet
		ifTrue: [aData unsortedNodes contains: [:nd2 |
					aBlock value: v value: nd2 xpathStringData xpathAsNumber]]
		ifFalse: [aBlock value: v value: aData xpathAsNumber]</body>

<body package="XPath" selector="xpathIsNumber">xpathIsNumber

	^true</body>

<body package="XPath" selector="xpathMayRequireNodeSetTopLevel">xpathMayRequireNodeSetTopLevel

	^true</body>

<body package="XPath" selector="xpathMayRequireSortTopLevel">xpathMayRequireSortTopLevel

	^true</body>
</methods>

<methods>
<class-id>XML.Document</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self root xpathStringData</body>
</methods>

<methods>
<class-id>XML.Text</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self characterData</body>
</methods>

<methods>
<class-id>XML.PI</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self text</body>
</methods>

<methods>
<class-id>XML.Element</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self characterData</body>
</methods>

<methods>
<class-id>Core.Boolean</class-id> <category>converting</category>

<body package="XPath" selector="xpathAsBoolean">xpathAsBoolean

	^self</body>

<body package="XPath" selector="xpathAsNumber">xpathAsNumber

	^self ifTrue: [1] ifFalse: [0]</body>

<body package="XPath" selector="xpathAsString">xpathAsString

	^self printString</body>
</methods>

<methods>
<class-id>Core.Boolean</class-id> <category>xml support</category>

<body package="XPath" selector="xpathCompareEquality:using:">xpathCompareEquality: aData using: aBlock

	aData isString
		ifTrue: [^aBlock value: self value: aData xpathAsBoolean].
	aData xpathIsNumber
		ifTrue: [^aBlock value: self value: aData xpathAsBoolean].
	aData xpathIsBoolean
		ifTrue: [^aBlock value: self value: aData xpathAsBoolean].
	aData xpathIsNodeSet
		ifTrue: [^aData unsortedNodes contains: [:nd2 |
					aBlock value: self value: nd2 xpathStringData xpathAsBoolean]].
	self error: ('Can''t compare a &lt;1s&gt; with a boolean' expandMacrosWith: aData class printString)</body>

<body package="XPath" selector="xpathCompareOrder:using:">xpathCompareOrder: aData using: aBlock

	| v |
	v := self xpathAsNumber.
	^aData xpathIsNodeSet
		ifTrue: [aData unsortedNodes contains: [:nd2 |
					aBlock value: v value: nd2 xpathStringData xpathAsNumber]]
		ifFalse: [aBlock value: v value: aData xpathAsNumber]</body>

<body package="XPath" selector="xpathIsBoolean">xpathIsBoolean

	^true</body>
</methods>

<methods>
<class-id>XML.Comment</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self text</body>
</methods>

<methods>
<class-id>Core.Object</class-id> <category>xml support</category>

<body package="XPath" selector="addToXPathHolder:for:">addToXPathHolder: anAssociation for: aNodeContext

	anAssociation value == nil
		ifTrue: [^anAssociation value: self].
	anAssociation value xpathIsNodeSet
		ifTrue: [^self error: 'An XPath expression is answering a combination of Nodes and non-Nodes'].
	self error: 'An XPath expression is answering more than one non-Node value'</body>

<body package="XPath" selector="xpathEvalIn:">xpathEvalIn: aNodeContext
	"This is private protocol--see #xpathValueIn: for the client protocol"

	^self</body>

<body package="XPath" selector="xpathIsBoolean">xpathIsBoolean

	^false</body>

<body package="XPath" selector="xpathIsNodeSet">xpathIsNodeSet

	^false</body>

<body package="XPath" selector="xpathIsNumber">xpathIsNumber

	^false</body>

<body package="XPath" selector="xpathMayRequireNodeSet">xpathMayRequireNodeSet

	^false</body>

<body package="XPath" selector="xpathMayRequireNodeSetTopLevel">xpathMayRequireNodeSetTopLevel

	^false</body>

<body package="XPath" selector="xpathMayRequireSort">xpathMayRequireSort

	^false</body>

<body package="XPath" selector="xpathMayRequireSortTopLevel">xpathMayRequireSortTopLevel

	^false</body>

<body package="XPath" selector="xpathUsedVarNames">xpathUsedVarNames

	^OrderedCollection new</body>

<body package="XPath" selector="xpathValueIn:">xpathValueIn: aNodeContext
	"This is public protocol--see #xpathEvalIn: for the private internal protocol"

	^self</body>
</methods>

<methods>
<class-id>XML.Node</class-id> <category>enumerating</category>

<body package="XPath" selector="addToXPathHolder:for:">addToXPathHolder: anAssociation for: aNodeContext

	anAssociation value == nil
		ifTrue: [^anAssociation value: (aNodeContext copy add: self)].
	anAssociation value xpathIsNodeSet
		ifTrue: [^anAssociation value add: self].
	self error: 'An XPath expression is answering a combination of Nodes and non-Nodes'</body>
</methods>

<methods>
<class-id>XML.Node</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self subclassResponsibility</body>
</methods>

<methods>
<class-id>XML.Attribute</class-id> <category>accessing</category>

<body package="XPath" selector="xpathStringData">xpathStringData

	^self characterData</body>
</methods>

<initialize>
<class-id>XML.XPathFunction</class-id>
</initialize>

<initialize>
<class-id>XML.XPathBinaryExpression</class-id>
</initialize>

<do-it>"Imported Classes:"</do-it>

<do-it>self error: 'Attempting to file-in parcel imports.  Choose terminate or close'</do-it>

<class>
<name>Object</name>
<environment>Core</environment>
<super></super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Kernel-Objects</category>
<attributes>
<package>Kernel-Objects</package>
</attributes>
</class>

<class>
<name>Node</name>
<environment>XML</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>parent flags userData </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>Document</name>
<environment>XML</environment>
<super>XML.Node</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>root nodes dtd ids </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>Text</name>
<environment>XML</environment>
<super>XML.Node</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>text stripped </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>Array</name>
<environment>Core</environment>
<super>Core.ArrayedCollection</super>
<private>false</private>
<indexed-type>objects</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Collections-Arrayed</category>
<attributes>
<package>Collections-Arrayed</package>
</attributes>
</class>

<class>
<name>Element</name>
<environment>XML</environment>
<super>XML.Node</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>tag attributes namespaces elements definition </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>String</name>
<environment>Core</environment>
<super>Core.CharacterArray</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Collections-Text</category>
<attributes>
<package>Collections-Text</package>
</attributes>
</class>

<class>
<name>Boolean</name>
<environment>Core</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Kernel-Objects</category>
<attributes>
<package>Kernel-Objects</package>
</attributes>
</class>

<class>
<name>ReadStream</name>
<environment>Core</environment>
<super>Core.InternalStream</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Collections-Streams</category>
<attributes>
<package>Collections-Streams</package>
</attributes>
</class>

<class>
<name>Number</name>
<environment>Core</environment>
<super>Core.ArithmeticValue</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Magnitude-Numbers</category>
<attributes>
<package>Magnitude-Numbers</package>
</attributes>
</class>

<class>
<name>Attribute</name>
<environment>XML</environment>
<super>XML.Node</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>name value </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>Comment</name>
<environment>XML</environment>
<super>XML.Node</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>text </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>PI</name>
<environment>XML</environment>
<super>XML.Node</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>name text </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>XML-Nodes</category>
<attributes>
<package>XML</package>
</attributes>
</class>

<class>
<name>GeneralParser</name>
<environment>Kernel</environment>
<super>Kernel.Scanner</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>requestor class targetClass failBlock correctionDelta stack backupStack reservedWords keywordMarker retryBlock </inst-vars>
<class-inst-vars>typeTable reservedWords hasKeywords methodInfo </class-inst-vars>
<imports></imports>
<category>AT-ParserCompiler</category>
<attributes>
<package>AT Parser Compiler</package>
</attributes>
</class>

</st-source>
