require 'rexml/entity'
require 'rexml/doctype'
require 'rexml/child'
require 'rexml/doctype'
require 'rexml/parseexception'
module REXML
class Text < Child
include Comparable
SPECIALS = [ /&(?! SUBSTITUTES = ['&', '<', '>', '"', ''', ' ']
SLAICEPS = [ '<', '>', '"', "'", '&' ]
SETUTITSBUS = [ /</u, />/u, /"/u, /'/u, /&/u ]
attr_accessor :raw
ILLEGAL = /(<|&(?!(#{Entity::NAME})|( NUMERICENTITY = /&
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" )
if @raw
if @string =~ illegal
raise "Illegal character '#{$1}' in raw string \"#{@string}\""
end
end
end
def node_type
:text
end
def empty?
@string.size==0
end
def clone
return Text.new(self)
end
def <<( to_append )
@string << to_append.gsub( /\r\n?/, "\n" )
end
def <=>( other )
to_s() <=> other.to_s
end
REFERENCE = /#{Entity::REFERENCE}/
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
def inspect
@string.inspect
end
def value
@unnormalized if @unnormalized
doctype = nil
if @parent
doc = @parent.document
doctype = doc.doctype if doc
end
@unnormalized = Text::unnormalize( @string, doctype )
end
def value=( val )
@string = val.gsub( /\r\n?/, "\n" )
@unnormalized = nil
@normalized = nil
@raw = false
end
def wrap(string, width, addnewline=false)
return string if string.length <= width
place = string.rindex(' ', width) 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
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
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
def xpath
path = @parent.xpath
path += "/text()"
return path
end
def write_with_substitution out, input
copy = input.clone
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
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!( /& m=$1
m = "0#{m}" if m[0] == ?x
[Integer(m)].pack('U*')
}
end
copy
end
EREFERENCE = /&(?!#{Entity::NAME};)/
def Text::normalize( input, doctype=nil, entity_filter=nil )
copy = input.to_s
copy = copy.gsub( "&", "&" )
if doctype
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
DocType::DEFAULT_ENTITIES.each_value do |entity|
copy = copy.gsub(entity.value, "&#{entity.name};" )
end
end
copy
end
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!( /&/, '&' )
end
rv
end
end
end