1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

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
This commit is contained in:
ser 2004-05-16 19:08:03 +00:00
parent db0fac0266
commit abe1214b3d
12 changed files with 946 additions and 891 deletions

View file

@ -146,6 +146,12 @@ module REXML
def node_type def node_type
:attribute :attribute
end end
def inspect
rv = ""
write( rv )
rv
end
end end
end end
#vim:ts=2 sw=2 noexpandtab: #vim:ts=2 sw=2 noexpandtab:

View file

@ -59,7 +59,7 @@ module REXML
# c = CData.new( " Some text " ) # c = CData.new( " Some text " )
# c.write( $stdout ) #-> <![CDATA[ Some text ]]> # c.write( $stdout ) #-> <![CDATA[ Some text ]]>
def write( output=$stdout, indent=-1, transitive=false, ie_hack=false ) def write( output=$stdout, indent=-1, transitive=false, ie_hack=false )
indent( output, indent ) indent( output, indent ) unless transitive
output << START output << START
output << @string output << @string
output << STOP output << STOP

View file

@ -32,11 +32,12 @@ module REXML
# # <!DOCTYPE foo '-//I/Hate/External/IDs'> # # <!DOCTYPE foo '-//I/Hate/External/IDs'>
# dt = DocType.new( doctype_to_clone ) # dt = DocType.new( doctype_to_clone )
# # Incomplete. Shallow clone of doctype # # Incomplete. Shallow clone of doctype
# source = Source.new( '<!DOCTYPE foo "bar">' ) #
# dt = DocType.new( source ) # +Note+ that the constructor:
# # <!DOCTYPE foo "bar"> #
# dt = DocType.new( source, some_document ) # Doctype.new( Source.new( "<!DOCTYPE foo 'bar'>" ) )
# # Creates a doctype, and adds to the supplied document #
# is _deprecated_. Do not use it. It will probably disappear.
def initialize( first, parent=nil ) def initialize( first, parent=nil )
@entities = DEFAULT_ENTITIES @entities = DEFAULT_ENTITIES
@long_name = @uri = nil @long_name = @uri = nil
@ -54,6 +55,15 @@ module REXML
@external_id = first[1] @external_id = first[1]
@long_name = first[2] @long_name = first[2]
@uri = first[3] @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
end end

View file

@ -176,68 +176,72 @@ module REXML
tag_stack = [] tag_stack = []
in_doctype = false in_doctype = false
entities = nil entities = nil
while true begin
event = parser.pull while true
case event[0] event = parser.pull
when :end_document case event[0]
return when :end_document
when :start_element return
tag_stack.push(event[1]) when :start_element
# find the observers for namespaces tag_stack.push(event[1])
build_context = build_context.add_element( event[1], event[2] ) # find the observers for namespaces
when :end_element build_context = build_context.add_element( event[1], event[2] )
tag_stack.pop when :end_element
build_context = build_context.parent tag_stack.pop
when :text build_context = build_context.parent
if not in_doctype when :text
if build_context[-1].instance_of? Text if not in_doctype
build_context[-1] << event[1] if build_context[-1].instance_of? Text
else build_context[-1] << event[1]
build_context.add( else
Text.new( event[1], build_context.whitespace, nil, true ) build_context.add(
) unless ( Text.new( event[1], build_context.whitespace, nil, true )
event[1].strip.size==0 and ) unless (
build_context.ignore_whitespace_nodes event[1].strip.size==0 and
) build_context.ignore_whitespace_nodes
end )
end end
when :comment end
c = Comment.new( event[1] ) when :comment
build_context.add( c ) c = Comment.new( event[1] )
when :cdata build_context.add( c )
c = CData.new( event[1] ) when :cdata
build_context.add( c ) c = CData.new( event[1] )
when :processing_instruction build_context.add( c )
build_context.add( Instruction.new( event[1], event[2] ) ) when :processing_instruction
when :end_doctype build_context.add( Instruction.new( event[1], event[2] ) )
in_doctype = false when :end_doctype
entities.each { |k,v| entities[k] = build_context.entities[k].value } in_doctype = false
build_context = build_context.parent entities.each { |k,v| entities[k] = build_context.entities[k].value }
when :start_doctype build_context = build_context.parent
doctype = DocType.new( event[1..-1], build_context ) when :start_doctype
build_context = doctype doctype = DocType.new( event[1..-1], build_context )
entities = {} build_context = doctype
in_doctype = true entities = {}
when :attlistdecl in_doctype = true
n = AttlistDecl.new( event[1..-1] ) when :attlistdecl
build_context.add( n ) n = AttlistDecl.new( event[1..-1] )
when :externalentity build_context.add( n )
n = ExternalEntity.new( event[1] ) when :externalentity
build_context.add( n ) n = ExternalEntity.new( event[1] )
when :elementdecl build_context.add( n )
n = ElementDecl.new( event[1] ) when :elementdecl
build_context.add(n) n = ElementDecl.new( event[1] )
when :entitydecl build_context.add(n)
entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/ when :entitydecl
build_context.add(Entity.new(event)) entities[ event[1] ] = event[2] unless event[2] =~ /PUBLIC|SYSTEM/
when :notationdecl build_context.add(Entity.new(event))
n = NotationDecl.new( *event[1..-1] ) when :notationdecl
build_context.add( n ) n = NotationDecl.new( *event[1..-1] )
when :xmldecl build_context.add( n )
x = XMLDecl.new( event[1], event[2], event[3] ) when :xmldecl
build_context.add( x ) x = XMLDecl.new( event[1], event[2], event[3] )
end build_context.add( x )
end end
end
rescue
raise ParseException.new( $!.message, parser.source, parser, $! )
end
end end
end end
end end

View file

@ -67,6 +67,22 @@ module REXML
end end
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. # Creates a shallow copy of self.
# d = Document.new "<a><b/><b/><c><d/></c></a>" # d = Document.new "<a><b/><b/><c><d/></c></a>"
# new_a = d.root.clone # new_a = d.root.clone
@ -643,7 +659,7 @@ module REXML
end end
writer << "/" writer << "/"
else 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" writer << "\n"
indent writer, indent+1 indent writer, indent+1
end end

View file

@ -6,7 +6,7 @@ module REXML
array_utf8 = content.unpack('U*') array_utf8 = content.unpack('U*')
array_enc = [] array_enc = []
array_utf8.each do |num| array_utf8.each do |num|
if num <= 0xFF if num <= 0x7F
array_enc << num array_enc << num
else else
# Numeric entity (&#nnnn;); shard by Stefan Scholl # Numeric entity (&#nnnn;); shard by Stefan Scholl

View file

@ -1,366 +1,372 @@
module REXML module REXML
# If you add a method, keep in mind two things: # If you add a method, keep in mind two things:
# (1) the first argument will always be a list of nodes from which to # (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 # 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. # should return an array with a value for each child in the array.
# (2) all method calls from XML will have "-" replaced with "_". # (2) all method calls from XML will have "-" replaced with "_".
# Therefore, in XML, "local-name()" is identical (and actually becomes) # Therefore, in XML, "local-name()" is identical (and actually becomes)
# "local_name()" # "local_name()"
module Functions module Functions
@@node = nil @@node = nil
@@index = nil @@index = nil
@@size = nil @@size = nil
@@variables = {} @@variables = {}
@@namespace_context = {} @@namespace_context = {}
def Functions::node=(value); @@node = value; end def Functions::node=(value); @@node = value; end
def Functions::index=(value); @@index = value; end def Functions::index=(value); @@index = value; end
def Functions::size=(value); @@size = value; end def Functions::size=(value); @@size = value; end
def Functions::variables=(value); @@variables = value; end def Functions::variables=(value); @@variables = value; end
def Functions::namespace_context=(value) def Functions::namespace_context=(value)
@@namespace_context = value @@namespace_context = value
end end
def Functions::node; @@node; end def Functions::node; @@node; end
def Functions::index; @@index; end def Functions::index; @@index; end
def Functions::size; @@size; end def Functions::size; @@size; end
def Functions::variables; @@variables; end def Functions::variables; @@variables; end
def Functions::namespace_context; @@namespace_context; end def Functions::namespace_context; @@namespace_context; end
def Functions::text( ) def Functions::text( )
if @@node.node_type == :element if @@node.node_type == :element
return @@node.text return @@node.text
elsif @@node.node_type == :text elsif @@node.node_type == :text
return @@node.value return @@node.value
else else
return false return false
end end
end end
def Functions::last( ) def Functions::last( )
@@size @@size
end end
def Functions::position( ) def Functions::position( )
@@index @@index
end end
def Functions::count( node_set ) def Functions::count( node_set )
node_set.size node_set.size
end end
# Since REXML is non-validating, this method is not implemented as it # Since REXML is non-validating, this method is not implemented as it
# requires a DTD # requires a DTD
def Functions::id( object ) def Functions::id( object )
end end
# UNTESTED # UNTESTED
def Functions::local_name( node_set=nil ) def Functions::local_name( node_set=nil )
get_namespace( node_set ) do |node| get_namespace( node_set ) do |node|
return node.local_name return node.local_name
end end
end end
def Functions::namespace_uri( node_set=nil ) def Functions::namespace_uri( node_set=nil )
get_namespace( node_set ) {|node| node.namespace} get_namespace( node_set ) {|node| node.namespace}
end end
def Functions::name( node_set=nil ) def Functions::name( node_set=nil )
get_namespace( node_set ) do |node| get_namespace( node_set ) do |node|
node.expanded_name node.expanded_name
end end
end end
# Helper method. # Helper method.
def Functions::get_namespace( node_set = nil ) def Functions::get_namespace( node_set = nil )
if node_set == nil if node_set == nil
yield @@node if defined? @@node.namespace yield @@node if defined? @@node.namespace
else else
if node_set.namespace if node_set.namespace
yield node_set yield node_set
else else
return unless node_set.kind_of? Enumerable return unless node_set.kind_of? Enumerable
node_set.each { |node| yield node if defined? node.namespace } node_set.each { |node| yield node if defined? node.namespace }
end end
end end
end end
# A node-set is converted to a string by returning the string-value of the # 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 # node in the node-set that is first in document order. If the node-set is
# empty, an empty string is returned. # empty, an empty string is returned.
# #
# A number is converted to a string as follows # A number is converted to a string as follows
# #
# NaN is converted to the string NaN # NaN is converted to the string NaN
# #
# positive zero is converted to the string 0 # positive zero is converted to the string 0
# #
# negative zero is converted to the string 0 # negative zero is converted to the string 0
# #
# positive infinity is converted to the string Infinity # positive infinity is converted to the string Infinity
# #
# negative 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 # 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 # as a Number with no decimal point and no leading zeros, preceded by a
# minus sign (-) if the number is negative # minus sign (-) if the number is negative
# #
# otherwise, the number is represented in decimal form as a Number # otherwise, the number is represented in decimal form as a Number
# including a decimal point with at least one digit before the decimal # 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 # 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 # minus sign (-) if the number is negative; there must be no leading zeros
# before the decimal point apart possibly from the one required digit # before the decimal point apart possibly from the one required digit
# immediately before the decimal point; beyond 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 # 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 # digits as are needed to uniquely distinguish the number from all other
# IEEE 754 numeric values. # IEEE 754 numeric values.
# #
# The boolean false value is converted to the string false. The boolean # The boolean false value is converted to the string false. The boolean
# true value is converted to the string true. # true value is converted to the string true.
# #
# An object of a type other than the four basic types is converted to a # 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. # string in a way that is dependent on that type.
def Functions::string( object=nil ) def Functions::string( object=nil )
#object = @context unless object #object = @context unless object
if object.instance_of? Array if object.instance_of? Array
string( object[0] ) string( object[0] )
elsif defined? object.node_type elsif defined? object.node_type
if object.node_type == :attribute if object.node_type == :attribute
object.value object.value
elsif object.node_type == :element elsif object.node_type == :element
object.text object.text
else else
object.to_s object.to_s
end end
else else
object.to_s object.to_s
end end
end end
# UNTESTED # UNTESTED
def Functions::concat( *objects ) def Functions::concat( *objects )
objects.join objects.join
end end
# Fixed by Mike Stok # Fixed by Mike Stok
def Functions::starts_with( string, test ) def Functions::starts_with( string, test )
string(string).index(string(test)) == 0 string(string).index(string(test)) == 0
end end
# Fixed by Mike Stok # Fixed by Mike Stok
def Functions::contains( string, test ) def Functions::contains( string, test )
string(string).include? string(test) string(string).include? string(test)
end end
# Kouhei fixed this # Kouhei fixed this
def Functions::substring_before( string, test ) def Functions::substring_before( string, test )
ruby_string = string(string) ruby_string = string(string)
ruby_index = ruby_string.index(string(test)) ruby_index = ruby_string.index(string(test))
if ruby_index.nil? if ruby_index.nil?
"" ""
else else
ruby_string[ 0...ruby_index ] ruby_string[ 0...ruby_index ]
end end
end end
# Kouhei fixed this too # Kouhei fixed this too
def Functions::substring_after( string, test ) def Functions::substring_after( string, test )
ruby_string = string(string) ruby_string = string(string)
ruby_index = ruby_string.index(string(test)) ruby_index = ruby_string.index(string(test))
if ruby_index.nil? if ruby_index.nil?
"" ""
else else
ruby_string[ ruby_index+1..-1 ] ruby_string[ ruby_index+1..-1 ]
end end
end end
# Take equal portions of Mike Stok and Sean Russell; mix # Take equal portions of Mike Stok and Sean Russell; mix
# vigorously, and pour into a tall, chilled glass. Serves 10,000. # vigorously, and pour into a tall, chilled glass. Serves 10,000.
def Functions::substring( string, start, length=nil ) def Functions::substring( string, start, length=nil )
ruby_string = string(string) ruby_string = string(string)
ruby_length = if length.nil? ruby_length = if length.nil?
ruby_string.length.to_f ruby_string.length.to_f
else else
number(length) number(length)
end end
ruby_start = number(start) ruby_start = number(start)
# Handle the special cases # Handle the special cases
return '' if ( return '' if (
ruby_length.nan? or ruby_length.nan? or
ruby_start.nan? or ruby_start.nan? or
ruby_start.infinite? ruby_start.infinite?
) )
infinite_length = ruby_length.infinite? == 1 infinite_length = ruby_length.infinite? == 1
ruby_length = ruby_string.length if infinite_length ruby_length = ruby_string.length if infinite_length
# Now, get the bounds. The XPath bounds are 1..length; the ruby bounds # 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. # are 0..length. Therefore, we have to offset the bounds by one.
ruby_start = ruby_start.round - 1 ruby_start = ruby_start.round - 1
ruby_length = ruby_length.round ruby_length = ruby_length.round
if ruby_start < 0 if ruby_start < 0
ruby_length += ruby_start unless infinite_length ruby_length += ruby_start unless infinite_length
ruby_start = 0 ruby_start = 0
end end
return '' if ruby_length <= 0 return '' if ruby_length <= 0
ruby_string[ruby_start,ruby_length] ruby_string[ruby_start,ruby_length]
end end
# UNTESTED # UNTESTED
def Functions::string_length( string ) def Functions::string_length( string )
string(string).length string(string).length
end end
# UNTESTED # UNTESTED
def Functions::normalize_space( string=nil ) def Functions::normalize_space( string=nil )
string = string(@@node) if string.nil? string = string(@@node) if string.nil?
if string.kind_of? Array if string.kind_of? Array
string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string} string.collect{|x| string.to_s.strip.gsub(/\s+/um, ' ') if string}
else else
string.to_s.strip.gsub(/\s+/um, ' ') string.to_s.strip.gsub(/\s+/um, ' ')
end end
end end
# This is entirely Mike Stok's beast # This is entirely Mike Stok's beast
def Functions::translate( string, tr1, tr2 ) def Functions::translate( string, tr1, tr2 )
from = string(tr1) from = string(tr1)
to = string(tr2) to = string(tr2)
# the map is our translation table. # the map is our translation table.
# #
# if a character occurs more than once in the # if a character occurs more than once in the
# from string then we ignore the second & # from string then we ignore the second &
# subsequent mappings # subsequent mappings
# #
# if a charactcer maps to nil then we delete it # if a charactcer maps to nil then we delete it
# in the output. This happens if the from # in the output. This happens if the from
# string is longer than the to string # string is longer than the to string
# #
# there's nothing about - or ^ being special in # there's nothing about - or ^ being special in
# http://www.w3.org/TR/xpath#function-translate # http://www.w3.org/TR/xpath#function-translate
# so we don't build ranges or negated classes # so we don't build ranges or negated classes
map = Hash.new map = Hash.new
0.upto(from.length - 1) { |pos| 0.upto(from.length - 1) { |pos|
from_char = from[pos] from_char = from[pos]
unless map.has_key? from_char unless map.has_key? from_char
map[from_char] = map[from_char] =
if pos < to.length if pos < to.length
to[pos] to[pos]
else else
nil nil
end end
end end
} }
string(string).unpack('U*').collect { |c| string(string).unpack('U*').collect { |c|
if map.has_key? c then map[c] else c end if map.has_key? c then map[c] else c end
}.compact.pack('U*') }.compact.pack('U*')
end end
# UNTESTED # UNTESTED
def Functions::boolean( object=nil ) def Functions::boolean( object=nil )
if object.kind_of? String if object.kind_of? String
if object =~ /\d+/u if object =~ /\d+/u
return object.to_f != 0 return object.to_f != 0
else else
return object.size > 0 return object.size > 0
end end
elsif object.kind_of? Array elsif object.kind_of? Array
object = object.find{|x| x and true} object = object.find{|x| x and true}
end end
return object ? true : false return object ? true : false
end end
# UNTESTED # UNTESTED
def Functions::not( object ) def Functions::not( object )
not boolean( object ) not boolean( object )
end end
# UNTESTED # UNTESTED
def Functions::true( ) def Functions::true( )
true true
end end
# UNTESTED # UNTESTED
def Functions::false( ) def Functions::false( )
false false
end end
# UNTESTED # UNTESTED
def Functions::lang( language ) def Functions::lang( language )
lang = false lang = false
node = @@node node = @@node
attr = nil attr = nil
until node.nil? until node.nil?
if node.node_type == :element if node.node_type == :element
attr = node.attributes["xml:lang"] attr = node.attributes["xml:lang"]
unless attr.nil? unless attr.nil?
lang = compare_language(string(language), attr) lang = compare_language(string(language), attr)
break break
else else
end end
end end
node = node.parent node = node.parent
end end
lang lang
end end
def Functions::compare_language lang1, lang2 def Functions::compare_language lang1, lang2
lang2.downcase.index(lang1.downcase) == 0 lang2.downcase.index(lang1.downcase) == 0
end end
# a string that consists of optional whitespace followed by an optional # a string that consists of optional whitespace followed by an optional
# minus sign followed by a Number followed by whitespace is converted to # minus sign followed by a Number followed by whitespace is converted to
# the IEEE 754 number that is nearest (according to the IEEE 754 # the IEEE 754 number that is nearest (according to the IEEE 754
# round-to-nearest rule) to the mathematical value represented by the # round-to-nearest rule) to the mathematical value represented by the
# string; any other string is converted to NaN # string; any other string is converted to NaN
# #
# boolean true is converted to 1; boolean false is converted to 0 # 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 # 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 # 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 # 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 # number in a way that is dependent on that type
def Functions::number( object=nil ) def Functions::number( object=nil )
object = @@node unless object object = @@node unless object
if object == true if object == true
Float(1) Float(1)
elsif object == false elsif object == false
Float(0) Float(0)
elsif object.kind_of? Array elsif object.kind_of? Array
string( object ).to_f number(string( object ))
elsif object.kind_of? Float elsif object.kind_of? Float
object object
else else
object.to_s.to_f str = string( object )
end #puts "STRING OF #{object.inspect} = #{str}"
end if str =~ /^\d+/
object.to_s.to_f
else
(0.0 / 0.0)
end
end
end
def Functions::sum( nodes ) def Functions::sum( nodes )
end end
def Functions::floor( number ) def Functions::floor( number )
number(number).floor number(number).floor
end end
def Functions::ceiling( number ) def Functions::ceiling( number )
number(number).ceil number(number).ceil
end end
def Functions::round( number ) def Functions::round( number )
begin begin
number(number).round number(number).round
rescue FloatDomainError rescue FloatDomainError
number(number) number(number)
end end
end end
def Functions::method_missing( id ) def Functions::method_missing( id )
puts "METHOD MISSING #{id.id2name}" puts "METHOD MISSING #{id.id2name}"
XPath.match( @@node, id.id2name ) XPath.match( @@node, id.id2name )
end end
end end
end end

View file

@ -1,49 +1,51 @@
module REXML module REXML
class ParseException < RuntimeError class ParseException < RuntimeError
attr_accessor :source, :parser, :continued_exception attr_accessor :source, :parser, :continued_exception
def initialize( message, source=nil, parser=nil, exception=nil ) def initialize( message, source=nil, parser=nil, exception=nil )
super(message) super(message)
@source = source @source = source
@parser = parser @parser = parser
@continued_exception = exception @continued_exception = exception
end end
def to_s def to_s
# Quote the original exception, if there was one # Quote the original exception, if there was one
if @continued_exception if @continued_exception
err = @continued_exception.inspect err = @continued_exception.inspect
err << "\n" err << "\n"
err << @continued_exception.backtrace.join("\n") err << @continued_exception.backtrace.join("\n")
err << "\n...\n" err << "\n...\n"
else else
err = "" err = ""
end end
# Get the stack trace and error message # Get the stack trace and error message
err << super err << super
# Add contextual information # Add contextual information
if @source if @source
err << "\nLine: #{line}\n" err << "\nLine: #{line}\n"
err << "Position: #{position}\n" err << "Position: #{position}\n"
err << "Last 80 unconsumed characters:\n" err << "Last 80 unconsumed characters:\n"
err << @source.buffer[0..80].gsub(/\n/, ' ') err << @source.buffer[0..80].gsub(/\n/, ' ')
end end
err err
end end
def position def position
@source.current_line[0] if @source and @source.current_line @source.current_line[0] if @source and defined? @source.current_line and
end @source.current_line
end
def line def line
@source.current_line[2] if @source and @source.current_line @source.current_line[2] if @source and defined? @source.current_line and
end @source.current_line
end
def context def context
@source.current_line @source.current_line
end end
end end
end end

View file

@ -100,6 +100,8 @@ module REXML
self.stream = source self.stream = source
end end
attr_reader :source
def stream=( source ) def stream=( source )
if source.kind_of? String if source.kind_of? String
@source = Source.new(source) @source = Source.new(source)

View file

@ -10,8 +10,8 @@
# #
# Main page:: http://www.germane-software.com/software/rexml # Main page:: http://www.germane-software.com/software/rexml
# Author:: Sean Russell <serATgermaneHYPHENsoftwareDOTcom> # Author:: Sean Russell <serATgermaneHYPHENsoftwareDOTcom>
# Version:: 3.0.4 # Version:: @ANT_VERSION@
# Date:: +2004/115 # Date:: @ANT_DATE@
# #
# This API documentation can be downloaded from the REXML home page, or can # This API documentation can be downloaded from the REXML home page, or can
# be accessed online[http://www.germane-software.com/software/rexml_doc] # 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] # online[http://www.germane-software.com/software/rexml/docs/tutorial.html]
module REXML module REXML
Copyright = "Copyright © 2001, 2002, 2003, 2004 Sean Russell <ser@germane-software.com>" Copyright = "Copyright © 2001, 2002, 2003, 2004 Sean Russell <ser@germane-software.com>"
Date = "+2004/115" Date = "@ANT_DATE@"
Version = "3.0.4" Version = "@ANT_VERSION@"
end end

View file

@ -194,7 +194,7 @@ module REXML
@raw = false @raw = false
end 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 return string if level < 0
new_string = '' new_string = ''
string.each { |line| string.each { |line|
@ -211,7 +211,7 @@ module REXML
if not (@parent and @parent.whitespace) then if not (@parent and @parent.whitespace) then
s = wrap(s, 60, false) if @parent and @parent.context[:wordwrap] == :all 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 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 end
s.squeeze!(" \n\t") if @parent and !@parent.whitespace s.squeeze!(" \n\t") if @parent and !@parent.whitespace
end end

View file

@ -3,309 +3,308 @@ require 'rexml/xmltokens'
require 'rexml/parsers/xpathparser' require 'rexml/parsers/xpathparser'
module REXML module REXML
# You don't want to use this class. Really. Use XPath, which is a wrapper # 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. # 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 # There is strange, dark magic at work in this code. Beware. Go back! Go
# back while you still can! # back while you still can!
class XPathParser class XPathParser
include XMLTokens include XMLTokens
LITERAL = /^'([^']*)'|^"([^"]*)"/u LITERAL = /^'([^']*)'|^"([^"]*)"/u
def initialize( ) def initialize( )
@parser = REXML::Parsers::XPathParser.new @parser = REXML::Parsers::XPathParser.new
@namespaces = {} @namespaces = {}
@variables = {} @variables = {}
end end
def namespaces=( namespaces={} ) def namespaces=( namespaces={} )
Functions::namespace_context = namespaces Functions::namespace_context = namespaces
@namespaces = namespaces @namespaces = namespaces
end end
def variables=( vars={} ) def variables=( vars={} )
Functions::variables = vars Functions::variables = vars
@variables = vars @variables = vars
end end
def parse path, nodeset def parse path, nodeset
path_stack = @parser.parse( path ) path_stack = @parser.parse( path )
#puts "PARSE: #{path} => #{path_stack.inspect}" #puts "PARSE: #{path} => #{path_stack.inspect}"
#puts "PARSE: nodeset = #{nodeset.collect{|x|x.to_s}.inspect}" #puts "PARSE: nodeset = #{nodeset.collect{|x|x.to_s}.inspect}"
match( path_stack, nodeset ) match( path_stack, nodeset )
end end
def predicate path, nodeset def predicate path, nodeset
path_stack = @parser.predicate( path ) path_stack = @parser.predicate( path )
return Predicate( path_stack, nodeset ) return Predicate( path_stack, nodeset )
end end
def []=( variable_name, value ) def []=( variable_name, value )
@variables[ variable_name ] = value @variables[ variable_name ] = value
end end
private private
def match( path_stack, nodeset ) def match( path_stack, nodeset )
while ( path_stack.size > 0 and nodeset.size > 0 ) while ( path_stack.size > 0 and nodeset.size > 0 )
#puts "PARSE: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'" #puts "PARSE: #{path_stack.inspect} '#{nodeset.collect{|n|n.class}.inspect}'"
nodeset = internal_parse( path_stack, nodeset ) nodeset = internal_parse( path_stack, nodeset )
#puts "NODESET: #{nodeset.size}" #puts "NODESET: #{nodeset}"
#puts "PATH_STACK: #{path_stack.inspect}" #puts "PATH_STACK: #{path_stack.inspect}"
end end
nodeset nodeset
end end
def internal_parse path_stack, nodeset def internal_parse path_stack, nodeset
#puts "INTERNAL_PARSE RETURNING WITH NO RESULTS" if nodeset.size == 0 or path_stack.size == 0 #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 return nodeset if nodeset.size == 0 or path_stack.size == 0
#puts "INTERNAL_PARSE: #{path_stack.inspect}, #{nodeset.collect{|n| n.class}.inspect}" #puts "INTERNAL_PARSE: #{path_stack.inspect}, #{nodeset.collect{|n| n.class}.inspect}"
case path_stack.shift case path_stack.shift
when :document when :document
return [ nodeset[0].root.parent ] return [ nodeset[0].root.parent ]
when :qname when :qname
prefix = path_stack.shift prefix = path_stack.shift
name = path_stack.shift name = path_stack.shift
#puts "QNAME #{prefix}#{prefix.size>0?':':''}#{name}" #puts "QNAME #{prefix}#{prefix.size>0?':':''}#{name}"
n = nodeset.clone n = nodeset.clone
ns = @namespaces[prefix] ns = @namespaces[prefix]
ns = ns ? ns : '' ns = ns ? ns : ''
n.delete_if do |node| n.delete_if do |node|
# FIXME: This DOUBLES the time XPath searches take # FIXME: This DOUBLES the time XPath searches take
ns = node.namespace( prefix ) if node.node_type == :element and ns == '' 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 #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 ) !(node.node_type == :element and node.name == name and node.namespace == ns )
end end
return n return n
when :any when :any
n = nodeset.clone n = nodeset.clone
n.delete_if { |node| node.node_type != :element } n.delete_if { |node| node.node_type != :element }
return n return n
when :self when :self
# THIS SPACE LEFT INTENTIONALLY BLANK # THIS SPACE LEFT INTENTIONALLY BLANK
when :processing_instruction when :processing_instruction
target = path_stack.shift target = path_stack.shift
n = nodeset.clone n = nodeset.clone
n.delete_if do |node| n.delete_if do |node|
(node.node_type != :processing_instruction) or (node.node_type != :processing_instruction) or
( !target.nil? and ( node.target != target ) ) ( !target.nil? and ( node.target != target ) )
end end
return n return n
when :text when :text
#puts ":TEXT" #puts ":TEXT"
n = nodeset.clone n = nodeset.clone
n.delete_if do |node| n.delete_if do |node|
#puts "#{node} :: #{node.node_type}" #puts "#{node} :: #{node.node_type}"
node.node_type != :text node.node_type != :text
end end
return n return n
when :comment when :comment
n = nodeset.clone n = nodeset.clone
n.delete_if do |node| n.delete_if do |node|
node.node_type != :comment node.node_type != :comment
end end
return n return n
when :node when :node
return nodeset return nodeset
# FIXME: I suspect the following XPath will fail: # FIXME: I suspect the following XPath will fail:
# /a/*/*[1] # /a/*/*[1]
when :child when :child
#puts "CHILD" #puts "CHILD"
new_nodeset = [] new_nodeset = []
nt = nil nt = nil
for node in nodeset for node in nodeset
nt = node.node_type nt = node.node_type
new_nodeset += node.children if nt == :element or nt == :document new_nodeset += node.children if nt == :element or nt == :document
end end
#path_stack[0,(path_stack.size-ps_clone.size)] = [] #path_stack[0,(path_stack.size-ps_clone.size)] = []
return new_nodeset return new_nodeset
when :literal when :literal
literal = path_stack.shift literal = path_stack.shift
if literal =~ /^\d+(\.\d+)?$/ if literal =~ /^\d+(\.\d+)?$/
return ($1 ? literal.to_f : literal.to_i) return ($1 ? literal.to_f : literal.to_i)
end end
#puts "RETURNING '#{literal}'" #puts "RETURNING '#{literal}'"
return literal return literal
when :attribute when :attribute
#puts ":ATTRIBUTE" new_nodeset = []
new_nodeset = [] case path_stack.shift
case path_stack.shift when :qname
when :qname prefix = path_stack.shift
prefix = path_stack.shift name = path_stack.shift
name = path_stack.shift for element in nodeset
for element in nodeset if element.node_type == :element
if element.node_type == :element #puts element.name
#puts element.name #puts "looking for attribute #{name} in '#{@namespaces[prefix]}'"
#puts "looking for attribute #{name} in '#{@namespaces[prefix]}'" attr = element.attribute( name, @namespaces[prefix] )
attr = element.attribute( name, @namespaces[prefix] ) #puts ":ATTRIBUTE: attr => #{attr}"
#puts ":ATTRIBUTE: attr => #{attr}" new_nodeset << attr if attr
new_nodeset << attr if attr end
end end
end when :any
when :any for element in nodeset
for element in nodeset if element.node_type == :element
if element.node_type == :element attr = element.attributes
attr = element.attributes end
end end
end end
end #puts "RETURNING #{new_nodeset.collect{|n|n.to_s}.inspect}"
#puts "RETURNING #{new_nodeset.collect{|n|n.to_s}.inspect}" return new_nodeset
return new_nodeset
when :parent when :parent
return internal_parse( path_stack, nodeset.collect{|n| n.parent}.compact ) return internal_parse( path_stack, nodeset.collect{|n| n.parent}.compact )
when :ancestor when :ancestor
#puts "ANCESTOR" #puts "ANCESTOR"
new_nodeset = [] new_nodeset = []
for node in nodeset for node in nodeset
while node.parent while node.parent
node = node.parent node = node.parent
new_nodeset << node unless new_nodeset.include? node new_nodeset << node unless new_nodeset.include? node
end end
end end
#nodeset = new_nodeset.uniq #nodeset = new_nodeset.uniq
return new_nodeset return new_nodeset
when :ancestor_or_self when :ancestor_or_self
new_nodeset = [] new_nodeset = []
for node in nodeset for node in nodeset
if node.node_type == :element if node.node_type == :element
new_nodeset << node new_nodeset << node
while ( node.parent ) while ( node.parent )
node = node.parent node = node.parent
new_nodeset << node unless new_nodeset.includes? node new_nodeset << node unless new_nodeset.includes? node
end end
end end
end end
#nodeset = new_nodeset.uniq #nodeset = new_nodeset.uniq
return new_nodeset return new_nodeset
when :predicate when :predicate
#puts "@"*80 #puts "@"*80
#puts "NODESET = #{nodeset.collect{|n|n.to_s}.inspect}" #puts "NODESET = #{nodeset.collect{|n|n.to_s}.inspect}"
predicate = path_stack.shift predicate = path_stack.shift
new_nodeset = [] new_nodeset = []
Functions::size = nodeset.size Functions::size = nodeset.size
nodeset.size.times do |index| nodeset.size.times do |index|
node = nodeset[index] node = nodeset[index]
Functions::node = node Functions::node = node
Functions::index = index+1 Functions::index = index+1
#puts "Node #{node} and index=#{index+1}" #puts "Node #{node} and index=#{index+1}"
result = Predicate( predicate, node ) result = Predicate( predicate, node )
#puts "Predicate returned #{result} (#{result.class}) for #{node.class}" #puts "Predicate returned #{result} (#{result.class}) for #{node.class}"
if result.kind_of? Numeric if result.kind_of? Numeric
#puts "#{result} == #{index} => #{result == index}" #puts "#{result} == #{index} => #{result == index}"
new_nodeset << node if result == (index+1) new_nodeset << node if result == (index+1)
elsif result.instance_of? Array elsif result.instance_of? Array
new_nodeset << node if result.size > 0 new_nodeset << node if result.size > 0
else else
new_nodeset << node if result new_nodeset << node if result
end end
end end
#puts "Nodeset after predicate #{predicate.inspect} has #{new_nodeset.size} nodes" #puts "Nodeset after predicate #{predicate.inspect} has #{new_nodeset.size} nodes"
#puts "NODESET: #{new_nodeset.collect{|n|n.to_s}.inspect}" #puts "NODESET: #{new_nodeset.collect{|n|n.to_s}.inspect}"
return new_nodeset return new_nodeset
when :descendant_or_self when :descendant_or_self
rv = descendant_or_self( path_stack, nodeset ) rv = descendant_or_self( path_stack, nodeset )
path_stack.clear path_stack.clear
return rv return rv
when :descendant when :descendant
#puts ":DESCENDANT" #puts ":DESCENDANT"
results = [] results = []
nt = nil nt = nil
for node in nodeset for node in nodeset
nt = node.node_type nt = node.node_type
results += internal_parse( path_stack.clone.unshift( :descendant_or_self ), results += internal_parse( path_stack.clone.unshift( :descendant_or_self ),
node.children ) if nt == :element or nt == :document node.children ) if nt == :element or nt == :document
end end
return results return results
when :following_sibling when :following_sibling
results = [] results = []
for node in nodeset for node in nodeset
all_siblings = node.parent.children all_siblings = node.parent.children
current_index = all_siblings.index( node ) current_index = all_siblings.index( node )
following_siblings = all_siblings[ current_index+1 .. -1 ] following_siblings = all_siblings[ current_index+1 .. -1 ]
results += internal_parse( path_stack.clone, following_siblings ) results += internal_parse( path_stack.clone, following_siblings )
end end
return results return results
when :preceding_sibling when :preceding_sibling
results = [] results = []
for node in nodeset for node in nodeset
all_siblings = node.parent.children all_siblings = node.parent.children
current_index = all_siblings.index( node ) current_index = all_siblings.index( node )
preceding_siblings = all_siblings[ 0 .. current_index-1 ] preceding_siblings = all_siblings[ 0 .. current_index-1 ]
results += internal_parse( path_stack.clone, preceding_siblings ) results += internal_parse( path_stack.clone, preceding_siblings )
end end
return results return results
when :preceding when :preceding
new_nodeset = [] new_nodeset = []
for node in nodeset for node in nodeset
new_nodeset += preceding( node ) new_nodeset += preceding( node )
end end
return new_nodeset return new_nodeset
when :following when :following
new_nodeset = [] new_nodeset = []
for node in nodeset for node in nodeset
new_nodeset += following( node ) new_nodeset += following( node )
end end
return new_nodeset return new_nodeset
when :namespace when :namespace
new_set = [] new_set = []
for node in nodeset for node in nodeset
new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
end end
return new_nodeset return new_nodeset
when :variable when :variable
var_name = path_stack.shift var_name = path_stack.shift
return @variables[ var_name ] return @variables[ var_name ]
end end
nodeset nodeset
end end
########################################################## ##########################################################
# FIXME # FIXME
# The next two methods are BAD MOJO! # The next two methods are BAD MOJO!
# This is my achilles heel. If anybody thinks of a better # This is my achilles heel. If anybody thinks of a better
# way of doing this, be my guest. This really sucks, but # way of doing this, be my guest. This really sucks, but
# it took me three days to get it to work at all. # it took me three days to get it to work at all.
# ######################################################## # ########################################################
def descendant_or_self( path_stack, nodeset ) def descendant_or_self( path_stack, nodeset )
rs = [] rs = []
d_o_s( path_stack, nodeset, rs ) d_o_s( path_stack, nodeset, rs )
#puts "RS = #{rs.collect{|n|n.to_s}.inspect}" #puts "RS = #{rs.collect{|n|n.to_s}.inspect}"
document_order(rs.flatten.compact) document_order(rs.flatten.compact)
end end
def d_o_s( p, ns, r ) def d_o_s( p, ns, r )
nt = nil nt = nil
ns.each_index do |i| ns.each_index do |i|
n = ns[i] n = ns[i]
x = match( p.clone, [ n ] ) x = match( p.clone, [ n ] )
nt = n.node_type nt = n.node_type
d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0 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 r.concat(x) if x.size > 0
end end
end end
# Reorders an array of nodes so that they are in document order # Reorders an array of nodes so that they are in document order
@ -327,221 +326,231 @@ module REXML
def recurse( nodeset, &block ) def recurse( nodeset, &block )
for node in nodeset for node in nodeset
yield node yield node
recurse( node, &block ) if node.node_type == :element recurse( node, &block ) if node.node_type == :element
end end
end end
# Given a predicate, a node, and a context, evaluates to true or false. # Given a predicate, a node, and a context, evaluates to true or false.
def Predicate( predicate, node ) def Predicate( predicate, node )
predicate = predicate.clone predicate = predicate.clone
#puts "#"*20 #puts "#"*20
#puts "Predicate( #{predicate.inspect}, #{node.class} )" #puts "Predicate( #{predicate.inspect}, #{node.class} )"
results = [] results = []
case (predicate[0]) case (predicate[0])
when :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq when :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
eq = predicate.shift eq = predicate.shift
left = Predicate( predicate.shift, node ) left = Predicate( predicate.shift, node )
right = Predicate( predicate.shift, node ) right = Predicate( predicate.shift, node )
return equality_relational_compare( left, eq, right ) #puts "LEFT = #{left.inspect}"
#puts "RIGHT = #{right.inspect}"
return equality_relational_compare( left, eq, right )
when :div, :mod, :mult, :plus, :minus when :div, :mod, :mult, :plus, :minus
op = predicate.shift op = predicate.shift
left = Predicate( predicate.shift, node ) left = Predicate( predicate.shift, node )
right = Predicate( predicate.shift, node ) right = Predicate( predicate.shift, node )
left = Functions::number( left ) #puts "LEFT = #{left.inspect}"
right = Functions::number( right ) #puts "RIGHT = #{right.inspect}"
case op left = Functions::number( left )
when :div right = Functions::number( right )
return left.to_f / right.to_f #puts "LEFT = #{left.inspect}"
when :mod #puts "RIGHT = #{right.inspect}"
return left % right case op
when :mult when :div
return left * right return left.to_f / right.to_f
when :plus when :mod
return left + right return left % right
when :minus when :mult
return left - right return left * right
end when :plus
return left + right
when :minus
return left - right
end
when :union when :union
predicate.shift predicate.shift
left = Predicate( predicate.shift, node ) left = Predicate( predicate.shift, node )
right = Predicate( predicate.shift, node ) right = Predicate( predicate.shift, node )
return (left | right) return (left | right)
when :neg when :neg
predicate.shift predicate.shift
operand = Functions::number(Predicate( predicate, node )) operand = Functions::number(Predicate( predicate, node ))
return -operand return -operand
when :not when :not
predicate.shift predicate.shift
return !Predicate( predicate.shift, node ) return !Predicate( predicate.shift, node )
when :function when :function
predicate.shift predicate.shift
func_name = predicate.shift.tr('-', '_') func_name = predicate.shift.tr('-', '_')
arguments = predicate.shift arguments = predicate.shift
#puts "\nFUNCTION: #{func_name}" #puts "\nFUNCTION: #{func_name}"
#puts "ARGUMENTS: #{arguments.inspect} #{node.to_s}" #puts "ARGUMENTS: #{arguments.inspect} #{node.to_s}"
args = arguments.collect { |arg| Predicate( arg, node ) } args = arguments.collect { |arg| Predicate( arg, node ) }
#puts "FUNCTION: #{func_name}( #{args.collect{|n|n.to_s}.inspect} )" #puts "FUNCTION: #{func_name}( #{args.collect{|n|n.to_s}.inspect} )"
result = Functions.send( func_name, *args ) result = Functions.send( func_name, *args )
#puts "RESULTS: #{result.inspect}" #puts "RESULTS: #{result.inspect}"
return result return result
else else
return match( predicate, [ node ] ) return match( predicate, [ node ] )
end end
end end
# Builds a nodeset of all of the following nodes of the supplied node, # Builds a nodeset of all of the following nodes of the supplied node,
# in document order # in document order
def following( node ) def following( node )
all_siblings = node.parent.children all_siblings = node.parent.children
current_index = all_siblings.index( node ) current_index = all_siblings.index( node )
following_siblings = all_siblings[ current_index+1 .. -1 ] following_siblings = all_siblings[ current_index+1 .. -1 ]
following = [] following = []
recurse( following_siblings ) { |node| following << node } recurse( following_siblings ) { |node| following << node }
following.shift following.shift
#puts "following is returning #{puta following}" #puts "following is returning #{puta following}"
following following
end end
# Builds a nodeset of all of the preceding nodes of the supplied node, # Builds a nodeset of all of the preceding nodes of the supplied node,
# in reverse document order # in reverse document order
def preceding( node ) def preceding( node )
all_siblings = node.parent.children all_siblings = node.parent.children
current_index = all_siblings.index( node ) current_index = all_siblings.index( node )
preceding_siblings = all_siblings[ 0 .. current_index-1 ] preceding_siblings = all_siblings[ 0 .. current_index-1 ]
preceding_siblings.reverse! preceding_siblings.reverse!
preceding = [] preceding = []
recurse( preceding_siblings ) { |node| preceding << node } recurse( preceding_siblings ) { |node| preceding << node }
preceding.reverse preceding.reverse
end end
def equality_relational_compare( set1, op, set2 ) def equality_relational_compare( set1, op, set2 )
#puts "EQ_REL_COMP: #{set1.to_s}, #{op}, #{set2.to_s}" #puts "EQ_REL_COMP: #{set1.to_s}, #{op}, #{set2.to_s}"
#puts "#{set1.class.name} #{op} #{set2.class.name}" #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}" #puts "#{set1.size} & #{set2.size}"
if set1.size == 1 and set2.size == 1 if set1.size == 1 and set2.size == 1
set1 = set1[0] set1 = set1[0]
set2 = set2[0] set2 = set2[0]
elsif set1.size == 0 or set2.size == 0 elsif set1.size == 0 or set2.size == 0
nd = set1.size==0 ? set2 : set1 nd = set1.size==0 ? set2 : set1
nd.each { |il| return true if compare( il, op, nil ) } nd.each { |il| return true if compare( il, op, nil ) }
else else
set1.each do |i1| set1.each do |i1|
i1 = i1.to_s i1 = i1.to_s
set2.each do |i2| set2.each do |i2|
i2 = i2.to_s i2 = i2.to_s
return true if compare( i1, op, i2 ) return true if compare( i1, op, i2 )
end end
end end
return false return false
end end
end end
#puts "COMPARING VALUES" #puts "COMPARING VALUES"
# If one is nodeset and other is number, compare number to each item # If one is nodeset and other is number, compare number to each item
# in nodeset s.t. number op number(string(item)) # in nodeset s.t. number op number(string(item))
# If one is nodeset and other is string, compare string to each item # If one is nodeset and other is string, compare string to each item
# in nodeset s.t. string op string(item) # in nodeset s.t. string op string(item)
# If one is nodeset and other is boolean, compare boolean to each item # If one is nodeset and other is boolean, compare boolean to each item
# in nodeset s.t. boolean op boolean(item) # in nodeset s.t. boolean op boolean(item)
if set1.kind_of? Array or set2.kind_of? Array if set1.kind_of? Array or set2.kind_of? Array
#puts "ISA ARRAY" #puts "ISA ARRAY"
if set1.kind_of? Array if set1.kind_of? Array
a = set1 a = set1
b = set2.to_s b = set2.to_s
else else
a = set2 a = set2
b = set1.to_s b = set1.to_s
end end
case b case b
when 'true', 'false' when 'true', 'false'
b = Functions::boolean( b ) b = Functions::boolean( b )
for v in a for v in a
v = Functions::boolean(v) v = Functions::boolean(v)
return true if compare( v, op, b ) return true if compare( v, op, b )
end end
when /^\d+(\.\d+)?$/ when /^\d+(\.\d+)?$/
b = Functions::number( b ) b = Functions::number( b )
for v in a #puts "B = #{b.inspect}"
v = Functions::number(v) for v in a
return true if compare( v, op, b ) #puts "v = #{v.inspect}"
end v = Functions::number(v)
else #puts "v = #{v.inspect}"
b = Functions::string( b ) #puts compare(v,op,b)
for v in a return true if compare( v, op, b )
v = Functions::string(v) end
return true if compare( v, op, b ) else
end b = Functions::string( b )
end for v in a
else v = Functions::string(v)
# If neither is nodeset, return true if compare( v, op, b )
# If op is = or != end
# If either boolean, convert to boolean end
# If either number, convert to number else
# Else, convert to string # If neither is nodeset,
# Else # If op is = or !=
# Convert both to numbers and compare # If either boolean, convert to boolean
s1 = set1.to_s # If either number, convert to number
s2 = set2.to_s # Else, convert to string
#puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}" # Else
if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false' # Convert both to numbers and compare
#puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}" s1 = set1.to_s
#puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}" s2 = set2.to_s
set1 = Functions::boolean( set1 ) #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
set2 = Functions::boolean( set2 ) if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
else #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
if op == :eq or op == :neq #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/ set1 = Functions::boolean( set1 )
set1 = Functions::number( s1 ) set2 = Functions::boolean( set2 )
set2 = Functions::number( s2 ) else
else if op == :eq or op == :neq
set1 = Functions::string( set1 ) if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
set2 = Functions::string( set2 ) set1 = Functions::number( s1 )
end set2 = Functions::number( s2 )
else else
set1 = Functions::number( set1 ) set1 = Functions::string( set1 )
set2 = Functions::number( set2 ) set2 = Functions::string( set2 )
end end
end else
#puts "EQ_REL_COMP: #{set1} #{op} #{set2}" set1 = Functions::number( set1 )
set2 = Functions::number( set2 )
end
end
#puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
#puts ">>> #{compare( set1, op, set2 )}" #puts ">>> #{compare( set1, op, set2 )}"
return compare( set1, op, set2 ) return compare( set1, op, set2 )
end end
return false return false
end end
def compare a, op, b def compare a, op, b
#puts "COMPARE #{a.to_s} #{op} #{b.to_s}" #puts "COMPARE #{a.to_s} #{op} #{b.to_s}"
case op case op
when :eq when :eq
a == b a == b
when :neq when :neq
a != b a != b
when :lt when :lt
a < b a < b
when :lteq when :lteq
a <= b a <= b
when :gt when :gt
a > b a > b
when :gteq when :gteq
a >= b a >= b
when :and when :and
a and b a and b
when :or when :or
a or b a or b
else else
false false
end end
end end
end end
end end