CFPropertyList 2.0.14 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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