CFPropertyList 2.0.14 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +79 -0
- data/{README → README.rdoc} +8 -1
- data/THANKS +7 -0
- data/lib/{rbBinaryCFPropertyList.rb → cfpropertylist/rbBinaryCFPropertyList.rb} +223 -228
- data/lib/{rbCFPlistError.rb → cfpropertylist/rbCFPlistError.rb} +1 -1
- data/lib/{rbCFPropertyList.rb → cfpropertylist/rbCFPropertyList.rb} +157 -59
- data/lib/{rbCFTypes.rb → cfpropertylist/rbCFTypes.rb} +158 -50
- data/lib/{rbXMLCFPropertyList.rb → cfpropertylist/rbLibXMLParser.rb} +36 -12
- data/lib/cfpropertylist/rbNokogiriParser.rb +151 -0
- data/lib/cfpropertylist/rbPlainCFPropertyList.rb +199 -0
- data/lib/cfpropertylist/rbREXMLParser.rb +148 -0
- data/lib/cfpropertylist.rb +2 -2
- metadata +52 -59
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
require 'libxml'
|
4
3
|
require 'kconv'
|
5
4
|
require 'date'
|
6
5
|
require 'time'
|
@@ -44,6 +43,10 @@ require 'time'
|
|
44
43
|
# Copyright:: Copyright (c) 2010
|
45
44
|
# License:: MIT License
|
46
45
|
module CFPropertyList
|
46
|
+
class << self
|
47
|
+
attr_accessor :xml_parser_interface
|
48
|
+
end
|
49
|
+
|
47
50
|
# interface class for PList parsers
|
48
51
|
class ParserInterface
|
49
52
|
# load a plist
|
@@ -56,25 +59,15 @@ module CFPropertyList
|
|
56
59
|
return true
|
57
60
|
end
|
58
61
|
end
|
59
|
-
end
|
60
62
|
|
61
|
-
class
|
62
|
-
|
63
|
-
|
64
|
-
attr_accessor :blob
|
65
|
-
end
|
63
|
+
class XMLParserInterface < ParserInterface
|
64
|
+
def new_node(name)
|
65
|
+
end
|
66
66
|
|
67
|
-
|
68
|
-
# Returns whether or not +str+ is a blob.
|
69
|
-
# @return [true,false] If true, this string contains binary data. If false, its a regular string
|
70
|
-
def blob?
|
71
|
-
@blob
|
67
|
+
def new_text(val)
|
72
68
|
end
|
73
|
-
end
|
74
69
|
|
75
|
-
|
76
|
-
def bytesize
|
77
|
-
self.length
|
70
|
+
def append_node(parent, child)
|
78
71
|
end
|
79
72
|
end
|
80
73
|
end
|
@@ -82,10 +75,29 @@ end
|
|
82
75
|
dirname = File.dirname(__FILE__)
|
83
76
|
require dirname + '/rbCFPlistError.rb'
|
84
77
|
require dirname + '/rbCFTypes.rb'
|
85
|
-
require dirname + '/rbXMLCFPropertyList.rb'
|
86
78
|
require dirname + '/rbBinaryCFPropertyList.rb'
|
79
|
+
require dirname + '/rbPlainCFPropertyList.rb'
|
80
|
+
|
81
|
+
begin
|
82
|
+
require dirname + '/rbLibXMLParser.rb'
|
83
|
+
temp = LibXML::XML::Parser::Options::NOBLANKS # check if we have a version with parser options
|
84
|
+
temp = false # avoid a warning
|
85
|
+
try_nokogiri = false
|
86
|
+
CFPropertyList.xml_parser_interface = CFPropertyList::LibXMLParser
|
87
|
+
rescue LoadError, NameError
|
88
|
+
try_nokogiri = true
|
89
|
+
end
|
90
|
+
|
91
|
+
if try_nokogiri then
|
92
|
+
begin
|
93
|
+
require dirname + '/rbNokogiriParser.rb'
|
94
|
+
CFPropertyList.xml_parser_interface = CFPropertyList::NokogiriXMLParser
|
95
|
+
rescue LoadError
|
96
|
+
require dirname + '/rbREXMLParser.rb'
|
97
|
+
CFPropertyList.xml_parser_interface = CFPropertyList::ReXMLParser
|
98
|
+
end
|
99
|
+
end
|
87
100
|
|
88
|
-
require 'iconv' unless "".respond_to?("encode")
|
89
101
|
|
90
102
|
module CFPropertyList
|
91
103
|
# Create CFType hierarchy by guessing the correct CFType, e.g.
|
@@ -99,43 +111,59 @@ module CFPropertyList
|
|
99
111
|
# +convert_unknown_to_string+:: Convert unknown objects to string calling to_str()
|
100
112
|
# +converter_method+:: Convert unknown objects to known objects calling +method_name+
|
101
113
|
#
|
102
|
-
# cftypes = CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash)
|
114
|
+
# cftypes = CFPropertyList.guess(x,:convert_unknown_to_string => true,:converter_method => :to_hash, :converter_with_opts => true)
|
103
115
|
def guess(object, options = {})
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
116
|
+
case object
|
117
|
+
when Integer then CFInteger.new(object)
|
118
|
+
when UidFixnum then CFUid.new(object)
|
119
|
+
when Float then CFReal.new(object)
|
120
|
+
when TrueClass, FalseClass then CFBoolean.new(object)
|
121
|
+
|
122
|
+
when Blob
|
123
|
+
CFData.new(object, CFData::DATA_RAW)
|
124
|
+
|
125
|
+
when String, Symbol
|
126
|
+
CFString.new(object.to_s)
|
127
|
+
|
128
|
+
when Time, DateTime, Date
|
129
|
+
CFDate.new(object)
|
130
|
+
|
131
|
+
when Array, Enumerator
|
117
132
|
ary = Array.new
|
118
|
-
object.each do
|
119
|
-
|o|
|
133
|
+
object.each do |o|
|
120
134
|
ary.push CFPropertyList.guess(o, options)
|
121
135
|
end
|
136
|
+
CFArray.new(ary)
|
122
137
|
|
123
|
-
|
124
|
-
elsif(object.is_a?(Hash)) then
|
138
|
+
when Hash
|
125
139
|
hsh = Hash.new
|
126
|
-
object.each_pair do
|
127
|
-
|k,v|
|
140
|
+
object.each_pair do |k,v|
|
128
141
|
k = k.to_s if k.is_a?(Symbol)
|
129
142
|
hsh[k] = CFPropertyList.guess(v, options)
|
130
143
|
end
|
131
|
-
|
132
|
-
return CFDictionary.new(hsh)
|
133
|
-
elsif options[:converter_method] and object.respond_to?(options[:converter_method]) then
|
134
|
-
return CFPropertyList.guess(object.send(options[:converter_method]))
|
135
|
-
elsif options[:convert_unknown_to_string] then
|
136
|
-
return CFString.new(object.to_s)
|
144
|
+
CFDictionary.new(hsh)
|
137
145
|
else
|
138
|
-
|
146
|
+
case
|
147
|
+
when Object.const_defined?('BigDecimal') && object.is_a?(BigDecimal)
|
148
|
+
CFReal.new(object)
|
149
|
+
when object.respond_to?(:read)
|
150
|
+
raw_data = object.read
|
151
|
+
# treat the data as a bytestring (ASCII-8BIT) if Ruby supports it. Do this by forcing
|
152
|
+
# the encoding, on the assumption that the bytes were read correctly, and just tagged with
|
153
|
+
# an inappropriate encoding, rather than transcoding.
|
154
|
+
raw_data.force_encoding(Encoding::ASCII_8BIT) if raw_data.respond_to?(:force_encoding)
|
155
|
+
CFData.new(raw_data, CFData::DATA_RAW)
|
156
|
+
when options[:converter_method] && object.respond_to?(options[:converter_method])
|
157
|
+
if options[:converter_with_opts]
|
158
|
+
CFPropertyList.guess(object.send(options[:converter_method],options),options)
|
159
|
+
else
|
160
|
+
CFPropertyList.guess(object.send(options[:converter_method]),options)
|
161
|
+
end
|
162
|
+
when options[:convert_unknown_to_string]
|
163
|
+
CFString.new(object.to_s)
|
164
|
+
else
|
165
|
+
raise CFTypeError.new("Unknown class #{object.class.to_s}. Try using :convert_unknown_to_string if you want to use unknown object types!")
|
166
|
+
end
|
139
167
|
end
|
140
168
|
end
|
141
169
|
|
@@ -143,10 +171,10 @@ module CFPropertyList
|
|
143
171
|
def native_types(object,keys_as_symbols=false)
|
144
172
|
return if object.nil?
|
145
173
|
|
146
|
-
if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) then
|
174
|
+
if(object.is_a?(CFDate) || object.is_a?(CFString) || object.is_a?(CFInteger) || object.is_a?(CFReal) || object.is_a?(CFBoolean)) || object.is_a?(CFUid) then
|
147
175
|
return object.value
|
148
176
|
elsif(object.is_a?(CFData)) then
|
149
|
-
return object.decoded_value
|
177
|
+
return CFPropertyList::Blob.new(object.decoded_value)
|
150
178
|
elsif(object.is_a?(CFArray)) then
|
151
179
|
ary = []
|
152
180
|
object.value.each do
|
@@ -177,17 +205,22 @@ module CFPropertyList
|
|
177
205
|
# Format constant for XML format
|
178
206
|
FORMAT_XML = 2
|
179
207
|
|
208
|
+
# Format constant for the old plain format
|
209
|
+
FORMAT_PLAIN = 3
|
210
|
+
|
180
211
|
# Format constant for automatic format recognizing
|
181
212
|
FORMAT_AUTO = 0
|
182
213
|
|
183
|
-
@@parsers = [Binary,
|
214
|
+
@@parsers = [Binary, CFPropertyList.xml_parser_interface, PlainParser]
|
184
215
|
|
185
216
|
# Path of PropertyList
|
186
217
|
attr_accessor :filename
|
187
|
-
#
|
218
|
+
# the original format of the PropertyList
|
188
219
|
attr_accessor :format
|
189
220
|
# the root value in the plist file
|
190
221
|
attr_accessor :value
|
222
|
+
# default value for XML generation; if true generate formatted XML
|
223
|
+
attr_accessor :formatted
|
191
224
|
|
192
225
|
# initialize a new CFPropertyList, arguments are:
|
193
226
|
#
|
@@ -200,11 +233,22 @@ module CFPropertyList
|
|
200
233
|
@filename = opts[:file]
|
201
234
|
@format = opts[:format] || FORMAT_AUTO
|
202
235
|
@data = opts[:data]
|
236
|
+
@formatted = opts[:formatted]
|
203
237
|
|
204
238
|
load(@filename) unless @filename.nil?
|
205
239
|
load_str(@data) unless @data.nil?
|
206
240
|
end
|
207
241
|
|
242
|
+
# returns a list of registered parsers
|
243
|
+
def self.parsers
|
244
|
+
@@parsers
|
245
|
+
end
|
246
|
+
|
247
|
+
# set a list of parsers
|
248
|
+
def self.parsers=(val)
|
249
|
+
@@parsers = val
|
250
|
+
end
|
251
|
+
|
208
252
|
# Load an XML PropertyList
|
209
253
|
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
|
210
254
|
def load_xml(filename=nil)
|
@@ -217,6 +261,12 @@ module CFPropertyList
|
|
217
261
|
load(filename,List::FORMAT_BINARY)
|
218
262
|
end
|
219
263
|
|
264
|
+
# read a plain plist file
|
265
|
+
# filename = nil:: The filename to read from; if nil, read from the file defined by instance variable +filename+
|
266
|
+
def load_plain(filename=nil)
|
267
|
+
load(filename,List::FORMAT_PLAIN)
|
268
|
+
end
|
269
|
+
|
220
270
|
# load a plist from a XML string
|
221
271
|
# str:: The string containing the plist
|
222
272
|
def load_xml_str(str=nil)
|
@@ -229,6 +279,12 @@ module CFPropertyList
|
|
229
279
|
load_str(str,List::FORMAT_BINARY)
|
230
280
|
end
|
231
281
|
|
282
|
+
# load a plist from a plain string
|
283
|
+
# str:: The string containing the plist
|
284
|
+
def load_plain_str(str=nil)
|
285
|
+
load_str(str,List::FORMAT_PLAIN)
|
286
|
+
end
|
287
|
+
|
232
288
|
# load a plist from a string
|
233
289
|
# str = nil:: The string containing the plist
|
234
290
|
# format = nil:: The format of the plist
|
@@ -238,7 +294,7 @@ module CFPropertyList
|
|
238
294
|
|
239
295
|
@value = {}
|
240
296
|
case format
|
241
|
-
when List::FORMAT_BINARY, List::FORMAT_XML then
|
297
|
+
when List::FORMAT_BINARY, List::FORMAT_XML, List::FORMAT_PLAIN then
|
242
298
|
prsr = @@parsers[format-1].new
|
243
299
|
@value = prsr.load({:data => str})
|
244
300
|
|
@@ -247,11 +303,19 @@ module CFPropertyList
|
|
247
303
|
version = str[6..7]
|
248
304
|
|
249
305
|
prsr = nil
|
306
|
+
|
250
307
|
if filetype == "bplist" then
|
251
|
-
raise CFFormatError.new("
|
308
|
+
raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
|
252
309
|
prsr = Binary.new
|
310
|
+
@format = List::FORMAT_BINARY
|
253
311
|
else
|
254
|
-
|
312
|
+
if str =~ /^<(\?xml|!DOCTYPE|plist)/
|
313
|
+
prsr = CFPropertyList.xml_parser_interface.new
|
314
|
+
@format = List::FORMAT_XML
|
315
|
+
else
|
316
|
+
prsr = PlainParser.new
|
317
|
+
@format = List::FORMAT_PLAIN
|
318
|
+
end
|
255
319
|
end
|
256
320
|
|
257
321
|
@value = prsr.load({:data => str})
|
@@ -269,25 +333,35 @@ module CFPropertyList
|
|
269
333
|
raise IOError.new("File #{file} not readable!") unless File.readable? file
|
270
334
|
|
271
335
|
case format
|
272
|
-
when List::FORMAT_BINARY, List::FORMAT_XML then
|
336
|
+
when List::FORMAT_BINARY, List::FORMAT_XML, List::FORMAT_PLAIN then
|
273
337
|
prsr = @@parsers[format-1].new
|
274
338
|
@value = prsr.load({:file => file})
|
275
339
|
|
276
340
|
when List::FORMAT_AUTO then # what we now do is ugly, but neccessary to recognize the file format
|
277
|
-
magic_number = IO.read(file,
|
341
|
+
magic_number = IO.read(file,12)
|
342
|
+
raise IOError.new("File #{file} is empty.") unless magic_number
|
278
343
|
filetype = magic_number[0..5]
|
279
344
|
version = magic_number[6..7]
|
280
345
|
|
281
346
|
prsr = nil
|
282
347
|
if filetype == "bplist" then
|
283
|
-
raise CFFormatError.new("
|
348
|
+
raise CFFormatError.new("Wrong file version #{version}") unless version == "00"
|
284
349
|
prsr = Binary.new
|
350
|
+
@format = List::FORMAT_BINARY
|
285
351
|
else
|
286
|
-
|
352
|
+
if magic_number =~ /^<(\?xml|!DOCTYPE|plist)/
|
353
|
+
prsr = CFPropertyList.xml_parser_interface.new
|
354
|
+
@format = List::FORMAT_XML
|
355
|
+
else
|
356
|
+
prsr = PlainParser.new
|
357
|
+
@format = List::FORMAT_PLAIN
|
358
|
+
end
|
287
359
|
end
|
288
360
|
|
289
361
|
@value = prsr.load({:file => file})
|
290
362
|
end
|
363
|
+
|
364
|
+
raise CFFormatError.new("Invalid format or parser error!") if @value.nil?
|
291
365
|
end
|
292
366
|
|
293
367
|
# Serialize CFPropertyList object to specified format and write it to file
|
@@ -297,7 +371,9 @@ module CFPropertyList
|
|
297
371
|
format = @format if format.nil?
|
298
372
|
file = @filename if file.nil?
|
299
373
|
|
300
|
-
|
374
|
+
if format != FORMAT_BINARY && format != FORMAT_XML && format != FORMAT_PLAIN
|
375
|
+
raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML")
|
376
|
+
end
|
301
377
|
|
302
378
|
if(!File.exists?(file)) then
|
303
379
|
raise IOError.new("File #{file} not writable!") unless File.writable?(File.dirname(file))
|
@@ -306,7 +382,10 @@ module CFPropertyList
|
|
306
382
|
end
|
307
383
|
|
308
384
|
opts[:root] = @value
|
385
|
+
opts[:formatted] = @formatted unless opts.has_key?(:formatted)
|
386
|
+
|
309
387
|
prsr = @@parsers[format-1].new
|
388
|
+
|
310
389
|
content = prsr.to_str(opts)
|
311
390
|
|
312
391
|
File.open(file, 'wb') {
|
@@ -319,13 +398,21 @@ module CFPropertyList
|
|
319
398
|
# format = List::FORMAT_BINARY:: The format to save the plist
|
320
399
|
# opts={}:: Pass parser options
|
321
400
|
def to_str(format=List::FORMAT_BINARY,opts={})
|
401
|
+
if format != FORMAT_BINARY && format != FORMAT_XML && format != FORMAT_PLAIN
|
402
|
+
raise CFFormatError.new("Format #{format} not supported, use List::FORMAT_BINARY or List::FORMAT_XML")
|
403
|
+
end
|
404
|
+
|
322
405
|
prsr = @@parsers[format-1].new
|
406
|
+
|
323
407
|
opts[:root] = @value
|
408
|
+
opts[:formatted] = @formatted unless opts.has_key?(:formatted)
|
409
|
+
|
324
410
|
return prsr.to_str(opts)
|
325
411
|
end
|
326
412
|
end
|
327
413
|
end
|
328
414
|
|
415
|
+
|
329
416
|
class Array
|
330
417
|
# convert an array to plist format
|
331
418
|
def to_plist(options={})
|
@@ -333,7 +420,18 @@ class Array
|
|
333
420
|
|
334
421
|
plist = CFPropertyList::List.new
|
335
422
|
plist.value = CFPropertyList.guess(self, options)
|
336
|
-
plist.to_str(options[:plist_format])
|
423
|
+
plist.to_str(options[:plist_format], options)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
class Enumerator
|
428
|
+
# convert an array to plist format
|
429
|
+
def to_plist(options={})
|
430
|
+
options[:plist_format] ||= CFPropertyList::List::FORMAT_BINARY
|
431
|
+
|
432
|
+
plist = CFPropertyList::List.new
|
433
|
+
plist.value = CFPropertyList.guess(self, options)
|
434
|
+
plist.to_str(options[:plist_format], options)
|
337
435
|
end
|
338
436
|
end
|
339
437
|
|
@@ -344,7 +442,7 @@ class Hash
|
|
344
442
|
|
345
443
|
plist = CFPropertyList::List.new
|
346
444
|
plist.value = CFPropertyList.guess(self, options)
|
347
|
-
plist.to_str(options[:plist_format])
|
445
|
+
plist.to_str(options[:plist_format], options)
|
348
446
|
end
|
349
447
|
end
|
350
448
|
|