class REXML::Parsers::BaseParser
Using the Pull Parser
This API is experimental, and subject to change.
parser = PullParser.new( "<a>text<b att='val'/>txet</a>" ) while parser.has_next? res = parser.next puts res[1]['att'] if res.start_tag? and res[0] == 'b' end
See the PullEvent class for information on the content of the results. The data is identical to the arguments passed for the various events to the StreamListener API.
Notice that:
parser = PullParser.new( "<a>BAD DOCUMENT" ) while parser.has_next? res = parser.next raise res[1] if res.error? end
Nat Price gave me some good ideas for the API.
Constants
- ATTDEF
- ATTDEF_RE
- ATTLISTDECL_PATTERN
- ATTLISTDECL_START
- ATTRIBUTE_PATTERN
- ATTTYPE
- ATTVALUE
- CDATA_END
- CDATA_PATTERN
- CDATA_START
- CLOSE_MATCH
- COMBININGCHAR
- COMMENT_PATTERN
- COMMENT_START
- DEFAULTDECL
- DEFAULT_ENTITIES
- DIGIT
- DOCTYPE_END
- DOCTYPE_START
- ELEMENTDECL_PATTERN
- ELEMENTDECL_START
- ENCODING
- ENTITYDECL
- ENTITYDEF
- ENTITYVALUE
- ENTITY_START
- ENUMERATEDTYPE
- ENUMERATION
- EREFERENCE
- EXTENDER
- EXTERNALID
- EXTERNAL_ID_PUBLIC
- EXTERNAL_ID_SYSTEM
- GEDECL
- INSTRUCTION_PATTERN
- INSTRUCTION_START
- LETTER
- NAME
- NAMECHAR
- NCNAME_STR
- NDATADECL
- NMTOKEN
- NMTOKENS
- NOTATIONDECL_START
- NOTATIONTYPE
- PEDECL
- PEDEF
- PEREFERENCE
- PUBIDCHAR
-
Entityconstants - PUBIDLITERAL
- PUBLIC_ID
- QNAME
- QNAME_STR
- REFERENCE
- REFERENCE_RE
- STANDALONE
- SYSTEMENTITY
- SYSTEMLITERAL
- TAG_MATCH
- TEXT_PATTERN
- UNAME_STR
-
Just for backward compatibility. For example, kramdown uses this. Itβs not used in
REXML. - VERSION
- XMLDECL_PATTERN
- XMLDECL_START
Attributes
Public Class Methods
Source
# File lib/rexml/parsers/baseparser.rb, line 169 def initialize( source ) self.stream = source @listeners = [] @prefixes = Set.new @entity_expansion_count = 0 @entity_expansion_limit = Security.entity_expansion_limit @entity_expansion_text_limit = Security.entity_expansion_text_limit @source.ensure_buffer @version = nil end
Public Instance Methods
Source
# File lib/rexml/parsers/baseparser.rb, line 180 def add_listener( listener ) @listeners << listener end
Source
# File lib/rexml/parsers/baseparser.rb, line 215 def empty? (@source.empty? and @stack.empty?) end
Returns true if there are no more events
# File lib/rexml/parsers/baseparser.rb, line 540 def entity( reference, entities, expanding: nil ) return unless entities value = entities[ reference ] return if value.nil? expanding ||= Set.new raise REXML::ParseException.new("Detected an entity reference loop: #{reference}", @source) if expanding.include?(reference) record_entity_expansion expanding.add(reference) result = unnormalize( value, entities, nil, expanding: expanding ) expanding.delete(reference) result end
Source
# File lib/rexml/parsers/baseparser.rb, line 220 def has_next? !(@source.empty? and @stack.empty?) end
Returns true if there are more events. Synonymous with !empty?
# File lib/rexml/parsers/baseparser.rb, line 557 def normalize( input, entities=nil, entity_filter=nil ) copy = input.clone # Doing it like this rather than in a loop improves the speed copy.gsub!( EREFERENCE, '&' ) entities.each do |key, value| copy.gsub!( value, "&#{key};" ) unless entity_filter and entity_filter.include?(entity) end if entities copy.gsub!( EREFERENCE, '&' ) DEFAULT_ENTITIES.each do |key, value| copy.gsub!( value[3], value[1] ) end copy end
Escapes all possible entities
Source
# File lib/rexml/parsers/baseparser.rb, line 236 def peek depth=0 raise %Q[Illegal argument "#{depth}"] if depth < -1 temp = [] if depth == -1 temp.push(pull()) until empty? else while @stack.size+temp.size < depth+1 temp.push(pull()) end end @stack += temp if temp.size > 0 @stack[depth] end
Peek at the depth event in the stack. The first element on the stack is at depth 0. If depth is -1, will parse to the end of the input stream and return the last event, which is always :end_document. Be aware that this causes the stream to be parsed up to the depth event, so you can effectively pre-parse the entire document (pull the entire thing into memory) using this method.
Source
# File lib/rexml/parsers/baseparser.rb, line 205 def position if @source.respond_to? :position @source.position else # FIXME 0 end end
Source
# File lib/rexml/parsers/baseparser.rb, line 251 def pull @source.drop_parsed_content pull_event.tap do |event| @listeners.each do |listener| listener.receive event end end end
Returns the next event. This is a PullEvent object.
Source
# File lib/rexml/parsers/baseparser.rb, line 194 def reset @closed = nil @have_root = false @document_status = nil @tags = [] @stack = [] @entities = [] @namespaces = {"xml" => Private::XML_PREFIXED_NAMESPACE} @namespaces_restore_stack = [] end
Source
# File lib/rexml/parsers/baseparser.rb, line 189 def stream=( source ) @source = SourceFactory.create_from( source ) reset end
# File lib/rexml/parsers/baseparser.rb, line 573 def unnormalize( string, entities=nil, filter=nil, expanding: nil ) if string.include?("\r") rv = string.gsub( Private::CARRIAGE_RETURN_NEWLINE_PATTERN, "\n" ) else rv = string.dup end matches = rv.scan( REFERENCE_RE ) return rv if matches.size == 0 rv.gsub!( Private::CHARACTER_REFERENCES ) { m=$1 if m.start_with?("x") code_point = Integer(m[1..-1], 16) else code_point = Integer(m, 10) end Text.expand_character_reference(code_point, "&##{m};") } matches.collect!{|x|x[0]}.compact! if filter matches.reject! do |entity_reference| filter.include?(entity_reference) end end if matches.size > 0 matches.tally.each do |entity_reference, n| entity_expansion_count_before = @entity_expansion_count entity_value = entity( entity_reference, entities, expanding: expanding ) if entity_value if n > 1 entity_expansion_count_delta = @entity_expansion_count - entity_expansion_count_before record_entity_expansion(entity_expansion_count_delta * (n - 1)) end re = Private::DEFAULT_ENTITIES_PATTERNS[entity_reference] || /&#{entity_reference};/ rv.gsub!( re, entity_value ) if rv.bytesize > @entity_expansion_text_limit raise "entity expansion has grown too large" end else er = DEFAULT_ENTITIES[entity_reference] rv.gsub!( er[0], er[2] ) if er end end rv.gsub!( Private::DEFAULT_ENTITIES_PATTERNS['amp'], '&' ) end rv end
Unescapes all possible entities
Source
# File lib/rexml/parsers/baseparser.rb, line 226 def unshift token @stack.unshift(token) end
Push an event back on the head of the stream. This method has (theoretically) infinite depth.