source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/soap/property.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 6.9 KB
Line 
1# soap/property.rb: SOAP4R - Property implementation.
2# Copyright (C) 2003 NAKAMURA, Hiroshi <[email protected]>.
3
4# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5# redistribute it and/or modify it under the same terms of Ruby's license;
6# either the dual license version in 2003, or any later version.
7
8
9module SOAP
10
11
12# Property stream format:
13#
14# line separator is \r?\n. 1 line per a property.
15# line which begins with '#' is a comment line. empty line is ignored, too.
16# key/value separator is ':' or '='.
17# '\' as escape character. but line separator cannot be escaped.
18# \s at the head/tail of key/value are trimmed.
19#
20# '[' + key + ']' indicates property section. for example,
21#
22# [aaa.bbb]
23# ccc = ddd
24# eee.fff = ggg
25# []
26# aaa.hhh = iii
27#
28# is the same as;
29#
30# aaa.bbb.ccc = ddd
31# aaa.bbb.eee.fff = ggg
32# aaa.hhh = iii
33#
34class Property
35 FrozenError = (RUBY_VERSION >= "1.9.0") ? RuntimeError : TypeError
36
37 include Enumerable
38
39 module Util
40 def const_from_name(fqname)
41 fqname.split("::").inject(Kernel) { |klass, name| klass.const_get(name) }
42 end
43 module_function :const_from_name
44
45 def require_from_name(fqname)
46 require File.join(fqname.split("::").collect { |ele| ele.downcase })
47 end
48 module_function :require_from_name
49 end
50
51 def self.load(stream)
52 new.load(stream)
53 end
54
55 def self.loadproperty(propname)
56 new.loadproperty(propname)
57 end
58
59 def initialize
60 @store = Hash.new
61 @hook = Hash.new
62 @self_hook = Array.new
63 @locked = false
64 end
65
66 KEY_REGSRC = '([^=:\\\\]*(?:\\\\.[^=:\\\\]*)*)'
67 DEF_REGSRC = '\\s*' + KEY_REGSRC + '\\s*[=:]\\s*(.*)'
68 COMMENT_REGEXP = Regexp.new('^(?:#.*|)$')
69 CATDEF_REGEXP = Regexp.new("^\\[\\s*#{KEY_REGSRC}\\s*\\]$")
70 LINE_REGEXP = Regexp.new("^#{DEF_REGSRC}$")
71 def load(stream)
72 key_prefix = ""
73 stream.each_with_index do |line, lineno|
74 line.sub!(/\r?\n\z/, '')
75 case line
76 when COMMENT_REGEXP
77 next
78 when CATDEF_REGEXP
79 key_prefix = $1.strip
80 when LINE_REGEXP
81 key, value = $1.strip, $2.strip
82 key = "#{key_prefix}.#{key}" unless key_prefix.empty?
83 key, value = loadstr(key), loadstr(value)
84 self[key] = value
85 else
86 raise TypeError.new(
87 "property format error at line #{lineno + 1}: `#{line}'")
88 end
89 end
90 self
91 end
92
93 # find property from $:.
94 def loadproperty(propname)
95 return loadpropertyfile(propname) if File.file?(propname)
96 $:.each do |path|
97 if File.file?(file = File.join(path, propname))
98 return loadpropertyfile(file)
99 end
100 end
101 nil
102 end
103
104 # name: a Symbol, String or an Array
105 def [](name)
106 referent(name_to_a(name))
107 end
108
109 # name: a Symbol, String or an Array
110 # value: an Object
111 def []=(name, value)
112 name_pair = name_to_a(name).freeze
113 hooks = assign(name_pair, value)
114 hooks.each do |hook|
115 hook.call(name_pair, value)
116 end
117 value
118 end
119
120 # value: an Object
121 # key is generated by property
122 def <<(value)
123 self[generate_new_key] = value
124 end
125
126 # name: a Symbol, String or an Array; nil means hook to the root
127 # cascade: true/false; for cascading hook of sub key
128 # hook: block which will be called with 2 args, name and value
129 def add_hook(name = nil, cascade = false, &hook)
130 if name == nil or name == true or name == false
131 cascade = name
132 assign_self_hook(cascade, &hook)
133 else
134 assign_hook(name_to_a(name), cascade, &hook)
135 end
136 end
137
138 def each
139 @store.each do |key, value|
140 yield(key, value)
141 end
142 end
143
144 def empty?
145 @store.empty?
146 end
147
148 def keys
149 @store.keys
150 end
151
152 def values
153 @store.values
154 end
155
156 def lock(cascade = false)
157 if cascade
158 each_key do |key|
159 key.lock(cascade)
160 end
161 end
162 @locked = true
163 self
164 end
165
166 def unlock(cascade = false)
167 @locked = false
168 if cascade
169 each_key do |key|
170 key.unlock(cascade)
171 end
172 end
173 self
174 end
175
176 def locked?
177 @locked
178 end
179
180protected
181
182 def deref_key(key)
183 check_lock(key)
184 ref = @store[key] ||= self.class.new
185 unless propkey?(ref)
186 raise ArgumentError.new("key `#{key}' already defined as a value")
187 end
188 ref
189 end
190
191 def local_referent(key)
192 check_lock(key)
193 if propkey?(@store[key]) and @store[key].locked?
194 raise FrozenError.new("cannot split any key from locked property")
195 end
196 @store[key]
197 end
198
199 def local_assign(key, value)
200 check_lock(key)
201 if @locked
202 if propkey?(value)
203 raise FrozenError.new("cannot add any key to locked property")
204 elsif propkey?(@store[key])
205 raise FrozenError.new("cannot override any key in locked property")
206 end
207 end
208 @store[key] = value
209 end
210
211 def local_hook(key, direct)
212 hooks = []
213 (@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade|
214 hooks << hook if direct or cascade
215 end
216 hooks
217 end
218
219 def local_assign_hook(key, cascade, &hook)
220 check_lock(key)
221 @store[key] ||= nil
222 (@hook[key] ||= []) << [hook, cascade]
223 end
224
225private
226
227 NO_HOOK = [].freeze
228
229 def referent(ary)
230 ary[0..-2].inject(self) { |ref, name|
231 ref.deref_key(to_key(name))
232 }.local_referent(to_key(ary.last))
233 end
234
235 def assign(ary, value)
236 ref = self
237 hook = NO_HOOK
238 ary[0..-2].each do |name|
239 key = to_key(name)
240 hook += ref.local_hook(key, false)
241 ref = ref.deref_key(key)
242 end
243 last_key = to_key(ary.last)
244 ref.local_assign(last_key, value)
245 hook + ref.local_hook(last_key, true)
246 end
247
248 def assign_hook(ary, cascade, &hook)
249 ary[0..-2].inject(self) { |ref, name|
250 ref.deref_key(to_key(name))
251 }.local_assign_hook(to_key(ary.last), cascade, &hook)
252 end
253
254 def assign_self_hook(cascade, &hook)
255 check_lock(nil)
256 @self_hook << [hook, cascade]
257 end
258
259 def each_key
260 self.each do |key, value|
261 if propkey?(value)
262 yield(value)
263 end
264 end
265 end
266
267 def check_lock(key)
268 if @locked and (key.nil? or [email protected]?(key))
269 raise FrozenError.new("cannot add any key to locked property")
270 end
271 end
272
273 def propkey?(value)
274 value.is_a?(::SOAP::Property)
275 end
276
277 def name_to_a(name)
278 case name
279 when Symbol
280 [name]
281 when String
282 name.scan(/[^.\\]+(?:\\.[^.\\])*/) # split with unescaped '.'
283 when Array
284 name
285 else
286 raise ArgumentError.new("Unknown name #{name}(#{name.class})")
287 end
288 end
289
290 def to_key(name)
291 name.to_s.downcase
292 end
293
294 def generate_new_key
295 if @store.empty?
296 "0"
297 else
298 (key_max + 1).to_s
299 end
300 end
301
302 def key_max
303 (@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i
304 end
305
306 def loadpropertyfile(file)
307 puts "find property at #{file}" if $DEBUG
308 File.open(file) do |f|
309 load(f)
310 end
311 end
312
313 def loadstr(str)
314 str.gsub(/\\./) { |c| eval("\"#{c}\"") }
315 end
316end
317
318
319end
320
321
322# for ruby/1.6.
323unless Enumerable.instance_methods.include?('inject')
324 module Enumerable
325 def inject(init)
326 result = init
327 each do |item|
328 result = yield(result, item)
329 end
330 result
331 end
332 end
333end
Note: See TracBrowser for help on using the repository browser.