CFPropertyList 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +34 -0
- data/lib/rbBinaryCFPropertyList.rb +669 -0
- data/lib/rbCFPlistError.rb +19 -0
- data/lib/rbCFPropertyList.rb +316 -0
- data/lib/rbCFTypes.rb +233 -0
- data/lib/rbXMLCFPropertyList.rb +122 -0
- metadata +79 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# CFFormatError implementation
|
4
|
+
#
|
5
|
+
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
|
6
|
+
# Copyright:: Copyright (c) 2010
|
7
|
+
# License:: MIT License
|
8
|
+
|
9
|
+
class CFPlistError < Exception
|
10
|
+
end
|
11
|
+
|
12
|
+
# Exception thrown when format errors occur
|
13
|
+
class CFFormatError < CFPlistError
|
14
|
+
end
|
15
|
+
|
16
|
+
class CFTypeError < CFPlistError
|
17
|
+
end
|
18
|
+
|
19
|
+
# eof
|
@@ -0,0 +1,316 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# CFPropertyList implementation
|
4
|
+
# class to read, manipulate and write both XML and binary property list
|
5
|
+
# files (plist(5)) as defined by Apple
|
6
|
+
#
|
7
|
+
# == Example
|
8
|
+
#
|
9
|
+
# # create a arbitrary data structure of basic data types
|
10
|
+
# data = {
|
11
|
+
# 'name' => 'John Doe',
|
12
|
+
# 'missing' => true,
|
13
|
+
# 'last_seen' => Time.now,
|
14
|
+
# 'friends' => ['Jane Doe','Julian Doe'],
|
15
|
+
# 'likes' => {
|
16
|
+
# 'me' => false
|
17
|
+
# }
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# # create CFPropertyList::List object
|
21
|
+
# plist = CFPropertyList::List.new
|
22
|
+
#
|
23
|
+
# # call CFPropertyList.guess() to create corresponding CFType values
|
24
|
+
# # pass in optional :convert_unknown_to_string => true to convert things like symbols into strings.
|
25
|
+
# plist.value = CFPropertyList.guess(data)
|
26
|
+
#
|
27
|
+
# # write plist to file
|
28
|
+
# plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)
|
29
|
+
#
|
30
|
+
# # … later, read it again
|
31
|
+
# plist = CFPropertyList::List.new({:file => "example.plist"})
|
32
|
+
# data = CFPropertyList.native_types(plist.value)
|
33
|
+
#
|
34
|
+
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
|
35
|
+
# Copyright:: Copyright (c) 2010
|
36
|
+
# License:: Distributes under the same terms as Ruby
|
37
|
+
|
38
|
+
require 'libxml'
|
39
|
+
require 'kconv'
|
40
|
+
require 'date'
|
41
|
+
|
42
|
+
module CFPropertyList
|
43
|
+
# interface class for PList parsers
|
44
|
+
class ParserInterface
|
45
|
+
# load a plist
|
46
|
+
def load(opts={})
|
47
|
+
return ""
|
48
|
+
end
|
49
|
+
|
50
|
+
# convert a plist to string
|
51
|
+
def to_str(opts={})
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
dirname = File.dirname(__FILE__)
|
58
|
+
require dirname + '/rbCFPlistError.rb'
|
59
|
+
require dirname + '/rbCFTypes.rb'
|
60
|
+
require dirname + '/rbXMLCFPropertyList.rb'
|
61
|
+
require dirname + '/rbBinaryCFPropertyList.rb'
|
62
|
+
|
63
|
+
require 'iconv' unless "".respond_to?("encode")
|
64
|
+
|
65
|
+
module CFPropertyList
|
66
|
+
# Create CFType hierarchy by guessing the correct CFType, e.g.
|
67
|
+
#
|
68
|
+
# x = {
|
69
|
+
# 'a' => ['b','c','d']
|
70
|
+
# }
|
71
|
+
# cftypes = CFPropertyList.guess(x)
|
72
|
+
#
|
73
|
+
# pass optional options hash. Only possible value actually:
|
74
|
+
# +convert_unknown_to_string+:: Convert unknown objects to string calling to_str()
|
75
|
+
# +converter_method+:: Convert unknown objects to known objects calling +method_name+
|
76
|
+
#
|
77
|
+
# cftypes = CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash)
|
78
|
+
def guess(object, options = {})
|
79
|
+
if(object.is_a?(Fixnum) || object.is_a?(Integer)) then
|
80
|
+
return CFInteger.new(object)
|
81
|
+
elsif(object.is_a?(Float) || (Object.const_defined?('BigDecimal') and object.is_a?(BigDecimal))) then
|
82
|
+
return CFReal.new(object)
|
83
|
+
elsif(object.is_a?(TrueClass) || object.is_a?(FalseClass)) then
|
84
|
+
return CFBoolean.new(object)
|
85
|
+
elsif(object.is_a?(String)) then
|
86
|
+
return CFString.new(object)
|
87
|
+
elsif(object.is_a?(Time) || object.is_a?(DateTime)) then
|
88
|
+
return CFDate.new(object)
|
89
|
+
elsif(object.is_a?(IO)) then
|
90
|
+
return CFData.new(object.read, CFData::DATA_RAW)
|
91
|
+
elsif(object.is_a?(Array)) then
|
92
|
+
ary = Array.new
|
93
|
+
object.each do
|
94
|
+
|o|
|
95
|
+
ary.push CFPropertyList.guess(o, options)
|
96
|
+
end
|
97
|
+
|
98
|
+
return CFArray.new(ary)
|
99
|
+
elsif(object.is_a?(Hash)) then
|
100
|
+
hsh = Hash.new
|
101
|
+
object.each_pair do
|
102
|
+
|k,v|
|
103
|
+
k = k.to_s if k.is_a?(Symbol)
|
104
|
+
hsh[k] = CFPropertyList.guess(v, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
return CFDictionary.new(hsh)
|
108
|
+
elsif options[:converter_method] and object.respond_to?(options[:converter_method]) then
|
109
|
+
return CFPropertyList.guess(object.send(options[:converter_method]))
|
110
|
+
elsif options[:convert_unknown_to_string] then
|
111
|
+
return CFString.new(object.to_s)
|
112
|
+
else
|
113
|
+
raise CFTypeError.new("Unknown class #{object.class.to_s}! Try using :convert_unknown_to_string if you want to use unknown object types!")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Converts a CFType hiercharchy to native Ruby types
|
118
|
+
def native_types(object,keys_as_symbols=false)
|
119
|
+
return if object.nil?
|
120
|
+
|
121
|
+
if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) then
|
122
|
+
return object.value
|
123
|
+
elsif(object.is_a?(CFData)) then
|
124
|
+
return object.decoded_value
|
125
|
+
elsif(object.is_a?(CFArray)) then
|
126
|
+
ary = []
|
127
|
+
object.value.each do
|
128
|
+
|v|
|
129
|
+
ary.push CFPropertyList.native_types(v)
|
130
|
+
end
|
131
|
+
|
132
|
+
return ary
|
133
|
+
elsif(object.is_a?(CFDictionary)) then
|
134
|
+
hsh = {}
|
135
|
+
object.value.each_pair do
|
136
|
+
|k,v|
|
137
|
+
k = k.to_sym if keys_as_symbols
|
138
|
+
hsh[k] = CFPropertyList.native_types(v)
|
139
|
+
end
|
140
|
+
|
141
|
+
return hsh
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
module_function :guess, :native_types
|
146
|
+
|
147
|
+
class List
|
148
|
+
# Format constant for binary format
|
149
|
+
FORMAT_BINARY = 1
|
150
|
+
|
151
|
+
# Format constant for XML format
|
152
|
+
FORMAT_XML = 2
|
153
|
+
|
154
|
+
# Format constant for automatic format recognizing
|
155
|
+
FORMAT_AUTO = 0
|
156
|
+
|
157
|
+
@@parsers = [Binary,XML]
|
158
|
+
|
159
|
+
# Path of PropertyList
|
160
|
+
attr_accessor :filename
|
161
|
+
# Path of PropertyList
|
162
|
+
attr_accessor :format
|
163
|
+
# the root value in the plist file
|
164
|
+
attr_accessor :value
|
165
|
+
|
166
|
+
def initialize(opts={})
|
167
|
+
@filename = opts[:file]
|
168
|
+
@format = opts[:format] || FORMAT_AUTO
|
169
|
+
@data = opts[:data]
|
170
|
+
|
171
|
+
load(@filename) unless @filename.nil?
|
172
|
+
load_str(@data) unless @data.nil?
|
173
|
+
end
|
174
|
+
|
175
|
+
# Load an XML PropertyList
|
176
|
+
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
|
177
|
+
def load_xml(filename=nil)
|
178
|
+
load(filename,List::FORMAT_XML)
|
179
|
+
end
|
180
|
+
|
181
|
+
# read a binary plist file
|
182
|
+
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
|
183
|
+
def load_binary(filename=nil)
|
184
|
+
load(filename,List::FORMAT_BINARY)
|
185
|
+
end
|
186
|
+
|
187
|
+
# load a plist from a XML string
|
188
|
+
# str:: The string containing the plist
|
189
|
+
def load_xml_str(str=nil)
|
190
|
+
load_str(str,List::FORMAT_XML)
|
191
|
+
end
|
192
|
+
|
193
|
+
# load a plist from a binary string
|
194
|
+
# str:: The string containing the plist
|
195
|
+
def load_binary_str(str=nil)
|
196
|
+
load_str(str,List::FORMAT_BINARY)
|
197
|
+
end
|
198
|
+
|
199
|
+
# load a plist from a string
|
200
|
+
# str = nil:: The string containing the plist
|
201
|
+
# format = nil:: The format of the plist
|
202
|
+
def load_str(str=nil,format=nil)
|
203
|
+
str = @data if str.nil?
|
204
|
+
format = @format if format.nil?
|
205
|
+
|
206
|
+
@value = {}
|
207
|
+
case format
|
208
|
+
when List::FORMAT_BINARY, List::FORMAT_XML then
|
209
|
+
prsr = @@parsers[format-1].new
|
210
|
+
@value = prsr.load({:data => str})
|
211
|
+
|
212
|
+
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
|
213
|
+
filetype = str[0..5]
|
214
|
+
version = str[6..7]
|
215
|
+
|
216
|
+
prsr = nil
|
217
|
+
if filetype == "bplist" then
|
218
|
+
raise CFFormatError.new("Wong file version #{version}") unless version == "00"
|
219
|
+
prsr = Binary.new
|
220
|
+
else
|
221
|
+
prsr = XML.new
|
222
|
+
end
|
223
|
+
|
224
|
+
@value = prsr.load({:data => str})
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Read a plist file
|
229
|
+
# file = nil:: The filename of the file to read. If nil, use +filename+ instance variable
|
230
|
+
# format = nil:: The format of the plist file. Auto-detect if nil
|
231
|
+
def load(file=nil,format=nil)
|
232
|
+
file = @filename if file.nil?
|
233
|
+
format = @format if format.nil?
|
234
|
+
@value = {}
|
235
|
+
|
236
|
+
raise IOError.new("File #{file} not readable!") unless File.readable? file
|
237
|
+
|
238
|
+
case format
|
239
|
+
when List::FORMAT_BINARY, List::FORMAT_XML then
|
240
|
+
prsr = @@parsers[format-1].new
|
241
|
+
@value = prsr.load({:file => file})
|
242
|
+
|
243
|
+
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
|
244
|
+
magic_number = IO.read(file,8)
|
245
|
+
filetype = magic_number[0..5]
|
246
|
+
version = magic_number[6..7]
|
247
|
+
|
248
|
+
prsr = nil
|
249
|
+
if filetype == "bplist" then
|
250
|
+
raise CFFormatError.new("Wong file version #{version}") unless version == "00"
|
251
|
+
prsr = Binary.new
|
252
|
+
else
|
253
|
+
prsr = XML.new
|
254
|
+
end
|
255
|
+
|
256
|
+
@value = prsr.load({:file => file})
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Serialize CFPropertyList object to specified format and write it to file
|
261
|
+
# file = nil:: The filename of the file to write to. Uses +filename+ instance variable if nil
|
262
|
+
# format = nil:: The format to save in. Uses +format+ instance variable if nil
|
263
|
+
def save(file=nil,format=nil,opts={})
|
264
|
+
format = @format if format.nil?
|
265
|
+
file = @filename if file.nil?
|
266
|
+
|
267
|
+
raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML") if format != FORMAT_BINARY && format != FORMAT_XML
|
268
|
+
|
269
|
+
if(!File.exists?(file)) then
|
270
|
+
raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
|
271
|
+
elsif(!File.writable?(file)) then
|
272
|
+
raise IOError.new("File #{file} not writable!")
|
273
|
+
end
|
274
|
+
|
275
|
+
opts[:root] = @value
|
276
|
+
prsr = @@parsers[format-1].new
|
277
|
+
content = prsr.to_str(opts)
|
278
|
+
|
279
|
+
File.open(file, 'wb') {
|
280
|
+
|fd|
|
281
|
+
fd.write content
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
285
|
+
# convert plist to string
|
286
|
+
# format = List::FORMAT_BINARY:: The format to save the plist
|
287
|
+
# opts={}:: Pass parser options
|
288
|
+
def to_str(format=List::FORMAT_BINARY,opts={})
|
289
|
+
prsr = @@parsers[format-1].new
|
290
|
+
opts[:root] = @value
|
291
|
+
return prsr.to_str(opts)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
class Array
|
297
|
+
def to_plist(options={})
|
298
|
+
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
|
299
|
+
|
300
|
+
plist = CFPropertyList::List.new
|
301
|
+
plist.value = CFPropertyList.guess(self, options)
|
302
|
+
plist.to_str(options[:plist_format])
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class Hash
|
307
|
+
def to_plist(options={})
|
308
|
+
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
|
309
|
+
|
310
|
+
plist = CFPropertyList::List.new
|
311
|
+
plist.value = CFPropertyList.guess(self, options)
|
312
|
+
plist.to_str(options[:plist_format])
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# eof
|
data/lib/rbCFTypes.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# CFTypes, e.g. CFString, CFInteger
|
4
|
+
# needed to create unambiguous plists
|
5
|
+
#
|
6
|
+
# Author:: Christian Kruse (mailto:cjk@wwwtech.de)
|
7
|
+
# Copyright:: Copyright (c) 2009
|
8
|
+
# License:: Distributes under the same terms as Ruby
|
9
|
+
|
10
|
+
require 'base64'
|
11
|
+
|
12
|
+
module CFPropertyList
|
13
|
+
# This class defines the base class for all CFType classes
|
14
|
+
#
|
15
|
+
class CFType
|
16
|
+
# value of the type
|
17
|
+
attr_accessor :value
|
18
|
+
|
19
|
+
|
20
|
+
# set internal value to parameter value by default
|
21
|
+
def initialize(value=nil)
|
22
|
+
@value = value
|
23
|
+
end
|
24
|
+
|
25
|
+
# convert type to XML
|
26
|
+
def to_xml
|
27
|
+
end
|
28
|
+
|
29
|
+
# convert type to binary
|
30
|
+
def to_binary(bplist)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# This class holds string values, both, UTF-8 and UTF-16BE
|
35
|
+
# It will convert the value to UTF-16BE if necessary (i.e. if non-ascii char contained)
|
36
|
+
class CFString < CFType
|
37
|
+
# convert to XML
|
38
|
+
def to_xml
|
39
|
+
n = LibXML::XML::Node.new('string')
|
40
|
+
n << LibXML::XML::Node.new_text(@value) unless @value.nil?
|
41
|
+
return n
|
42
|
+
end
|
43
|
+
|
44
|
+
# convert to binary
|
45
|
+
def to_binary(bplist)
|
46
|
+
return bplist.string_to_binary(@value);
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# This class holds integer/fixnum values
|
51
|
+
class CFInteger < CFType
|
52
|
+
# convert to XML
|
53
|
+
def to_xml
|
54
|
+
return LibXML::XML::Node.new('integer') << LibXML::XML::Node.new_text(@value.to_s)
|
55
|
+
end
|
56
|
+
|
57
|
+
# convert to binary
|
58
|
+
def to_binary(bplist)
|
59
|
+
return bplist.num_to_binary(self)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# This class holds float values
|
64
|
+
class CFReal < CFType
|
65
|
+
# convert to XML
|
66
|
+
def to_xml
|
67
|
+
return LibXML::XML::Node.new('real') << LibXML::XML::Node.new_text(@value.to_s)
|
68
|
+
end
|
69
|
+
|
70
|
+
# convert to binary
|
71
|
+
def to_binary(bplist)
|
72
|
+
return bplist.num_to_binary(self)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# This class holds Time values. While Apple uses seconds since 2001,
|
77
|
+
# the rest of the world uses seconds since 1970. So if you access value
|
78
|
+
# directly, you get the Time class. If you access via get_value you either
|
79
|
+
# geht the timestamp or the Apple timestamp
|
80
|
+
class CFDate < CFType
|
81
|
+
TIMESTAMP_APPLE = 0
|
82
|
+
TIMESTAMP_UNIX = 1;
|
83
|
+
DATE_DIFF_APPLE_UNIX = 978307200
|
84
|
+
|
85
|
+
# create a XML date strimg from a time object
|
86
|
+
def CFDate.date_string(val)
|
87
|
+
# 2009-05-13T20:23:43Z
|
88
|
+
val.getutc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
89
|
+
end
|
90
|
+
|
91
|
+
# parse a XML date string
|
92
|
+
def CFDate.parse_date(val)
|
93
|
+
# 2009-05-13T20:23:43Z
|
94
|
+
val =~ %r{^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$}
|
95
|
+
year,month,day,hour,min,sec = $1, $2, $3, $4, $5, $6
|
96
|
+
return Time.utc(year,month,day,hour,min,sec).getlocal
|
97
|
+
end
|
98
|
+
|
99
|
+
# set value to defined state
|
100
|
+
def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX)
|
101
|
+
if(value.is_a?(Time) || value.nil?) then
|
102
|
+
@value = value.nil? ? Time.now : value
|
103
|
+
else
|
104
|
+
set_value(value,format)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# set value with timestamp, either Apple or UNIX
|
109
|
+
def set_value(value,format=CFDate::TIMESTAMP_UNIX)
|
110
|
+
if(format == CFDate::TIMESTAMP_UNIX) then
|
111
|
+
@value = Time.at(value)
|
112
|
+
else
|
113
|
+
@value = Time.at(value + CFDate::DATE_DIFF_APPLE_UNIX)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# get timestamp, either UNIX or Apple timestamp
|
118
|
+
def get_value(format=CFDate::TIMESTAMP_UNIX)
|
119
|
+
if(format == CFDate::TIMESTAMP_UNIX) then
|
120
|
+
return @value.to_i
|
121
|
+
else
|
122
|
+
return @value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# convert to XML
|
127
|
+
def to_xml
|
128
|
+
return LibXML::XML::Node.new('date') << LibXML::XML::Node.new_text(CFDate::date_string(@value))
|
129
|
+
end
|
130
|
+
|
131
|
+
# convert to binary
|
132
|
+
def to_binary(bplist)
|
133
|
+
return bplist.date_to_binary(@value)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# This class contains a boolean value
|
138
|
+
class CFBoolean < CFType
|
139
|
+
# convert to XML
|
140
|
+
def to_xml
|
141
|
+
return LibXML::XML::Node.new(@value ? 'true' : 'false')
|
142
|
+
end
|
143
|
+
|
144
|
+
# convert to binary
|
145
|
+
def to_binary(bplist)
|
146
|
+
return bplist.bool_to_binary(@value);
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# This class contains binary data values
|
151
|
+
class CFData < CFType
|
152
|
+
# Base64 encoded data
|
153
|
+
DATA_BASE64 = 0
|
154
|
+
# Raw data
|
155
|
+
DATA_RAW = 1
|
156
|
+
|
157
|
+
# set value to defined state, either base64 encoded or raw
|
158
|
+
def initialize(value=nil,format=DATA_BASE64)
|
159
|
+
if(format == DATA_RAW) then
|
160
|
+
@value = Base64.encode64(value)
|
161
|
+
else
|
162
|
+
@value = value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# get base64 decoded value
|
167
|
+
def decoded_value
|
168
|
+
return Base64.decode64(@value)
|
169
|
+
end
|
170
|
+
|
171
|
+
# convert to XML
|
172
|
+
def to_xml
|
173
|
+
return LibXML::XML::Node.new('data') << LibXML::XML::Node.new_text(@value)
|
174
|
+
end
|
175
|
+
|
176
|
+
# convert to binary
|
177
|
+
def to_binary(bplist)
|
178
|
+
return bplist.data_to_binary(decoded_value())
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# This class contains an array of values
|
183
|
+
class CFArray < CFType
|
184
|
+
# create a new array CFType
|
185
|
+
def initialize(val=[])
|
186
|
+
@value = val
|
187
|
+
end
|
188
|
+
|
189
|
+
# convert to XML
|
190
|
+
def to_xml
|
191
|
+
n = LibXML::XML::Node.new('array')
|
192
|
+
@value.each do
|
193
|
+
|v|
|
194
|
+
n << v.to_xml
|
195
|
+
end
|
196
|
+
|
197
|
+
return n
|
198
|
+
end
|
199
|
+
|
200
|
+
# convert to binary
|
201
|
+
def to_binary(bplist)
|
202
|
+
return bplist.array_to_binary(self)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# this class contains a hash of values
|
207
|
+
class CFDictionary < CFType
|
208
|
+
# Create new CFDictonary type.
|
209
|
+
def initialize(value={})
|
210
|
+
@value = value
|
211
|
+
end
|
212
|
+
|
213
|
+
# convert to XML
|
214
|
+
def to_xml
|
215
|
+
n = LibXML::XML::Node.new('dict')
|
216
|
+
@value.each_pair do
|
217
|
+
|key,value|
|
218
|
+
k = LibXML::XML::Node.new('key') << LibXML::XML::Node.new_text(key)
|
219
|
+
n << k
|
220
|
+
n << value.to_xml
|
221
|
+
end
|
222
|
+
|
223
|
+
return n
|
224
|
+
end
|
225
|
+
|
226
|
+
# convert to binary
|
227
|
+
def to_binary(bplist)
|
228
|
+
return bplist.dict_to_binary(self)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# eof
|