1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
# -*- coding: utf-8 -*-
require 'rexml/document'
module CFPropertyList
# XML parser
class ReXMLParser < ParserInterface
# read a XML file
# opts::
# * :file - The filename of the file to load
# * :data - The data to parse
def load(opts)
doc = nil
if(opts.has_key?(:file)) then
File.open(opts[:file], "rb") { |fd| doc = REXML::Document.new(fd) }
else
doc = REXML::Document.new(opts[:data])
end
if doc
root = doc.root.elements[1]
return import_xml(root)
end
rescue REXML::ParseException => e
raise CFFormatError.new('invalid XML: ' + e.message)
end
# serialize CFPropertyList object to XML
# opts = {}:: Specify options: :formatted - Use indention and line breaks
def to_str(opts={})
doc = REXML::Document.new
@doc = doc
doc.context[:attribute_quote] = :quote
doc.add_element 'plist', {'version' => '1.0'}
doc.root << opts[:root].to_xml(self)
formatter = if opts[:formatted] then
f = REXML::Formatters::Pretty.new(2)
f.compact = true
f
else
REXML::Formatters::Default.new
end
str = formatter.write(doc.root, "")
str1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + str + "\n"
str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
return str1
end
def new_node(name)
REXML::Element.new(name)
end
def new_text(val)
val
end
def append_node(parent, child)
if child.is_a?(String) then
parent.add_text child
else
parent.elements << child
end
parent
end
protected
# get the value of a DOM node
def get_value(n)
content = n.text
content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
content
end
# import the XML values
def import_xml(node)
ret = nil
case node.name
when 'dict'
hsh = Hash.new
key = nil
if node.has_elements? then
node.elements.each do |n|
next if n.name == '#text' # avoid a bug of libxml
next if n.name == '#comment'
if n.name == "key" then
key = get_value(n)
key = '' if key.nil? # REXML returns nil if key is empty
else
raise CFFormatError.new("Format error!") if key.nil?
hsh[key] = import_xml(n)
key = nil
end
end
end
if hsh['CF$UID'] and hsh.keys.length == 1
ret = CFUid.new(hsh['CF$UID'].value)
else
ret = CFDictionary.new(hsh)
end
when 'array'
ary = Array.new
if node.has_elements? then
node.elements.each do |n|
next if n.name == '#text' # avoid a bug of libxml
ary.push import_xml(n)
end
end
ret = CFArray.new(ary)
when 'true'
ret = CFBoolean.new(true)
when 'false'
ret = CFBoolean.new(false)
when 'real'
ret = CFReal.new(get_value(node).to_f)
when 'integer'
ret = CFInteger.new(get_value(node).to_i)
when 'string'
ret = CFString.new(get_value(node))
ret.value = '' if ret.value.nil? # REXML returns nil for empty elements' .text attribute
when 'data'
ret = CFData.new(get_value(node))
when 'date'
ret = CFDate.new(CFDate.parse_date(get_value(node)))
end
return ret
end
end
end
# eof
|