CFPropertyList 2.0.14 → 3.0.0
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.
- 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
|
|