summaryrefslogtreecommitdiff
path: root/lib/rexml
diff options
context:
space:
mode:
authorser <ser@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-05-16 19:08:03 +0000
committerser <ser@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-05-16 19:08:03 +0000
commitabe1214b3dd57b3a24d722ba592146db8eaabd40 (patch)
tree5fbc86b061de7618c69e6469d18805f6b376cc1d /lib/rexml
parentdb0fac02665cb9e1e638959733e9572c845b4710 (diff)
Cross-ported the REXML changes (3.0.8) from the development branch to the
stable branch. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@6339 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rexml')
-rw-r--r--lib/rexml/attribute.rb6
-rw-r--r--lib/rexml/cdata.rb2
-rw-r--r--lib/rexml/doctype.rb20
-rw-r--r--lib/rexml/document.rb128
-rw-r--r--lib/rexml/element.rb18
-rw-r--r--lib/rexml/encodings/US-ASCII.rb2
-rw-r--r--lib/rexml/functions.rb728
-rw-r--r--lib/rexml/parseexception.rb82
-rw-r--r--lib/rexml/parsers/baseparser.rb2
-rw-r--r--lib/rexml/rexml.rb8
-rw-r--r--lib/rexml/text.rb4
-rw-r--r--lib/rexml/xpath_parser.rb993
12 files changed, 1024 insertions, 969 deletions
diff --git a/lib/rexml/attribute.rb b/lib/rexml/attribute.rb
index 90d7e66122..9eb3c211ea 100644
--- a/lib/rexml/attribute.rb
+++ b/lib/rexml/attribute.rb
@@ -146,6 +146,12 @@ module REXML
def node_type
:attribute
end
+
+ def inspect
+ rv = ""
+ write( rv )
+ rv
+ end
end
end
#vim:ts=2 sw=2 noexpandtab:
diff --git a/lib/rexml/cdata.rb b/lib/rexml/cdata.rb
index 402a0187ff..9e82376cd8 100644
--- a/lib/rexml/cdata.rb
+++ b/lib/rexml/cdata.rb
@@ -59,7 +59,7 @@ module REXML
# c = CData.new( " Some text " )
# c.write( $stdout ) #-> <![CDATA[ Some text ]]>
def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
- indent( output, indent )
+ indent( output, indent ) unless transitive
output << START
output << @string
output << STOP
diff --git a/lib/rexml/doctype.rb b/lib/rexml/doctype.rb
index b523155f8f..652a04fce2 100644
--- a/lib/rexml/doctype.rb
+++ b/lib/rexml/doctype.rb
@@ -32,11 +32,12 @@ module REXML
# # <!DOCTYPE foo '-//I/Hate/External/IDs'>
# dt = DocType.new( doctype_to_clone )
# # Incomplete. Shallow clone of doctype
- # source = Source.new( '<!DOCTYPE foo "bar">' )
- # dt = DocType.new( source )
- # # <!DOCTYPE foo "bar">
- # dt = DocType.new( source, some_document )
- # # Creates a doctype, and adds to the supplied document
+ #
+ # +Note+ that the constructor:
+ #
+ # Doctype.new( Source.new( "<!DOCTYPE foo 'bar'>" ) )
+ #
+ # is _deprecated_. Do not use it. It will probably disappear.
def initialize( first, parent=nil )
@entities = DEFAULT_ENTITIES
@long_name = @uri = nil
@@ -54,6 +55,15 @@ module REXML
@external_id = first[1]
@long_name = first[2]
@uri = first[3]
+ elsif first.kind_of? Source
+ super( parent )
+ parser = Parsers::BaseParser.new( first )
+ event = parser.pull
+ if event[0] == :start_doctype
+ @name, @external_id, @long_name, @uri, = event[1..-1]
+ end
+ else
+ super()
end
end
diff --git a/lib/rexml/document.rb b/lib/rexml/document.rb
index 52500f2afd..1378bb212c 100644
--- a/lib/rexml/document.rb
+++ b/lib/rexml/document.rb
@@ -176,68 +176,72 @@ module REXML
tag_stack = []
in_doctype = false
entities = nil
- while true
- event = parser.pull
- case event[0]
- when :end_document
- return
- when :start_element
- tag_stack.push(event[1])
- # find the observers for namespaces
- build_context = build_context.add_element( event[1], event[2] )
- when :end_element
- tag_stack.pop
- build_context = build_context.parent
- when :text
- if not in_doctype
- if build_context[-1].instance_of? Text
- build_context[-1] << event[1]
- else
- build_context.add(
- Text.new( event[1], build_context.whitespace, nil, true )
- ) unless (
- event[1].strip.size==0 and
- build_context.ignore_whitespace_nodes
- )
- end
- end
- when :comment
- c = Comment.new( event[1] )
- build_context.add( c )
- when :cdata
- c = CData.new( event[1] )
- build_context.add( c )
- when :processing_instruction
- build_context.add( Instruction.new( event[1], event[2] ) )
- when :end_doctype
- in_doctype = false
- entities.each { |k,v| entities[k] = build_context.entities[k].value }
- build_context = build_context.parent
- when :start_doctype
- doctype = DocType.new( event[1..-1], build_context )
- build_context = doctype
- entities = {}
- in_doctype = true
- when :attlistdecl
- n = AttlistDecl.new( event[1..-1] )
- build_context.add( n )
- when :externalentity
- n = ExternalEntity.new( event[1] )
- build_context.add( n )
- when :elementdecl
- n = ElementDecl.new( event[1] )
- build_context.add(n)
- when :entitydecl
- entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
- build_context.add(Entity.new(event))
- when :notationdecl
- n = NotationDecl.new( *event[1..-1] )
- build_context.add( n )
- when :xmldecl
- x = XMLDecl.new( event[1], event[2], event[3] )
- build_context.add( x )
- end
- end
+ begin
+ while true
+ event = parser.pull
+ case event[0]
+ when :end_document
+ return
+ when :start_element
+ tag_stack.push(event[1])
+ # find the observers for namespaces
+ build_context = build_context.add_element( event[1], event[2] )
+ when :end_element
+ tag_stack.pop
+ build_context = build_context.parent
+ when :text
+ if not in_doctype
+ if build_context[-1].instance_of? Text
+ build_context[-1] << event[1]
+ else
+ build_context.add(
+ Text.new( event[1], build_context.whitespace, nil, true )
+ ) unless (
+ event[1].strip.size==0 and
+ build_context.ignore_whitespace_nodes
+ )
+ end
+ end
+ when :comment
+ c = Comment.new( event[1] )
+ build_context.add( c )
+ when :cdata
+ c = CData.new( event[1] )
+ build_context.add( c )
+ when :processing_instruction
+ build_context.add( Instruction.new( event[1], event[2] ) )
+ when :end_doctype
+ in_doctype = false
+ entities.each { |k,v| entities[k] = build_context.entities[k].value }
+ build_context = build_context.parent
+ when :start_doctype
+ doctype = DocType.new( event[1..-1], build_context )
+ build_context = doctype
+ entities = {}
+ in_doctype = true
+ when :attlistdecl
+ n = AttlistDecl.new( event[1..-1] )
+ build_context.add( n )
+ when :externalentity
+ n = ExternalEntity.new( event[1] )
+ build_context.add( n )
+ when :elementdecl
+ n = ElementDecl.new( event[1] )
+ build_context.add(n)
+ when :entitydecl
+ entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
+ build_context.add(Entity.new(event))
+ when :notationdecl
+ n = NotationDecl.new( *event[1..-1] )
+ build_context.add( n )
+ when :xmldecl
+ x = XMLDecl.new( event[1], event[2], event[3] )
+ build_context.add( x )
+ end
+ end
+ rescue
+ raise ParseException.new( $!.message, parser.source, parser, $! )
+ end
end
end
end
diff --git a/lib/rexml/element.rb b/lib/rexml/element.rb
index 40cac168b9..ffc81bed91 100644
--- a/lib/rexml/element.rb
+++ b/lib/rexml/element.rb
@@ -67,6 +67,22 @@ module REXML
end
end
+ def inspect
+ rv = "<#@expanded_name"
+
+ @attributes.each_attribute do |attr|
+ rv << " "
+ attr.write( rv, 0 )
+ end unless @attributes.empty?
+
+ if children.size > 0
+ rv << " ... </>"
+ else
+ rv << "/>"
+ end
+ end
+
+
# Creates a shallow copy of self.
# d = Document.new "<a><b/><b/><c><d/></c></a>"
# new_a = d.root.clone
@@ -643,7 +659,7 @@ module REXML
end
writer << "/"
else
- if transitive and indent>-1 and !@children[0].kind_of? Text
+ if transitive and indent>-1 and !@children[0].instance_of? Text
writer << "\n"
indent writer, indent+1
end
diff --git a/lib/rexml/encodings/US-ASCII.rb b/lib/rexml/encodings/US-ASCII.rb
index f4e4527c2d..fe8f6df303 100644
--- a/lib/rexml/encodings/US-ASCII.rb
+++ b/lib/rexml/encodings/US-ASCII.rb
@@ -6,7 +6,7 @@ module REXML
array_utf8 = content.unpack('U*')
array_enc = []
array_utf8.each do |num|
- if num <= 0xFF
+ if num <= 0x7F
array_enc << num
else
# Numeric entity (&#nnnn;); shard by Stefan Scholl
diff --git a/lib/rexml/functions.rb b/lib/rexml/functions.rb
index d64ba7e378..9cbff99537 100644
--- a/lib/rexml/functions.rb
+++ b/lib/rexml/functions.rb
@@ -1,366 +1,372 @@
module REXML
- # If you add a method, keep in mind two things:
- # (1) the first argument will always be a list of nodes from which to
- # filter. In the case of context methods (such as position), the function
- # should return an array with a value for each child in the array.
- # (2) all method calls from XML will have "-" replaced with "_".
- # Therefore, in XML, "local-name()" is identical (and actually becomes)
- # "local_name()"
- module Functions
- @@node = nil
+ # If you add a method, keep in mind two things:
+ # (1) the first argument will always be a list of nodes from which to
+ # filter. In the case of context methods (such as position), the function
+ # should return an array with a value for each child in the array.
+ # (2) all method calls from XML will have "-" replaced with "_".
+ # Therefore, in XML, "local-name()" is identical (and actually becomes)
+ # "local_name()"
+ module Functions
+ @@node = nil
@@index = nil
@@size = nil
- @@variables = {}
- @@namespace_context = {}
-
- def Functions::node=(value); @@node = value; end
- def Functions::index=(value); @@index = value; end
- def Functions::size=(value); @@size = value; end
- def Functions::variables=(value); @@variables = value; end
- def Functions::namespace_context=(value)
- @@namespace_context = value
- end
- def Functions::node; @@node; end
- def Functions::index; @@index; end
- def Functions::size; @@size; end
- def Functions::variables; @@variables; end
- def Functions::namespace_context; @@namespace_context; end
-
- def Functions::text( )
- if @@node.node_type == :element
- return @@node.text
- elsif @@node.node_type == :text
- return @@node.value
- else
- return false
- end
- end
-
- def Functions::last( )
- @@size
- end
-
- def Functions::position( )
- @@index
- end
-
- def Functions::count( node_set )
- node_set.size
- end
-
- # Since REXML is non-validating, this method is not implemented as it
- # requires a DTD
- def Functions::id( object )
- end
-
- # UNTESTED
- def Functions::local_name( node_set=nil )
- get_namespace( node_set ) do |node|
- return node.local_name
- end
- end
-
- def Functions::namespace_uri( node_set=nil )
- get_namespace( node_set ) {|node| node.namespace}
- end
-
- def Functions::name( node_set=nil )
- get_namespace( node_set ) do |node|
- node.expanded_name
- end
- end
-
- # Helper method.
- def Functions::get_namespace( node_set = nil )
- if node_set == nil
- yield @@node if defined? @@node.namespace
- else
- if node_set.namespace
- yield node_set
- else
- return unless node_set.kind_of? Enumerable
- node_set.each { |node| yield node if defined? node.namespace }
- end
- end
- end
-
- # A node-set is converted to a string by returning the string-value of the
- # node in the node-set that is first in document order. If the node-set is
- # empty, an empty string is returned.
- #
- # A number is converted to a string as follows
- #
- # NaN is converted to the string NaN
- #
- # positive zero is converted to the string 0
- #
- # negative zero is converted to the string 0
- #
- # positive infinity is converted to the string Infinity
- #
- # negative infinity is converted to the string -Infinity
- #
- # if the number is an integer, the number is represented in decimal form
- # as a Number with no decimal point and no leading zeros, preceded by a
- # minus sign (-) if the number is negative
- #
- # otherwise, the number is represented in decimal form as a Number
- # including a decimal point with at least one digit before the decimal
- # point and at least one digit after the decimal point, preceded by a
- # minus sign (-) if the number is negative; there must be no leading zeros
- # before the decimal point apart possibly from the one required digit
- # immediately before the decimal point; beyond the one required digit
- # after the decimal point there must be as many, but only as many, more
- # digits as are needed to uniquely distinguish the number from all other
- # IEEE 754 numeric values.
- #
- # The boolean false value is converted to the string false. The boolean
- # true value is converted to the string true.
- #
- # An object of a type other than the four basic types is converted to a
- # string in a way that is dependent on that type.
- def Functions::string( object=nil )
- #object = @context unless object
- if object.instance_of? Array
- string( object[0] )
- elsif defined? object.node_type
- if object.node_type == :attribute
- object.value
- elsif object.node_type == :element
- object.text
- else
- object.to_s
- end
- else
- object.to_s
- end
- end
-
- # UNTESTED
- def Functions::concat( *objects )
- objects.join
- end
-
- # Fixed by Mike Stok
- def Functions::starts_with( string, test )
- string(string).index(string(test)) == 0
- end
-
- # Fixed by Mike Stok
- def Functions::contains( string, test )
- string(string).include? string(test)
- end
-
- # Kouhei fixed this
- def Functions::substring_before( string, test )
- ruby_string = string(string)
- ruby_index = ruby_string.index(string(test))
- if ruby_index.nil?
- ""
- else
- ruby_string[ 0...ruby_index ]
- end
- end
+ @@variables = {}
+ @@namespace_context = {}
+
+ def Functions::node=(value); @@node = value; end
+ def Functions::index=(value); @@index = value; end
+ def Functions::size=(value); @@size = value; end
+ def Functions::variables=(value); @@variables = value; end
+ def Functions::namespace_context=(value)
+ @@namespace_context = value
+ end
+ def Functions::node; @@node; end
+ def Functions::index; @@index; end
+ def Functions::size; @@size; end
+ def Functions::variables; @@variables; end
+ def Functions::namespace_context; @@namespace_context; end
+
+ def Functions::text( )
+ if @@node.node_type == :element
+ return @@node.text
+ elsif @@node.node_type == :text
+ return @@node.value
+ else
+ return false
+ end
+ end
+
+ def Functions::last( )
+ @@size
+ end
+
+ def Functions::position( )
+ @@index
+ end
+
+ def Functions::count( node_set )
+ node_set.size
+ end
+
+ # Since REXML is non-validating, this method is not implemented as it
+ # requires a DTD
+ def Functions::id( object )
+ end
+
+ # UNTESTED
+ def Functions::local_name( node_set=nil )
+ get_namespace( node_set ) do |node|
+ return node.local_name
+ end
+ end
+
+ def Functions::namespace_uri( node_set=nil )
+ get_namespace( node_set ) {|node| node.namespace}
+ end
+
+ def Functions::name( node_set=nil )
+ get_namespace( node_set ) do |node|
+ node.expanded_name
+ end
+ end
+
+ # Helper method.
+ def Functions::get_namespace( node_set = nil )
+ if node_set == nil
+ yield @@node if defined? @@node.namespace
+ else
+ if node_set.namespace
+ yield node_set
+ else
+ return unless node_set.kind_of? Enumerable
+ node_set.each { |node| yield node if defined? node.namespace }
+ end
+ end
+ end
+
+ # A node-set is converted to a string by returning the string-value of the
+ # node in the node-set that is first in document order. If the node-set is
+ # empty, an empty string is returned.
+ #
+ # A number is converted to a string as follows
+ #
+ # NaN is converted to the string NaN
+ #
+ # positive zero is converted to the string 0
+ #
+ # negative zero is converted to the string 0
+ #
+ # positive infinity is converted to the string Infinity
+ #
+ # negative infinity is converted to the string -Infinity
+ #
+ # if the number is an integer, the number is represented in decimal form
+ # as a Number with no decimal point and no leading zeros, preceded by a
+ # minus sign (-) if the number is negative
+ #
+ # otherwise, the number is represented in decimal form as a Number
+ # including a decimal point with at least one digit before the decimal
+ # point and at least one digit after the decimal point, preceded by a
+ # minus sign (-) if the number is negative; there must be no leading zeros
+ # before the decimal point apart possibly from the one required digit
+ # immediately before the decimal point; beyond the one required digit
+ # after the decimal point there must be as many, but only as many, more
+ # digits as are needed to uniquely distinguish the number from all other
+ # IEEE 754 numeric values.
+ #
+ # The boolean false value is converted to the string false. The boolean
+ # true value is converted to the string true.
+ #
+ # An object of a type other than the four basic types is converted to a
+ # string in a way that is dependent on that type.
+ def Functions::string( object=nil )
+ #object = @context unless object
+ if object.instance_of? Array
+ string( object[0] )
+ elsif defined? object.node_type
+ if object.node_type == :attribute
+ object.value
+ elsif object.node_type == :element
+ object.text
+ else
+ object.to_s
+ end
+ else
+ object.to_s
+ end
+ end
+
+ # UNTESTED
+ def Functions::concat( *objects )
+ objects.join
+ end
+
+ # Fixed by Mike Stok
+ def Functions::starts_with( string, test )
+ string(string).index(string(test)) == 0
+ end
+
+ # Fixed by Mike Stok
+ def Functions::contains( string, test )
+ string(string).include? string(test)
+ end
+
+ # Kouhei fixed this
+ def Functions::substring_before( string, test )
+ ruby_string = string(string)
+ ruby_index = ruby_string.index(string(test))
+ if ruby_index.nil?
+ ""
+ else
+ ruby_string[ 0...ruby_index ]
+ end
+ end
- # Kouhei fixed this too
- def Functions::substring_after( string, test )
- ruby_string = string(string)
- ruby_index = ruby_string.index(string(test))
- if ruby_index.nil?
- ""
- else
- ruby_string[ ruby_index+1..-1 ]
- end
- end
-
- # Take equal portions of Mike Stok and Sean Russell; mix
- # vigorously, and pour into a tall, chilled glass. Serves 10,000.
- def Functions::substring( string, start, length=nil )
- ruby_string = string(string)
- ruby_length = if length.nil?
- ruby_string.length.to_f
- else
- number(length)
- end
- ruby_start = number(start)
-
- # Handle the special cases
- return '' if (
- ruby_length.nan? or
- ruby_start.nan? or
- ruby_start.infinite?
- )
-
- infinite_length = ruby_length.infinite? == 1
- ruby_length = ruby_string.length if infinite_length
-
- # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds
- # are 0..length. Therefore, we have to offset the bounds by one.
- ruby_start = ruby_start.round - 1
- ruby_length = ruby_length.round
-
- if ruby_start < 0
- ruby_length += ruby_start unless infinite_length
- ruby_start = 0
- end
- return '' if ruby_length <= 0
- ruby_string[ruby_start,ruby_length]
- end
-
- # UNTESTED
- def Functions::string_length( string )
- string(string).length
- end
-
- # UNTESTED
- def Functions::normalize_space( string=nil )
- string = string(@@node) if string.nil?
- if string.kind_of? Array
- string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string}
- else
- string.to_s.strip.gsub(/\s+/um, ' ')
- end
- end
-
- # This is entirely Mike Stok's beast
- def Functions::translate( string, tr1, tr2 )
- from = string(tr1)
- to = string(tr2)
-
- # the map is our translation table.
- #
- # if a character occurs more than once in the
- # from string then we ignore the second &
- # subsequent mappings
- #
- # if a charactcer maps to nil then we delete it
- # in the output. This happens if the from
- # string is longer than the to string
- #
- # there's nothing about - or ^ being special in
- # http://www.w3.org/TR/xpath#function-translate
- # so we don't build ranges or negated classes
-
- map = Hash.new
- 0.upto(from.length - 1) { |pos|
- from_char = from[pos]
- unless map.has_key? from_char
- map[from_char] =
- if pos < to.length
- to[pos]
- else
- nil
- end
- end
- }
-
- string(string).unpack('U*').collect { |c|
- if map.has_key? c then map[c] else c end
- }.compact.pack('U*')
- end
-
- # UNTESTED
- def Functions::boolean( object=nil )
- if object.kind_of? String
- if object =~ /\d+/u
- return object.to_f != 0
- else
- return object.size > 0
- end
- elsif object.kind_of? Array
- object = object.find{|x| x and true}
- end
- return object ? true : false
- end
-
- # UNTESTED
- def Functions::not( object )
- not boolean( object )
- end
-
- # UNTESTED
- def Functions::true( )
- true
- end
-
- # UNTESTED
- def Functions::false( )
- false
- end
-
- # UNTESTED
- def Functions::lang( language )
- lang = false
- node = @@node
- attr = nil
- until node.nil?
- if node.node_type == :element
- attr = node.attributes["xml:lang"]
- unless attr.nil?
- lang = compare_language(string(language), attr)
- break
- else
- end
- end
- node = node.parent
- end
- lang
- end
-
- def Functions::compare_language lang1, lang2
- lang2.downcase.index(lang1.downcase) == 0
- end
-
- # a string that consists of optional whitespace followed by an optional
- # minus sign followed by a Number followed by whitespace is converted to
- # the IEEE 754 number that is nearest (according to the IEEE 754
- # round-to-nearest rule) to the mathematical value represented by the
- # string; any other string is converted to NaN
- #
- # boolean true is converted to 1; boolean false is converted to 0
- #
- # a node-set is first converted to a string as if by a call to the string
- # function and then converted in the same way as a string argument
- #
- # an object of a type other than the four basic types is converted to a
- # number in a way that is dependent on that type
- def Functions::number( object=nil )
- object = @@node unless object
- if object == true
- Float(1)
- elsif object == false
- Float(0)
- elsif object.kind_of? Array
- string( object ).to_f
- elsif object.kind_of? Float
- object
- else
- object.to_s.to_f
- end
- end
-
- def Functions::sum( nodes )
- end
-
- def Functions::floor( number )
- number(number).floor
- end
-
- def Functions::ceiling( number )
- number(number).ceil
- end
-
- def Functions::round( number )
- begin
- number(number).round
- rescue FloatDomainError
- number(number)
- end
- end
-
- def Functions::method_missing( id )
- puts "METHOD MISSING #{id.id2name}"
- XPath.match( @@node, id.id2name )
- end
- end
+ # Kouhei fixed this too
+ def Functions::substring_after( string, test )
+ ruby_string = string(string)
+ ruby_index = ruby_string.index(string(test))
+ if ruby_index.nil?
+ ""
+ else
+ ruby_string[ ruby_index+1..-1 ]
+ end
+ end
+
+ # Take equal portions of Mike Stok and Sean Russell; mix
+ # vigorously, and pour into a tall, chilled glass. Serves 10,000.
+ def Functions::substring( string, start, length=nil )
+ ruby_string = string(string)
+ ruby_length = if length.nil?
+ ruby_string.length.to_f
+ else
+ number(length)
+ end
+ ruby_start = number(start)
+
+ # Handle the special cases
+ return '' if (
+ ruby_length.nan? or
+ ruby_start.nan? or
+ ruby_start.infinite?
+ )
+
+ infinite_length = ruby_length.infinite? == 1
+ ruby_length = ruby_string.length if infinite_length
+
+ # Now, get the bounds. The XPath bounds are 1..length; the ruby bounds
+ # are 0..length. Therefore, we have to offset the bounds by one.
+ ruby_start = ruby_start.round - 1
+ ruby_length = ruby_length.round
+
+ if ruby_start < 0
+ ruby_length += ruby_start unless infinite_length
+ ruby_start = 0
+ end
+ return '' if ruby_length <= 0
+ ruby_string[ruby_start,ruby_length]
+ end
+
+ # UNTESTED
+ def Functions::string_length( string )
+ string(string).length
+ end
+
+ # UNTESTED
+ def Functions::normalize_space( string=nil )
+ string = string(@@node) if string.nil?
+ if string.kind_of? Array
+ string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string}
+ else
+ string.to_s.strip.gsub(/\s+/um, ' ')
+ end
+ end
+
+ # This is entirely Mike Stok's beast
+ def Functions::translate( string, tr1, tr2 )
+ from = string(tr1)
+ to = string(tr2)
+
+ # the map is our translation table.
+ #
+ # if a character occurs more than once in the
+ # from string then we ignore the second &
+ # subsequent mappings
+ #
+ # if a charactcer maps to nil then we delete it
+ # in the output. This happens if the from
+ # string is longer than the to string
+ #
+ # there's nothing about - or ^ being special in
+ # http://www.w3.org/TR/xpath#function-translate
+ # so we don't build ranges or negated classes
+
+ map = Hash.new
+ 0.upto(from.length - 1) { |pos|
+ from_char = from[pos]
+ unless map.has_key? from_char
+ map[from_char] =
+ if pos < to.length
+ to[pos]
+ else
+ nil
+ end
+ end
+ }
+
+ string(string).unpack('U*').collect { |c|
+ if map.has_key? c then map[c] else c end
+ }.compact.pack('U*')
+ end
+
+ # UNTESTED
+ def Functions::boolean( object=nil )
+ if object.kind_of? String
+ if object =~ /\d+/u
+ return object.to_f != 0
+ else
+ return object.size > 0
+ end
+ elsif object.kind_of? Array
+ object = object.find{|x| x and true}
+ end
+ return object ? true : false
+ end
+
+ # UNTESTED
+ def Functions::not( object )
+ not boolean( object )
+ end
+
+ # UNTESTED
+ def Functions::true( )
+ true
+ end
+
+ # UNTESTED
+ def Functions::false( )
+ false
+ end
+
+ # UNTESTED
+ def Functions::lang( language )
+ lang = false
+ node = @@node
+ attr = nil
+ until node.nil?
+ if node.node_type == :element
+ attr = node.attributes["xml:lang"]
+ unless attr.nil?
+ lang = compare_language(string(language), attr)
+ break
+ else
+ end
+ end
+ node = node.parent
+ end
+ lang
+ end
+
+ def Functions::compare_language lang1, lang2
+ lang2.downcase.index(lang1.downcase) == 0
+ end
+
+ # a string that consists of optional whitespace followed by an optional
+ # minus sign followed by a Number followed by whitespace is converted to
+ # the IEEE 754 number that is nearest (according to the IEEE 754
+ # round-to-nearest rule) to the mathematical value represented by the
+ # string; any other string is converted to NaN
+ #
+ # boolean true is converted to 1; boolean false is converted to 0
+ #
+ # a node-set is first converted to a string as if by a call to the string
+ # function and then converted in the same way as a string argument
+ #
+ # an object of a type other than the four basic types is converted to a
+ # number in a way that is dependent on that type
+ def Functions::number( object=nil )
+ object = @@node unless object
+ if object == true
+ Float(1)
+ elsif object == false
+ Float(0)
+ elsif object.kind_of? Array
+ number(string( object ))
+ elsif object.kind_of? Float
+ object
+ else
+ str = string( object )
+ #puts "STRING OF #{object.inspect} = #{str}"
+ if str =~ /^\d+/
+ object.to_s.to_f
+ else
+ (0.0 / 0.0)
+ end
+ end
+ end
+
+ def Functions::sum( nodes )
+ end
+
+ def Functions::floor( number )
+ number(number).floor
+ end
+
+ def Functions::ceiling( number )
+ number(number).ceil
+ end
+
+ def Functions::round( number )
+ begin
+ number(number).round
+ rescue FloatDomainError
+ number(number)
+ end
+ end
+
+ def Functions::method_missing( id )
+ puts "METHOD MISSING #{id.id2name}"
+ XPath.match( @@node, id.id2name )
+ end
+ end
end
diff --git a/lib/rexml/parseexception.rb b/lib/rexml/parseexception.rb
index 66a4214548..feb7a7e638 100644
--- a/lib/rexml/parseexception.rb
+++ b/lib/rexml/parseexception.rb
@@ -1,49 +1,51 @@
module REXML
- class ParseException < RuntimeError
- attr_accessor :source, :parser, :continued_exception
+ class ParseException < RuntimeError
+ attr_accessor :source, :parser, :continued_exception
- def initialize( message, source=nil, parser=nil, exception=nil )
- super(message)
- @source = source
- @parser = parser
- @continued_exception = exception
- end
+ def initialize( message, source=nil, parser=nil, exception=nil )
+ super(message)
+ @source = source
+ @parser = parser
+ @continued_exception = exception
+ end
- def to_s
- # Quote the original exception, if there was one
- if @continued_exception
- err = @continued_exception.inspect
- err << "\n"
- err << @continued_exception.backtrace.join("\n")
- err << "\n...\n"
- else
- err = ""
- end
+ def to_s
+ # Quote the original exception, if there was one
+ if @continued_exception
+ err = @continued_exception.inspect
+ err << "\n"
+ err << @continued_exception.backtrace.join("\n")
+ err << "\n...\n"
+ else
+ err = ""
+ end
- # Get the stack trace and error message
- err << super
+ # Get the stack trace and error message
+ err << super
- # Add contextual information
- if @source
- err << "\nLine: #{line}\n"
- err << "Position: #{position}\n"
- err << "Last 80 unconsumed characters:\n"
- err << @source.buffer[0..80].gsub(/\n/, ' ')
- end
-
- err
- end
+ # Add contextual information
+ if @source
+ err << "\nLine: #{line}\n"
+ err << "Position: #{position}\n"
+ err << "Last 80 unconsumed characters:\n"
+ err << @source.buffer[0..80].gsub(/\n/, ' ')
+ end
+
+ err
+ end
- def position
- @source.current_line[0] if @source and @source.current_line
- end
+ def position
+ @source.current_line[0] if @source and defined? @source.current_line and
+ @source.current_line
+ end
- def line
- @source.current_line[2] if @source and @source.current_line
- end
+ def line
+ @source.current_line[2] if @source and defined? @source.current_line and
+ @source.current_line
+ end
- def context
- @source.current_line
- end
- end
+ def context
+ @source.current_line
+ end
+ end
end
diff --git a/lib/rexml/parsers/baseparser.rb b/lib/rexml/parsers/baseparser.rb
index 92033a9c2b..fbb1ec06a8 100644
--- a/lib/rexml/parsers/baseparser.rb
+++ b/lib/rexml/parsers/baseparser.rb
@@ -100,6 +100,8 @@ module REXML
self.stream = source
end
+ attr_reader :source
+
def stream=( source )
if source.kind_of? String
@source = Source.new(source)
diff --git a/lib/rexml/rexml.rb b/lib/rexml/rexml.rb
index dcae04e954..bcce9a9711 100644
--- a/lib/rexml/rexml.rb
+++ b/lib/rexml/rexml.rb
@@ -10,8 +10,8 @@
#
# Main page:: http://www.germane-software.com/software/rexml
# Author:: Sean Russell <serATgermaneHYPHENsoftwareDOTcom>
-# Version:: 3.0.4
-# Date:: +2004/115
+# Version:: @ANT_VERSION@
+# Date:: @ANT_DATE@
#
# This API documentation can be downloaded from the REXML home page, or can
# be accessed online[http://www.germane-software.com/software/rexml_doc]
@@ -21,6 +21,6 @@
# online[http://www.germane-software.com/software/rexml/docs/tutorial.html]
module REXML
Copyright = "Copyright © 2001, 2002, 2003, 2004 Sean Russell <ser@germane-software.com>"
- Date = "+2004/115"
- Version = "3.0.4"
+ Date = "@ANT_DATE@"
+ Version = "@ANT_VERSION@"
end
diff --git a/lib/rexml/text.rb b/lib/rexml/text.rb
index 1d2d2dd711..388256ac81 100644
--- a/lib/rexml/text.rb
+++ b/lib/rexml/text.rb
@@ -194,7 +194,7 @@ module REXML
@raw = false
end
- def indent(string, level=1, style="\t", indentfirstline=true)
+ def indent_text(string, level=1, style="\t", indentfirstline=true)
return string if level < 0
new_string = ''
string.each { |line|
@@ -211,7 +211,7 @@ module REXML
if not (@parent and @parent.whitespace) then
s = wrap(s, 60, false) if @parent and @parent.context[:wordwrap] == :all
if @parent and not @parent.context[:indentstyle].nil? and indent > 0 and s.count("\n") > 0
- s = indent(s, indent, @parent.context[:indentstyle], false)
+ s = indent_text(s, indent, @parent.context[:indentstyle], false)
end
s.squeeze!(" \n\t") if @parent and !@parent.whitespace
end
diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb
index 215104c2c5..8aadb8ef86 100644
--- a/lib/rexml/xpath_parser.rb
+++ b/lib/rexml/xpath_parser.rb
@@ -3,309 +3,308 @@ require 'rexml/xmltokens'
require 'rexml/parsers/xpathparser'
module REXML
- # You don't want to use this class. Really. Use XPath, which is a wrapper
- # for this class. Believe me. You don't want to poke around in here.
- # There is strange, dark magic at work in this code. Beware. Go back! Go
- # back while you still can!
- class XPathParser
- include XMLTokens
- LITERAL = /^'([^']*)'|^"([^"]*)"/u
-
- def initialize( )
- @parser = REXML::Parsers::XPathParser.new
- @namespaces = {}
- @variables = {}
- end
-
- def namespaces=( namespaces={} )
- Functions::namespace_context = namespaces
- @namespaces = namespaces
- end
-
- def variables=( vars={} )
- Functions::variables = vars
- @variables = vars
- end
-
- def parse path, nodeset
- path_stack = @parser.parse( path )
+ # You don't want to use this class. Really. Use XPath, which is a wrapper
+ # for this class. Believe me. You don't want to poke around in here.
+ # There is strange, dark magic at work in this code. Beware. Go back! Go
+ # back while you still can!
+ class XPathParser
+ include XMLTokens
+ LITERAL = /^'([^']*)'|^"([^"]*)"/u
+
+ def initialize( )
+ @parser = REXML::Parsers::XPathParser.new
+ @namespaces = {}
+ @variables = {}
+ end
+
+ def namespaces=( namespaces={} )
+ Functions::namespace_context = namespaces
+ @namespaces = namespaces
+ end
+
+ def variables=( vars={} )
+ Functions::variables = vars
+ @variables = vars
+ end
+
+ def parse path, nodeset
+ path_stack = @parser.parse( path )
#puts "PARSE: #{path} => #{path_stack.inspect}"
#puts "PARSE: nodeset = #{nodeset.collect{|x|x.to_s}.inspect}"
- match( path_stack, nodeset )
- end
-
- def predicate path, nodeset
- path_stack = @parser.predicate( path )
- return Predicate( path_stack, nodeset )
- end
-
- def []=( variable_name, value )
- @variables[ variable_name ] = value
- end
-
- private
-
- def match( path_stack, nodeset )
- while ( path_stack.size > 0 and nodeset.size > 0 )
- #puts "PARSE: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
- nodeset = internal_parse( path_stack, nodeset )
- #puts "NODESET: #{nodeset.size}"
- #puts "PATH_STACK: #{path_stack.inspect}"
- end
- nodeset
- end
-
- def internal_parse path_stack, nodeset
+ match( path_stack, nodeset )
+ end
+
+ def predicate path, nodeset
+ path_stack = @parser.predicate( path )
+ return Predicate( path_stack, nodeset )
+ end
+
+ def []=( variable_name, value )
+ @variables[ variable_name ] = value
+ end
+
+ private
+
+ def match( path_stack, nodeset )
+ while ( path_stack.size > 0 and nodeset.size > 0 )
+ #puts "PARSE: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
+ nodeset = internal_parse( path_stack, nodeset )
+ #puts "NODESET: #{nodeset}"
+ #puts "PATH_STACK: #{path_stack.inspect}"
+ end
+ nodeset
+ end
+
+ def internal_parse path_stack, nodeset
#puts "INTERNAL_PARSE RETURNING WITH NO RESULTS" if nodeset.size == 0 or path_stack.size == 0
- return nodeset if nodeset.size == 0 or path_stack.size == 0
- #puts "INTERNAL_PARSE: #{path_stack.inspect}, #{nodeset.collect{|n| n.class}.inspect}"
- case path_stack.shift
- when :document
- return [ nodeset[0].root.parent ]
-
- when :qname
- prefix = path_stack.shift
- name = path_stack.shift
- #puts "QNAME #{prefix}#{prefix.size>0?':':''}#{name}"
- n = nodeset.clone
- ns = @namespaces[prefix]
- ns = ns ? ns : ''
- n.delete_if do |node|
- # FIXME: This DOUBLES the time XPath searches take
- ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
- #puts "NODE: '#{node.to_s}'; node.has_name?( #{name.inspect}, #{ns.inspect} ): #{ node.has_name?( name, ns )}; node.namespace() = #{node.namespace().inspect}; node.prefix = #{node.prefix().inspect}" if node.node_type == :element
- !(node.node_type == :element and node.name == name and node.namespace == ns )
- end
- return n
-
- when :any
- n = nodeset.clone
- n.delete_if { |node| node.node_type != :element }
- return n
-
- when :self
- # THIS SPACE LEFT INTENTIONALLY BLANK
-
- when :processing_instruction
- target = path_stack.shift
- n = nodeset.clone
- n.delete_if do |node|
- (node.node_type != :processing_instruction) or
- ( !target.nil? and ( node.target != target ) )
- end
- return n
-
- when :text
- #puts ":TEXT"
- n = nodeset.clone
- n.delete_if do |node|
- #puts "#{node} :: #{node.node_type}"
- node.node_type != :text
- end
- return n
-
- when :comment
- n = nodeset.clone
- n.delete_if do |node|
- node.node_type != :comment
- end
- return n
-
- when :node
- return nodeset
-
- # FIXME: I suspect the following XPath will fail:
- # /a/*/*[1]
- when :child
- #puts "CHILD"
- new_nodeset = []
- nt = nil
- for node in nodeset
- nt = node.node_type
- new_nodeset += node.children if nt == :element or nt == :document
- end
- #path_stack[0,(path_stack.size-ps_clone.size)] = []
- return new_nodeset
-
- when :literal
- literal = path_stack.shift
- if literal =~ /^\d+(\.\d+)?$/
- return ($1 ? literal.to_f : literal.to_i)
- end
- #puts "RETURNING '#{literal}'"
- return literal
-
- when :attribute
- #puts ":ATTRIBUTE"
- new_nodeset = []
- case path_stack.shift
- when :qname
- prefix = path_stack.shift
- name = path_stack.shift
- for element in nodeset
- if element.node_type == :element
- #puts element.name
- #puts "looking for attribute #{name} in '#{@namespaces[prefix]}'"
- attr = element.attribute( name, @namespaces[prefix] )
- #puts ":ATTRIBUTE: attr => #{attr}"
- new_nodeset << attr if attr
- end
- end
- when :any
- for element in nodeset
- if element.node_type == :element
- attr = element.attributes
- end
- end
- end
- #puts "RETURNING #{new_nodeset.collect{|n|n.to_s}.inspect}"
- return new_nodeset
-
- when :parent
- return internal_parse( path_stack, nodeset.collect{|n| n.parent}.compact )
-
- when :ancestor
- #puts "ANCESTOR"
- new_nodeset = []
- for node in nodeset
- while node.parent
- node = node.parent
- new_nodeset << node unless new_nodeset.include? node
- end
- end
- #nodeset = new_nodeset.uniq
- return new_nodeset
-
- when :ancestor_or_self
- new_nodeset = []
- for node in nodeset
- if node.node_type == :element
- new_nodeset << node
- while ( node.parent )
- node = node.parent
- new_nodeset << node unless new_nodeset.includes? node
- end
- end
- end
- #nodeset = new_nodeset.uniq
- return new_nodeset
-
- when :predicate
- #puts "@"*80
- #puts "NODESET = #{nodeset.collect{|n|n.to_s}.inspect}"
- predicate = path_stack.shift
- new_nodeset = []
- Functions::size = nodeset.size
- nodeset.size.times do |index|
- node = nodeset[index]
- Functions::node = node
- Functions::index = index+1
- #puts "Node #{node} and index=#{index+1}"
- result = Predicate( predicate, node )
- #puts "Predicate returned #{result} (#{result.class}) for #{node.class}"
- if result.kind_of? Numeric
- #puts "#{result} == #{index} => #{result == index}"
- new_nodeset << node if result == (index+1)
- elsif result.instance_of? Array
- new_nodeset << node if result.size > 0
- else
- new_nodeset << node if result
- end
- end
- #puts "Nodeset after predicate #{predicate.inspect} has #{new_nodeset.size} nodes"
- #puts "NODESET: #{new_nodeset.collect{|n|n.to_s}.inspect}"
- return new_nodeset
-
- when :descendant_or_self
- rv = descendant_or_self( path_stack, nodeset )
- path_stack.clear
- return rv
-
- when :descendant
- #puts ":DESCENDANT"
- results = []
- nt = nil
- for node in nodeset
- nt = node.node_type
- results += internal_parse( path_stack.clone.unshift( :descendant_or_self ),
- node.children ) if nt == :element or nt == :document
- end
- return results
-
- when :following_sibling
- results = []
- for node in nodeset
- all_siblings = node.parent.children
- current_index = all_siblings.index( node )
- following_siblings = all_siblings[ current_index+1 .. -1 ]
- results += internal_parse( path_stack.clone, following_siblings )
- end
- return results
-
- when :preceding_sibling
- results = []
- for node in nodeset
- all_siblings = node.parent.children
- current_index = all_siblings.index( node )
- preceding_siblings = all_siblings[ 0 .. current_index-1 ]
- results += internal_parse( path_stack.clone, preceding_siblings )
- end
- return results
-
- when :preceding
- new_nodeset = []
- for node in nodeset
- new_nodeset += preceding( node )
- end
- return new_nodeset
-
- when :following
- new_nodeset = []
- for node in nodeset
- new_nodeset += following( node )
- end
- return new_nodeset
-
- when :namespace
- new_set = []
- for node in nodeset
- new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
- end
- return new_nodeset
-
- when :variable
- var_name = path_stack.shift
- return @variables[ var_name ]
-
- end
- nodeset
- end
-
- ##########################################################
+ return nodeset if nodeset.size == 0 or path_stack.size == 0
+ #puts "INTERNAL_PARSE: #{path_stack.inspect}, #{nodeset.collect{|n| n.class}.inspect}"
+ case path_stack.shift
+ when :document
+ return [ nodeset[0].root.parent ]
+
+ when :qname
+ prefix = path_stack.shift
+ name = path_stack.shift
+ #puts "QNAME #{prefix}#{prefix.size>0?':':''}#{name}"
+ n = nodeset.clone
+ ns = @namespaces[prefix]
+ ns = ns ? ns : ''
+ n.delete_if do |node|
+ # FIXME: This DOUBLES the time XPath searches take
+ ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
+ #puts "NODE: '#{node.to_s}'; node.has_name?( #{name.inspect}, #{ns.inspect} ): #{ node.has_name?( name, ns )}; node.namespace() = #{node.namespace().inspect}; node.prefix = #{node.prefix().inspect}" if node.node_type == :element
+ !(node.node_type == :element and node.name == name and node.namespace == ns )
+ end
+ return n
+
+ when :any
+ n = nodeset.clone
+ n.delete_if { |node| node.node_type != :element }
+ return n
+
+ when :self
+ # THIS SPACE LEFT INTENTIONALLY BLANK
+
+ when :processing_instruction
+ target = path_stack.shift
+ n = nodeset.clone
+ n.delete_if do |node|
+ (node.node_type != :processing_instruction) or
+ ( !target.nil? and ( node.target != target ) )
+ end
+ return n
+
+ when :text
+ #puts ":TEXT"
+ n = nodeset.clone
+ n.delete_if do |node|
+ #puts "#{node} :: #{node.node_type}"
+ node.node_type != :text
+ end
+ return n
+
+ when :comment
+ n = nodeset.clone
+ n.delete_if do |node|
+ node.node_type != :comment
+ end
+ return n
+
+ when :node
+ return nodeset
+
+ # FIXME: I suspect the following XPath will fail:
+ # /a/*/*[1]
+ when :child
+ #puts "CHILD"
+ new_nodeset = []
+ nt = nil
+ for node in nodeset
+ nt = node.node_type
+ new_nodeset += node.children if nt == :element or nt == :document
+ end
+ #path_stack[0,(path_stack.size-ps_clone.size)] = []
+ return new_nodeset
+
+ when :literal
+ literal = path_stack.shift
+ if literal =~ /^\d+(\.\d+)?$/
+ return ($1 ? literal.to_f : literal.to_i)
+ end
+ #puts "RETURNING '#{literal}'"
+ return literal
+
+ when :attribute
+ new_nodeset = []
+ case path_stack.shift
+ when :qname
+ prefix = path_stack.shift
+ name = path_stack.shift
+ for element in nodeset
+ if element.node_type == :element
+ #puts element.name
+ #puts "looking for attribute #{name} in '#{@namespaces[prefix]}'"
+ attr = element.attribute( name, @namespaces[prefix] )
+ #puts ":ATTRIBUTE: attr => #{attr}"
+ new_nodeset << attr if attr
+ end
+ end
+ when :any
+ for element in nodeset
+ if element.node_type == :element
+ attr = element.attributes
+ end
+ end
+ end
+ #puts "RETURNING #{new_nodeset.collect{|n|n.to_s}.inspect}"
+ return new_nodeset
+
+ when :parent
+ return internal_parse( path_stack, nodeset.collect{|n| n.parent}.compact )
+
+ when :ancestor
+ #puts "ANCESTOR"
+ new_nodeset = []
+ for node in nodeset
+ while node.parent
+ node = node.parent
+ new_nodeset << node unless new_nodeset.include? node
+ end
+ end
+ #nodeset = new_nodeset.uniq
+ return new_nodeset
+
+ when :ancestor_or_self
+ new_nodeset = []
+ for node in nodeset
+ if node.node_type == :element
+ new_nodeset << node
+ while ( node.parent )
+ node = node.parent
+ new_nodeset << node unless new_nodeset.includes? node
+ end
+ end
+ end
+ #nodeset = new_nodeset.uniq
+ return new_nodeset
+
+ when :predicate
+ #puts "@"*80
+ #puts "NODESET = #{nodeset.collect{|n|n.to_s}.inspect}"
+ predicate = path_stack.shift
+ new_nodeset = []
+ Functions::size = nodeset.size
+ nodeset.size.times do |index|
+ node = nodeset[index]
+ Functions::node = node
+ Functions::index = index+1
+ #puts "Node #{node} and index=#{index+1}"
+ result = Predicate( predicate, node )
+ #puts "Predicate returned #{result} (#{result.class}) for #{node.class}"
+ if result.kind_of? Numeric
+ #puts "#{result} == #{index} => #{result == index}"
+ new_nodeset << node if result == (index+1)
+ elsif result.instance_of? Array
+ new_nodeset << node if result.size > 0
+ else
+ new_nodeset << node if result
+ end
+ end
+ #puts "Nodeset after predicate #{predicate.inspect} has #{new_nodeset.size} nodes"
+ #puts "NODESET: #{new_nodeset.collect{|n|n.to_s}.inspect}"
+ return new_nodeset
+
+ when :descendant_or_self
+ rv = descendant_or_self( path_stack, nodeset )
+ path_stack.clear
+ return rv
+
+ when :descendant
+ #puts ":DESCENDANT"
+ results = []
+ nt = nil
+ for node in nodeset
+ nt = node.node_type
+ results += internal_parse( path_stack.clone.unshift( :descendant_or_self ),
+ node.children ) if nt == :element or nt == :document
+ end
+ return results
+
+ when :following_sibling
+ results = []
+ for node in nodeset
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ following_siblings = all_siblings[ current_index+1 .. -1 ]
+ results += internal_parse( path_stack.clone, following_siblings )
+ end
+ return results
+
+ when :preceding_sibling
+ results = []
+ for node in nodeset
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ preceding_siblings = all_siblings[ 0 .. current_index-1 ]
+ results += internal_parse( path_stack.clone, preceding_siblings )
+ end
+ return results
+
+ when :preceding
+ new_nodeset = []
+ for node in nodeset
+ new_nodeset += preceding( node )
+ end
+ return new_nodeset
+
+ when :following
+ new_nodeset = []
+ for node in nodeset
+ new_nodeset += following( node )
+ end
+ return new_nodeset
+
+ when :namespace
+ new_set = []
+ for node in nodeset
+ new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
+ end
+ return new_nodeset
+
+ when :variable
+ var_name = path_stack.shift
+ return @variables[ var_name ]
+
+ end
+ nodeset
+ end
+
+ ##########################################################
# FIXME
- # The next two methods are BAD MOJO!
- # This is my achilles heel. If anybody thinks of a better
- # way of doing this, be my guest. This really sucks, but
- # it took me three days to get it to work at all.
- # ########################################################
-
- def descendant_or_self( path_stack, nodeset )
- rs = []
- d_o_s( path_stack, nodeset, rs )
+ # The next two methods are BAD MOJO!
+ # This is my achilles heel. If anybody thinks of a better
+ # way of doing this, be my guest. This really sucks, but
+ # it took me three days to get it to work at all.
+ # ########################################################
+
+ def descendant_or_self( path_stack, nodeset )
+ rs = []
+ d_o_s( path_stack, nodeset, rs )
#puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
document_order(rs.flatten.compact)
- end
-
- def d_o_s( p, ns, r )
- nt = nil
- ns.each_index do |i|
- n = ns[i]
- x = match( p.clone, [ n ] )
- nt = n.node_type
- d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
+ end
+
+ def d_o_s( p, ns, r )
+ nt = nil
+ ns.each_index do |i|
+ n = ns[i]
+ x = match( p.clone, [ n ] )
+ nt = n.node_type
+ d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
r.concat(x) if x.size > 0
- end
- end
+ end
+ end
# Reorders an array of nodes so that they are in document order
@@ -327,221 +326,231 @@ module REXML
def recurse( nodeset, &block )
for node in nodeset
- yield node
+ yield node
recurse( node, &block ) if node.node_type == :element
end
end
- # Given a predicate, a node, and a context, evaluates to true or false.
- def Predicate( predicate, node )
- predicate = predicate.clone
- #puts "#"*20
- #puts "Predicate( #{predicate.inspect}, #{node.class} )"
- results = []
- case (predicate[0])
- when :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
- eq = predicate.shift
- left = Predicate( predicate.shift, node )
- right = Predicate( predicate.shift, node )
- return equality_relational_compare( left, eq, right )
-
- when :div, :mod, :mult, :plus, :minus
- op = predicate.shift
- left = Predicate( predicate.shift, node )
- right = Predicate( predicate.shift, node )
- left = Functions::number( left )
- right = Functions::number( right )
- case op
- when :div
- return left.to_f / right.to_f
- when :mod
- return left % right
- when :mult
- return left * right
- when :plus
- return left + right
- when :minus
- return left - right
- end
+ # Given a predicate, a node, and a context, evaluates to true or false.
+ def Predicate( predicate, node )
+ predicate = predicate.clone
+ #puts "#"*20
+ #puts "Predicate( #{predicate.inspect}, #{node.class} )"
+ results = []
+ case (predicate[0])
+ when :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
+ eq = predicate.shift
+ left = Predicate( predicate.shift, node )
+ right = Predicate( predicate.shift, node )
+ #puts "LEFT = #{left.inspect}"
+ #puts "RIGHT = #{right.inspect}"
+ return equality_relational_compare( left, eq, right )
+
+ when :div, :mod, :mult, :plus, :minus
+ op = predicate.shift
+ left = Predicate( predicate.shift, node )
+ right = Predicate( predicate.shift, node )
+ #puts "LEFT = #{left.inspect}"
+ #puts "RIGHT = #{right.inspect}"
+ left = Functions::number( left )
+ right = Functions::number( right )
+ #puts "LEFT = #{left.inspect}"
+ #puts "RIGHT = #{right.inspect}"
+ case op
+ when :div
+ return left.to_f / right.to_f
+ when :mod
+ return left % right
+ when :mult
+ return left * right
+ when :plus
+ return left + right
+ when :minus
+ return left - right
+ end
when :union
predicate.shift
- left = Predicate( predicate.shift, node )
- right = Predicate( predicate.shift, node )
+ left = Predicate( predicate.shift, node )
+ right = Predicate( predicate.shift, node )
return (left | right)
- when :neg
- predicate.shift
- operand = Functions::number(Predicate( predicate, node ))
- return -operand
-
- when :not
- predicate.shift
- return !Predicate( predicate.shift, node )
-
- when :function
- predicate.shift
- func_name = predicate.shift.tr('-', '_')
- arguments = predicate.shift
- #puts "\nFUNCTION: #{func_name}"
- #puts "ARGUMENTS: #{arguments.inspect} #{node.to_s}"
- args = arguments.collect { |arg| Predicate( arg, node ) }
- #puts "FUNCTION: #{func_name}( #{args.collect{|n|n.to_s}.inspect} )"
- result = Functions.send( func_name, *args )
- #puts "RESULTS: #{result.inspect}"
- return result
-
- else
- return match( predicate, [ node ] )
-
- end
- end
-
- # Builds a nodeset of all of the following nodes of the supplied node,
- # in document order
- def following( node )
- all_siblings = node.parent.children
- current_index = all_siblings.index( node )
- following_siblings = all_siblings[ current_index+1 .. -1 ]
- following = []
- recurse( following_siblings ) { |node| following << node }
- following.shift
- #puts "following is returning #{puta following}"
- following
- end
-
- # Builds a nodeset of all of the preceding nodes of the supplied node,
- # in reverse document order
- def preceding( node )
- all_siblings = node.parent.children
- current_index = all_siblings.index( node )
- preceding_siblings = all_siblings[ 0 .. current_index-1 ]
-
- preceding_siblings.reverse!
- preceding = []
- recurse( preceding_siblings ) { |node| preceding << node }
- preceding.reverse
- end
-
- def equality_relational_compare( set1, op, set2 )
+ when :neg
+ predicate.shift
+ operand = Functions::number(Predicate( predicate, node ))
+ return -operand
+
+ when :not
+ predicate.shift
+ return !Predicate( predicate.shift, node )
+
+ when :function
+ predicate.shift
+ func_name = predicate.shift.tr('-', '_')
+ arguments = predicate.shift
+ #puts "\nFUNCTION: #{func_name}"
+ #puts "ARGUMENTS: #{arguments.inspect} #{node.to_s}"
+ args = arguments.collect { |arg| Predicate( arg, node ) }
+ #puts "FUNCTION: #{func_name}( #{args.collect{|n|n.to_s}.inspect} )"
+ result = Functions.send( func_name, *args )
+ #puts "RESULTS: #{result.inspect}"
+ return result
+
+ else
+ return match( predicate, [ node ] )
+
+ end
+ end
+
+ # Builds a nodeset of all of the following nodes of the supplied node,
+ # in document order
+ def following( node )
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ following_siblings = all_siblings[ current_index+1 .. -1 ]
+ following = []
+ recurse( following_siblings ) { |node| following << node }
+ following.shift
+ #puts "following is returning #{puta following}"
+ following
+ end
+
+ # Builds a nodeset of all of the preceding nodes of the supplied node,
+ # in reverse document order
+ def preceding( node )
+ all_siblings = node.parent.children
+ current_index = all_siblings.index( node )
+ preceding_siblings = all_siblings[ 0 .. current_index-1 ]
+
+ preceding_siblings.reverse!
+ preceding = []
+ recurse( preceding_siblings ) { |node| preceding << node }
+ preceding.reverse
+ end
+
+ def equality_relational_compare( set1, op, set2 )
#puts "EQ_REL_COMP: #{set1.to_s}, #{op}, #{set2.to_s}"
#puts "#{set1.class.name} #{op} #{set2.class.name}"
- if set1.kind_of? Array and set2.kind_of? Array
+ if set1.kind_of? Array and set2.kind_of? Array
#puts "#{set1.size} & #{set2.size}"
- if set1.size == 1 and set2.size == 1
- set1 = set1[0]
- set2 = set2[0]
+ if set1.size == 1 and set2.size == 1
+ set1 = set1[0]
+ set2 = set2[0]
elsif set1.size == 0 or set2.size == 0
nd = set1.size==0 ? set2 : set1
nd.each { |il| return true if compare( il, op, nil ) }
- else
- set1.each do |i1|
- i1 = i1.to_s
- set2.each do |i2|
- i2 = i2.to_s
- return true if compare( i1, op, i2 )
- end
- end
- return false
- end
- end
+ else
+ set1.each do |i1|
+ i1 = i1.to_s
+ set2.each do |i2|
+ i2 = i2.to_s
+ return true if compare( i1, op, i2 )
+ end
+ end
+ return false
+ end
+ end
#puts "COMPARING VALUES"
- # If one is nodeset and other is number, compare number to each item
- # in nodeset s.t. number op number(string(item))
- # If one is nodeset and other is string, compare string to each item
- # in nodeset s.t. string op string(item)
- # If one is nodeset and other is boolean, compare boolean to each item
- # in nodeset s.t. boolean op boolean(item)
- if set1.kind_of? Array or set2.kind_of? Array
+ # If one is nodeset and other is number, compare number to each item
+ # in nodeset s.t. number op number(string(item))
+ # If one is nodeset and other is string, compare string to each item
+ # in nodeset s.t. string op string(item)
+ # If one is nodeset and other is boolean, compare boolean to each item
+ # in nodeset s.t. boolean op boolean(item)
+ if set1.kind_of? Array or set2.kind_of? Array
#puts "ISA ARRAY"
- if set1.kind_of? Array
- a = set1
- b = set2.to_s
- else
- a = set2
- b = set1.to_s
- end
-
- case b
- when 'true', 'false'
- b = Functions::boolean( b )
- for v in a
- v = Functions::boolean(v)
- return true if compare( v, op, b )
- end
- when /^\d+(\.\d+)?$/
- b = Functions::number( b )
- for v in a
- v = Functions::number(v)
- return true if compare( v, op, b )
- end
- else
- b = Functions::string( b )
- for v in a
- v = Functions::string(v)
- return true if compare( v, op, b )
- end
- end
- else
- # If neither is nodeset,
- # If op is = or !=
- # If either boolean, convert to boolean
- # If either number, convert to number
- # Else, convert to string
- # Else
- # Convert both to numbers and compare
- s1 = set1.to_s
- s2 = set2.to_s
- #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
- if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
- #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
- #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
- set1 = Functions::boolean( set1 )
- set2 = Functions::boolean( set2 )
- else
- if op == :eq or op == :neq
- if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
- set1 = Functions::number( s1 )
- set2 = Functions::number( s2 )
- else
- set1 = Functions::string( set1 )
- set2 = Functions::string( set2 )
- end
- else
- set1 = Functions::number( set1 )
- set2 = Functions::number( set2 )
- end
- end
- #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
+ if set1.kind_of? Array
+ a = set1
+ b = set2.to_s
+ else
+ a = set2
+ b = set1.to_s
+ end
+
+ case b
+ when 'true', 'false'
+ b = Functions::boolean( b )
+ for v in a
+ v = Functions::boolean(v)
+ return true if compare( v, op, b )
+ end
+ when /^\d+(\.\d+)?$/
+ b = Functions::number( b )
+ #puts "B = #{b.inspect}"
+ for v in a
+ #puts "v = #{v.inspect}"
+ v = Functions::number(v)
+ #puts "v = #{v.inspect}"
+ #puts compare(v,op,b)
+ return true if compare( v, op, b )
+ end
+ else
+ b = Functions::string( b )
+ for v in a
+ v = Functions::string(v)
+ return true if compare( v, op, b )
+ end
+ end
+ else
+ # If neither is nodeset,
+ # If op is = or !=
+ # If either boolean, convert to boolean
+ # If either number, convert to number
+ # Else, convert to string
+ # Else
+ # Convert both to numbers and compare
+ s1 = set1.to_s
+ s2 = set2.to_s
+ #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
+ if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
+ #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
+ #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
+ set1 = Functions::boolean( set1 )
+ set2 = Functions::boolean( set2 )
+ else
+ if op == :eq or op == :neq
+ if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
+ set1 = Functions::number( s1 )
+ set2 = Functions::number( s2 )
+ else
+ set1 = Functions::string( set1 )
+ set2 = Functions::string( set2 )
+ end
+ else
+ set1 = Functions::number( set1 )
+ set2 = Functions::number( set2 )
+ end
+ end
+ #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
#puts ">>> #{compare( set1, op, set2 )}"
- return compare( set1, op, set2 )
- end
- return false
- end
+ return compare( set1, op, set2 )
+ end
+ return false
+ end
- def compare a, op, b
+ def compare a, op, b
#puts "COMPARE #{a.to_s} #{op} #{b.to_s}"
- case op
- when :eq
- a == b
- when :neq
- a != b
- when :lt
- a < b
- when :lteq
- a <= b
- when :gt
- a > b
- when :gteq
- a >= b
- when :and
- a and b
- when :or
- a or b
- else
- false
- end
- end
- end
+ case op
+ when :eq
+ a == b
+ when :neq
+ a != b
+ when :lt
+ a < b
+ when :lteq
+ a <= b
+ when :gt
+ a > b
+ when :gteq
+ a >= b
+ when :and
+ a and b
+ when :or
+ a or b
+ else
+ false
+ end
+ end
+ end
end