when_exe 0.2.100 → 0.3.1

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