require "rexml/parseexception"

module REXML
	# Represents a node in the tree.  Nodes are never encountered except as
	# superclasses of other objects.  Nodes have siblings.
	module Node
		# The order in which the substitutions occur
		SPECIALS = [ /&(?!#?[\w-]+;)/u, /</u, />/u, /"/u, /'/u, /\r/u ]
		SUBSTITUTES = [ '&amp;', '&lt;', '&gt;', '&quot;', '&apos;', '&#13;']
		# Characters which are substituted in written strings
		SLAICEPS = [ '<', '>', '"', "'", '&' ]
		SETUTITSBUS = [ /&lt;/u, /&gt;/u, /&quot;/u, /&apos;/u, /&amp;/u ]

		# @return the next sibling (nil if unset)
		def next_sibling_node
			return nil if @parent.nil?
			@parent[ @parent.index(self) + 1 ]
		end

		# @return the previous sibling (nil if unset)
		def previous_sibling_node
			return nil if @parent.nil?
			ind = @parent.index(self)
			return nil if ind == 0
			@parent[ ind - 1 ]
		end

		def to_s indent=-1
			rv = ""
			write rv,indent
			rv
		end

		def indent to, ind
			to << " "*ind unless ind<1
		end
		
		# Writes out text, substituting special characters beforehand.
		# @param out A String, IO, or any other object supporting <<( String )
		# @param 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
		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

		# Reads text, substituting entities
		def Node::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!( /&#0*((?:\d+)|(?:x[a-f0-9]+));/ ) {|m|
					m=$1
					#m='0' if m==''
					m = "0#{m}" if m[0] == ?x
					[Integer(m)].pack('U*')
				}
				copy.gsub!( SETUTITSBUS[4], SLAICEPS[4] )
			end

			copy
		end
	end
end
