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 ) #-> 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 # # # dt = DocType.new( doctype_to_clone ) # # Incomplete. Shallow clone of doctype - # source = Source.new( '' ) - # dt = DocType.new( source ) - # # - # dt = DocType.new( source, some_document ) - # # Creates a doctype, and adds to the supplied document + # + # +Note+ that the constructor: + # + # Doctype.new( Source.new( "" ) ) + # + # 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 "" # 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 = {} + @@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::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::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::last( ) + @@size + end - def Functions::position( ) - @@index - end + def Functions::position( ) + @@index + end - def Functions::count( node_set ) - node_set.size - 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 + # 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 + # 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::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 + 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 + # 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 + # 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 + # 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::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 + # 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 + 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 + # 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) + # 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? - ) + # 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 + 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 + 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::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 + # 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) + # 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 + # 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 - } + 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 + 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::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::not( object ) + not boolean( object ) + end - # UNTESTED - def Functions::true( ) - true - end + # UNTESTED + def Functions::true( ) + true + end - # UNTESTED - def Functions::false( ) - false - 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 + # 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 + 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 + # 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::sum( nodes ) + end + + def Functions::floor( number ) + number(number).floor + end - def Functions::ceiling( number ) - number(number).ceil - 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::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 + 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 -# 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 " - 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 + # 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 initialize( ) + @parser = REXML::Parsers::XPathParser.new + @namespaces = {} + @variables = {} + end - def namespaces=( namespaces={} ) - Functions::namespace_context = namespaces - @namespaces = namespaces - end + def namespaces=( namespaces={} ) + Functions::namespace_context = namespaces + @namespaces = namespaces + end - def variables=( vars={} ) - Functions::variables = vars - @variables = vars - end + def variables=( vars={} ) + Functions::variables = vars + @variables = vars + end - def parse path, nodeset - path_stack = @parser.parse( path ) + 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 + match( path_stack, nodeset ) + end - def predicate path, nodeset - path_stack = @parser.predicate( path ) - return Predicate( 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 + def []=( variable_name, value ) + @variables[ variable_name ] = value + end - private + 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 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 + 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 ] + 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 :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 :any + n = nodeset.clone + n.delete_if { |node| node.node_type != :element } + return n - when :self - # THIS SPACE LEFT INTENTIONALLY BLANK + 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 :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 :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 :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 :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 :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 :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 + #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 :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 :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_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 :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 :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_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 :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 :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 :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 ] + when :variable + var_name = path_stack.shift + return @variables[ var_name ] - end - nodeset - end + 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 + 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 + 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 ) + # 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 ) - 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 + 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 :neg + predicate.shift + operand = Functions::number(Predicate( predicate, node )) + return -operand - when :not - predicate.shift - return !Predicate( predicate.shift, node ) + 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 + 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 ] ) + else + return match( predicate, [ node ] ) - end - end + 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 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 ] + # 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 + preceding_siblings.reverse! + preceding = [] + recurse( preceding_siblings ) { |node| preceding << node } + preceding.reverse + 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 "#{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 + 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}" + 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