Class: REXML::Text

Inherits:
Child show all
Includes:
Comparable
Defined in:
lib/rexml/text.rb

Overview

Represents text nodes in an XML document

Direct Known Subclasses

CData

Constant Summary collapse

SPECIALS =

The order in which the substitutions occur

[ /&(?!#?[\w-]+;)/u, /</u, />/u, /"/u, /'/u, /\r/u ]
SUBSTITUTES =
['&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#13;']
SLAICEPS =

Characters which are substituted in written strings

[ '<', '>', '"', "'", '&' ]
SETUTITSBUS =
[ /&lt;/u, /&gt;/u, /&quot;/u, /&apos;/u, /&amp;/u ]
NEEDS_A_SECOND_CHECK =
/(<|&((#{Entity::NAME});|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));)?)/um
NUMERICENTITY =
/&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/
VALID_CHAR =
[
  0x9, 0xA, 0xD,
  (0x20..0xD7FF),
  (0xE000..0xFFFD),
  (0x10000..0x10FFFF)
]
VALID_XML_CHARS =
/^(
    [\x09\x0A\x0D\x20-\x7E]            # ASCII
  | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
  |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
  | [\xE1-\xEC\xEE][\x80-\xBF]{2}      # straight 3-byte
  |  \xEF[\x80-\xBE]{2}                #
  |  \xEF\xBF[\x80-\xBD]               # excluding U+fffe and U+ffff
  |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
  |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
  | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
  |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
)*$/nx
REFERENCE =
/#{Entity::REFERENCE}/

Instance Attribute Summary collapse

Attributes inherited from Child

#parent

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Child

#bytes, #document, #next_sibling=, #previous_sibling=, #remove, #replace_with

Methods included from Node

#each_recursive, #find_first_recursive, #indent, #index_in_parent, #next_sibling_node, #parent?, #previous_sibling_node

Constructor Details

#initialize(arg, respect_whitespace = false, parent = nil, raw = nil, entity_filter = nil, illegal = NEEDS_A_SECOND_CHECK) ⇒ Text

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



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/rexml/text.rb', line 94

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

Instance Attribute Details

#rawObject

If raw is true, then REXML leaves the value alone



21
22
23
# File 'lib/rexml/text.rb', line 21

def raw
  @raw
end

Class Method Details

.check(string, pattern, doctype) ⇒ Object

check for illegal characters



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rexml/text.rb', line 131

def Text.check string, pattern, doctype

  # illegal anywhere
  if string !~ VALID_XML_CHARS
    if String.method_defined? :encode
      string.chars.each do |c|
        case c.ord
        when *VALID_CHAR
        else
          raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
        end
      end
    else
      string.scan(/[\x00-\x7F]|[\x80-\xBF][\xC0-\xF0]*|[\xC0-\xF0]/n) do |c|
        case c.unpack('U')
        when *VALID_CHAR
        else
          raise "Illegal character #{c.inspect} in raw string #{string.inspect}"
        end
      end
    end
  end

  # context sensitive
  string.scan(pattern) do
    if $1[-1] != ?;
      raise "Illegal character #{$1.inspect} in raw string #{string.inspect}"
    elsif $1[0] == ?&
      if $5 and $5[0] == ?#
        case ($5[1] == ?x ? $5[2..-1].to_i(16) : $5[1..-1].to_i)
        when *VALID_CHAR
        else
          raise "Illegal character #{$1.inspect} in raw string #{string.inspect}"
        end
      # FIXME: below can't work but this needs API change.
      # elsif @parent and $3 and !SUBSTITUTES.include?($1)
      #   if !doctype or !doctype.entities.has_key?($3)
      #     raise "Undeclared entity '#{$1}' in raw string \"#{string}\""
      #   end
      end
    end
  end
end

Instance Method Details

#<<(to_append) ⇒ Object

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”’.



194
195
196
197
198
# File 'lib/rexml/text.rb', line 194

def <<( to_append )
  @string << to_append.gsub( /\r\n?/, "\n" )
  clear_cache
  self
end

#<=>(other) ⇒ Object

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



203
204
205
# File 'lib/rexml/text.rb', line 203

def <=>( other )
  to_s() <=> other.to_s
end

#cloneObject



184
185
186
# File 'lib/rexml/text.rb', line 184

def clone
  return Text.new(self, true)
end

#doctypeObject



207
208
209
210
211
212
# File 'lib/rexml/text.rb', line 207

def doctype
  if @parent
    doc = @parent.document
    doc.doctype if doc
  end
end

#empty?Boolean

Returns:

  • (Boolean)


179
180
181
# File 'lib/rexml/text.rb', line 179

def empty?
  @string.size==0
end

#indent_text(string, level = 1, style = "\t", indentfirstline = true) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
# File 'lib/rexml/text.rb', line 278

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

#inspectObject



233
234
235
# File 'lib/rexml/text.rb', line 233

def inspect
  @string.inspect
end

#node_typeObject



175
176
177
# File 'lib/rexml/text.rb', line 175

def node_type
  :text
end

#parent=(parent) ⇒ Object



125
126
127
128
# File 'lib/rexml/text.rb', line 125

def parent= parent
  super(parent)
  Text.check(@string, NEEDS_A_SECOND_CHECK, doctype) if @raw and @parent
end

#to_sObject

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"


228
229
230
231
# File 'lib/rexml/text.rb', line 228

def to_s
  return @string if @raw
  @normalized ||= Text::normalize( @string, doctype, @entity_filter )
end

#valueObject

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"


250
251
252
# File 'lib/rexml/text.rb', line 250

def value
  @unnormalized ||= Text::unnormalize( @string, doctype )
end

#value=(val) ⇒ Object

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>


261
262
263
264
265
# File 'lib/rexml/text.rb', line 261

def value=( val )
  @string = val.gsub( /\r\n?/, "\n" )
  clear_cache
  @raw = false
end

#wrap(string, width, addnewline = false) ⇒ Object



267
268
269
270
271
272
273
274
275
276
# File 'lib/rexml/text.rb', line 267

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) ⇒ Object

DEPRECATED

See REXML::Formatters



293
294
295
296
297
298
299
300
301
# File 'lib/rexml/text.rb', line 293

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) ⇒ Object

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


325
326
327
328
329
330
331
332
333
334
335
# File 'lib/rexml/text.rb', line 325

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

#xpathObject

FIXME This probably won’t work properly



305
306
307
308
309
# File 'lib/rexml/text.rb', line 305

def xpath
  path = @parent.xpath
  path += "/text()"
  return path
end