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