when_exe 0.2.100 → 0.3.1

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.
Files changed (193) hide show
  1. data/LICENSE.ja.txt +25 -25
  2. data/LICENSE.txt +31 -31
  3. data/bin/irb.rc +5 -0
  4. data/bin/locales.rb +2 -2
  5. data/bin/when.rb +16 -0
  6. data/bin/when.rb.config +7 -0
  7. data/lib/when_exe.rb +616 -14
  8. data/lib/when_exe/basictypes.rb +615 -0
  9. data/lib/when_exe/calendartypes.rb +1700 -0
  10. data/lib/when_exe/coordinates.rb +1936 -0
  11. data/lib/when_exe/core/compatibility.rb +54 -0
  12. data/lib/when_exe/core/duration.rb +72 -72
  13. data/lib/when_exe/core/extension.rb +382 -0
  14. data/lib/when_exe/ephemeris.rb +1845 -0
  15. data/lib/when_exe/googlecalendar.rb +140 -0
  16. data/lib/when_exe/icalendar.rb +1587 -0
  17. data/lib/when_exe/inspect.rb +1237 -0
  18. data/lib/when_exe/locales/af.rb +90 -0
  19. data/lib/when_exe/locales/ar.rb +145 -0
  20. data/lib/when_exe/locales/az.rb +90 -0
  21. data/lib/when_exe/locales/bg.rb +90 -0
  22. data/lib/when_exe/locales/bn.rb +94 -0
  23. data/lib/when_exe/locales/bs.rb +121 -0
  24. data/lib/when_exe/locales/ca.rb +92 -0
  25. data/lib/when_exe/locales/cs.rb +107 -0
  26. data/lib/when_exe/locales/cy.rb +150 -0
  27. data/lib/when_exe/locales/da.rb +84 -0
  28. data/lib/when_exe/locales/de.rb +92 -0
  29. data/lib/when_exe/locales/de_AT.rb +92 -0
  30. data/lib/when_exe/locales/de_CH.rb +92 -0
  31. data/lib/when_exe/locales/el.rb +93 -0
  32. data/lib/when_exe/locales/en.rb +88 -0
  33. data/lib/when_exe/locales/en_AU.rb +88 -0
  34. data/lib/when_exe/locales/en_CA.rb +88 -0
  35. data/lib/when_exe/locales/en_GB.rb +88 -0
  36. data/lib/when_exe/locales/en_IN.rb +88 -0
  37. data/lib/when_exe/locales/en_NZ.rb +88 -0
  38. data/lib/when_exe/locales/eo.rb +89 -0
  39. data/lib/when_exe/locales/es.rb +84 -0
  40. data/lib/when_exe/locales/es_419.rb +84 -0
  41. data/lib/when_exe/locales/es_AR.rb +84 -0
  42. data/lib/when_exe/locales/es_CL.rb +84 -0
  43. data/lib/when_exe/locales/es_CO.rb +84 -0
  44. data/lib/when_exe/locales/es_MX.rb +84 -0
  45. data/lib/when_exe/locales/es_PE.rb +85 -0
  46. data/lib/when_exe/locales/es_VE.rb +84 -0
  47. data/lib/when_exe/locales/et.rb +94 -0
  48. data/lib/when_exe/locales/eu.rb +95 -0
  49. data/lib/when_exe/locales/fa.rb +80 -0
  50. data/lib/when_exe/locales/fi.rb +89 -0
  51. data/lib/when_exe/locales/fr.rb +88 -0
  52. data/lib/when_exe/locales/fr_CA.rb +88 -0
  53. data/lib/when_exe/locales/fr_CH.rb +88 -0
  54. data/lib/when_exe/locales/gl.rb +81 -0
  55. data/lib/when_exe/locales/he.rb +84 -0
  56. data/lib/when_exe/locales/hi.rb +80 -0
  57. data/lib/when_exe/locales/hi_IN.rb +84 -0
  58. data/lib/when_exe/locales/hr.rb +128 -0
  59. data/lib/when_exe/locales/hu.rb +84 -0
  60. data/lib/when_exe/locales/id.rb +89 -0
  61. data/lib/when_exe/locales/is.rb +89 -0
  62. data/lib/when_exe/locales/it.rb +87 -0
  63. data/lib/when_exe/locales/it_CH.rb +87 -0
  64. data/lib/when_exe/locales/ja.rb +78 -0
  65. data/lib/when_exe/locales/kn.rb +86 -0
  66. data/lib/when_exe/locales/ko.rb +78 -0
  67. data/lib/when_exe/locales/links.rb +2342 -0
  68. data/lib/when_exe/locales/lo.rb +123 -0
  69. data/lib/when_exe/locales/locales.rb +91 -0
  70. data/lib/when_exe/locales/lt.rb +111 -0
  71. data/lib/when_exe/locales/lv.rb +118 -0
  72. data/lib/when_exe/locales/mk.rb +93 -0
  73. data/lib/when_exe/locales/mn.rb +80 -0
  74. data/lib/when_exe/locales/nb.rb +81 -0
  75. data/lib/when_exe/locales/ne.rb +81 -0
  76. data/lib/when_exe/locales/nl.rb +92 -0
  77. data/lib/when_exe/locales/nn.rb +73 -0
  78. data/lib/when_exe/locales/or.rb +84 -0
  79. data/lib/when_exe/locales/pl.rb +128 -0
  80. data/lib/when_exe/locales/pt.rb +88 -0
  81. data/lib/when_exe/locales/pt_BR.rb +88 -0
  82. data/lib/when_exe/locales/rm.rb +143 -0
  83. data/lib/when_exe/locales/ro.rb +105 -0
  84. data/lib/when_exe/locales/ru.rb +128 -0
  85. data/lib/when_exe/locales/sk.rb +109 -0
  86. data/lib/when_exe/locales/sl.rb +122 -0
  87. data/lib/when_exe/locales/sr.rb +122 -0
  88. data/lib/when_exe/locales/sv.rb +83 -0
  89. data/lib/when_exe/locales/sw.rb +89 -0
  90. data/lib/when_exe/locales/th.rb +78 -0
  91. data/lib/when_exe/locales/tl.rb +99 -0
  92. data/lib/when_exe/locales/tr.rb +96 -0
  93. data/lib/when_exe/locales/uk.rb +128 -0
  94. data/lib/when_exe/locales/uz.rb +128 -0
  95. data/lib/when_exe/locales/vi.rb +94 -0
  96. data/lib/when_exe/locales/wo.rb +82 -0
  97. data/lib/when_exe/locales/zh_CN.rb +77 -0
  98. data/lib/when_exe/locales/zh_HK.rb +77 -0
  99. data/lib/when_exe/locales/zh_TW.rb +77 -0
  100. data/lib/when_exe/mini_application.rb +252 -0
  101. data/lib/when_exe/parts/enumerator.rb +472 -0
  102. data/lib/when_exe/parts/geometric_complex.rb +379 -0
  103. data/lib/when_exe/parts/locale.rb +513 -0
  104. data/lib/when_exe/parts/method_cash.rb +207 -0
  105. data/lib/when_exe/parts/resource.rb +806 -0
  106. data/lib/when_exe/parts/timezone.rb +182 -0
  107. data/lib/when_exe/region/bahai.rb +145 -0
  108. data/lib/when_exe/region/balinese.rb +627 -0
  109. data/lib/when_exe/region/chinese.rb +896 -0
  110. data/lib/when_exe/region/chinese_calendar.rb +919 -0
  111. data/lib/when_exe/region/chinese_epoch.rb +1245 -0
  112. data/lib/when_exe/region/christian.rb +644 -0
  113. data/lib/when_exe/region/far_east.rb +192 -0
  114. data/lib/when_exe/region/french.rb +66 -0
  115. data/lib/when_exe/region/geologicalage.rb +639 -0
  116. data/lib/when_exe/region/indian.rb +1066 -0
  117. data/lib/when_exe/region/iranian.rb +66 -0
  118. data/lib/when_exe/region/islamic.rb +105 -0
  119. data/lib/when_exe/region/japanese.rb +851 -0
  120. data/lib/when_exe/region/japanese_notes.rb +964 -0
  121. data/lib/when_exe/region/japanese_residues.rb +1149 -0
  122. data/lib/when_exe/region/javanese.rb +228 -0
  123. data/lib/when_exe/region/jewish.rb +127 -0
  124. data/lib/when_exe/region/korean.rb +267 -0
  125. data/lib/when_exe/region/m17n.rb +115 -0
  126. data/lib/when_exe/region/martian.rb +215 -0
  127. data/lib/when_exe/region/mayan.rb +122 -0
  128. data/lib/when_exe/region/moon.rb +333 -0
  129. data/lib/when_exe/region/nihon_shoki.rb +73 -0
  130. data/lib/when_exe/region/planets.rb +585 -0
  131. data/lib/when_exe/region/pope.rb +298 -0
  132. data/lib/when_exe/region/residue.rb +229 -0
  133. data/lib/when_exe/region/roman.rb +325 -0
  134. data/lib/when_exe/region/ryukyu.rb +98 -0
  135. data/lib/when_exe/region/shire.rb +254 -0
  136. data/lib/when_exe/region/sun.rb +210 -0
  137. data/lib/when_exe/region/thai.rb +227 -0
  138. data/lib/when_exe/region/tibetan.rb +233 -0
  139. data/lib/when_exe/region/v50.rb +111 -0
  140. data/lib/when_exe/region/vietnamese.rb +173 -0
  141. data/lib/when_exe/region/world.rb +197 -0
  142. data/lib/when_exe/timestandard.rb +547 -0
  143. data/lib/when_exe/tmduration.rb +330 -330
  144. data/lib/when_exe/tmobjects.rb +1295 -0
  145. data/lib/when_exe/tmposition.rb +1955 -0
  146. data/lib/when_exe/tmreference.rb +1547 -0
  147. data/lib/when_exe/version.rb +10 -3
  148. data/link_to_online_documents +4 -0
  149. data/test/examples/JapanHolidays.ics +456 -0
  150. data/test/examples/Millennium.ics +17 -0
  151. data/test/examples/NewYork.ics +61 -0
  152. data/test/examples/Residue.m17n +135 -0
  153. data/test/examples/Spatial.m17n +179 -0
  154. data/test/examples/Terms.m17n +39 -0
  155. data/test/examples/Test.ics +53 -0
  156. data/test/examples/USA-DST.ics +61 -0
  157. data/test/examples/geometric_complex.rb +41 -0
  158. data/test/examples/sample.xml +14 -0
  159. data/test/examples/today.rb +61 -0
  160. data/test/test.rb +54 -19
  161. data/test/test.rb.config +1 -0
  162. data/test/test/basictypes.rb +368 -0
  163. data/test/test/calendartypes.rb +57 -0
  164. data/test/test/coordinates.rb +380 -0
  165. data/test/test/ephemeris.rb +127 -0
  166. data/test/test/googlecalendar.rb +167 -0
  167. data/test/test/icalendar.rb +848 -0
  168. data/test/test/inspect.rb +115 -0
  169. data/test/test/parts.rb +480 -0
  170. data/test/test/region/chinese.rb +161 -0
  171. data/test/test/region/french.rb +33 -0
  172. data/test/test/region/geologicalage.rb +14 -0
  173. data/test/test/region/indian.rb +55 -0
  174. data/test/test/region/iran.rb +54 -0
  175. data/test/test/region/islamic.rb +18 -0
  176. data/test/test/region/japanese.rb +62 -0
  177. data/test/test/region/jewish.rb +61 -0
  178. data/test/test/region/m17n.rb +181 -0
  179. data/test/test/region/mayan.rb +78 -0
  180. data/test/test/region/moon.rb +14 -0
  181. data/test/test/region/planets.rb +14 -0
  182. data/test/test/region/residue.rb +123 -0
  183. data/test/test/region/sun.rb +14 -0
  184. data/test/test/region/thai.rb +94 -0
  185. data/test/test/region/tibetan.rb +30 -0
  186. data/test/test/tmobjects.rb +356 -57
  187. data/test/test/tmposition.rb +237 -0
  188. data/test/test/tmreference.rb +95 -0
  189. data/when_exe.gemspec +2 -2
  190. metadata +187 -7
  191. data/doc/COPYING +0 -31
  192. data/doc/COPYING.ja +0 -25
  193. data/doc/document_url +0 -1
@@ -0,0 +1,207 @@
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2013 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ #
9
+ # 本ライブラリのための諸々の部品
10
+ #
11
+ module When::Parts
12
+
13
+ #
14
+ # == メソッドの実行結果をキャッシュし処理の高速化を行う
15
+ #
16
+ # === fn というメソッドをキャッシュ化
17
+ # * fn ではなく fn_ を定義しておく
18
+ # * fn メソッドが呼ばれると fn_ を実行し、{引数=>戻り値} を Hash に記憶する
19
+ # * 同じ引数で再度 fn メソッドが呼ばれると Hash から戻り値を取り出して返す
20
+ #
21
+ # === a_to_b と b_to_a という互いに逆関数のメソッドをキャッシュ化
22
+ # * a_to_b ではなく a_to_b_ , b_to_a ではなく b_to_a_ を定義しておく
23
+ # * a_to_b メソッドが呼ばれると a_to_b_ を実行し、{引数=>戻り値}, {戻り値=>引数}を Hash に記憶する
24
+ # * 同じ引数で再度 a_to_b メソッドが呼ばれると Hash から戻り値を取り出して返す
25
+ # * b_to_a メソッドが呼ばれ Hash に戻り値があれば Hash から戻り値を取り出して返す
26
+ #
27
+ # == 特記事項
28
+ #
29
+ # === Argument identification
30
+ # The eql? method of When::TM::(Temporal)Position is not overridden.
31
+ # It seems that the cost of identification of the argument exceeds the merit of the method cash.
32
+ # I do not recommend applying the methodcash to the method which takes
33
+ # When::TM::(Temporal)Position as an argument.
34
+ #
35
+ # === Multi-thread critical situation
36
+ # There is a problem in consistency of hash when this function is used in multi-thread environment.
37
+ # If the initialize method sets Mutex in instance variable @_m_cash_lock_,
38
+ # this function gives up use of hash in the critical situation.
39
+ #
40
+ # class Foo
41
+ # include MethodCash
42
+ #
43
+ # def initialize
44
+ # ...
45
+ # @_m_cash_lock_ = Mutex.new
46
+ # ...
47
+ # end
48
+ # end
49
+ #
50
+ # 参考 http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/47663
51
+ #
52
+ module MethodCash
53
+
54
+ alias :method_missing_ :method_missing
55
+
56
+ #
57
+ # 最初に発生する method_missing で、キャッシュ機能を登録する
58
+ #
59
+ # @param [Symbol] name メソッド名
60
+ # @param [Array] args メソッド引数
61
+ # @param [block] block ブロック(省略可)
62
+ #
63
+ # @return [void]
64
+ #
65
+ def method_missing(name, *args, &block)
66
+
67
+ return method_missing_(name, *args, &block) unless respond_to?("#{name}_", true)
68
+
69
+ if ((name.to_s =~ /^(_*)(.+?)_to_(.+)$/) && respond_to?("#{$1}#{$3}_to_#{$2}_", true))
70
+ prefix, from, to = $~[1..3]
71
+ begin
72
+ if (@_m_cash_lock_)
73
+ return send("#{prefix}#{from}_to_#{to}_", *args, &block) unless @_m_cash_lock_.try_lock
74
+ unlock = "ensure ; @_m_cash_lock_.locked? && @_m_cash_lock_.unlock"
75
+ end
76
+ [[from, to],[to, from]].each do |pair|
77
+ a, b = pair
78
+ lock = @_m_cash_lock_ ? " return #{prefix}#{a}_to_#{b}_(*args) unless @_m_cash_lock_.try_lock" : ''
79
+ instance_eval %Q{
80
+ def #{prefix}#{a}_to_#{b}(*args)
81
+ key = _key_simplefy(args)
82
+ inv = @_m_cash_["#{prefix}#{a}_to_#{b}"][key]
83
+ return inv if inv
84
+ begin
85
+ #{lock}
86
+ inv = #{prefix}#{a}_to_#{b}_(*args)
87
+ @_m_cash_["#{prefix}#{b}_to_#{a}"][_key_simplefy(inv)] = args
88
+ @_m_cash_["#{prefix}#{a}_to_#{b}"][key] = inv
89
+ return inv
90
+ #{unlock}
91
+ end
92
+ end
93
+ }
94
+ end
95
+ key = _key_simplefy(args)
96
+ inv = send("#{prefix}#{from}_to_#{to}_", *args)
97
+ @_m_cash_ ||= {}
98
+ @_m_cash_["#{prefix}#{to}_to_#{from}"] ||= {}
99
+ @_m_cash_["#{prefix}#{from}_to_#{to}"] ||= {}
100
+ @_m_cash_["#{prefix}#{to}_to_#{from}"][_key_simplefy(inv)] = args
101
+ @_m_cash_["#{prefix}#{from}_to_#{to}"][key] = inv
102
+ return inv
103
+ ensure
104
+ @_m_cash_lock_ && @_m_cash_lock_.locked? && @_m_cash_lock_.unlock
105
+ end
106
+
107
+ else
108
+ begin
109
+ respond = respond_to?("#{name}_setup", true)
110
+ setup = respond ? "#{name}_setup(key, *args)" :
111
+ "(@_m_cash_[\"#{name}\"][key] = #{name}_(*args))"
112
+ if (@_m_cash_lock_)
113
+ return send("#{name}_", *args, &block) unless @_m_cash_lock_.try_lock
114
+ lock = " return #{name}_(*args) unless @_m_cash_lock_.try_lock"
115
+ unlock = "ensure ; @_m_cash_lock_.locked? && @_m_cash_lock_.unlock"
116
+ end
117
+ instance_eval %Q{
118
+ def #{name}(*args)
119
+ key = _key_simplefy(args)
120
+ ret = @_m_cash_["#{name}"][key]
121
+ return ret if ret
122
+ begin
123
+ #{lock}
124
+ return #{setup}
125
+ #{unlock}
126
+ end
127
+ end
128
+ }
129
+ key = _key_simplefy(args)
130
+ @_m_cash_ ||= {}
131
+ @_m_cash_["#{name}"] ||= {}
132
+ if (respond)
133
+ return send("#{name}_setup", key, *args)
134
+ else
135
+ return(@_m_cash_["#{name}"][key] ||= send("#{name}_", *args))
136
+ end
137
+ ensure
138
+ @_m_cash_lock_ && @_m_cash_lock_.locked? && @_m_cash_lock_.unlock
139
+ end
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def _key_simplefy(args)
146
+ key = args.kind_of?(Array) ? args.dup : args
147
+ key = key[0] while key.kind_of?(Array) && key.length<=1
148
+ return key
149
+ end
150
+ end
151
+
152
+ #
153
+ # デバッグのためのオブジェクト内部記録用
154
+ # @private
155
+ module SnapShot
156
+
157
+ @@depth = 2
158
+
159
+ class << self
160
+ def _default(depth=2)
161
+ @@depth = depth
162
+ end
163
+
164
+ def _snapshot(object, depth=@@depth, processed={})
165
+ obj = object.dup rescue (return object)
166
+ # def obj.is_snapshoted? ; true ; end
167
+ if (depth==0 || processed.key?(object))
168
+ return object if object.instance_of?(String)
169
+ string = object.to_s
170
+ string = "<##{object.class}>" + string unless (string =~ /^#</)
171
+ return string
172
+ else
173
+ object.instance_variables.each do |v|
174
+ if ((v =~ /^@_/) && object.kind_of?(SnapShot))
175
+ obj._remove_instance_variable(v)
176
+ else
177
+ src = object.instance_variable_get(v)
178
+ case src
179
+ when Array
180
+ val = src.map {|e| _snapshot(e, depth-1, processed)}
181
+ when Hash
182
+ val = {}
183
+ src.each_pair {|k,c| val[_snapshot(k, depth-1, processed)] = _snapshot(c, depth-1, processed)}
184
+ else
185
+ val = _snapshot(src, depth-1, processed)
186
+ end
187
+ obj.instance_variable_set(v, val)
188
+ end
189
+ end
190
+ processed[object] = true
191
+ end
192
+ return obj
193
+ end
194
+ end
195
+
196
+ def _snapshot(depth=@@depth, processed={})
197
+ SnapShot._snapshot(self, depth, processed)
198
+ end
199
+
200
+ private
201
+
202
+ def _remove_instance_variable(name)
203
+ remove_instance_variable(name) if (name =~ /^@_/)
204
+ end
205
+ end
206
+ # module Resource ; include SnapShot ; end
207
+ end
@@ -0,0 +1,806 @@
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2013 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ #
9
+ # 本ライブラリのための諸々の部品
10
+ #
11
+ module When::Parts
12
+
13
+ #
14
+ # Resource which has 'International Resource Identifier'
15
+ #
16
+ module Resource
17
+
18
+ # 登録済み Prefix
19
+ Prefix = {'_w' => When::SourceURI + '/',
20
+ '_p' => When::SourceURI + 'Parts/',
21
+ '_b' => When::SourceURI + 'BasicTypes/',
22
+ '_m' => When::SourceURI + 'BasicTypes/M17n/',
23
+ '_co' => When::SourceURI + 'Coordinates/',
24
+ '_l' => When::SourceURI + 'Coordinates/Spatial?',
25
+ '_v' => When::SourceURI + 'V/',
26
+ '_rs' => When::SourceURI + 'RS/',
27
+ '_ex' => When::SourceURI + 'EX/',
28
+ '_tm' => When::SourceURI + 'TM/',
29
+ '_e' => When::SourceURI + 'TM/CalendarEra/',
30
+ '_t' => When::SourceURI + 'TimeStandard/',
31
+ '_ep' => When::SourceURI + 'Ephemeris/',
32
+ '_c' => When::SourceURI + 'CalendarTypes/',
33
+ '_n' => When::SourceURI + 'CalendarTypes/CalendarNote/',
34
+ '_sc' => When::SourceURI + 'Ephemeris/V50/'
35
+ }
36
+
37
+ # @private
38
+ PrefixKeys = Prefix.keys.sort.reverse.map {|k| k.downcase}
39
+
40
+ # @private
41
+ PrefixValues = Prefix.values.sort.reverse
42
+
43
+ # @private
44
+ PrefixIndex = Prefix.invert
45
+
46
+ # @private
47
+ LabelProperty = nil
48
+
49
+ # @private
50
+ class Element
51
+ attr_reader :predicate
52
+ attr_reader :object
53
+ attr_reader :attribute
54
+ attr_accessor :namespace
55
+ attr_reader :marked
56
+
57
+ def initialize(key, object=nil, marked=nil)
58
+ key = key.downcase.gsub(/-/,'_') if (key==key.upcase)
59
+ @predicate, @namespace = key.split(/:/).reverse
60
+ @object = object
61
+ @marked = marked
62
+ @attribute = {}
63
+ end
64
+ end
65
+
66
+ # @private
67
+ module Synchronize
68
+
69
+ # 排他実行
70
+ #
71
+ # 与えられたブロックを必要なら排他制御をして実行する
72
+ #
73
+ def synchronize
74
+ if @_lock_
75
+ @_lock_.synchronize do
76
+ yield
77
+ end
78
+ else
79
+ yield
80
+ end
81
+ end
82
+ end
83
+
84
+ #
85
+ # Resource の has-a 親子関係を管理する
86
+ #
87
+ module Pool
88
+
89
+ include Synchronize
90
+
91
+ # 初期化
92
+ # @return [void]
93
+ def _setup_
94
+ @_lock_ = Mutex.new if When.multi_thread
95
+ @_lock_.lock if @_lock_
96
+ @_pool = {}
97
+ @_lock_.unlock if @_lock_
98
+ end
99
+
100
+ # オブジェクト参照
101
+ #
102
+ # @param [String] label
103
+ #
104
+ # @return [When::Parts::Resource] 指定した label で登録した子 Resource を返す
105
+ #
106
+ def [](label)
107
+
108
+ # nil label
109
+ return _pool[label] unless label
110
+
111
+ # 階層構造の確認
112
+ unless label =~ /\?/
113
+ terms = label.split(/::/)
114
+ terms.shift if terms[0] == ''
115
+ return terms.inject(self) {|obj,term| obj = obj[term]} if terms.length >= 2
116
+ end
117
+
118
+ # 階層がない場合
119
+ path, options = label.split(/\?/, 2)
120
+ label = Resource._extract_prefix(path)
121
+ label += '?' + options if options
122
+ _pool[label.gsub(/%3A%3A/, '::')]
123
+ end
124
+
125
+ # オブジェクト登録
126
+ #
127
+ # 指定した label で子 Resource を登録する
128
+ #
129
+ # @param [String] label
130
+ # @param [When::Parts::Resource] obj
131
+ #
132
+ # @return [void]
133
+ #
134
+ def []=(label, obj)
135
+ # raise NameError, "Name duplication" if (@_pool[label])
136
+ _pool[label] = obj
137
+ end
138
+
139
+ # @private
140
+ def pool_keys
141
+ _pool.keys
142
+ end
143
+
144
+ # @private
145
+ def _pool
146
+ _setup_ unless @_pool
147
+ @_pool
148
+ end
149
+ end
150
+
151
+ class << self
152
+
153
+ include Pool
154
+
155
+ # オブジェクト生成&参照
156
+ #
157
+ # 指定した iri の When::Parts::Resource オブジェクトを取得する。
158
+ # 当該オブジェクトが未登録であれば生成する。
159
+ #
160
+ # @param [String] iri International Resource Identifier
161
+ # @param [String] namespace (デフォルトの名前空間, 指定がないときは名前空間を省略しない)
162
+ #
163
+ # @return [When::Parts::Resource]
164
+ #
165
+ def _instance(iri, namespace=nil)
166
+ # 配列は個別に処理
167
+ return iri.map {|e| _instance(e, namespace)} if iri.kind_of?(Array)
168
+
169
+ # 文字列以外はそのまま返す
170
+ return iri unless iri.instance_of?(String)
171
+
172
+ # 階層がある場合は、階層をたどる
173
+ iri = namespace + iri if namespace && iri !~ /^[_a-z\d]+:[^:]/i
174
+ root, *leaves= iri.split(/::/)
175
+ return leaves.inject(_instance(root)) {|obj,leaf| obj[leaf]} unless leaves==[] || iri =~ /\?/
176
+
177
+ # 登録ずみなら、参照
178
+ path, query = _extract_prefix(iri).split(/\?/, 2)
179
+ iri = query ? (path + '?' + query) : path
180
+ if When.multi_thread
181
+ my_mutex = nil
182
+ @_lock_.synchronize do
183
+ @_pool ||= {}
184
+ unless @_pool[iri]
185
+ my_mutex = Mutex.new
186
+ @_pool[iri] = my_mutex
187
+ end
188
+ end
189
+ case @_pool[iri]
190
+ when my_mutex; my_mutex.synchronize {_create_object(iri, path, query) }
191
+ when Mutex ; @_pool[iri].synchronize { @_pool[iri] }
192
+ else ; @_pool[iri]
193
+ end
194
+ else
195
+ @_pool ||= {}
196
+ @_pool[iri] ? @_pool[iri] : _create_object(iri, path, query)
197
+ end
198
+ end
199
+
200
+ # @private
201
+ def _path_with_prefix(obj, simple=true)
202
+ path = obj.kind_of?(Class) ? obj.to_s.sub(/^When::/, When::SourceURI).gsub(/::/, '/') :
203
+ obj.iri
204
+ return path unless simple
205
+ PrefixValues.each do |value|
206
+ index = path.index(value)
207
+ return PrefixIndex[value] + ':' + path[value.length..-1] if index
208
+ end
209
+ return path
210
+ end
211
+
212
+ # @private
213
+ def _parse(line, type=nil)
214
+ return line unless line.kind_of?(String)
215
+ line.sub!(/\s#.*$/, '')
216
+ return Locale._split($1) if type && line =~ /^#{type}:(.+)$/i
217
+ return Locale._split(line) unless line =~ /^(\*)?([A-Z][-A-Z_]{0,255})(?:;(.*?))?:(.*)$/i
218
+
219
+ marked, key, property, value = $~[1..4]
220
+ element = Element.new(key, value, marked)
221
+ if (property)
222
+ element.attribute['.'] = property+':'+value
223
+ property.split(/;/) do |pr|
224
+ prop = Element.new(*pr.split(/=/, 2))
225
+ element.attribute[prop.predicate] = prop
226
+ end
227
+ else
228
+ element.attribute['.'] = value
229
+ end
230
+ return element
231
+ end
232
+
233
+ # @private
234
+ def _extract_prefix(path, capitalize=false)
235
+ if (path =~ /^(.+?):+(.+)$/)
236
+ prefix, klass = $~[1..2]
237
+ if capitalize
238
+ prefix = '_' + prefix.downcase
239
+ klass = klass.capitalize if klass == klass.upcase
240
+ end
241
+ path = Prefix[prefix] + klass if (Prefix[prefix])
242
+ elsif capitalize && path =~ /^(v[^\/]+|daylight$|standard$)/i
243
+ klass = path.sub(/^v/i, '').capitalize
244
+ path = Prefix['_v'] + klass if When::V.const_defined?(klass) &&
245
+ When::V.const_get(klass).kind_of?(Class)
246
+ end
247
+ return path
248
+ end
249
+
250
+ private
251
+
252
+ def _create_object(iri, path, query)
253
+ options = {}
254
+ replace = {}
255
+ if query
256
+ options = Hash[*query.split(/&/).map{ |pair| pair.split(/=/, 2) }.flatten]
257
+ keys = options.keys
258
+ keys.each do |key|
259
+ replace[$1] = options.delete(key) if key =~ /^!(.+)/
260
+ end
261
+ end
262
+ options['..'] = iri
263
+
264
+ obj = nil
265
+ list = _class(path)
266
+ if list
267
+ # direct URI
268
+ case list[0]
269
+ when Class
270
+ obj = list[0].new(options)
271
+ when Array
272
+ if list[0][0].kind_of?(Class)
273
+ # 配列の先頭がクラスである場合
274
+ klass, *list = list[0]
275
+ unless list[-1].kind_of?(Hash)
276
+ if list.length == 1
277
+ list[0] = {'.'=>Array(list[0])}
278
+ else
279
+ list << {}
280
+ end
281
+ end
282
+ else
283
+ # 配列の先頭がクラスではない場合
284
+ klass, *list = [list[1], *list[0]]
285
+ list << {} unless list[-1].kind_of?(Hash)
286
+ end
287
+ list[-1] = list[-1].merge(options)
288
+ obj = klass.new(*list)
289
+ else
290
+ obj = list[0]
291
+ end
292
+ else
293
+ # external Resource
294
+ parsed = nil
295
+ OpenURI
296
+ begin
297
+ open(path, "1".respond_to?(:force_encoding) ? 'r:utf-8' : 'r') do |file|
298
+ resource = file.read
299
+ replace.keys.each do |key|
300
+ resource.gsub!(/#\{#{key}\}/, replace[key])
301
+ end
302
+ parsed = (resource[0..5]=='BEGIN:') ? _ics(resource.split(/[\n\r]+/)) :
303
+ _xml(REXML::Document.new(resource).root)
304
+ end
305
+ rescue OpenURI::HTTPError => error
306
+ message = error.message + " - #{path}"
307
+ error = error.respond_to?(:uri) ?
308
+ error.class.new(message, error.io, error.uri) :
309
+ error.class.new(message, error.io)
310
+ raise error
311
+ end
312
+ options['.'] = parsed
313
+ obj = parsed[0].new(options)
314
+ end
315
+ @_pool[iri] = obj
316
+ end
317
+
318
+ def _class(path)
319
+ return nil unless (path =~ /^http:\/\/hosi\.org\/When\/(.*)/)
320
+ list = [When]
321
+ $1.split(/\//).each do |mod|
322
+ if list[0].const_defined?(mod)
323
+ list.unshift(list[0].const_get(mod))
324
+ else
325
+ return nil unless (list[0] == When::V)
326
+ list.unshift(When::V::Root)
327
+ return list
328
+ end
329
+ end
330
+ return list
331
+ end
332
+
333
+ # .xml フォーマットの読み込み
334
+ def _xml(xml, namespace={})
335
+ obj = [_class(_extract_prefix(xml.attributes['type'].to_s))[0]]
336
+ xml.attributes.each_pair do |key,value|
337
+ expanded_name = value.expanded_name
338
+ next unless (expanded_name =~ /^xmlns/)
339
+ key = '' if expanded_name == 'xmlns'
340
+ namespace[key] = value.to_s
341
+ end
342
+ obj << Element.new('xmlns:namespace', namespace) if (namespace.size>0)
343
+ xml.each do |e|
344
+ next unless defined? e.name
345
+ if (e.attributes['type'])
346
+ obj << _xml(e, namespace)
347
+ else
348
+ element = Element.new(e.expanded_name, e.attributes['ref']||e.text)
349
+ e.attributes.each_pair do |key,value|
350
+ attr = Element.new(value.name, value)
351
+ attr.namespace = value.prefix
352
+ element.attribute[key] = attr
353
+ end
354
+ obj << element
355
+ end
356
+ end
357
+ return obj
358
+ end
359
+
360
+ # .ics フォーマットの読み込み
361
+ def _ics(ics, type=nil)
362
+ obj = [type] if type
363
+ indent = nil
364
+ while (line = ics.shift) do
365
+ line.chomp!
366
+ case line
367
+ when /^\s*BEGIN:(.*)$/
368
+ if (type)
369
+ obj[-1] = _parse(obj[-1], type) if obj.length > 1
370
+ obj << _ics(ics, $1)
371
+ else
372
+ type = $1
373
+ obj = [type]
374
+ end
375
+ when /^\s*END:(.*)$/
376
+ raise TypeError, "Irregal Type : #{$1}" unless (type == $1)
377
+ obj[0] = _class(_extract_prefix(type, true))[0]
378
+ obj[-1] = _parse(obj[-1], type)
379
+ return obj
380
+ when /^\s*#/
381
+ when /^(\s*)(.*)$/
382
+ indent = $1 unless indent
383
+ if (indent.length < $1.length)
384
+ obj[-1] += line[(indent.length+1)..-1] # See RFC5545 3.1 Content Lines
385
+ else
386
+ obj << $2
387
+ obj[-2] = _parse(obj[-2], type)
388
+ end
389
+ end
390
+ end
391
+ raise ArgumentError, "BEGIN-END mismatch"
392
+ end
393
+ end
394
+
395
+ include Synchronize
396
+
397
+ # @private
398
+ attr_reader :_pool
399
+
400
+ # self が has-a 関係で包含するオブジェクト
401
+ #
402
+ # @return [Array<When::Parts::Resource>]
403
+ #
404
+ attr_reader :child
405
+
406
+ #
407
+ # Resource包含階層で使用する namespace
408
+ #
409
+ # When::BasicTypes::M17n の生成に使用する namespace を定義する。
410
+ # RFC 5545 に対する拡張である。
411
+ # xml で記述する場合には、本ライブラリ以外でも namespace を定義している。
412
+ #
413
+ # @return [Hash] { prefix => prefix文字列 }
414
+ #
415
+ attr_reader :namespace
416
+
417
+ #
418
+ # Resource包含階層で使用する locale
419
+ #
420
+ # When::BasicTypes::M17n の生成に使用する locale を定義する。
421
+ # RFC 5545 に対する拡張である。
422
+ #
423
+ # @return [Array<String>]
424
+ #
425
+ attr_reader :locale
426
+
427
+ # strftime で有効な locale
428
+ #
429
+ # @return [Array<String>]
430
+ #
431
+ attr_reader :keys
432
+
433
+ # オブジェクトの IRI
434
+ #
435
+ # @return [Sring]
436
+ #
437
+ def iri
438
+ return @iri if @iri
439
+ root = @_pool['..']
440
+ path = root.instance_of?(String) ? root : label.to_s
441
+ path = path.gsub(/::/, '%3A%3A')
442
+ if root.respond_to?(:iri)
443
+ prefix = root.iri
444
+ path = prefix + '::' + path if prefix
445
+ end
446
+ @iri = path
447
+ end
448
+
449
+ # IRI または child の番号によるオブジェクト参照
450
+ #
451
+ # @param [String] iri オブジェクトの IRI
452
+ # @param [Numeric] iri child の index
453
+ #
454
+ # @return [When::parts::Resource]
455
+ #
456
+ def [](iri)
457
+ case iri
458
+ when Numeric
459
+ return child[iri * 1]
460
+ when String
461
+ obj = self
462
+ iri.split(/::/).each do |label|
463
+ return obj.child if label == '*'
464
+ if obj == Resource
465
+ obj = Resource._instance(label)
466
+ else
467
+ case label
468
+ when '' ; obj = Resource
469
+ when '.' # obj = obj
470
+ else ; obj = obj._pool[label.gsub(/%3A%3A/, '::')]
471
+ end
472
+ end
473
+ raise ArgumentError, "IRI not found: #{iri}" unless obj
474
+ end
475
+ return obj
476
+ else
477
+ super(iri)
478
+ #raise ArgumentError, "IRI not found: #{iri}"
479
+ end
480
+ end
481
+
482
+ # self を直接に包含するオブジェクト
483
+ #
484
+ # @return [When::Parts::Resource]
485
+ #
486
+ def parent
487
+ @_pool['..'].kind_of?(Resource) ? @_pool['..'] : nil
488
+ end
489
+
490
+ # self を包含するオブジェクト階層
491
+ #
492
+ # @param [Class] klass 階層を遡るクラス
493
+ #
494
+ # @return [When::Parts::Resource]
495
+ #
496
+ def hierarchy(klass=self.class)
497
+ hierarchy = []
498
+ parent = self
499
+ while parent.kind_of?(klass)
500
+ hierarchy << parent
501
+ parent = parent.parent
502
+ end
503
+ hierarchy.reverse
504
+ end
505
+
506
+ # self が other を包含するか
507
+ #
508
+ # @return [Boolean]
509
+ # [ true - 包含する ]
510
+ # [ false - 包含しない ]
511
+ #
512
+ def include?(other)
513
+ c = other
514
+ while c.kind_of?(Resource)
515
+ return true if c.equal?(self)
516
+ c = c.parent
517
+ end
518
+ return false
519
+ end
520
+
521
+ # other が self を包含するか
522
+ #
523
+ # @return [Boolean]
524
+ # [ true - 包含される ]
525
+ # [ false - 包含されない ]
526
+ #
527
+ def included?(other)
528
+ other.include?(self)
529
+ end
530
+
531
+ # 前のオブジェクト
532
+ #
533
+ # @return [When::Parts::Resource]
534
+ #
535
+ def prev
536
+ c = self
537
+ c = c.child[0] while c.child && c.child.size > 0
538
+ c = c._pool['.<-']
539
+ c = c.child[-1] while c && c.child && c.child.size > 0
540
+ c
541
+ end
542
+
543
+ # 次のオブジェクト
544
+ #
545
+ # @return [When::Parts::Resource]
546
+ #
547
+ def next
548
+ c = self
549
+ c = c.child[0] while c.child && c.child.size > 0
550
+ c._pool['.->']
551
+ end
552
+ alias :succ :next
553
+
554
+ # オブジェクト包含階層の末端か?
555
+ #
556
+ # @return [Boolean]
557
+ # [ true - IRIが付与された他のオブジェクトを包含していない ]
558
+ # [ false - IRIが付与された他のオブジェクトを包含している ]
559
+ #
560
+ def leaf?
561
+ !@child || (@child.length==0)
562
+ end
563
+
564
+ # IRIが付与されているか?
565
+ #
566
+ # @return [Boolean]
567
+ # [ true - IRIが付与されている ]
568
+ # [ false - IRIが付与されていない ]
569
+ #
570
+ def registered?
571
+ leaf = self
572
+ while leaf._pool['..'].respond_to?(:_pool)
573
+ root = leaf._pool['..']
574
+ return false unless leaf.equal?(root._pool[leaf.label])
575
+ leaf = root
576
+ end
577
+ Resource._pool.value?(leaf)
578
+ end
579
+
580
+ # When::BasicTypes::M17n の生成/参照
581
+ #
582
+ # @param [When::BasicTypes::M17n] source 処理を行わず、そのままsourceを返す
583
+ # @param [String] source locale と 文字列の対応
584
+ # @param [Array] source 要素を個別に解釈して生成したオブジェクトのArrayを返す
585
+ # @param [Hash] namespace prefix の指定
586
+ # @param [Array] locale locale の定義順序の指定
587
+ # @param [Hash] options (see {When::BasicTypes::M17n.new}[link:When/BasicTypes/M17n.html#method-c-new])
588
+ #
589
+ # @return [When::BasicTypes::M17n or Array<them>]
590
+ #
591
+ def m17n(source, namespace=nil, locale=nil, options={})
592
+ case source
593
+ when Array ; When::BasicTypes::M17n.new(source, namespace, locale, options)
594
+ when When::BasicTypes::M17n ; source
595
+ when String
596
+ return self[$1] if source =~ /^\s*\[((\.{1,2}|::)+[^\]]+)\]/
597
+ When::BasicTypes::M17n.new(source, namespace, locale, options)
598
+ else ; raise TypeError, "Invalid Type: #{source.class}"
599
+ end
600
+ end
601
+
602
+ # オブジェクトを順に取り出す enumerator
603
+ #
604
+ # @param [Symbol] direction 取り出す方向
605
+ # [ :forward - 昇順 ]
606
+ # [ :reverse - 降順 ]
607
+ #
608
+ def enum_for(direction=:forward)
609
+ Enumerator.new(self, direction)
610
+ end
611
+ alias :to_enum :enum_for
612
+
613
+ # Enumerator 生成のダミー
614
+ #
615
+ # @param [Object] other
616
+ #
617
+ # @return [Enumerator]
618
+ #
619
+ def ^(other)
620
+ return nil
621
+ end
622
+
623
+ # 順次実行
624
+ #
625
+ # @param [Array] args 各サブクラスの enum_for にそのまま渡す
626
+ # @param [Block] block 実行するブロック
627
+ #
628
+ # @return [Enumerator]
629
+ #
630
+ def each(*args, &block)
631
+ enum = enum_for(*args)
632
+ return enum unless block
633
+ enum.each(&block)
634
+ end
635
+
636
+ # map, collect の再定義
637
+ #
638
+ # has-a 関係の子 Resource に対して map/collect を行う
639
+ #
640
+ # @param [Block] block 実行するブロック
641
+ #
642
+ # @return [Array]
643
+ #
644
+ def map(&block)
645
+ @child.map(&block)
646
+ end
647
+ alias :collect :map
648
+
649
+ protected
650
+
651
+ # @private
652
+ def pool_keys
653
+ _pool.keys
654
+ end
655
+
656
+ private
657
+
658
+ # 属性の設定
659
+ def _attributes(args)
660
+ options =_get_options(args)
661
+ _set_variables(options)
662
+ return args, options
663
+ end
664
+
665
+ # option の読み出し
666
+ def _get_options(args)
667
+ options = args[-1].kind_of?(Hash) ? args.pop : {}
668
+ @_pool = {}
669
+ @_pool['..'] = options['..'] if (options['..'])
670
+
671
+ # 配下のオブジェクトの生成と関連付け
672
+ if (options.key?('.'))
673
+ _child(options)
674
+ end
675
+
676
+ options
677
+ end
678
+
679
+ # 変数の設定
680
+ def _set_variables(options)
681
+ @options = options[:options] || {} if options.key?(:options)
682
+ options.each_pair do |key,value|
683
+ unless (key =~ /^options$|^[_.]/)
684
+ # スキームの":"がエンコーディングされていたら、valueをデコード
685
+ if (value =~ /^\w+%3A/i)
686
+ value.gsub!(/%[0-9A-F]{2}/i) do |c|
687
+ c.sub(/%/,'0x').hex.chr
688
+ end
689
+ end
690
+ case "#{key}"
691
+ when 'namespace' ; value = Locale._namespace(value)
692
+ when 'locale' ; value = Locale._locale(value)
693
+ end
694
+ instance_variable_set("@#{key}", value)
695
+ end
696
+ end
697
+ end
698
+
699
+ # 配下のオブジェクトの生成
700
+ def _child(options)
701
+ @child = []
702
+ query = options.dup
703
+ options['..'] = self
704
+ leaf = options['.']
705
+ label_candidates = nil
706
+
707
+ leaf.each_index do |i|
708
+ element = Resource._parse(leaf[i])
709
+ case element
710
+ when Array
711
+ if element[0].kind_of?(Class)
712
+ list = []
713
+ element.each do |e|
714
+ if e.kind_of?(Hash)
715
+ list += e.keys.map {|key| Resource::Element.new(key, e[key])}
716
+ else
717
+ list << e
718
+ end
719
+ end
720
+ options['.'] = list
721
+ @child << element[0].new(options.dup)
722
+ else
723
+ options.delete('.')
724
+ @child << self.class.new(*(element + [options]))
725
+ end
726
+
727
+ when Resource::Element
728
+ key = element.predicate
729
+ value = element.object
730
+ if (value)
731
+ case key
732
+ when 'namespace'
733
+ options[key] ||= {}
734
+ options[key] = options[key].merge(Locale._namespace(value))
735
+ when 'locale'
736
+ options[key] = Locale._locale(value)
737
+ else
738
+ if (value.instance_of?(String) && value =~ /^\[/)
739
+ options.delete('.')
740
+ value = m17n(value, nil, nil, options.dup)
741
+ @_pool[value.to_s] = value
742
+ end
743
+ if element.marked || key == self.class::LabelProperty
744
+ @label = value
745
+ else
746
+ options[key] = value
747
+ label_candidates ||= value
748
+ end
749
+ end
750
+ else
751
+ options.delete(key)
752
+ end
753
+ end
754
+ end
755
+ options.update(query)
756
+ unless @label
757
+ raise ArgumentError, "label attribute not found: #{leaf}" unless label_candidates
758
+ @label = label_candidates
759
+ end
760
+ end
761
+
762
+ # 配下のオブジェクトの前後関係の設定
763
+ def _sequence
764
+ return unless @child
765
+ prev = @_pool['..'].child[-1] if @_pool['..'].respond_to?(:child)
766
+ @child.each do |v|
767
+ if prev
768
+ v._pool['.<-'] = prev
769
+ prev._pool['.->'] = v
770
+ while (prev.child && prev.child[-1]) do
771
+ prev = prev.child[-1]
772
+ prev._pool['.->'] = v
773
+ end
774
+ end
775
+ @_pool[v.label.to_s] = v
776
+ prev = v
777
+ end
778
+ end
779
+
780
+ # その他のメソッド
781
+ # When::Parts::GeometricComplex で定義されていないメソッドは
782
+ # 処理を @child (type: Array) に委譲する
783
+ #
784
+ def method_missing(name, *args, &block)
785
+ @child.send(name.to_sym, *args, &block)
786
+ end
787
+
788
+ #
789
+ # オブジェクトを順に取り出す enumerator
790
+ #
791
+ class Enumerator < When::Parts::Enumerator
792
+
793
+ #
794
+ # 次のオブジェクトを取り出す
795
+ #
796
+ # @return [When::Parts::Resource]
797
+ #
798
+ def succ
799
+ value = @current
800
+ @current = (@current==:first) ? @first : ((@direction == :reverse) ? @current.prev : @current.next)
801
+ @current = nil unless @first.leaf? || @first.include?(@current)
802
+ return value
803
+ end
804
+ end
805
+ end
806
+ end