plist 3.1.0 → 3.6.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 → LICENSE.txt} +0 -0
- data/lib/plist.rb +5 -8
- data/lib/plist/generator.rb +138 -187
- data/lib/plist/parser.rb +76 -45
- data/lib/plist/version.rb +5 -0
- metadata +78 -73
- data/CHANGELOG +0 -103
- data/README.rdoc +0 -158
- data/Rakefile +0 -148
- data/test/assets/AlbumData.xml +0 -203
- data/test/assets/Cookies.plist +0 -104
- data/test/assets/commented.plist +0 -9
- data/test/assets/example_data.bin +0 -0
- data/test/assets/example_data.jpg +0 -0
- data/test/assets/example_data.plist +0 -259
- data/test/assets/test_data_elements.plist +0 -24
- data/test/assets/test_empty_key.plist +0 -13
- data/test/test_data_elements.rb +0 -124
- data/test/test_generator.rb +0 -54
- data/test/test_generator_basic_types.rb +0 -53
- data/test/test_generator_collections.rb +0 -77
- data/test/test_parser.rb +0 -97
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2a76784a6b7e953d1cd9b6deaf390eb7556fd0df83e8970db7ac4640c71493b4
|
4
|
+
data.tar.gz: 8324c2b332839aa772eb77a4a760b24e43e24c363c99d01c2b2b4b11e311938a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c33686b606d723821333314b4a18fe6ac39d65a3712aa1cd43896dbbd4bfb95130aa0a1f62b4a3b260c2a633d44681eec0766fef5651ae70580340a905e4f6e4
|
7
|
+
data.tar.gz: 5b63b55469235ec79f14af6f25c05cbc8e6088ce9532e9dfc5aa0887e533a7ee67424953d62e0b3dfe046c7745d9a2add1e0b8ddf07c34f46c5a551c92a9a158
|
data/{LICENSE → LICENSE.txt}
RENAMED
File without changes
|
data/lib/plist.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
3
|
# = plist
|
4
4
|
#
|
5
5
|
# This is the main file for plist. Everything interesting happens in
|
@@ -13,9 +13,6 @@ require 'base64'
|
|
13
13
|
require 'cgi'
|
14
14
|
require 'stringio'
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
module Plist
|
20
|
-
VERSION = '3.1.0'
|
21
|
-
end
|
16
|
+
require_relative 'plist/generator'
|
17
|
+
require_relative 'plist/parser'
|
18
|
+
require_relative 'plist/version'
|
data/lib/plist/generator.rb
CHANGED
@@ -1,222 +1,173 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
3
|
# = plist
|
4
4
|
#
|
5
5
|
# Copyright 2006-2010 Ben Bleything and Patrick May
|
6
6
|
# Distributed under the MIT License
|
7
7
|
#
|
8
8
|
|
9
|
-
module Plist
|
10
|
-
|
11
|
-
#
|
12
|
-
# You can dump an object to a plist in one of two ways:
|
13
|
-
#
|
14
|
-
# * <tt>Plist::Emit.dump(obj)</tt>
|
15
|
-
# * <tt>obj.to_plist</tt>
|
16
|
-
# * This requires that you mixin the <tt>Plist::Emit</tt> module, which is already done for +Array+ and +Hash+.
|
17
|
-
#
|
18
|
-
# The following Ruby classes are converted into native plist types:
|
19
|
-
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false
|
20
|
-
# * +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the <array> and <dict> containers (respectively).
|
21
|
-
# * +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a <data> element.
|
22
|
-
# * User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to <tt>Marshal.dump</tt> and the result placed in a <data> element.
|
23
|
-
#
|
24
|
-
# For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below.
|
25
|
-
module Plist::Emit
|
26
|
-
# Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+.
|
27
|
-
def to_plist(envelope = true)
|
28
|
-
return Plist::Emit.dump(self, envelope)
|
29
|
-
end
|
30
|
-
|
31
|
-
# Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+.
|
32
|
-
def save_plist(filename)
|
33
|
-
Plist::Emit.save_plist(self, filename)
|
34
|
-
end
|
35
|
-
|
36
|
-
# The following Ruby classes are converted into native plist types:
|
37
|
-
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time
|
9
|
+
module Plist
|
10
|
+
# === Create a plist
|
11
|
+
# You can dump an object to a plist in one of two ways:
|
38
12
|
#
|
39
|
-
#
|
13
|
+
# * <tt>Plist::Emit.dump(obj)</tt>
|
14
|
+
# * <tt>obj.to_plist</tt>
|
15
|
+
# * This requires that you mixin the <tt>Plist::Emit</tt> module, which is already done for +Array+ and +Hash+.
|
40
16
|
#
|
41
|
-
#
|
17
|
+
# The following Ruby classes are converted into native plist types:
|
18
|
+
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false
|
19
|
+
# * +Array+ and +Hash+ are both recursive; their elements will be converted into plist nodes inside the <array> and <dict> containers (respectively).
|
20
|
+
# * +IO+ (and its descendants) and +StringIO+ objects are read from and their contents placed in a <data> element.
|
21
|
+
# * User classes may implement +to_plist_node+ to dictate how they should be serialized; otherwise the object will be passed to <tt>Marshal.dump</tt> and the result placed in a <data> element.
|
42
22
|
#
|
43
|
-
#
|
44
|
-
|
45
|
-
|
23
|
+
# For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below.
|
24
|
+
module Emit
|
25
|
+
DEFAULT_INDENT = "\t"
|
46
26
|
|
47
|
-
|
27
|
+
# Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+.
|
28
|
+
def to_plist(envelope = true, options = {})
|
29
|
+
Plist::Emit.dump(self, envelope, options)
|
30
|
+
end
|
48
31
|
|
49
|
-
|
50
|
-
|
32
|
+
# Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+.
|
33
|
+
def save_plist(filename, options = {})
|
34
|
+
Plist::Emit.save_plist(self, filename, options)
|
35
|
+
end
|
51
36
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
37
|
+
# The following Ruby classes are converted into native plist types:
|
38
|
+
# Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time
|
39
|
+
#
|
40
|
+
# Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes.
|
41
|
+
#
|
42
|
+
# +IO+ and +StringIO+ objects are encoded and placed in <data> elements; other objects are <tt>Marshal.dump</tt>'ed unless they implement +to_plist_node+.
|
43
|
+
#
|
44
|
+
# The +envelope+ parameters dictates whether or not the resultant plist fragment is wrapped in the normal XML/plist header and footer. Set it to false if you only want the fragment.
|
45
|
+
def self.dump(obj, envelope = true, options = {})
|
46
|
+
options = { :indent => DEFAULT_INDENT }.merge(options)
|
47
|
+
|
48
|
+
output = PlistBuilder.new(options[:indent]).build(obj)
|
49
|
+
output = wrap(output) if envelope
|
50
|
+
|
51
|
+
output
|
56
52
|
end
|
57
|
-
end
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
54
|
+
# Writes the serialized object's plist to the specified filename.
|
55
|
+
def self.save_plist(obj, filename, options = {})
|
56
|
+
File.open(filename, 'wb') do |f|
|
57
|
+
f.write(obj.to_plist(true, options))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
class PlistBuilder
|
64
|
+
def initialize(indent_str)
|
65
|
+
@indent_str = indent_str.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def build(element, level=0)
|
69
|
+
if element.respond_to? :to_plist_node
|
70
|
+
element.to_plist_node
|
70
71
|
else
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
case element
|
73
|
+
when Array
|
74
|
+
if element.empty?
|
75
|
+
tag('array', nil, level)
|
76
|
+
else
|
77
|
+
tag('array', nil, level) {
|
78
|
+
element.collect {|e| build(e, level + 1) }.join
|
79
|
+
}
|
80
|
+
end
|
81
|
+
when Hash
|
82
|
+
if element.empty?
|
83
|
+
tag('dict', nil, level)
|
84
|
+
else
|
85
|
+
tag('dict', '', level) do
|
86
|
+
element.sort_by{|k,v| k.to_s }.collect do |k,v|
|
87
|
+
tag('key', CGI.escapeHTML(k.to_s), level + 1) +
|
88
|
+
build(v, level + 1)
|
89
|
+
end.join
|
90
|
+
end
|
91
|
+
end
|
92
|
+
when true, false
|
93
|
+
tag(element, nil, level)
|
94
|
+
when Time
|
95
|
+
tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), level)
|
96
|
+
when Date # also catches DateTime
|
97
|
+
tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'), level)
|
98
|
+
when String, Symbol, Integer, Float
|
99
|
+
tag(element_type(element), CGI.escapeHTML(element.to_s), level)
|
100
|
+
when IO, StringIO
|
101
|
+
data = element.tap(&:rewind).read
|
102
|
+
data_tag(data, level)
|
103
|
+
else
|
104
|
+
data = Marshal.dump(element)
|
105
|
+
comment_tag('The <data> element below contains a Ruby object which has been serialized with Marshal.dump.') +
|
106
|
+
data_tag(data, level)
|
107
|
+
end
|
74
108
|
end
|
75
|
-
|
76
|
-
if element.empty?
|
77
|
-
output << "<dict/>\n"
|
78
|
-
else
|
79
|
-
inner_tags = []
|
109
|
+
end
|
80
110
|
|
81
|
-
|
82
|
-
v = element[k]
|
83
|
-
inner_tags << tag('key', CGI::escapeHTML(k.to_s))
|
84
|
-
inner_tags << plist_node(v)
|
85
|
-
end
|
111
|
+
private
|
86
112
|
|
87
|
-
|
88
|
-
|
89
|
-
}
|
113
|
+
def tag(type, contents, level, &block)
|
114
|
+
if block_given?
|
115
|
+
indent("<#{type}>\n", level) +
|
116
|
+
block.call +
|
117
|
+
indent("</#{type}>\n", level)
|
118
|
+
elsif contents.to_s.empty?
|
119
|
+
indent("<#{type}/>\n", level)
|
120
|
+
else
|
121
|
+
indent("<#{type}>#{contents.to_s}</#{type}>\n", level)
|
90
122
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
95
|
-
when Date # also catches DateTime
|
96
|
-
output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
97
|
-
when String, Symbol, Fixnum, Bignum, Integer, Float
|
98
|
-
output << tag(element_type(element), CGI::escapeHTML(element.to_s))
|
99
|
-
when IO, StringIO
|
100
|
-
element.rewind
|
101
|
-
contents = element.read
|
123
|
+
end
|
124
|
+
|
125
|
+
def data_tag(data, level)
|
102
126
|
# note that apple plists are wrapped at a different length then
|
103
127
|
# what ruby's base64 wraps by default.
|
104
128
|
# I used #encode64 instead of #b64encode (which allows a length arg)
|
105
129
|
# because b64encode is b0rked and ignores the length arg.
|
106
|
-
data
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
130
|
+
tag('data', nil, level) do
|
131
|
+
Base64.encode64(data)
|
132
|
+
.gsub(/\s+/, '')
|
133
|
+
.scan(/.{1,68}/o)
|
134
|
+
.collect { |line| indent(line, level) }
|
135
|
+
.join("\n")
|
136
|
+
.concat("\n")
|
137
|
+
end
|
114
138
|
end
|
115
|
-
end
|
116
|
-
|
117
|
-
return output
|
118
|
-
end
|
119
|
-
|
120
|
-
def self.comment(content)
|
121
|
-
return "<!-- #{content} -->\n"
|
122
|
-
end
|
123
|
-
|
124
|
-
def self.tag(type, contents = '', &block)
|
125
|
-
out = nil
|
126
|
-
|
127
|
-
if block_given?
|
128
|
-
out = IndentedString.new
|
129
|
-
out << "<#{type}>"
|
130
|
-
out.raise_indent
|
131
|
-
|
132
|
-
out << block.call
|
133
|
-
|
134
|
-
out.lower_indent
|
135
|
-
out << "</#{type}>"
|
136
|
-
else
|
137
|
-
out = "<#{type}>#{contents.to_s}</#{type}>\n"
|
138
|
-
end
|
139
|
-
|
140
|
-
return out.to_s
|
141
|
-
end
|
142
|
-
|
143
|
-
def self.wrap(contents)
|
144
|
-
output = ''
|
145
|
-
|
146
|
-
output << '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
|
147
|
-
output << '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
|
148
|
-
output << '<plist version="1.0">' + "\n"
|
149
|
-
|
150
|
-
output << contents
|
151
|
-
|
152
|
-
output << '</plist>' + "\n"
|
153
|
-
|
154
|
-
return output
|
155
|
-
end
|
156
|
-
|
157
|
-
def self.element_type(item)
|
158
|
-
case item
|
159
|
-
when String, Symbol
|
160
|
-
'string'
|
161
|
-
|
162
|
-
when Fixnum, Bignum, Integer
|
163
|
-
'integer'
|
164
|
-
|
165
|
-
when Float
|
166
|
-
'real'
|
167
139
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
private
|
173
|
-
class IndentedString #:nodoc:
|
174
|
-
attr_accessor :indent_string
|
175
|
-
|
176
|
-
def initialize(str = "\t")
|
177
|
-
@indent_string = str
|
178
|
-
@contents = ''
|
179
|
-
@indent_level = 0
|
180
|
-
end
|
181
|
-
|
182
|
-
def to_s
|
183
|
-
return @contents
|
184
|
-
end
|
185
|
-
|
186
|
-
def raise_indent
|
187
|
-
@indent_level += 1
|
188
|
-
end
|
189
|
-
|
190
|
-
def lower_indent
|
191
|
-
@indent_level -= 1 if @indent_level > 0
|
192
|
-
end
|
193
|
-
|
194
|
-
def <<(val)
|
195
|
-
if val.is_a? Array
|
196
|
-
val.each do |f|
|
197
|
-
self << f
|
198
|
-
end
|
199
|
-
else
|
200
|
-
# if it's already indented, don't bother indenting further
|
201
|
-
unless val =~ /\A#{@indent_string}/
|
202
|
-
indent = @indent_string * @indent_level
|
140
|
+
def indent(str, level)
|
141
|
+
@indent_str.to_s * level + str
|
142
|
+
end
|
203
143
|
|
204
|
-
|
144
|
+
def element_type(item)
|
145
|
+
case item
|
146
|
+
when String, Symbol
|
147
|
+
'string'
|
148
|
+
when Integer
|
149
|
+
'integer'
|
150
|
+
when Float
|
151
|
+
'real'
|
205
152
|
else
|
206
|
-
|
153
|
+
raise "Don't know about this data type... something must be wrong!"
|
207
154
|
end
|
155
|
+
end
|
208
156
|
|
209
|
-
|
210
|
-
|
157
|
+
def comment_tag(content)
|
158
|
+
return "<!-- #{content} -->\n"
|
211
159
|
end
|
212
160
|
end
|
213
|
-
end
|
214
|
-
end
|
215
161
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
162
|
+
def self.wrap(contents)
|
163
|
+
output = '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
|
164
|
+
output << '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
|
165
|
+
output << '<plist version="1.0">' + "\n"
|
166
|
+
output << contents
|
167
|
+
output << '</plist>' + "\n"
|
168
|
+
|
169
|
+
output
|
170
|
+
end
|
220
171
|
end
|
221
172
|
end
|
222
173
|
|
data/lib/plist/parser.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
3
|
# = plist
|
4
4
|
#
|
5
5
|
# Copyright 2006-2010 Ben Bleything and Patrick May
|
@@ -11,42 +11,48 @@
|
|
11
11
|
# === Load a plist file
|
12
12
|
# This is the main point of the library:
|
13
13
|
#
|
14
|
-
# r = Plist
|
14
|
+
# r = Plist.parse_xml(filename_or_xml)
|
15
15
|
module Plist
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
16
|
+
# Raised when an element is not implemented
|
17
|
+
class UnimplementedElementError < RuntimeError; end
|
18
|
+
|
19
|
+
# Note that I don't use these two elements much:
|
20
|
+
#
|
21
|
+
# + Date elements are returned as DateTime objects.
|
22
|
+
# + Data elements are implemented as Tempfiles
|
23
|
+
#
|
24
|
+
# Plist.parse_xml will blow up if it encounters a Date element.
|
25
|
+
# If you encounter such an error, or if you have a Date element which
|
26
|
+
# can't be parsed into a Time object, please create an issue
|
27
|
+
# attaching your plist file at https://github.com/patsplat/plist/issues
|
28
|
+
# so folks can implement the proper support.
|
29
|
+
def self.parse_xml(filename_or_xml)
|
26
30
|
listener = Listener.new
|
27
|
-
#parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
|
31
|
+
# parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
|
28
32
|
parser = StreamParser.new(filename_or_xml, listener)
|
29
33
|
parser.parse
|
30
34
|
listener.result
|
31
35
|
end
|
32
36
|
|
33
37
|
class Listener
|
34
|
-
#include REXML::StreamListener
|
38
|
+
# include REXML::StreamListener
|
35
39
|
|
36
40
|
attr_accessor :result, :open
|
37
41
|
|
38
42
|
def initialize
|
39
43
|
@result = nil
|
40
|
-
@open =
|
44
|
+
@open = []
|
41
45
|
end
|
42
46
|
|
43
|
-
|
44
47
|
def tag_start(name, attributes)
|
45
|
-
@open.push PTag
|
48
|
+
@open.push PTag.mappings[name].new
|
46
49
|
end
|
47
50
|
|
48
|
-
def text(
|
49
|
-
|
51
|
+
def text(contents)
|
52
|
+
if @open.last
|
53
|
+
@open.last.text ||= ''
|
54
|
+
@open.last.text.concat(contents)
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
58
|
def tag_end(name)
|
@@ -60,11 +66,11 @@ module Plist
|
|
60
66
|
end
|
61
67
|
|
62
68
|
class StreamParser
|
63
|
-
def initialize(
|
69
|
+
def initialize(plist_data_or_file, listener)
|
64
70
|
if plist_data_or_file.respond_to? :read
|
65
71
|
@xml = plist_data_or_file.read
|
66
|
-
elsif File.
|
67
|
-
@xml = File.read(
|
72
|
+
elsif File.exist? plist_data_or_file
|
73
|
+
@xml = File.read(plist_data_or_file)
|
68
74
|
else
|
69
75
|
@xml = plist_data_or_file
|
70
76
|
end
|
@@ -72,26 +78,35 @@ module Plist
|
|
72
78
|
@listener = listener
|
73
79
|
end
|
74
80
|
|
75
|
-
TEXT
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
+
TEXT = /([^<]+)/
|
82
|
+
CDATA = /<!\[CDATA\[(.*?)\]\]>/
|
83
|
+
XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/m
|
84
|
+
DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/m
|
85
|
+
COMMENT_START = /\A<!--/
|
86
|
+
COMMENT_END = /.*?-->/m
|
87
|
+
UNIMPLEMENTED_ERROR = 'Unimplemented element. ' \
|
88
|
+
'Consider reporting via https://github.com/patsplat/plist/issues'
|
81
89
|
|
82
90
|
def parse
|
83
|
-
plist_tags = PTag
|
91
|
+
plist_tags = PTag.mappings.keys.join('|')
|
84
92
|
start_tag = /<(#{plist_tags})([^>]*)>/i
|
85
93
|
end_tag = /<\/(#{plist_tags})[^>]*>/i
|
86
94
|
|
87
95
|
require 'strscan'
|
88
96
|
|
89
|
-
@scanner = StringScanner.new(
|
97
|
+
@scanner = StringScanner.new(@xml)
|
90
98
|
until @scanner.eos?
|
91
99
|
if @scanner.scan(COMMENT_START)
|
92
100
|
@scanner.scan(COMMENT_END)
|
93
101
|
elsif @scanner.scan(XMLDECL_PATTERN)
|
102
|
+
encoding = parse_encoding_from_xml_declaration(@scanner[1])
|
103
|
+
next if encoding.nil?
|
104
|
+
|
105
|
+
# use the specified encoding for the rest of the file
|
106
|
+
next unless String.method_defined?(:force_encoding)
|
107
|
+
@scanner.string = @scanner.rest.force_encoding(encoding)
|
94
108
|
elsif @scanner.scan(DOCTYPE_PATTERN)
|
109
|
+
next
|
95
110
|
elsif @scanner.scan(start_tag)
|
96
111
|
@listener.tag_start(@scanner[1], nil)
|
97
112
|
if (@scanner[2] =~ /\/$/)
|
@@ -99,32 +114,49 @@ module Plist
|
|
99
114
|
end
|
100
115
|
elsif @scanner.scan(TEXT)
|
101
116
|
@listener.text(@scanner[1])
|
117
|
+
elsif @scanner.scan(CDATA)
|
118
|
+
@listener.text(@scanner[1])
|
102
119
|
elsif @scanner.scan(end_tag)
|
103
120
|
@listener.tag_end(@scanner[1])
|
104
121
|
else
|
105
|
-
raise
|
122
|
+
raise UnimplementedElementError.new(UNIMPLEMENTED_ERROR)
|
106
123
|
end
|
107
124
|
end
|
108
125
|
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def parse_encoding_from_xml_declaration(xml_declaration)
|
130
|
+
return unless defined?(Encoding)
|
131
|
+
|
132
|
+
xml_encoding = xml_declaration.match(/(?:\A|\s)encoding=(?:"(.*?)"|'(.*?)')(?:\s|\Z)/)
|
133
|
+
|
134
|
+
return if xml_encoding.nil?
|
135
|
+
|
136
|
+
begin
|
137
|
+
Encoding.find(xml_encoding[1])
|
138
|
+
rescue ArgumentError
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
109
142
|
end
|
110
143
|
|
111
144
|
class PTag
|
112
|
-
|
113
|
-
|
114
|
-
@@mappings
|
145
|
+
def self.mappings
|
146
|
+
@mappings ||= {}
|
115
147
|
end
|
116
148
|
|
117
|
-
def
|
149
|
+
def self.inherited(sub_class)
|
118
150
|
key = sub_class.to_s.downcase
|
119
|
-
key.gsub!(/^plist::/, ''
|
151
|
+
key.gsub!(/^plist::/, '')
|
120
152
|
key.gsub!(/^p/, '') unless key == "plist"
|
121
153
|
|
122
|
-
|
154
|
+
mappings[key] = sub_class
|
123
155
|
end
|
124
156
|
|
125
157
|
attr_accessor :text, :children
|
126
158
|
def initialize
|
127
|
-
@children =
|
159
|
+
@children = []
|
128
160
|
end
|
129
161
|
|
130
162
|
def to_ruby
|
@@ -140,7 +172,7 @@ module Plist
|
|
140
172
|
|
141
173
|
class PDict < PTag
|
142
174
|
def to_ruby
|
143
|
-
dict =
|
175
|
+
dict = {}
|
144
176
|
key = nil
|
145
177
|
|
146
178
|
children.each do |c|
|
@@ -158,13 +190,13 @@ module Plist
|
|
158
190
|
|
159
191
|
class PKey < PTag
|
160
192
|
def to_ruby
|
161
|
-
CGI
|
193
|
+
CGI.unescapeHTML(text || '')
|
162
194
|
end
|
163
195
|
end
|
164
196
|
|
165
197
|
class PString < PTag
|
166
198
|
def to_ruby
|
167
|
-
CGI
|
199
|
+
CGI.unescapeHTML(text || '')
|
168
200
|
end
|
169
201
|
end
|
170
202
|
|
@@ -210,11 +242,10 @@ module Plist
|
|
210
242
|
require 'base64'
|
211
243
|
class PData < PTag
|
212
244
|
def to_ruby
|
213
|
-
data = Base64.decode64(text.gsub(/\s+/, ''))
|
214
|
-
|
245
|
+
data = Base64.decode64(text.gsub(/\s+/, '')) unless text.nil?
|
215
246
|
begin
|
216
247
|
return Marshal.load(data)
|
217
|
-
rescue Exception
|
248
|
+
rescue Exception
|
218
249
|
io = StringIO.new
|
219
250
|
io.write data
|
220
251
|
io.rewind
|