class REXML::Text - rexml: Ruby Standard Library Documentation

# class REXML::Text

Represents text nodes in an XML document

### Constants

EREFERENCE
NEEDS_A_SECOND_CHECK
NUMERICENTITY
REFERENCE
SETUTITSBUS
SLAICEPS

  Characters which are substituted in written strings

SPECIALS

  The order in which the substitutions occur

SUBSTITUTES
VALID_CHAR
VALID_XML_CHARS

### Attributes

raw[RW]

If `raw` is true, then `REXML` leaves the value alone

### Public Class Methods

check(string, pattern, doctype) click to toggle source

check for illegal characters

    # File rexml-3.4.0/lib/rexml/text.rb, line 116
    def Text.check string, pattern, doctype

      # illegal anywhere
      if !string.match?(VALID_XML_CHARS)
        string.chars.each do |c|
          case c.ord
          when *VALID_CHAR
          else
            raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
          end
        end
      end

      pos = 0
      while (index = string.index(/<|&/, pos))
        if string[index] == "<"
          raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
        end

        unless (end_index = string.index(/[^\s];/, index + 1))
          raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
        end

        value = string[(index + 1)..end_index]
        if /\s/.match?(value)
          raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
        end

        if value[0] == "#"
          character_reference = value[1..-1]

          unless (/\A(\d+|x[0-9a-fA-F]+)\z/.match?(character_reference))
            if character_reference[0] == "x" || character_reference[-1] == "x"
              raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
            else
              raise "Illegal character #{string.inspect} in raw string #{string.inspect}"
            end
          end

          case (character_reference[0] == "x" ? character_reference[1..-1].to_i(16) : character_reference[0..-1].to_i)
          when *VALID_CHAR
          else
            raise "Illegal character #{string.inspect} in raw string #{string.inspect}"
          end
        elsif !(/\A#{Entity::NAME}\z/um.match?(value))
          raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
        end

        pos = end_index + 1
      end

      string
    end

new(arg, respect_whitespace=false, parent=nil, raw=nil, entity_filter=nil, illegal=NEEDS_A_SECOND_CHECK ) click to toggle source

Constructor `arg` if a String, the content is set to the String. If a `Text`, the object is shallowly cloned.

`respect_whitespace` (boolean, false) if true, whitespace is respected

`parent` (nil) if this is a `Parent` object, the parent will be set to this.

`raw` (nil) This argument can be given three values. If true, then the value of used to construct this object is expected to contain no unescaped XML markup, and `REXML` will not change the text. If this value is false, the string may contain any characters, and `REXML` will escape any and all defined entities whose values are contained in the text. If this value is nil (the default), then the raw value of the parent will be used as the raw value for this node. If there is no raw value for the parent, and no value is supplied, the default is false. Use this field if you have entities defined for some text, and you don’t want `REXML` to escape that text in output.

    Text.new( "<&", false, nil, false ) #-> "&lt;&amp;"
    Text.new( "&lt;&amp;", false, nil, false ) #-> "&amp;lt;&amp;amp;"
    Text.new( "<&", false, nil, true )  #-> Parse exception
    Text.new( "&lt;&amp;", false, nil, true )  #-> "&lt;&amp;"
    # Assume that the entity "s" is defined to be "sean"
    # and that the entity    "r" is defined to be "russell"
    Text.new( "sean russell" )          #-> "&s; &r;"
    Text.new( "sean russell", false, nil, true ) #-> "sean russell"

`entity_filter` (nil) This can be an array of entities to match in the supplied text. This argument is only useful if `raw` is set to false.

    Text.new( "sean russell", false, nil, false, ["s"] ) #-> "&s; russell"
    Text.new( "sean russell", false, nil, true, ["s"] ) #-> "sean russell"

In the last example, the `entity_filter` argument is ignored.

`illegal` INTERNAL USE ONLY

Calls superclass method `REXML::Child::new`

    # File rexml-3.4.0/lib/rexml/text.rb, line 79
    def initialize(arg, respect_whitespace=false, parent=nil, raw=nil,
      entity_filter=nil, illegal=NEEDS_A_SECOND_CHECK )

      @raw = false
      @parent = nil
      @entity_filter = nil

      if parent
        super( parent )
        @raw = parent.raw
      end

      if arg.kind_of? String
        @string = arg.dup
      elsif arg.kind_of? Text
        @string = arg.instance_variable_get(:@string).dup
        @raw = arg.raw
        @entity_filter = arg.instance_variable_get(:@entity_filter)
      else
        raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
      end

      @string.squeeze!(" \n\t") unless respect_whitespace
      @string.gsub!(/\r\n?/, "\n")
      @raw = raw unless raw.nil?
      @entity_filter = entity_filter if entity_filter
      clear_cache

      Text.check(@string, illegal, doctype) if @raw
    end

### Private Class Methods

expand(ref, doctype, filter) click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 404
    def Text.expand(ref, doctype, filter)
      if ref[1] == ?#
        if ref[2] == ?x
          [ref[3...-1].to_i(16)].pack('U*')
        else
          [ref[2...-1].to_i].pack('U*')
        end
      elsif ref == '&amp;'
        '&'
      elsif filter and filter.include?( ref[1...-1] )
        ref
      elsif doctype
        doctype.entity( ref[1...-1] ) or ref
      else
        entity_value = DocType::DEFAULT_ENTITIES[ ref[1...-1] ]
        entity_value ? entity_value.value : ref
      end
    end

normalize( input, doctype=nil, entity_filter=nil ) click to toggle source

Escapes all possible entities

    # File rexml-3.4.0/lib/rexml/text.rb, line 366
    def Text::normalize( input, doctype=nil, entity_filter=nil )
      copy = input.to_s
      # Doing it like this rather than in a loop improves the speed
      #copy = copy.gsub( EREFERENCE, '&amp;' )
      copy = copy.gsub( "&", "&amp;" ) if copy.include?("&")
      if doctype
        # Replace all ampersands that aren't part of an entity
        doctype.entities.each_value do |entity|
          copy = copy.gsub( entity.value,
            "&#{entity.name};" ) if entity.value and
              not( entity_filter and entity_filter.include?(entity.name) )
        end
      else
        # Replace all ampersands that aren't part of an entity
        DocType::DEFAULT_ENTITIES.each_value do |entity|
          if copy.include?(entity.value)
            copy = copy.gsub(entity.value, "&#{entity.name};" )
          end
        end
      end
      copy
    end

read_with_substitution( input, illegal=nil ) click to toggle source

Reads text, substituting entities

    # File rexml-3.4.0/lib/rexml/text.rb, line 340
    def Text::read_with_substitution( input, illegal=nil )
      copy = input.clone

      if copy =~ illegal
        raise ParseException.new( "malformed text: Illegal character #$& in \"#{copy}\"" )
      end if illegal

      copy.gsub!( /\r\n?/, "\n" )
      if copy.include? ?&
        copy.gsub!( SETUTITSBUS[0], SLAICEPS[0] )
        copy.gsub!( SETUTITSBUS[1], SLAICEPS[1] )
        copy.gsub!( SETUTITSBUS[2], SLAICEPS[2] )
        copy.gsub!( SETUTITSBUS[3], SLAICEPS[3] )
        copy.gsub!( SETUTITSBUS[4], SLAICEPS[4] )
        copy.gsub!( /&#0*((?:\d+)|(?:x[a-f0-9]+));/ ) {
          m=$1
          #m='0' if m==''
          m = "0#{m}" if m[0] == ?x
          [Integer(m)].pack('U*')
        }
      end
      copy
    end

unnormalize( string, doctype=nil, filter=nil, illegal=nil, entity_expansion_text_limit: nil ) click to toggle source

Unescapes all possible entities

    # File rexml-3.4.0/lib/rexml/text.rb, line 390
    def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil, entity_expansion_text_limit: nil )
      entity_expansion_text_limit ||= Security.entity_expansion_text_limit
      sum = 0
      string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) {
        s = Text.expand($&, doctype, filter)
        if sum + s.bytesize > entity_expansion_text_limit
          raise "entity expansion has grown too large"
        else
          sum += s.bytesize
        end
        s
      }
    end

### Public Instance Methods

<<( to_append ) click to toggle source

Appends text to this text node. The text is appended in the `raw` mode of this text node.

`returns` the text itself to enable method chain like ‘text << “XXX” << “YYY”’.

    # File rexml-3.4.0/lib/rexml/text.rb, line 189
    def <<( to_append )
      @string << to_append.gsub( /\r\n?/, "\n" )
      clear_cache
      self
    end

<=>( other ) click to toggle source

`other` a String or a `Text` `returns` the result of (`to_s` <=> arg.to_s)

    # File rexml-3.4.0/lib/rexml/text.rb, line 198
    def <=>( other )
      to_s() <=> other.to_s
    end

clone() click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 179
    def clone
      return Text.new(self, true)
    end

doctype() click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 202
    def doctype
      if @parent
        doc = @parent.document
        doc.doctype if doc
      end
    end

empty?() click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 174
    def empty?
      @string.size==0
    end

indent_text(string, level=1, style="\t", indentfirstline=true) click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 274
    def indent_text(string, level=1, style="\t", indentfirstline=true)
      return string if level < 0
      new_string = ''
      string.each_line { |line|
        indent_string = style * level
        new_line = (indent_string + line).sub(/[\s]+$/,'')
        new_string << new_line
      }
      new_string.strip! unless indentfirstline
      return new_string
    end

inspect() click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 228
    def inspect
      @string.inspect
    end

node_type() click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 170
    def node_type
      :text
    end

parent=(parent) click to toggle source
Calls superclass method `REXML::Child#parent=`

    # File rexml-3.4.0/lib/rexml/text.rb, line 110
    def parent= parent
      super(parent)
      Text.check(@string, NEEDS_A_SECOND_CHECK, doctype) if @raw and @parent
    end

to_s() click to toggle source

Returns the string value of this text node. This string is always escaped, meaning that it is a valid XML text node string, and all entities that can be escaped, have been inserted. This method respects the entity filter set in the constructor.

    # Assume that the entity "s" is defined to be "sean", and that the
    # entity "r" is defined to be "russell"
    t = Text.new( "< & sean russell", false, nil, false, ['s'] )
    t.to_s   #-> "&lt; &amp; &s; russell"
    t = Text.new( "< & &s; russell", false, nil, false )
    t.to_s   #-> "&lt; &amp; &s; russell"
    u = Text.new( "sean russell", false, nil, true )
    u.to_s   #-> "sean russell"

    # File rexml-3.4.0/lib/rexml/text.rb, line 223
    def to_s
      return @string if @raw
      @normalized ||= Text::normalize( @string, doctype, @entity_filter )
    end

value() click to toggle source

Returns the string value of this text. This is the text without entities, as it might be used programmatically, or printed to the console. This ignores the ‘raw’ attribute setting, and any entity_filter.

    # Assume that the entity "s" is defined to be "sean", and that the
    # entity "r" is defined to be "russell"
    t = Text.new( "< & sean russell", false, nil, false, ['s'] )
    t.value   #-> "< & sean russell"
    t = Text.new( "< & &s; russell", false, nil, false )
    t.value   #-> "< & sean russell"
    u = Text.new( "sean russell", false, nil, true )
    u.value   #-> "sean russell"

    # File rexml-3.4.0/lib/rexml/text.rb, line 245
    def value
      @unnormalized ||= Text::unnormalize(@string, doctype,
                                          entity_expansion_text_limit: document&.entity_expansion_text_limit)
    end

value=( val ) click to toggle source

Sets the contents of this text node. This expects the text to be unnormalized. It returns self.

    e = Element.new( "a" )
    e.add_text( "foo" )   # <a>foo</a>
    e[0].value = "bar"    # <a>bar</a>
    e[0].value = "<a>"    # <a>&lt;a&gt;</a>

    # File rexml-3.4.0/lib/rexml/text.rb, line 257
    def value=( val )
      @string = val.gsub( /\r\n?/, "\n" )
      clear_cache
      @raw = false
    end

wrap(string, width, addnewline=false) click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 263
    def wrap(string, width, addnewline=false)
      # Recursively wrap string at width.
      return string if string.length <= width
      place = string.rindex(' ', width) # Position in string with last ' ' before cutoff
      if addnewline then
        return "\n" + string[0,place] + "\n" + wrap(string[place+1..-1], width)
      else
        return string[0,place] + "\n" + wrap(string[place+1..-1], width)
      end
    end

write( writer, indent=-1, transitive=false, ie_hack=false ) click to toggle source

## DEPRECATED¶ ↑

See `REXML::Formatters`

    # File rexml-3.4.0/lib/rexml/text.rb, line 289
    def write( writer, indent=-1, transitive=false, ie_hack=false )
      Kernel.warn("#{self.class.name}.write is deprecated.  See REXML::Formatters", uplevel: 1)
      formatter = if indent > -1
          REXML::Formatters::Pretty.new( indent )
        else
          REXML::Formatters::Default.new
        end
      formatter.write( self, writer )
    end

write_with_substitution(out, input) click to toggle source

Writes out text, substituting special characters beforehand. `out` A String, IO, or any other object supporting <<( String ) `input` the text to substitute and the write out

    z=utf8.unpack("U*")
    ascOut=""
    z.each{|r|
      if r <  0x100
        ascOut.concat(r.chr)
      else
        ascOut.concat(sprintf("&#x%x;", r))
      end
    }
    puts ascOut

    # File rexml-3.4.0/lib/rexml/text.rb, line 321
    def write_with_substitution out, input
      copy = input.clone
      # Doing it like this rather than in a loop improves the speed
      copy.gsub!( SPECIALS[0], SUBSTITUTES[0] )
      copy.gsub!( SPECIALS[1], SUBSTITUTES[1] )
      copy.gsub!( SPECIALS[2], SUBSTITUTES[2] )
      copy.gsub!( SPECIALS[3], SUBSTITUTES[3] )
      copy.gsub!( SPECIALS[4], SUBSTITUTES[4] )
      copy.gsub!( SPECIALS[5], SUBSTITUTES[5] )
      out << copy
    end

xpath() click to toggle source

FIXME This probably won’t work properly

    # File rexml-3.4.0/lib/rexml/text.rb, line 301
    def xpath
      path = @parent.xpath
      path += "/text()"
      return path
    end

### Private Instance Methods

clear_cache() click to toggle source

    # File rexml-3.4.0/lib/rexml/text.rb, line 334
    def clear_cache
      @normalized = nil
      @unnormalized = nil
    end