require 'xmlscan/parser' require 'time' class Plist class Visitor include XMLScan::Visitor def initialize @stack = [] @preserve = false end attr_reader :stack def on_stag(name) sym = name.intern push(sym) @preserve = [:string, :key, :data].include?(sym) end def on_chardata(str) return if !@preserve && /^\s*$/ =~ str if tail.class == String str = pop + str end push(str) end def on_stag_end_empty(name) @preserve = false case name when 'true' eval_true when 'false' eval_true end end def on_etag(name) @preserve = false case name when 'dict' eval_dict when 'integer' eval_integer when 'real' eval_real when 'string' eval_string when 'date' eval_date when 'array' eval_array when 'key' eval_key when 'plist' eval_plist end end def eval_integer value = pop.to_i pop_stag(:integer) push(value) end def eval_real value = pop.to_f pop_stag(:real) push(value) end def eval_string value = pop_str pop_stag(:string) push(value) end def eval_key value = pop pop_stag(:key) push(value) end def eval_date value = Time.parse(pop) pop_stag(:date) push(value) end def eval_true pop_stag(:true) push(true) end def eval_false pop_stag(:false) push(false) end def eval_array value = [] while v = pop break if v == :array value.push(v) end push(value) end def eval_dict value = {} while v = pop break if v == :dict k = pop break if k == :dict #FIXME value[k] = v end push(value) end def eval_plist value = pop pop_stag(:plist) push(value) end def push(obj) @stack.push(obj) end def pop @stack.pop end def pop_str (tail.class == String) ? pop : '' end def pop_stag(sym) return pop unless $DEBUG v = pop raise([sym, v].inspect) unless v == sym return v end def tail @stack[-1] end end def self.file_to_plist(fname) File.open(fname) do |f| visitor = Visitor.new parser = XMLScan::XMLParser.new(visitor) parser.parse(f) visitor.tail end end end