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.
@@ -10,66 +10,129 @@
10
10
  require 'base64'
11
11
 
12
12
  module CFPropertyList
13
+ ##
14
+ # Blob is intended to distinguish between a Ruby String instance that should
15
+ # be converted to a CFString type and a Ruby String instance that should be
16
+ # converted to a CFData type
17
+ class Blob < String
18
+ end
19
+
20
+ ##
21
+ # UidFixnum is intended to distinguish between a Ruby Integer
22
+ # instance that should be converted to a CFInteger/CFReal type and a
23
+ # Ruby Integer instance that should be converted to a CFUid type.
24
+ class UidFixnum < Integer
25
+ end
26
+
13
27
  # This class defines the base class for all CFType classes
14
28
  #
15
29
  class CFType
16
30
  # value of the type
17
31
  attr_accessor :value
18
32
 
19
-
20
- # set internal value to parameter value by default
21
33
  def initialize(value=nil)
22
34
  @value = value
23
35
  end
24
36
 
25
- # convert type to XML
26
- def to_xml
37
+ def to_xml(parser)
27
38
  end
28
39
 
29
- # convert type to binary
30
40
  def to_binary(bplist)
31
41
  end
42
+
43
+ def to_plain(plist)
44
+ end
32
45
  end
33
46
 
34
47
  # This class holds string values, both, UTF-8 and UTF-16BE
35
48
  # It will convert the value to UTF-16BE if necessary (i.e. if non-ascii char contained)
36
49
  class CFString < CFType
37
50
  # 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
51
+ def to_xml(parser)
52
+ n = parser.new_node('string')
53
+ n = parser.append_node(n, parser.new_text(@value)) unless @value.nil?
54
+ n
42
55
  end
43
56
 
44
57
  # convert to binary
45
58
  def to_binary(bplist)
46
- return bplist.string_to_binary(@value);
59
+ bplist.string_to_binary(@value);
60
+ end
61
+
62
+ def to_plain(plist)
63
+ if @value =~ /^\w+$/
64
+ @value
65
+ else
66
+ quoted
67
+ end
68
+ end
69
+
70
+ def quoted
71
+ str = '"'
72
+ @value.each_char do |c|
73
+ str << case c
74
+ when '"'
75
+ '\\"'
76
+ when '\\'
77
+ '\\'
78
+ when "\a"
79
+ "\\a"
80
+ when "\b"
81
+ "\\b"
82
+ when "\f"
83
+ "\\f"
84
+ when "\n"
85
+ "\n"
86
+ when "\v"
87
+ "\\v"
88
+ when "\r"
89
+ "\\r"
90
+ when "\t"
91
+ "\\t"
92
+ else
93
+ c
94
+ end
95
+ end
96
+
97
+ str << '"'
47
98
  end
48
99
  end
49
100
 
50
101
  # This class holds integer/fixnum values
51
102
  class CFInteger < CFType
52
103
  # convert to XML
53
- def to_xml
54
- return LibXML::XML::Node.new('integer') << LibXML::XML::Node.new_text(@value.to_s)
104
+ def to_xml(parser)
105
+ n = parser.new_node('integer')
106
+ n = parser.append_node(n, parser.new_text(@value.to_s))
107
+ n
55
108
  end
56
109
 
57
110
  # convert to binary
58
111
  def to_binary(bplist)
59
- return bplist.num_to_binary(self)
112
+ bplist.num_to_binary(self)
113
+ end
114
+
115
+ def to_plain(plist)
116
+ @value.to_s
60
117
  end
61
118
  end
62
119
 
63
120
  # This class holds float values
64
121
  class CFReal < CFType
65
122
  # convert to XML
66
- def to_xml
67
- return LibXML::XML::Node.new('real') << LibXML::XML::Node.new_text(@value.to_s)
123
+ def to_xml(parser)
124
+ n = parser.new_node('real')
125
+ n = parser.append_node(n, parser.new_text(@value.to_s))
126
+ n
68
127
  end
69
128
 
70
129
  # convert to binary
71
130
  def to_binary(bplist)
72
- return bplist.num_to_binary(self)
131
+ bplist.num_to_binary(self)
132
+ end
133
+
134
+ def to_plain(plist)
135
+ @value.to_s
73
136
  end
74
137
  end
75
138
 
@@ -79,7 +142,7 @@ module CFPropertyList
79
142
  # geht the timestamp or the Apple timestamp
80
143
  class CFDate < CFType
81
144
  TIMESTAMP_APPLE = 0
82
- TIMESTAMP_UNIX = 1;
145
+ TIMESTAMP_UNIX = 1
83
146
  DATE_DIFF_APPLE_UNIX = 978307200
84
147
 
85
148
  # create a XML date strimg from a time object
@@ -100,6 +163,10 @@ module CFPropertyList
100
163
  def initialize(value = nil,format=CFDate::TIMESTAMP_UNIX)
101
164
  if(value.is_a?(Time) || value.nil?) then
102
165
  @value = value.nil? ? Time.now : value
166
+ elsif value.instance_of? Date
167
+ @value = Time.utc(value.year, value.month, value.day, 0, 0, 0)
168
+ elsif value.instance_of? DateTime
169
+ @value = value.to_time.utc
103
170
  else
104
171
  set_value(value,format)
105
172
  end
@@ -117,33 +184,43 @@ module CFPropertyList
117
184
  # get timestamp, either UNIX or Apple timestamp
118
185
  def get_value(format=CFDate::TIMESTAMP_UNIX)
119
186
  if(format == CFDate::TIMESTAMP_UNIX) then
120
- return @value.to_i
187
+ @value.to_i
121
188
  else
122
- return @value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
189
+ @value.to_f - CFDate::DATE_DIFF_APPLE_UNIX
123
190
  end
124
191
  end
125
192
 
126
193
  # convert to XML
127
- def to_xml
128
- return LibXML::XML::Node.new('date') << LibXML::XML::Node.new_text(CFDate::date_string(@value))
194
+ def to_xml(parser)
195
+ n = parser.new_node('date')
196
+ n = parser.append_node(n, parser.new_text(CFDate::date_string(@value)))
197
+ n
129
198
  end
130
199
 
131
200
  # convert to binary
132
201
  def to_binary(bplist)
133
- return bplist.date_to_binary(@value)
202
+ bplist.date_to_binary(@value)
203
+ end
204
+
205
+ def to_plain(plist)
206
+ @value.strftime("%Y-%m-%d %H:%M:%S %z")
134
207
  end
135
208
  end
136
209
 
137
210
  # This class contains a boolean value
138
211
  class CFBoolean < CFType
139
212
  # convert to XML
140
- def to_xml
141
- return LibXML::XML::Node.new(@value ? 'true' : 'false')
213
+ def to_xml(parser)
214
+ parser.new_node(@value ? 'true' : 'false')
142
215
  end
143
216
 
144
217
  # convert to binary
145
218
  def to_binary(bplist)
146
- return bplist.bool_to_binary(@value);
219
+ bplist.bool_to_binary(@value);
220
+ end
221
+
222
+ def to_plain(plist)
223
+ @value ? "true" : "false"
147
224
  end
148
225
  end
149
226
 
@@ -156,9 +233,8 @@ module CFPropertyList
156
233
 
157
234
  # set value to defined state, either base64 encoded or raw
158
235
  def initialize(value=nil,format=DATA_BASE64)
159
- if(format == DATA_RAW) then
236
+ if(format == DATA_RAW)
160
237
  @raw_value = value
161
- @raw_value.blob = true
162
238
  else
163
239
  @value = value
164
240
  end
@@ -171,19 +247,23 @@ module CFPropertyList
171
247
 
172
248
  # get base64 decoded value
173
249
  def decoded_value
174
- @raw_value ||= String.new(Base64.decode64(@value))
175
- @raw_value.blob = true
176
- @raw_value
250
+ @raw_value ||= Blob.new(Base64.decode64(@value))
177
251
  end
178
252
 
179
253
  # convert to XML
180
- def to_xml
181
- return LibXML::XML::Node.new('data') << LibXML::XML::Node.new_text(encoded_value())
254
+ def to_xml(parser)
255
+ n = parser.new_node('data')
256
+ n = parser.append_node(n, parser.new_text(encoded_value()))
257
+ n
182
258
  end
183
259
 
184
260
  # convert to binary
185
261
  def to_binary(bplist)
186
- return bplist.data_to_binary(decoded_value())
262
+ bplist.data_to_binary(decoded_value())
263
+ end
264
+
265
+ def to_plain(plist)
266
+ "<" + decoded_value.unpack("H*").join("") + ">"
187
267
  end
188
268
  end
189
269
 
@@ -195,19 +275,22 @@ module CFPropertyList
195
275
  end
196
276
 
197
277
  # convert to XML
198
- def to_xml
199
- n = LibXML::XML::Node.new('array')
200
- @value.each do
201
- |v|
202
- n << v.to_xml
278
+ def to_xml(parser)
279
+ n = parser.new_node('array')
280
+ @value.each do |v|
281
+ n = parser.append_node(n, v.to_xml(parser))
203
282
  end
204
-
205
- return n
283
+ n
206
284
  end
207
285
 
208
286
  # convert to binary
209
287
  def to_binary(bplist)
210
- return bplist.array_to_binary(self)
288
+ bplist.array_to_binary(self)
289
+ end
290
+
291
+ def to_plain(plist)
292
+ ary = @value.map { |v| v.to_plain(plist) }
293
+ "( " + ary.join(", ") + " )"
211
294
  end
212
295
  end
213
296
 
@@ -219,21 +302,46 @@ module CFPropertyList
219
302
  end
220
303
 
221
304
  # convert to XML
222
- def to_xml
223
- n = LibXML::XML::Node.new('dict')
224
- @value.each_pair do
225
- |key,value|
226
- k = LibXML::XML::Node.new('key') << LibXML::XML::Node.new_text(key)
227
- n << k
228
- n << value.to_xml
305
+ def to_xml(parser)
306
+ n = parser.new_node('dict')
307
+ @value.each_pair do |key, value|
308
+ k = parser.append_node(parser.new_node('key'), parser.new_text(key.to_s))
309
+ n = parser.append_node(n, k)
310
+ n = parser.append_node(n, value.to_xml(parser))
311
+ end
312
+ n
313
+ end
314
+
315
+ # convert to binary
316
+ def to_binary(bplist)
317
+ bplist.dict_to_binary(self)
318
+ end
319
+
320
+ def to_plain(plist)
321
+ str = "{ "
322
+ cfstr = CFString.new()
323
+
324
+ @value.each do |k,v|
325
+ cfstr.value = k
326
+ str << cfstr.to_plain(plist) + " = " + v.to_plain(plist) + "; "
229
327
  end
230
328
 
231
- return n
329
+ str << "}"
330
+ end
331
+ end
332
+
333
+ class CFUid < CFType
334
+ def to_xml(parser)
335
+ CFDictionary.new({'CF$UID' => CFInteger.new(@value)}).to_xml(parser)
232
336
  end
233
337
 
234
338
  # convert to binary
235
339
  def to_binary(bplist)
236
- return bplist.dict_to_binary(self)
340
+ bplist.uid_to_binary(@value)
341
+ end
342
+
343
+ def to_plain(plist)
344
+ CFDictionary.new({'CF$UID' => CFInteger.new(@value)}).to_plain(plist)
237
345
  end
238
346
  end
239
347
  end
@@ -1,21 +1,29 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'libxml'
4
+
3
5
  module CFPropertyList
4
6
  # XML parser
5
- class XML < ParserInterface
7
+ class LibXMLParser < XMLParserInterface
6
8
  # read a XML file
7
9
  # opts::
8
10
  # * :file - The filename of the file to load
9
11
  # * :data - The data to parse
10
12
  def load(opts)
13
+ doc = nil
14
+
11
15
  if(opts.has_key?(:file)) then
12
16
  doc = LibXML::XML::Document.file(opts[:file],:options => LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NOENT)
13
17
  else
14
18
  doc = LibXML::XML::Document.string(opts[:data],:options => LibXML::XML::Parser::Options::NOBLANKS|LibXML::XML::Parser::Options::NOENT)
15
19
  end
16
20
 
17
- root = doc.root.first
18
- return import_xml(root)
21
+ if doc
22
+ root = doc.root.first
23
+ return import_xml(root)
24
+ end
25
+ rescue LibXML::XML::Error => e
26
+ raise CFFormatError.new('invalid XML: ' + e.message)
19
27
  end
20
28
 
21
29
  # serialize CFPropertyList object to XML
@@ -27,14 +35,13 @@ module CFPropertyList
27
35
  doc.encoding = LibXML::XML::Encoding::UTF_8
28
36
 
29
37
  doc.root['version'] = '1.0'
30
- doc.root << opts[:root].to_xml()
38
+ doc.root << opts[:root].to_xml(self)
31
39
 
32
40
  # ugly hack, but there's no other possibility I know
33
41
  str = doc.to_s(:indent => opts[:formatted])
34
42
  str1 = String.new
35
43
  first = false
36
- str.each_line do
37
- |line|
44
+ str.each_line do |line|
38
45
  str1 << line
39
46
  unless(first) then
40
47
  str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
@@ -47,6 +54,18 @@ module CFPropertyList
47
54
  return str1
48
55
  end
49
56
 
57
+ def new_node(name)
58
+ LibXML::XML::Node.new(name)
59
+ end
60
+
61
+ def new_text(val)
62
+ LibXML::XML::Node.new_text(val)
63
+ end
64
+
65
+ def append_node(parent, child)
66
+ parent << child
67
+ end
68
+
50
69
  protected
51
70
 
52
71
  # get the value of a DOM node
@@ -56,7 +75,7 @@ module CFPropertyList
56
75
  else
57
76
  n.content
58
77
  end
59
-
78
+
60
79
  content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
61
80
  content
62
81
  end
@@ -71,9 +90,9 @@ module CFPropertyList
71
90
  key = nil
72
91
 
73
92
  if node.children? then
74
- node.children.each do
75
- |n|
93
+ node.children.each do |n|
76
94
  next if n.text? # avoid a bug of libxml
95
+ next if n.comment?
77
96
 
78
97
  if n.name == "key" then
79
98
  key = get_value(n)
@@ -85,14 +104,19 @@ module CFPropertyList
85
104
  end
86
105
  end
87
106
 
88
- ret = CFDictionary.new(hsh)
107
+ if hsh['CF$UID'] and hsh.keys.length == 1
108
+ ret = CFUid.new(hsh['CF$UID'].value)
109
+ else
110
+ ret = CFDictionary.new(hsh)
111
+ end
89
112
 
90
113
  when 'array'
91
114
  ary = Array.new
92
115
 
93
116
  if node.children? then
94
- node.children.each do
95
- |n|
117
+ node.children.each do |n|
118
+ next if n.text? # avoid a bug of libxml
119
+ next if n.comment?
96
120
  ary.push import_xml(n)
97
121
  end
98
122
  end
@@ -0,0 +1,151 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'nokogiri'
4
+
5
+ module CFPropertyList
6
+ # XML parser
7
+ class NokogiriXMLParser < ParserInterface
8
+ # read a XML file
9
+ # opts::
10
+ # * :file - The filename of the file to load
11
+ # * :data - The data to parse
12
+ def load(opts)
13
+ doc = nil
14
+ if(opts.has_key?(:file)) then
15
+ File.open(opts[:file], "rb") { |fd| doc = Nokogiri::XML::Document.parse(fd, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS|Nokogiri::XML::ParseOptions::NOENT) }
16
+ else
17
+ doc = Nokogiri::XML::Document.parse(opts[:data], nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS|Nokogiri::XML::ParseOptions::NOENT)
18
+ end
19
+
20
+ if doc
21
+ root = doc.root.children.first
22
+ return import_xml(root)
23
+ end
24
+ rescue Nokogiri::XML::SyntaxError => e
25
+ raise CFFormatError.new('invalid XML: ' + e.message)
26
+ end
27
+
28
+ # serialize CFPropertyList object to XML
29
+ # opts = {}:: Specify options: :formatted - Use indention and line breaks
30
+ def to_str(opts={})
31
+ doc = Nokogiri::XML::Document.new
32
+ @doc = doc
33
+
34
+ doc.root = doc.create_element 'plist', :version => '1.0'
35
+ doc.encoding = 'UTF-8'
36
+
37
+ doc.root << opts[:root].to_xml(self)
38
+
39
+ # ugly hack, but there's no other possibility I know
40
+ s_opts = Nokogiri::XML::Node::SaveOptions::AS_XML
41
+ s_opts |= Nokogiri::XML::Node::SaveOptions::FORMAT if opts[:formatted]
42
+
43
+ str = doc.serialize(:save_with => s_opts)
44
+ str1 = String.new
45
+ first = false
46
+ str.each_line do |line|
47
+ str1 << line
48
+ unless(first) then
49
+ str1 << "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" if line =~ /^\s*<\?xml/
50
+ end
51
+
52
+ first = true
53
+ end
54
+
55
+ str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
56
+ return str1
57
+ end
58
+
59
+ def new_node(name)
60
+ @doc.create_element name
61
+ end
62
+
63
+ def new_text(val)
64
+ @doc.create_text_node val
65
+ end
66
+
67
+ def append_node(parent, child)
68
+ parent << child
69
+ end
70
+
71
+ protected
72
+
73
+ # get the value of a DOM node
74
+ def get_value(n)
75
+ content = if n.children.empty?
76
+ n.content
77
+ else
78
+ n.children.first.content
79
+ end
80
+
81
+ content.force_encoding('UTF-8') if content.respond_to?(:force_encoding)
82
+ content
83
+ end
84
+
85
+ # import the XML values
86
+ def import_xml(node)
87
+ ret = nil
88
+
89
+ case node.name
90
+ when 'dict'
91
+ hsh = Hash.new
92
+ key = nil
93
+ children = node.children
94
+
95
+ unless children.empty? then
96
+ children.each do |n|
97
+ next if n.text? # avoid a bug of libxml
98
+ next if n.comment?
99
+
100
+ if n.name == "key" then
101
+ key = get_value(n)
102
+ else
103
+ raise CFFormatError.new("Format error!") if key.nil?
104
+ hsh[key] = import_xml(n)
105
+ key = nil
106
+ end
107
+ end
108
+ end
109
+
110
+ if hsh['CF$UID'] and hsh.keys.length == 1
111
+ ret = CFUid.new(hsh['CF$UID'].value)
112
+ else
113
+ ret = CFDictionary.new(hsh)
114
+ end
115
+
116
+ when 'array'
117
+ ary = Array.new
118
+ children = node.children
119
+
120
+ unless children.empty? then
121
+ children.each do |n|
122
+ next if n.text? # avoid a bug of libxml
123
+ next if n.comment?
124
+ ary.push import_xml(n)
125
+ end
126
+ end
127
+
128
+ ret = CFArray.new(ary)
129
+
130
+ when 'true'
131
+ ret = CFBoolean.new(true)
132
+ when 'false'
133
+ ret = CFBoolean.new(false)
134
+ when 'real'
135
+ ret = CFReal.new(get_value(node).to_f)
136
+ when 'integer'
137
+ ret = CFInteger.new(get_value(node).to_i)
138
+ when 'string'
139
+ ret = CFString.new(get_value(node))
140
+ when 'data'
141
+ ret = CFData.new(get_value(node))
142
+ when 'date'
143
+ ret = CFDate.new(CFDate.parse_date(get_value(node)))
144
+ end
145
+
146
+ return ret
147
+ end
148
+ end
149
+ end
150
+
151
+ # eof