require 'rexml/encoding'
module REXML
class SourceFactory
def SourceFactory::create_from(arg)
if arg.kind_of? String
Source.new(arg)
elsif arg.respond_to? :read and
arg.respond_to? :readline and
arg.respond_to? :nil? and
arg.respond_to? :eof?
IOSource.new(arg)
elsif arg.kind_of? Source
arg
else
raise "#{arg.class} is not a valid input stream. It must walk \n"+
"like either a String, an IO, or a Source."
end
end
end
class Source
include Encoding
attr_reader :buffer
attr_reader :line
attr_reader :encoding
def initialize(arg, encoding=nil)
@orig = @buffer = arg
if encoding
self.encoding = encoding
else
self.encoding = check_encoding( @buffer )
end
@line = 0
end
def encoding=(enc)
return unless super
@line_break = encode( '>' )
if enc != UTF_8
@buffer = decode(@buffer)
@to_utf = true
else
@to_utf = false
end
end
def scan(pattern, cons=false)
return nil if @buffer.nil?
rv = @buffer.scan(pattern)
@buffer = $' if cons and rv.size>0
rv
end
def read
end
def consume( pattern )
@buffer = $' if pattern.match( @buffer )
end
def match_to( char, pattern )
return pattern.match(@buffer)
end
def match_to_consume( char, pattern )
md = pattern.match(@buffer)
@buffer = $'
return md
end
def match(pattern, cons=false)
md = pattern.match(@buffer)
@buffer = $' if cons and md
return md
end
def empty?
@buffer == ""
end
def position
@orig.index( @buffer )
end
def current_line
lines = @orig.split
res = lines.grep @buffer[0..30]
res = res[-1] if res.kind_of? Array
lines.index( res ) if res
end
end
class IOSource < Source
def initialize(arg, block_size=500, encoding=nil)
@er_source = @source = arg
@to_utf = false
@buffer = ""
str = @source.read( 2 )
if encoding
self.encoding = encoding
elsif 0xfe == str[0] && 0xff == str[1]
@line_break = "\000>"
elsif 0xff == str[0] && 0xfe == str[1]
@line_break = ">\000"
elsif 0xef == str[0] && 0xbb == str[1]
str += @source.read(1)
str = '' if (0xbf == str[2])
@line_break = ">"
else
@line_break = ">"
end
super str+@source.readline( @line_break )
end
def scan(pattern, cons=false)
rv = super
if rv.size == 0
until @buffer =~ pattern or @source.nil?
begin
str = @source.readline(@line_break)
str = decode(str) if @to_utf and str
@buffer << str
rescue Iconv::IllegalSequence
raise
rescue
@source = nil
end
end
rv = super
end
rv.taint
rv
end
def read
begin
str = @source.readline(@line_break)
str = decode(str) if @to_utf and str
@buffer << str
rescue Exception, NameError
@source = nil
end
end
def consume( pattern )
match( pattern, true )
end
def match( pattern, cons=false )
rv = pattern.match(@buffer)
@buffer = $' if cons and rv
while !rv and @source
begin
str = @source.readline(@line_break)
str = decode(str) if @to_utf and str
@buffer << str
rv = pattern.match(@buffer)
@buffer = $' if cons and rv
rescue
@source = nil
end
end
rv.taint
rv
end
def empty?
super and ( @source.nil? || @source.eof? )
end
def position
@er_source.stat.pipe? ? 0 : @er_source.pos
end
def current_line
begin
pos = @er_source.pos lineno = @er_source.lineno @er_source.rewind
line = 0 begin
while @er_source.pos < pos
@er_source.readline
line += 1
end
rescue
end
rescue IOError
pos = -1
line = -1
end
[pos, lineno, line]
end
end
end