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 ]
ILLEGAL =
/(<|&(?!(#{Entity::NAME})|(#0*((?:\d+)|(?:x[a-fA-F0-9]+)));))/um
NUMERICENTITY =
/&#0*((?:\d+)|(?:x[a-fA-F0-9]+));/
REFERENCE =
/#{Entity::REFERENCE}/
EREFERENCE =
/&(?!#{Entity::NAME};)/

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 = ILLEGAL) ⇒ 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.

pattern INTERNAL USE ONLY



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rexml/text.rb', line 60

def initialize(arg, respect_whitespace=false, parent=nil, raw=nil, 
  entity_filter=nil, illegal=ILLEGAL )

  @raw = false

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

  @raw = raw unless raw.nil?
  @entity_filter = entity_filter
  @normalized = @unnormalized = nil

  if arg.kind_of? String
    @string = arg.clone
    @string.squeeze!(" \n\t") unless respect_whitespace
  elsif arg.kind_of? Text
    @string = arg.to_s
    @raw = arg.raw
  elsif
    raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
  end

  @string.gsub!( /\r\n?/, "\n" )

  # check for illegal characters
  if @raw
    if @string =~ illegal
      raise "Illegal character '#{$1}' in raw string \"#{@string}\""
    end
  end
end

Instance Attribute Details

#rawObject

If raw is true, then REXML leaves the value alone



19
20
21
# File 'lib/rexml/text.rb', line 19

def raw
  @raw
end

Class Method Details

.normalize(input, doctype = nil, entity_filter = nil) ⇒ Object

Escapes all possible entities



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/rexml/text.rb', line 288

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 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) )
    end
  else
    # Replace all ampersands that aren't part of an entity
    DocType::DEFAULT_ENTITIES.each_value do |entity|
      copy = copy.gsub(entity.value, "&#{entity.name};" )
    end
  end
  copy
end

.read_with_substitution(input, illegal = nil) ⇒ Object

Reads text, substituting entities



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/rexml/text.rb', line 262

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

Unescapes all possible entities



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/rexml/text.rb', line 310

def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil )
  rv = string.clone
  rv.gsub!( /\r\n?/, "\n" )
  matches = rv.scan( REFERENCE )
  return rv if matches.size == 0
  rv.gsub!( NUMERICENTITY ) {|m|
    m=$1
    m = "0#{m}" if m[0] == ?x
    [Integer(m)].pack('U*')
  }
  matches.collect!{|x|x[0]}.compact!
  if matches.size > 0
    if doctype
      matches.each do |entity_reference|
        unless filter and filter.include?(entity_reference)
          entity_value = doctype.entity( entity_reference )
          re = /&#{entity_reference};/
          rv.gsub!( re, entity_value ) if entity_value
        end
      end
    else
      matches.each do |entity_reference|
        unless filter and filter.include?(entity_reference)
          entity_value = DocType::DEFAULT_ENTITIES[ entity_reference ]
          re = /&#{entity_reference};/
          rv.gsub!( re, entity_value.value ) if entity_value
        end
      end
    end
    rv.gsub!( /&amp;/, '&' )
  end
  rv
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.



112
113
114
# File 'lib/rexml/text.rb', line 112

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

#<=>(other) ⇒ Object

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



119
120
121
# File 'lib/rexml/text.rb', line 119

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

#cloneObject



105
106
107
# File 'lib/rexml/text.rb', line 105

def clone
  return Text.new(self)
end

#empty?Boolean

Returns:

  • (Boolean)


100
101
102
# File 'lib/rexml/text.rb', line 100

def empty?
  @string.size==0
end

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



202
203
204
205
206
207
208
209
210
211
212
# File 'lib/rexml/text.rb', line 202

def indent_text(string, level=1, style="\t", indentfirstline=true)
  return string if level < 0
  new_string = ''
  string.each { |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



150
151
152
# File 'lib/rexml/text.rb', line 150

def inspect
  @string.inspect
end

#node_typeObject



96
97
98
# File 'lib/rexml/text.rb', line 96

def node_type
  :text
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"


137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/rexml/text.rb', line 137

def to_s
  return @string if @raw
  return @normalized if @normalized

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

  @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"


167
168
169
170
171
172
173
174
175
# File 'lib/rexml/text.rb', line 167

def value
  @unnormalized if @unnormalized
  doctype = nil
  if @parent
    doc = @parent.document
    doctype = doc.doctype if doc
  end
  @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>


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

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

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



191
192
193
194
195
196
197
198
199
200
# File 'lib/rexml/text.rb', line 191

def wrap(string, width, addnewline=false)
  # Recursivly 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



217
218
219
220
221
222
223
224
225
# File 'lib/rexml/text.rb', line 217

def write( writer, indent=-1, transitive=false, ie_hack=false ) 
  Kernel.warn("#{self.class.name}.write is deprecated.  See REXML::Formatters")
  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


249
250
251
252
253
254
255
256
257
258
259
# File 'lib/rexml/text.rb', line 249

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



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

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