vpim 0.619 → 0.658

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ #--
2
+ ##############################################################
3
+ # Copyright 2006, Ben Bleything <ben@bleything.net> and #
4
+ # Patrick May <patrick@hexane.org> #
5
+ # #
6
+ # Distributed under the MIT license. #
7
+ ##############################################################
8
+ #++
9
+ # = Plist
10
+ #
11
+ # This is the main file for plist. Everything interesting happens in Plist and Plist::Emit.
12
+
13
+ require 'base64'
14
+ require 'cgi'
15
+ require 'stringio'
16
+
17
+ require 'plist/generator'
18
+ require 'plist/parser'
19
+
20
+ module Plist
21
+ VERSION = '3.0.0'
22
+ end
@@ -0,0 +1,224 @@
1
+ #--###########################################################
2
+ # Copyright 2006, Ben Bleything <ben@bleything.net> and #
3
+ # Patrick May <patrick@hexane.org> #
4
+ # #
5
+ # Distributed under the MIT license. #
6
+ ##############################################################
7
+ #++
8
+ # See Plist::Emit.
9
+ module Plist
10
+ # === Create a plist
11
+ # You can dump an object to a plist in one of two ways:
12
+ #
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+.
16
+ #
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.
22
+ #
23
+ # For detailed usage instructions, refer to USAGE[link:files/docs/USAGE.html] and the methods documented below.
24
+ module Emit
25
+ # Helper method for injecting into classes. Calls <tt>Plist::Emit.dump</tt> with +self+.
26
+ def to_plist(envelope = true)
27
+ return Plist::Emit.dump(self, envelope)
28
+ end
29
+
30
+ # Helper method for injecting into classes. Calls <tt>Plist::Emit.save_plist</tt> with +self+.
31
+ def save_plist(filename)
32
+ Plist::Emit.save_plist(self, filename)
33
+ end
34
+
35
+ # The following Ruby classes are converted into native plist types:
36
+ # Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time
37
+ #
38
+ # Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes.
39
+ #
40
+ # +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+.
41
+ #
42
+ # 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.
43
+ def self.dump(obj, envelope = true)
44
+ output = plist_node(obj)
45
+
46
+ output = wrap(output) if envelope
47
+
48
+ return output
49
+ end
50
+
51
+ # Writes the serialized object's plist to the specified filename.
52
+ def self.save_plist(obj, filename)
53
+ File.open(filename, 'wb') do |f|
54
+ f.write(obj.to_plist)
55
+ end
56
+ end
57
+
58
+ private
59
+ def self.plist_node(element)
60
+ output = ''
61
+
62
+ if element.respond_to? :to_plist_node
63
+ output << element.to_plist_node
64
+ else
65
+ case element
66
+ when Array
67
+ if element.empty?
68
+ output << "<array/>\n"
69
+ else
70
+ output << tag('array') {
71
+ element.collect {|e| plist_node(e)}
72
+ }
73
+ end
74
+ when Hash
75
+ if element.empty?
76
+ output << "<dict/>\n"
77
+ else
78
+ inner_tags = []
79
+
80
+ element.keys.sort.each do |k|
81
+ v = element[k]
82
+ inner_tags << tag('key', CGI::escapeHTML(k.to_s))
83
+ inner_tags << plist_node(v)
84
+ end
85
+
86
+ output << tag('dict') {
87
+ inner_tags
88
+ }
89
+ end
90
+ when true, false
91
+ output << "<#{element}/>\n"
92
+ when Time
93
+ output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'))
94
+ when Date # also catches DateTime
95
+ output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'))
96
+ when String, Symbol, Fixnum, Bignum, Integer, Float
97
+ output << tag(element_type(element), CGI::escapeHTML(element.to_s))
98
+ when IO, StringIO
99
+ element.rewind
100
+ contents = element.read
101
+ # note that apple plists are wrapped at a different length then
102
+ # what ruby's base64 wraps by default.
103
+ # I used #encode64 instead of #b64encode (which allows a length arg)
104
+ # because b64encode is b0rked and ignores the length arg.
105
+ data = "\n"
106
+ Base64::encode64(contents).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
107
+ output << tag('data', data)
108
+ else
109
+ output << comment( 'The <data> element below contains a Ruby object which has been serialized with Marshal.dump.' )
110
+ data = "\n"
111
+ Base64::encode64(Marshal.dump(element)).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
112
+ output << tag('data', data )
113
+ end
114
+ end
115
+
116
+ return output
117
+ end
118
+
119
+ def self.comment(content)
120
+ return "<!-- #{content} -->\n"
121
+ end
122
+
123
+ def self.tag(type, contents = '', &block)
124
+ out = nil
125
+
126
+ if block_given?
127
+ out = IndentedString.new
128
+ out << "<#{type}>"
129
+ out.raise_indent
130
+
131
+ out << block.call
132
+
133
+ out.lower_indent
134
+ out << "</#{type}>"
135
+ else
136
+ out = "<#{type}>#{contents.to_s}</#{type}>\n"
137
+ end
138
+
139
+ return out.to_s
140
+ end
141
+
142
+ def self.wrap(contents)
143
+ output = ''
144
+
145
+ output << '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
146
+ output << '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
147
+ output << '<plist version="1.0">' + "\n"
148
+
149
+ output << contents
150
+
151
+ output << '</plist>' + "\n"
152
+
153
+ return output
154
+ end
155
+
156
+ def self.element_type(item)
157
+ return case item
158
+ when String, Symbol: 'string'
159
+ when Fixnum, Bignum, Integer: 'integer'
160
+ when Float: 'real'
161
+ else
162
+ raise "Don't know about this data type... something must be wrong!"
163
+ end
164
+ end
165
+ private
166
+ class IndentedString #:nodoc:
167
+ attr_accessor :indent_string
168
+
169
+ @@indent_level = 0
170
+
171
+ def initialize(str = "\t")
172
+ @indent_string = str
173
+ @contents = ''
174
+ end
175
+
176
+ def to_s
177
+ return @contents
178
+ end
179
+
180
+ def raise_indent
181
+ @@indent_level += 1
182
+ end
183
+
184
+ def lower_indent
185
+ @@indent_level -= 1 if @@indent_level > 0
186
+ end
187
+
188
+ def <<(val)
189
+ if val.is_a? Array
190
+ val.each do |f|
191
+ self << f
192
+ end
193
+ else
194
+ # if it's already indented, don't bother indenting further
195
+ unless val =~ /\A#{@indent_string}/
196
+ indent = @indent_string * @@indent_level
197
+
198
+ @contents << val.gsub(/^/, indent)
199
+ else
200
+ @contents << val
201
+ end
202
+
203
+ # it already has a newline, don't add another
204
+ @contents << "\n" unless val =~ /\n$/
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ # we need to add this so sorting hash keys works properly
212
+ class Symbol #:nodoc:
213
+ def <=> (other)
214
+ self.to_s <=> other.to_s
215
+ end
216
+ end
217
+
218
+ class Array #:nodoc:
219
+ include Plist::Emit
220
+ end
221
+
222
+ class Hash #:nodoc:
223
+ include Plist::Emit
224
+ end
@@ -0,0 +1,225 @@
1
+ #--###########################################################
2
+ # Copyright 2006, Ben Bleything <ben@bleything.net> and #
3
+ # Patrick May <patrick@hexane.org> #
4
+ # #
5
+ # Distributed under the MIT license. #
6
+ ##############################################################
7
+ #++
8
+ # Plist parses Mac OS X xml property list files into ruby data structures.
9
+ #
10
+ # === Load a plist file
11
+ # This is the main point of the library:
12
+ #
13
+ # r = Plist::parse_xml( filename_or_xml )
14
+ module Plist
15
+ # Note that I don't use these two elements much:
16
+ #
17
+ # + Date elements are returned as DateTime objects.
18
+ # + Data elements are implemented as Tempfiles
19
+ #
20
+ # Plist::parse_xml will blow up if it encounters a data element.
21
+ # If you encounter such an error, or if you have a Date element which
22
+ # can't be parsed into a Time object, please send your plist file to
23
+ # plist@hexane.org so that I can implement the proper support.
24
+ def Plist::parse_xml( filename_or_xml )
25
+ listener = Listener.new
26
+ #parser = REXML::Parsers::StreamParser.new(File.new(filename), listener)
27
+ parser = StreamParser.new(filename_or_xml, listener)
28
+ parser.parse
29
+ listener.result
30
+ end
31
+
32
+ class Listener
33
+ #include REXML::StreamListener
34
+
35
+ attr_accessor :result, :open
36
+
37
+ def initialize
38
+ @result = nil
39
+ @open = Array.new
40
+ end
41
+
42
+
43
+ def tag_start(name, attributes)
44
+ @open.push PTag::mappings[name].new
45
+ end
46
+
47
+ def text( contents )
48
+ @open.last.text = contents if @open.last
49
+ end
50
+
51
+ def tag_end(name)
52
+ last = @open.pop
53
+ if @open.empty?
54
+ @result = last.to_ruby
55
+ else
56
+ @open.last.children.push last
57
+ end
58
+ end
59
+ end
60
+
61
+ class StreamParser
62
+ def initialize( filename_or_xml, listener )
63
+ @filename_or_xml = filename_or_xml
64
+ @listener = listener
65
+ end
66
+
67
+ TEXT = /([^<]+)/
68
+ XMLDECL_PATTERN = /<\?xml\s+(.*?)\?>*/um
69
+ DOCTYPE_PATTERN = /\s*<!DOCTYPE\s+(.*?)(\[|>)/um
70
+ COMMENT_START = /\A<!--/u
71
+ COMMENT_END = /.*?-->/um
72
+
73
+
74
+ def parse
75
+ plist_tags = PTag::mappings.keys.join('|')
76
+ start_tag = /<(#{plist_tags})([^>]*)>/i
77
+ end_tag = /<\/(#{plist_tags})[^>]*>/i
78
+
79
+ require 'strscan'
80
+
81
+ contents = (
82
+ if (File.exists? @filename_or_xml)
83
+ File.open(@filename_or_xml) {|f| f.read}
84
+ else
85
+ @filename_or_xml
86
+ end
87
+ )
88
+
89
+ @scanner = StringScanner.new( contents )
90
+ until @scanner.eos?
91
+ if @scanner.scan(COMMENT_START)
92
+ @scanner.scan(COMMENT_END)
93
+ elsif @scanner.scan(XMLDECL_PATTERN)
94
+ elsif @scanner.scan(DOCTYPE_PATTERN)
95
+ elsif @scanner.scan(start_tag)
96
+ @listener.tag_start(@scanner[1], nil)
97
+ if (@scanner[2] =~ /\/$/)
98
+ @listener.tag_end(@scanner[1])
99
+ end
100
+ elsif @scanner.scan(TEXT)
101
+ @listener.text(@scanner[1])
102
+ elsif @scanner.scan(end_tag)
103
+ @listener.tag_end(@scanner[1])
104
+ else
105
+ raise "Unimplemented element"
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ class PTag
112
+ @@mappings = { }
113
+ def PTag::mappings
114
+ @@mappings
115
+ end
116
+
117
+ def PTag::inherited( sub_class )
118
+ key = sub_class.to_s.downcase
119
+ key.gsub!(/^plist::/, '' )
120
+ key.gsub!(/^p/, '') unless key == "plist"
121
+
122
+ @@mappings[key] = sub_class
123
+ end
124
+
125
+ attr_accessor :text, :children
126
+ def initialize
127
+ @children = Array.new
128
+ end
129
+
130
+ def to_ruby
131
+ raise "Unimplemented: " + self.class.to_s + "#to_ruby on #{self.inspect}"
132
+ end
133
+ end
134
+
135
+ class PList < PTag
136
+ def to_ruby
137
+ children.first.to_ruby if children.first
138
+ end
139
+ end
140
+
141
+ class PDict < PTag
142
+ def to_ruby
143
+ dict = Hash.new
144
+ key = nil
145
+
146
+ children.each do |c|
147
+ if key.nil?
148
+ key = c.to_ruby
149
+ else
150
+ dict[key] = c.to_ruby
151
+ key = nil
152
+ end
153
+ end
154
+
155
+ dict
156
+ end
157
+ end
158
+
159
+ class PKey < PTag
160
+ def to_ruby
161
+ CGI::unescapeHTML(text || '')
162
+ end
163
+ end
164
+
165
+ class PString < PTag
166
+ def to_ruby
167
+ CGI::unescapeHTML(text || '')
168
+ end
169
+ end
170
+
171
+ class PArray < PTag
172
+ def to_ruby
173
+ children.collect do |c|
174
+ c.to_ruby
175
+ end
176
+ end
177
+ end
178
+
179
+ class PInteger < PTag
180
+ def to_ruby
181
+ text.to_i
182
+ end
183
+ end
184
+
185
+ class PTrue < PTag
186
+ def to_ruby
187
+ true
188
+ end
189
+ end
190
+
191
+ class PFalse < PTag
192
+ def to_ruby
193
+ false
194
+ end
195
+ end
196
+
197
+ class PReal < PTag
198
+ def to_ruby
199
+ text.to_f
200
+ end
201
+ end
202
+
203
+ require 'date'
204
+ class PDate < PTag
205
+ def to_ruby
206
+ DateTime.parse(text)
207
+ end
208
+ end
209
+
210
+ require 'base64'
211
+ class PData < PTag
212
+ def to_ruby
213
+ data = Base64.decode64(text.gsub(/\s+/, ''))
214
+
215
+ begin
216
+ return Marshal.load(data)
217
+ rescue Exception => e
218
+ io = StringIO.new
219
+ io.write data
220
+ io.rewind
221
+ return io
222
+ end
223
+ end
224
+ end
225
+ end