when_exe 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +171 -0
  3. data/lib/when_exe.rb +78 -47
  4. data/lib/when_exe/basictypes.rb +752 -747
  5. data/lib/when_exe/calendarnote.rb +805 -801
  6. data/lib/when_exe/calendartypes.rb +1583 -1531
  7. data/lib/when_exe/coordinates.rb +16 -15
  8. data/lib/when_exe/core/duration.rb +114 -110
  9. data/lib/when_exe/core/extension.rb +504 -504
  10. data/lib/when_exe/ephemeris.rb +1917 -1913
  11. data/lib/when_exe/ephemeris/moon.rb +333 -333
  12. data/lib/when_exe/ephemeris/notes.rb +389 -387
  13. data/lib/when_exe/ephemeris/planets.rb +585 -585
  14. data/lib/when_exe/ephemeris/sun.rb +214 -214
  15. data/lib/when_exe/googlecalendar.rb +144 -140
  16. data/lib/when_exe/icalendar.rb +1636 -1636
  17. data/lib/when_exe/inspect.rb +46 -22
  18. data/lib/when_exe/locales/akt.rb +176 -176
  19. data/lib/when_exe/locales/encoding_conversion.rb +134 -126
  20. data/lib/when_exe/locales/iast.rb +90 -90
  21. data/lib/when_exe/locales/locale.rb +750 -746
  22. data/lib/when_exe/locales/transliteration_table.rb +62 -62
  23. data/lib/when_exe/mini_application.rb +307 -305
  24. data/lib/when_exe/parts/enumerator.rb +2 -2
  25. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  26. data/lib/when_exe/parts/method_cash.rb +224 -224
  27. data/lib/when_exe/parts/resource.rb +1069 -1071
  28. data/lib/when_exe/parts/timezone.rb +240 -230
  29. data/lib/when_exe/region/armenian.rb +56 -56
  30. data/lib/when_exe/region/babylonian.rb +405 -0
  31. data/lib/when_exe/region/bahai.rb +146 -146
  32. data/lib/when_exe/region/balinese.rb +622 -622
  33. data/lib/when_exe/region/chinese.rb +95 -25
  34. data/lib/when_exe/region/chinese/calendars.rb +1016 -1016
  35. data/lib/when_exe/region/chinese/epochs.rb +1 -1
  36. data/lib/when_exe/region/chinese/twins.rb +803 -795
  37. data/lib/when_exe/region/christian.rb +824 -824
  38. data/lib/when_exe/region/coptic.rb +106 -87
  39. data/lib/when_exe/region/discordian.rb +225 -225
  40. data/lib/when_exe/region/far_east.rb +188 -188
  41. data/lib/when_exe/region/french.rb +56 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +58 -58
  44. data/lib/when_exe/region/indian.rb +1254 -1251
  45. data/lib/when_exe/region/iranian.rb +8 -8
  46. data/lib/when_exe/region/islamic.rb +3 -3
  47. data/lib/when_exe/region/japanese.rb +93 -99
  48. data/lib/when_exe/region/japanese/calendars.rb +396 -397
  49. data/lib/when_exe/region/japanese/epochs.rb +26 -26
  50. data/lib/when_exe/region/japanese/nihon_shoki.rb +71 -71
  51. data/lib/when_exe/region/japanese/notes.rb +1383 -1386
  52. data/lib/when_exe/region/japanese/residues.rb +1306 -1306
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -0
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +126 -126
  57. data/lib/when_exe/region/korean.rb +378 -378
  58. data/lib/when_exe/region/m17n.rb +114 -113
  59. data/lib/when_exe/region/martian.rb +258 -255
  60. data/lib/when_exe/region/mayan.rb +32 -32
  61. data/lib/when_exe/region/residue.rb +89 -89
  62. data/lib/when_exe/region/roman.rb +36 -24
  63. data/lib/when_exe/region/ryukyu.rb +97 -97
  64. data/lib/when_exe/region/shire.rb +240 -240
  65. data/lib/when_exe/region/soviet.rb +209 -0
  66. data/lib/when_exe/region/symmetry.rb +50 -50
  67. data/lib/when_exe/region/thai.rb +336 -335
  68. data/lib/when_exe/region/tibetan.rb +316 -315
  69. data/lib/when_exe/region/vietnamese.rb +440 -439
  70. data/lib/when_exe/region/weekdate.rb +80 -80
  71. data/lib/when_exe/region/world.rb +175 -175
  72. data/lib/when_exe/region/yerm.rb +14 -14
  73. data/lib/when_exe/region/zoroastrian.rb +203 -203
  74. data/lib/when_exe/timestandard.rb +707 -681
  75. data/lib/when_exe/tmduration.rb +338 -330
  76. data/lib/when_exe/tmobjects.rb +1346 -1325
  77. data/lib/when_exe/tmposition.rb +2115 -2072
  78. data/lib/when_exe/tmreference.rb +1693 -1669
  79. data/lib/when_exe/version.rb +1 -1
  80. data/link_to_online_documents +1 -1
  81. data/test/examples/JapanHolidaysRFC6350.ics +1 -1
  82. data/test/test.rb +67 -61
  83. data/test/test/basictypes.rb +409 -409
  84. data/test/test/calendarnote.rb +86 -69
  85. data/test/test/calendartypes.rb +97 -97
  86. data/test/test/coordinates.rb +396 -396
  87. data/test/test/ephemeris.rb +83 -74
  88. data/test/test/ephemeris/moon.rb +14 -14
  89. data/test/test/ephemeris/planets.rb +14 -14
  90. data/test/test/ephemeris/sun.rb +14 -14
  91. data/test/test/googlecalendar.rb +194 -176
  92. data/test/test/icalendar.rb +867 -858
  93. data/test/test/inspect.rb +117 -117
  94. data/test/test/parts.rb +487 -487
  95. data/test/test/region/balinese.rb +34 -0
  96. data/test/test/region/chinese.rb +218 -206
  97. data/test/test/region/christian.rb +245 -245
  98. data/test/test/region/coptic.rb +27 -27
  99. data/test/test/region/french.rb +33 -33
  100. data/test/test/region/geologicalage.rb +17 -17
  101. data/test/test/region/indian.rb +57 -57
  102. data/test/test/region/iran.rb +54 -54
  103. data/test/test/region/islamic.rb +18 -18
  104. data/test/test/region/japanese.rb +237 -219
  105. data/test/test/region/jewish.rb +61 -61
  106. data/test/test/region/m17n.rb +184 -184
  107. data/test/test/region/mayan.rb +195 -195
  108. data/test/test/region/residue.rb +147 -139
  109. data/test/test/region/thai.rb +116 -116
  110. data/test/test/region/tibetan.rb +30 -30
  111. data/test/test/region/vietnamese.rb +102 -102
  112. data/test/test/region/yerm.rb +146 -146
  113. data/test/test/timestandard.rb +81 -81
  114. data/test/test/tmobjects.rb +328 -328
  115. data/test/test/tmposition.rb +397 -284
  116. data/test/test/tmreference.rb +157 -157
  117. metadata +13 -10
@@ -1,801 +1,805 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2011-2014 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
- module When
9
-
10
- #
11
- # 暦注 - Calendar Note
12
- #
13
- class CalendarNote < TM::ReferenceSystem
14
-
15
- #
16
- # 暦注要素への名前アクセス機能提供
17
- #
18
- # @private
19
- module LabelAccess
20
- attr_reader :_pool
21
-
22
- #
23
- # 暦注要素への名前(label)によるアクセス
24
- #
25
- # @param [Numeric] key 配列インデックスと見なしてアクセス
26
- # @param [String] key 名前(label)と見なしてアクセス
27
- #
28
- # @return [Object] 暦注要素
29
- #
30
- def [](key)
31
- return super if key.kind_of?(Numeric)
32
- @_pool ||= Hash[*(inject([]) {|pair, v| pair << v.label.to_s << v})]
33
- @_pool[key]
34
- end
35
- end
36
-
37
- #
38
- # 暦注要素のひな形クラス
39
- #
40
- # @private
41
- class NoteElement < When::BasicTypes::Object
42
- #
43
- # _m17n_form のための要素生成
44
- #
45
- # @param [Hash] options 下記のとおり
46
- # @option options [Symbol] :method :to_m17n なら label を返す、その他は When::Parts::Resource#to_h 参照
47
- #
48
- def _to_hash_value(options={})
49
- options[:method] == :to_m17n ? label : super
50
- end
51
- end
52
-
53
- #
54
- # 暦注検索結果を保存するコンテナ
55
- #
56
- module NotesContainer
57
-
58
- class << self
59
- #
60
- # 永続データに暦注検索結果を登録する
61
- #
62
- # @private
63
- def register(notes, persistence, sdn)
64
- if persistence
65
- persistence.store(sdn, notes)
66
- persistence.extend(self)
67
- end
68
- notes.extend(self)
69
- end
70
-
71
- #
72
- # 永続データから暦注検索結果を取り出す
73
- #
74
- # @private
75
- def retrieve(persistence, sdn)
76
- return false unless persistence
77
- persistence.fetch(sdn, false)
78
- end
79
-
80
- #
81
- # パターンの値の一致を判定する
82
- #
83
- # @private
84
- def verify(pattern, value)
85
- value = value.label if !value.kind_of?(When::BasicTypes::M17n) && value.respond_to?(:label)
86
- pattern = When::EncodingConversion.to_internal_encoding(pattern)
87
- pattern.kind_of?(Regexp) ? value =~ pattern : value == pattern
88
- end
89
- end
90
-
91
- # 暦注検索結果コンテナの次元を下げる
92
- #
93
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
94
- #
95
- # @return [NotesContainer]
96
- #
97
- def simplify(compact=true)
98
- if kind_of?(Hash) && !key?(:note)
99
- # Persistent NotesContainer
100
- simplified = {}
101
- each_pair do |key, value|
102
- value = value.simplify(compact) if value.kind_of?(NotesContainer)
103
- simplified[key] = value if value
104
- end
105
- else
106
- # Non-Persistent NotesContainer
107
- simplified = self
108
- simplified = _compact(simplified) if compact
109
- simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
110
- end
111
- _bless(simplified)
112
- end
113
-
114
- # 暦注検索結果コンテナのハッシュ要素
115
- #
116
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
117
- # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
118
- #
119
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
120
- #
121
- # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
122
- #
123
- def value(compact=true, symbol=:value)
124
- if kind_of?(Hash) && !key?(:note)
125
- # Persistent NotesContainer
126
- sliced = {}
127
- each_pair do |key, val|
128
- sliced[key] = val[symbol]
129
- end
130
- else
131
- # Non-Persistent NotesContainer
132
- return _bless(slice(symbol)) if symbol.kind_of?(Integer)
133
- sliced = _dig(self) {|target| target.fetch(symbol, nil)}
134
- end
135
- sliced = _bless(sliced)
136
- compact ? sliced.simplify : sliced
137
- end
138
-
139
- # 暦注検索結果コンテナのハッシュ要素
140
- #
141
- # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
142
- #
143
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
144
- #
145
- # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
146
- #
147
- def [](symbol)
148
- value(false, symbol)
149
- end
150
-
151
- # 暦注検索結果コンテナの条件に合う要素を抽出する
152
- #
153
- # @param [Hash{Symbol=>String or Regexp}] condition 条件
154
- # @param [Boolean] compact 余分な nil や空配列を除去するか否か
155
- #
156
- # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
157
- #
158
- def subset(condition, compact=true)
159
- if kind_of?(Hash) && !key?(:note)
160
- # Persistent NotesContainer
161
- result = {}
162
- each_pair do |key, value|
163
- value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
164
- result[key] = value if value
165
- end
166
- else
167
- # Non-Persistent NotesContainer
168
- result = _dig(self) {|target|
169
- (condition.each_pair {|key, pattern| break nil unless NotesContainer.verify(pattern, target[key])}) ? target : nil
170
- }
171
- result = _compact(result) if compact
172
- end
173
- _bless(result)
174
- end
175
-
176
- private
177
-
178
- #
179
- # 対象オブジェクトを NotesContainer にする
180
- #
181
- def _bless(target)
182
- target.extend(NotesContainer) if target && !equal?(target)
183
- target
184
- end
185
-
186
- #
187
- # 多次元配列要素に対して再帰的に要素を抽出して操作を施す
188
- #
189
- def _dig(list, &block)
190
- return yield(list) unless list.kind_of?(Array)
191
- list.map {|element| _dig(element, &block)}
192
- end
193
-
194
- #
195
- # 多次元配列要素に対して再帰的に作用し nil や空配列を削除する
196
- #
197
- def _compact(list)
198
- return list unless list.kind_of?(Array)
199
- result = list.map {|element| _compact(element)}.compact
200
- return result unless result.empty?
201
- nil
202
- end
203
- end
204
-
205
- #
206
- # 暦法によってイベントの動作を変えるか否か
207
- #
208
- CalendarDepend = false
209
-
210
- # デフォルトイベント名
211
- #
212
- # @return [String]
213
- #
214
- # @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
215
- # SolarTermsクラスで 'term180' は、太陽黄経180度のイベントすなわち秋分を意味します。
216
- #
217
- attr_accessor :event
218
- protected :event=
219
-
220
- # デフォルトイベントの指定
221
- #
222
- # @param [String] event 指定値を@eventとした新しいオブジェクトを作る
223
- #
224
- # @return [When::CalendarNote]
225
- #
226
- def copy(event)
227
- c = self.clone
228
- c.event = event
229
- c
230
- end
231
-
232
- # 典型的なイベントの発生間隔
233
- #
234
- # @param [String] event
235
- #
236
- # @return [When::TM::PeriodDuration]
237
- #
238
- def duration(event=@event)
239
- void, event, parameter = event.split(/^([^\d]+)/)
240
- send((event+'_delta').downcase.to_sym, parameter)
241
- end
242
-
243
- # 指定の日時が指定イベントに該当するか?
244
- #
245
- # @param [When::TM::TemporalPosition] date チェックされる日時
246
- # @param [String] event チェックするイベント名
247
- #
248
- # @return [Boolean]
249
- # [ true - 該当する ]
250
- # [ false - 該当しない ]
251
- #
252
- def include?(date, event=@event)
253
- enum_for(date, :forward, event.downcase).next.include?(date)
254
- end
255
-
256
- # Enumeratorの生成
257
- #
258
- # @overload enum_for(range, options={})
259
- # @param [Range, When::Parts::GeometricComplex] range
260
- # [ 始点 - range.first ]
261
- # [ 終点 - range.last ]
262
- # @param [Hash] options 以下の通り
263
- # @option options [String] :event イベント名(デフォルトは@event)
264
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
265
- #
266
- # @overload enum_for(first, direction=:forward, options={})
267
- # @param [When::TM::TemporalPosition] first 始点
268
- # @param [Symbol] direction (options[:direction]で渡してもよい)
269
- # [:forward] 昇順(デフォルト)
270
- # [:reverse] 降順
271
- # @param [Hash] options 以下の通り
272
- # @option options [String] :event イベント名(デフォルトは@event)
273
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
274
- #
275
- # @return [Enumerator]
276
- #
277
- def enum_for(*args)
278
- params = args.dup
279
- options = params[-1].kind_of?(Hash) ? params.pop.dup : {}
280
- options[:event] ||= @event
281
- self.class::Enumerator.new(*(params[0].kind_of?(Range) ?
282
- [self, params[0], options] :
283
- [self, params[0], params[1] || :forward, options]))
284
- end
285
- alias :to_enum :enum_for
286
-
287
- # 暦注の計算
288
- #
289
- # @param [When::TM::TemporalPosition] date 暦注を計算する日時
290
- # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
291
- # @param [String] options { :notes => String } という Hash の指定と等価
292
- # @param [Integer] options { :indices => Integer} という Hash の指定と等価
293
- # @param [Hash] options 下記のとおり
294
- # @option options [Integer] :indices Integerで指定した暦座標の暦注を計算
295
- # [ When::DAY ( 0) - 日 ]
296
- # [ When::MONTH(-1) - 月 ]
297
- # [ When::YEAR (-2) - 年 ]
298
- #
299
- # @option options [Array<Integer>] :indices Integerで指定したすべて暦座標の暦注を計算
300
- # @option options [nil] :indices すべての暦座標の暦注を計算(デフォルト)
301
- # @option options [String] :notes 計算する暦注名(日の暦注)
302
- # @option options [Integer] :notes 計算する暦注のビット配列(日の暦注)
303
- # @option options [Array<Array<String>>] :notes 計算する暦注名の Array の Array
304
- # @option options [Array<Integer>] :notes 計算する暦注のビット配列の Array
305
- # @option options [:all] :notes すべての暦注を計算
306
- # @option options [:prime, nil] :notes @prime に登録した暦注を計算、@prime未登録なら :all と同じ(デフォルト)
307
- # @option options [Hash] :persistence {ユリウス通日=>暦注計算結果}を保持する永続オブジェクト
308
- # @option options [Hash] :conditions 暦注計算の条件
309
- # [ :location => 暦注計算の基準となる場所(String or When::Coordinates::Spatial) ]
310
- # [ その他のキー => 個々の暦注クラスごとにその他のキーを使用できる ]
311
- #
312
- # @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
313
- # see {When::TM::TemporalPosition._instance}
314
- #
315
- # @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
316
- # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
317
- #
318
- # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
319
- # When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
320
- # ビットアドレスの値が 1 の暦注が計算対象となる。
321
- #
322
- # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::NotesContainerモジュールをextendしている)
323
- # [ :note => 暦注要素 (When::Coordinates::Residue, String, When::BasicTypes::M17n) ]
324
- # [ :value => 暦注の値 (When::Coordinates::Residue, String, When::BasicTypes::M17n, When::TM::TemporalPosition) ]
325
- #
326
- # @note
327
- # 戻り値の :value が When::TM::TemporalPosition の場合、その日時オブジェクトの events[0] に暦注名の入った
328
- # 暦注に該当する日付である。(例) Christian クラス で easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
329
- # @note
330
- # 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
331
- #
332
- def notes(date, options={})
333
- dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
334
- retrieved = NotesContainer.retrieve(persistence, date.to_i)
335
- return retrieved unless retrieved == false
336
- NotesContainer.register(indices.map {|i|
337
- next [] unless i <= date.precision
338
- _note_values(dates, notes[i-1], _all_keys[i-1], _elements[i-1]) do |dates, focused_notes, notes_hash|
339
- focused_notes.each do |note|
340
- notes_hash[note] ||= _note_element(note, i, conditions, dates)
341
- end
342
- notes_hash
343
- end
344
- }, persistence, date.to_i)
345
- end
346
-
347
- #
348
- # 暦注の一致 or 不一致
349
- #
350
- # @param [When::TM::TemporalPosition] date 暦注を確認する日時
351
- # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
352
- # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
353
- # (指定の notes が存在する場合は前者、しない場合は後者)
354
- # @param [Integer] options { :indices => Integer } という Hash の指定と等価
355
- # @param [Hash] options 下記のとおり
356
- # @option options [暦注の値] :value 確認する暦注の値(または正規表現)
357
- # @option options [それぞれ] その他 {When::CalendarNote#notes} を参照
358
- #
359
- # @return [Boolean]
360
- # [ true - 暦注が一致 ]
361
- # [ false - 暦注が不一致 ]
362
- #
363
- def note?(date, options={})
364
- options = _find_note(options) if options.kind_of?(String) || options.kind_of?(Regexp)
365
- pattern = options.delete(:value) if options.kind_of?(Hash)
366
- result = notes(date, options)
367
- result.flatten!
368
- result.delete_if {|hash| hash.empty?}
369
- return false unless result.size > 0
370
- return true unless pattern
371
- result.each do |hash|
372
- return true if NotesContainer.verify(pattern, hash[:value])
373
- end
374
- return false
375
- end
376
-
377
- # 年の名前を暦注として返す
378
- # @method year
379
- # @return [When::BasicTypes::M17n]
380
-
381
- # 月の名前を暦注として返す
382
- # @method month
383
- # @return [When::BasicTypes::M17n]
384
-
385
- # 日の名前を暦注として返す
386
- # @method day
387
- # @return [When::BasicTypes::M17n]
388
-
389
- #
390
- # 標準的な暦注として、暦座標の値の名前を暦注として返すメソッドを登録
391
- #
392
- # @private
393
- ['year', 'month', 'day'].each do |c|
394
- module_eval %Q{
395
- def #{c}(date)
396
- date.name('#{c}')
397
- end
398
- }
399
- end
400
-
401
- private
402
-
403
- #
404
- # 年月日暦注計算の共通処理 - コールバック元
405
- #
406
- def _note_values(dates, focused_notes, all_notes, note_objects)
407
- return [] unless dates && all_notes
408
-
409
- # prepare focused notes
410
- case focused_notes
411
- when Integer
412
- bits = ~focused_notes << 1
413
- focused_notes = all_notes.dup.delete_if { (bits>>=1)[0] == 1 }
414
- when []
415
- focused_notes = all_notes
416
- when nil
417
- focused_notes = []
418
- end
419
- focused_notes = focused_notes.dup
420
- not_focused_notes = all_notes - focused_notes
421
- notes = {}
422
- not_focused_notes.each do |note|
423
- notes[note] = true
424
- end
425
- focused_notes.each do |note|
426
- notes[note] = nil
427
- end
428
-
429
- # update notes
430
- focused_notes_actual = focused_notes.dup
431
- notes = yield(dates, focused_notes_actual, notes)
432
- notes.keys.each do |note|
433
- notes.delete(note) unless focused_notes_actual.include?(note)
434
- end
435
-
436
- # return Array of Hash
437
- focused_notes.map {|note|
438
- case notes[note]
439
- when nil, false ; {}
440
- when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
441
- else
442
- if note_objects[note].respond_to?(:to_note_hash)
443
- note_objects[note].to_note_hash(notes[note], dates)
444
- else
445
- {:note=>note_objects[note].label, :value=>notes[note]}
446
- end
447
- end
448
- }
449
- end
450
-
451
- #
452
- # オブジェクトの正規化
453
- #
454
- def _normalize(args=[], options={})
455
- @_elements = (args.size == 0 && self.class.const_defined?(:Notes)) ?
456
- When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/Notes' :
457
- _to_iri(args, options[:prefix] || '_co:')
458
- if @_elements.kind_of?(Array)
459
- @_elements.each do |e|
460
- e.extend LabelAccess
461
- end
462
- end
463
- end
464
-
465
- #
466
- # 再帰的に配列の中を Resource化する
467
- #
468
- def _to_iri(args, prefix)
469
- args.map {|arg|
470
- case arg
471
- when String
472
- arg, method = $1, $2 if (arg =~ /^(.+)#([_A-Z0-9]+)$/i)
473
- obj = When.Resource(arg, prefix)
474
- obj = obj.copy(method) if method
475
- obj
476
- when Array
477
- _to_iri(arg, prefix)
478
- else
479
- arg
480
- end
481
- }
482
- end
483
-
484
- #
485
- # 暦日を当該暦注計算用クラスに変換
486
- #
487
- # 基底クラスである本クラスでは何もしないで、引数をそのまま返す
488
- #
489
- def _to_date_for_note(date)
490
- date
491
- end
492
-
493
- # 暦注要素
494
- #
495
- # @return [Array<Array<When::Parts::Resource>>]
496
- #
497
- def _elements
498
- @_elements = When.Resource(@_elements) if @_elements.kind_of?(String)
499
- @_elements
500
- end
501
-
502
- #
503
- # [[暦注名]](全暦注)
504
- #
505
- # @return [Array<Array<String, When::BasicTypes::M17n>>]
506
- #
507
- def _all_keys
508
- @_all_keys ||= _elements.map { |c|
509
- c.map {|n|
510
- n.label.to_s
511
- }
512
- }
513
- end
514
-
515
- #
516
- # [[暦注名]](主要暦注)
517
- #
518
- # @return [Array<Array<When::Parts::Resource>>]
519
- #
520
- def _prime_keys
521
- @prime ||= _all_keys
522
- end
523
-
524
- #
525
- # notes メソッドの引数を parse する
526
- #
527
- # @return [Array] dates, indices, notes
528
- #
529
- def _parse_note(date, options)
530
- options =
531
- case options
532
- when Hash ; When::EncodingConversion.to_internal_encoding(options)
533
- when String ; {:notes => When::EncodingConversion.to_internal_encoding(options)}
534
- when Integer ; {:indices => options}
535
- else ; {}
536
- end
537
- conditions = options.delete(:conditions) || {}
538
- _opt = options.dup
539
- persistence = _opt.delete(:persistence)
540
- notes = _notes(_opt.delete(:notes) || :prime)
541
- indices = _indices(_opt.delete(:indices), notes)
542
- _opt.keys.each do |key|
543
- conditions[key] = _opt[key] unless date.class::FormOptions.include?(key)
544
- end
545
- [_to_date_for_note(date), indices, notes, persistence, conditions, options]
546
- end
547
-
548
- #
549
- # 暦注を計算する暦座標の配列
550
- #
551
- # @return [Array<Integer>]
552
- #
553
- def _indices(indices, notes)
554
- case indices
555
- when nil ; (0...notes.size).to_a.reverse.map {|i| -i}
556
- when Range ; indices.to_a
557
- when Array ; indices
558
- else ; [indices.to_i]
559
- end
560
- end
561
-
562
- #
563
- # notes メソッドの 文字列引数の意味を解釈する
564
- #
565
- # @return [Hash] options for note String
566
- #
567
- def _find_note(note)
568
- note = When::EncodingConversion.to_internal_encoding(note)
569
- _elements.each_index do |index|
570
- return {:notes=>note, :indices => [-index]} if _elements[-1-index]._pool[note]
571
- end
572
- {:value => note}
573
- end
574
-
575
- #
576
- # 計算する暦注
577
- #
578
- # @return [Array<Array<String>, Integer>]
579
- #
580
- def _notes(notes)
581
- case notes
582
- when :all ; _all_keys
583
- when :prime ; _prime_keys
584
- when Integer ; [notes]
585
- when String ; [[notes]]
586
- else ; notes
587
- end
588
- end
589
-
590
- #
591
- # 暦注の計算
592
- #
593
- # @return [Object] 暦注の値
594
- #
595
- def _note_element(note, index, conditions, dates)
596
- void, event, *parameter = note.split(/^([^\d]+)/)
597
- method = event.downcase
598
- parameter << conditions unless conditions.empty?
599
- return send(method, dates, *parameter) if respond_to?(method)
600
- root = _elements[index-1][note]
601
- parent, leaf = root.iri.split('/Notes') if root.kind_of?(When::Parts::Resource)
602
- parent = When.Resource(parent) if leaf
603
- return parent.send(method, dates, *parameter) if parent.respond_to?(method)
604
- root.send(When::Coordinates::PRECISION_NAME[index].downcase, dates)
605
- end
606
-
607
- #
608
- # イベントを取得する Enumerator
609
- #
610
- class Enumerator < When::Parts::Enumerator
611
-
612
- #
613
- # 次のイベントを得る
614
- #
615
- # @return [When::TM::TemporalPosition]
616
- #
617
- def _succ
618
- @current = (@current==:first) ? @first : event_eval(@current + @delta)
619
- end
620
-
621
- # オブジェクトの生成
622
- #
623
- # @overload initialize(parent, range, options)
624
- # @param [When::CalendarNote] parent 暦注アルゴリズム
625
- # @param [Range, When::Parts::GeometricComplex] range
626
- # [ 始点 - range.first ]
627
- # [ 終点 - range.last ]
628
- # @param [Hash] options 以下の通り
629
- # @option options [String] :event イベント名
630
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
631
- #
632
- # @overload initialize(parent, first, direction, options)
633
- # @param [When::CalendarNote] parent 暦注アルゴリズム
634
- # @param [When::TM::TemporalPosition] first 始点
635
- # @param [Symbol] direction (options[:direction]で渡してもよい)
636
- # [:forward] 昇順(デフォルト)
637
- # [:reverse] 降順
638
- # @param [Hash] options 以下の通り
639
- # @option options [String] :event イベント名
640
- # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
641
- #
642
- def initialize(*args)
643
- if args[1].kind_of?(Range)
644
- parent, range, options = args
645
- first = range.first
646
- last = range.last
647
- direction = first < last ? :forward : :reverse
648
- else
649
- parent, first, direction, options = args
650
- end
651
- direction = options[:direction] if options[:direction]
652
- @parent = parent
653
- void, @event, @parameter = options.delete(:event).split(/^([^\d]+)/)
654
- @delta = @parent.send((@event+'_delta').to_sym, @parameter)
655
- instance_eval %Q{
656
- def event_eval(date)
657
- @parent.#{@event}(date, @parameter)
658
- end
659
- }
660
- date = event_eval(first)
661
- if direction == :reverse
662
- @delta = -@delta
663
- date = event_eval(first + @delta) if first.to_i < date.to_i
664
- else
665
- date = event_eval(first + @delta) if first.to_i > date.to_i
666
- end
667
- range ?
668
- super(@parent, range.exclude_end? ? date...last : date..last, options) :
669
- super(@parent, date, direction, options)
670
- end
671
- end
672
-
673
- #
674
- # 暦週
675
- #
676
- class Week < CalendarNote
677
-
678
- #
679
- # 暦週要素
680
- #
681
- class DayOfWeek < When::BasicTypes::M17n
682
-
683
- # 当該暦週要素の標準的な出現間隔
684
- #
685
- # @return [Integer]
686
- #
687
- attr_reader :delta
688
-
689
- # 所属する暦週オブジェクト
690
- #
691
- # @return [When::CalendarNote]
692
- #
693
- def week_note
694
- @week_note ||= When.CalendarNote(iri.split('/Notes').first)
695
- end
696
-
697
- # 当日または直前に当該暦週要素が現れる日付
698
- #
699
- # @return [When::TM::TemporalPosition]
700
- #
701
- def just_or_last(date)
702
- date = week_note._to_date_for_note(date)
703
- ([parent.child.length, @delta[When::DAY]].max*2).times do
704
- if equal?(week_note.week(date))
705
- date.events ||= []
706
- date.events << self
707
- return date
708
- end
709
- date -= When::P1D
710
- end
711
- raise ArgumentError, "#{self} not found"
712
- end
713
-
714
- private
715
-
716
- #
717
- # オブジェクトの正規化
718
- #
719
- def initialize(*args)
720
- super
721
- @delta = When::P1D * @delta.to_i if @delta && ! @delta.kind_of?(When::TM::Duration)
722
- end
723
- end
724
-
725
- #
726
- # 曜日の名前の一覧
727
- #
728
- # @param [When::TM::TemporalPosition] date
729
- #
730
- # @return [Array<When::CalendarNote::Week::DayOfWeek>]
731
- #
732
- def week_labels(date)
733
- @days_of_week.child[0...week(date)[:position].last]
734
- end
735
-
736
- # 七曜
737
- #
738
- # @param [When::TM::TemporalPosition] date
739
- #
740
- # @return [When::Coordinates::Residue] 七曜
741
- #
742
- def standard_week(date)
743
- When.Residue('Week')[date.to_i % 7]
744
- end
745
-
746
- #
747
- # オブジェクトの正規化
748
- #
749
- def _normalize(args=[], options={})
750
- super
751
- @days_of_week = When.CalendarNote("#{self.class.to_s.split('::').last}/Notes::day::Week")
752
- @days_of_week.child.length.times do |index|
753
- name = @days_of_week.child[index].label.to_s.downcase.gsub(/[- ]/, '_')
754
- self.class.module_eval %Q{
755
- def #{name}(date, parameter=nil)
756
- @days_of_week.child[#{index}].just_or_last(date)
757
- end
758
- } unless respond_to?(name)
759
- self.class.module_eval %Q{
760
- def #{name}_delta(parameter=nil)
761
- @days_of_week.child[#{index}].delta
762
- end
763
- } unless respond_to?(name + '_delta')
764
- end
765
- end
766
-
767
- #
768
- # イベントを取得する Enumerator
769
- #
770
- class Enumerator < When::CalendarNote::Enumerator
771
-
772
- #
773
- # 次のイベントを得る
774
- #
775
- # @return [When::TM::TemporalPosition]
776
- #
777
- def succ
778
- value = @current
779
- plus = @delta.sign > 0
780
- if @current==:first
781
- @first = event_eval(@first) unless plus
782
- @current = @first
783
- else
784
- if plus
785
- @current = event_eval(@current + @delta)
786
- else
787
- @last = event_eval(@current - When::P1D)
788
- @current = event_eval(@current + @delta)
789
- unless [@current.to_i, value.to_i].include?(@last.to_i)
790
- @current = @last
791
- return value
792
- end
793
- end
794
- @current = event_eval(@current + @delta * 2) if @current.to_i == value.to_i
795
- end
796
- return value
797
- end
798
- end
799
- end
800
- end
801
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2014 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
+ module When
9
+
10
+ #
11
+ # 暦注 - Calendar Note
12
+ #
13
+ class CalendarNote < TM::ReferenceSystem
14
+
15
+ #
16
+ # 暦注要素への名前アクセス機能提供
17
+ #
18
+ # @private
19
+ module LabelAccess
20
+ attr_reader :_pool
21
+
22
+ #
23
+ # 暦注要素への名前(label)によるアクセス
24
+ #
25
+ # @param [Numeric] key 配列インデックスと見なしてアクセス
26
+ # @param [String] key 名前(label)と見なしてアクセス
27
+ #
28
+ # @return [Object] 暦注要素
29
+ #
30
+ def [](key)
31
+ return super if key.kind_of?(Numeric)
32
+ @_pool ||= Hash[*(inject([]) {|pair, v| pair << v.label.to_s << v})]
33
+ @_pool[key]
34
+ end
35
+ end
36
+
37
+ #
38
+ # 暦注要素のひな形クラス
39
+ #
40
+ # @private
41
+ class NoteElement < When::BasicTypes::Object
42
+ #
43
+ # _m17n_form のための要素生成
44
+ #
45
+ # @param [Hash] options 下記のとおり
46
+ # @option options [Symbol] :method :to_m17n なら label を返す、その他は When::Parts::Resource#to_h 参照
47
+ #
48
+ def _to_hash_value(options={})
49
+ options[:method] == :to_m17n ? label : super
50
+ end
51
+ end
52
+
53
+ #
54
+ # 暦注検索結果を保存するコンテナ
55
+ #
56
+ module NotesContainer
57
+
58
+ class << self
59
+ #
60
+ # 永続データに暦注検索結果を登録する
61
+ #
62
+ # @private
63
+ def register(notes, persistence, sdn)
64
+ if persistence
65
+ persistence.store(sdn, notes)
66
+ persistence.extend(self)
67
+ end
68
+ notes.extend(self)
69
+ end
70
+
71
+ #
72
+ # 永続データから暦注検索結果を取り出す
73
+ #
74
+ # @private
75
+ def retrieve(persistence, sdn)
76
+ return false unless persistence
77
+ persistence.fetch(sdn, false)
78
+ end
79
+
80
+ #
81
+ # パターンの値の一致を判定する
82
+ #
83
+ # @private
84
+ def verify(pattern, value)
85
+ value = value.label if !value.kind_of?(When::BasicTypes::M17n) && value.respond_to?(:label)
86
+ pattern = When::EncodingConversion.to_internal_encoding(pattern)
87
+ pattern.kind_of?(Regexp) ? value =~ pattern : value == pattern
88
+ end
89
+ end
90
+
91
+ # 暦注検索結果コンテナの次元を下げる
92
+ #
93
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
94
+ #
95
+ # @return [NotesContainer]
96
+ #
97
+ def simplify(compact=true)
98
+ if kind_of?(Hash) && !key?(:note)
99
+ # Persistent NotesContainer
100
+ simplified = {}
101
+ each_pair do |key, value|
102
+ value = value.simplify(compact) if value.kind_of?(NotesContainer)
103
+ simplified[key] = value if value
104
+ end
105
+ else
106
+ # Non-Persistent NotesContainer
107
+ simplified = self
108
+ simplified = _compact(simplified) if compact
109
+ simplified = simplified[0] while simplified.kind_of?(Array) && simplified.size <= 1
110
+ end
111
+ _bless(simplified)
112
+ end
113
+
114
+ # 暦注検索結果コンテナのハッシュ要素
115
+ #
116
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
117
+ # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
118
+ #
119
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
120
+ #
121
+ # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
122
+ #
123
+ def value(compact=true, symbol=:value)
124
+ if kind_of?(Hash) && !key?(:note)
125
+ # Persistent NotesContainer
126
+ sliced = {}
127
+ each_pair do |key, val|
128
+ sliced[key] = val[symbol]
129
+ end
130
+ else
131
+ # Non-Persistent NotesContainer
132
+ return _bless(slice(symbol)) if symbol.kind_of?(Integer)
133
+ sliced = _dig(self) {|target| target.fetch(symbol, nil)}
134
+ end
135
+ sliced = _bless(sliced)
136
+ compact ? sliced.simplify : sliced
137
+ end
138
+
139
+ # 暦注検索結果コンテナのハッシュ要素
140
+ #
141
+ # @param [Symbol] symbol 取り出したいハッシュ要素(:note, :value, etc.)
142
+ #
143
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
144
+ #
145
+ # @note 引数 symbol が Integer の場合は添え字とみなして元の配列の要素を取り出す
146
+ #
147
+ def [](symbol)
148
+ value(false, symbol)
149
+ end
150
+
151
+ # 暦注検索結果コンテナの条件に合う要素を抽出する
152
+ #
153
+ # @param [Hash{Symbol=>String or Regexp}] condition 条件
154
+ # @param [Boolean] compact 余分な nil や空配列を除去するか否か
155
+ #
156
+ # @return [Array] ハッシュ要素からなる配列(元の構造が配列であった場合)
157
+ #
158
+ def subset(condition, compact=true)
159
+ if kind_of?(Hash) && !key?(:note)
160
+ # Persistent NotesContainer
161
+ result = {}
162
+ each_pair do |key, value|
163
+ value = value.subset(condition, compact) if value.kind_of?(NotesContainer)
164
+ result[key] = value if value
165
+ end
166
+ else
167
+ # Non-Persistent NotesContainer
168
+ result = _dig(self) {|target|
169
+ (condition.each_pair {|key, pattern| break nil unless NotesContainer.verify(pattern, target[key])}) ? target : nil
170
+ }
171
+ result = _compact(result) if compact
172
+ end
173
+ _bless(result)
174
+ end
175
+
176
+ private
177
+
178
+ #
179
+ # 対象オブジェクトを NotesContainer にする
180
+ #
181
+ def _bless(target)
182
+ target.extend(NotesContainer) if target && !equal?(target)
183
+ target
184
+ end
185
+
186
+ #
187
+ # 多次元配列要素に対して再帰的に要素を抽出して操作を施す
188
+ #
189
+ def _dig(list, &block)
190
+ return yield(list) unless list.kind_of?(Array)
191
+ list.map {|element| _dig(element, &block)}
192
+ end
193
+
194
+ #
195
+ # 多次元配列要素に対して再帰的に作用し nil や空配列を削除する
196
+ #
197
+ def _compact(list)
198
+ return list unless list.kind_of?(Array)
199
+ result = list.map {|element| _compact(element)}.compact
200
+ return result unless result.empty?
201
+ nil
202
+ end
203
+ end
204
+
205
+ #
206
+ # 暦法によってイベントの動作を変えるか否か
207
+ #
208
+ CalendarDepend = false
209
+
210
+ # デフォルトイベント名
211
+ #
212
+ # @return [String]
213
+ #
214
+ # @note イベント名の後ろに数字が使われている場合、数字部分以降はイベントメソッドの引数になります。
215
+ # SolarTermsクラスで 'term180' は、太陽黄経180度のイベントすなわち秋分を意味します。
216
+ #
217
+ attr_accessor :event
218
+ private :event=
219
+
220
+ # デフォルトイベントの指定
221
+ #
222
+ # @param [String] event 指定値を@eventとした新しいオブジェクトを作る
223
+ #
224
+ # @return [When::CalendarNote]
225
+ #
226
+ def copy(event)
227
+ c = self.clone
228
+ c.send(:event=, event)
229
+ c
230
+ end
231
+
232
+ # 典型的なイベントの発生間隔
233
+ #
234
+ # @param [String] event
235
+ #
236
+ # @return [When::TM::PeriodDuration]
237
+ #
238
+ def duration(event=@event)
239
+ void, event, parameter = event.split(/\A([^\d]+)/)
240
+ send((event+'_delta').downcase.to_sym, parameter)
241
+ end
242
+
243
+ # 指定の日時が指定イベントに該当するか?
244
+ #
245
+ # @param [When::TM::TemporalPosition] date チェックされる日時
246
+ # @param [String] event チェックするイベント名
247
+ #
248
+ # @return [Boolean]
249
+ # [ true - 該当する ]
250
+ # [ false - 該当しない ]
251
+ #
252
+ def include?(date, event=@event)
253
+ enum_for(date, :forward, event.downcase).next.include?(date)
254
+ end
255
+
256
+ # Enumeratorの生成
257
+ #
258
+ # @overload enum_for(range, options={})
259
+ # @param [Range, When::Parts::GeometricComplex] range
260
+ # [ 始点 - range.first ]
261
+ # [ 終点 - range.last ]
262
+ # @param [Hash] options 以下の通り
263
+ # @option options [String] :event イベント名(デフォルトは@event)
264
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
265
+ #
266
+ # @overload enum_for(first, direction=:forward, options={})
267
+ # @param [When::TM::TemporalPosition] first 始点
268
+ # @param [Symbol] direction (options[:direction]で渡してもよい)
269
+ # [:forward] 昇順(デフォルト)
270
+ # [:reverse] 降順
271
+ # @param [Hash] options 以下の通り
272
+ # @option options [String] :event イベント名(デフォルトは@event)
273
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
274
+ #
275
+ # @return [Enumerator]
276
+ #
277
+ def enum_for(*args)
278
+ params = args.dup
279
+ options = params[-1].kind_of?(Hash) ? params.pop.dup : {}
280
+ options[:event] ||= @event
281
+ self.class::Enumerator.new(*(params[0].kind_of?(Range) ?
282
+ [self, params[0], options] :
283
+ [self, params[0], params[1] || :forward, options]))
284
+ end
285
+ alias :to_enum :enum_for
286
+
287
+ # 暦注の計算
288
+ #
289
+ # @param [When::TM::TemporalPosition] date 暦注を計算する日時
290
+ # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
291
+ # @param [String] options { :notes => String } という Hash の指定と等価
292
+ # @param [Integer] options { :indices => Integer} という Hash の指定と等価
293
+ # @param [Hash] options 下記のとおり
294
+ # @option options [Integer] :indices Integerで指定した暦座標の暦注を計算
295
+ # [ When::DAY ( 0) - 日 ]
296
+ # [ When::MONTH(-1) - 月 ]
297
+ # [ When::YEAR (-2) - 年 ]
298
+ #
299
+ # @option options [Array<Integer>] :indices Integerで指定したすべて暦座標の暦注を計算
300
+ # @option options [nil] :indices すべての暦座標の暦注を計算(デフォルト)
301
+ # @option options [String] :notes 計算する暦注名(日の暦注)
302
+ # @option options [Integer] :notes 計算する暦注のビット配列(日の暦注)
303
+ # @option options [Array<Array<String>>] :notes 計算する暦注名の Array の Array
304
+ # @option options [Array<Integer>] :notes 計算する暦注のビット配列の Array
305
+ # @option options [:all] :notes すべての暦注を計算
306
+ # @option options [:prime, nil] :notes @prime に登録した暦注を計算、@prime未登録なら :all と同じ(デフォルト)
307
+ # @option options [Hash] :persistence {ユリウス通日=>暦注計算結果}を保持する永続オブジェクト
308
+ # @option options [Hash] :conditions 暦注計算の条件
309
+ # [ :location => 暦注計算の基準となる場所(String or When::Coordinates::Spatial) ]
310
+ # [ その他のキー => 個々の暦注クラスごとにその他のキーを使用できる ]
311
+ #
312
+ # @option options [Hash] その他のキー date を When::TM::TemporalPosition に変換するために使用する
313
+ # see {When::TM::TemporalPosition._instance}
314
+ #
315
+ # @note CalendarNoteオブジェクト生成時に _normalize メソッド内で @prime 変数を設定しておけば、
316
+ # 本メソッドの :prime オプションで参照される。(Balinese#_normalize等参照)
317
+ #
318
+ # @note 暦注のビットアドレスは、暦注サブクラスのNotes定数の中の定義順序による。
319
+ # When::CalendarNote クラスの場合 new の引数とした暦注要素リストの定義順序による。
320
+ # ビットアドレスの値が 1 の暦注が計算対象となる。
321
+ #
322
+ # @return [Array<Array<Hash>>] 暦注計算結果(When::CalendarNote::NotesContainerモジュールをextendしている)
323
+ # [ :note => 暦注要素 (When::Coordinates::Residue, String, When::BasicTypes::M17n) ]
324
+ # [ :value => 暦注の値 (When::Coordinates::Residue, String, When::BasicTypes::M17n, When::TM::TemporalPosition) ]
325
+ #
326
+ # @note
327
+ # 戻り値の :value が When::TM::TemporalPosition の場合、その日時オブジェクトの events[0] に暦注名の入った
328
+ # 暦注に該当する日付である。(例) Christian クラス で easter を計算した場合、当該年の復活祭の日付オブジェクトが返る。
329
+ # @note
330
+ # 暦注サブクラスの場合、暦注要素が増えたり、:note の暦注要素の型が変わったりすることがある。
331
+ #
332
+ def notes(date, options={})
333
+ dates, indices, notes, persistence, conditions, options = _parse_note(date, options)
334
+ retrieved = NotesContainer.retrieve(persistence, date.to_i)
335
+ return retrieved unless retrieved == false
336
+ NotesContainer.register(indices.map {|i|
337
+ next [] unless i <= date.precision
338
+ _note_values(dates, notes[i-1], _all_keys[i-1], _elements[i-1]) do |dates, focused_notes, notes_hash|
339
+ focused_notes.each do |note|
340
+ notes_hash[note] ||= _note_element(note, i, conditions, dates)
341
+ end
342
+ notes_hash
343
+ end
344
+ }, persistence, date.to_i)
345
+ end
346
+
347
+ #
348
+ # 暦注の一致 or 不一致
349
+ #
350
+ # @param [When::TM::TemporalPosition] date 暦注を確認する日時
351
+ # @param [When::TM::TemporalPosition 以外] date When::TM::TemporalPosition に変換して使用する
352
+ # @param [String] options { :notes => String } または { :value => String } という Hash の指定と等価
353
+ # (指定の notes が存在する場合は前者、しない場合は後者)
354
+ # @param [Integer] options { :indices => Integer } という Hash の指定と等価
355
+ # @param [Hash] options 下記のとおり
356
+ # @option options [暦注の値] :value 確認する暦注の値(または正規表現)
357
+ # @option options [それぞれ] その他 {When::CalendarNote#notes} を参照
358
+ #
359
+ # @return [Boolean]
360
+ # [ true - 暦注が一致 ]
361
+ # [ false - 暦注が不一致 ]
362
+ #
363
+ def note?(date, options={})
364
+ options = _find_note(options) if options.kind_of?(String) || options.kind_of?(Regexp)
365
+ pattern = options.delete(:value) if options.kind_of?(Hash)
366
+ result = notes(date, options)
367
+ result.flatten!
368
+ result.delete_if {|hash| hash.empty?}
369
+ return false unless result.size > 0
370
+ return true unless pattern
371
+ result.each do |hash|
372
+ return true if NotesContainer.verify(pattern, hash[:value])
373
+ end
374
+ return false
375
+ end
376
+
377
+ # 年の名前を暦注として返す
378
+ # @method year
379
+ # @return [When::BasicTypes::M17n]
380
+
381
+ # 月の名前を暦注として返す
382
+ # @method month
383
+ # @return [When::BasicTypes::M17n]
384
+
385
+ # 日の名前を暦注として返す
386
+ # @method day
387
+ # @return [When::BasicTypes::M17n]
388
+
389
+ #
390
+ # 標準的な暦注として、暦座標の値の名前を暦注として返すメソッドを登録
391
+ #
392
+ # @private
393
+ ['year', 'month', 'day'].each do |c|
394
+ module_eval %Q{
395
+ def #{c}(date)
396
+ date.name('#{c}')
397
+ end
398
+ }
399
+ end
400
+
401
+ private
402
+
403
+ #
404
+ # 年月日暦注計算の共通処理 - コールバック元
405
+ #
406
+ def _note_values(dates, focused_notes, all_notes, note_objects)
407
+ return [] unless dates && all_notes
408
+
409
+ # prepare focused notes
410
+ case focused_notes
411
+ when Integer
412
+ bits = ~focused_notes << 1
413
+ focused_notes = all_notes.dup.delete_if { (bits>>=1)[0] == 1 }
414
+ when []
415
+ focused_notes = all_notes
416
+ when nil
417
+ focused_notes = []
418
+ end
419
+ focused_notes = focused_notes.dup
420
+ not_focused_notes = all_notes - focused_notes
421
+ notes = {}
422
+ not_focused_notes.each do |note|
423
+ notes[note] = true
424
+ end
425
+ focused_notes.each do |note|
426
+ notes[note] = nil
427
+ end
428
+
429
+ # update notes
430
+ focused_notes_actual = focused_notes.dup
431
+ notes = yield(dates, focused_notes_actual, notes)
432
+ notes.keys.each do |note|
433
+ notes.delete(note) unless focused_notes_actual.include?(note)
434
+ end
435
+
436
+ # return Array of Hash
437
+ focused_notes.map {|note|
438
+ case notes[note]
439
+ when nil, false ; {}
440
+ when Hash ; {:note=>note_objects[note].label}.merge(notes[note])
441
+ else
442
+ if note_objects[note].respond_to?(:to_note_hash)
443
+ note_objects[note].to_note_hash(notes[note], dates)
444
+ else
445
+ {:note=>note_objects[note].label, :value=>notes[note]}
446
+ end
447
+ end
448
+ }
449
+ end
450
+
451
+ #
452
+ # オブジェクトの正規化
453
+ #
454
+ def _normalize(args=[], options={})
455
+ @_elements = (args.size == 0 && self.class.const_defined?(:Notes)) ?
456
+ When::Parts::Resource.base_uri + self.class.to_s.split(/::/)[1..-1].join('/') + '/Notes' :
457
+ _to_iri(args, options[:prefix] || '_co:')
458
+ if @_elements.kind_of?(Array)
459
+ @_elements.each do |e|
460
+ e.extend LabelAccess
461
+ end
462
+ end
463
+ end
464
+
465
+ #
466
+ # 再帰的に配列の中を Resource化する
467
+ #
468
+ def _to_iri(args, prefix)
469
+ args.map {|arg|
470
+ case arg
471
+ when String
472
+ arg, method = $1, $2 if (arg =~ /\A(.+)#([_A-Z0-9]+)\z/i)
473
+ obj = When.Resource(arg, prefix)
474
+ obj = obj.copy(method) if method
475
+ obj
476
+ when Array
477
+ _to_iri(arg, prefix)
478
+ else
479
+ arg
480
+ end
481
+ }
482
+ end
483
+
484
+ #
485
+ # 暦日を当該暦注計算用クラスに変換
486
+ #
487
+ # 基底クラスである本クラスでは何もしないで、引数をそのまま返す
488
+ #
489
+ def _to_date_for_note(date)
490
+ date
491
+ end
492
+
493
+ # 暦注要素
494
+ #
495
+ # @return [Array<Array<When::Parts::Resource>>]
496
+ #
497
+ def _elements
498
+ @_elements = When.Resource(@_elements) if @_elements.kind_of?(String)
499
+ @_elements
500
+ end
501
+
502
+ #
503
+ # [[暦注名]](全暦注)
504
+ #
505
+ # @return [Array<Array<String, When::BasicTypes::M17n>>]
506
+ #
507
+ def _all_keys
508
+ @_all_keys ||= _elements.map { |c|
509
+ c.map {|n|
510
+ n.label.to_s
511
+ }
512
+ }
513
+ end
514
+
515
+ #
516
+ # [[暦注名]](主要暦注)
517
+ #
518
+ # @return [Array<Array<When::Parts::Resource>>]
519
+ #
520
+ def _prime_keys
521
+ @prime ||= _all_keys
522
+ end
523
+
524
+ #
525
+ # notes メソッドの引数を parse する
526
+ #
527
+ # @return [Array] dates, indices, notes
528
+ #
529
+ def _parse_note(date, options)
530
+ options =
531
+ case options
532
+ when Hash ; When::EncodingConversion.to_internal_encoding(options)
533
+ when String ; {:notes => When::EncodingConversion.to_internal_encoding(options)}
534
+ when Integer ; {:indices => options}
535
+ else ; {}
536
+ end
537
+ conditions = options.delete(:conditions) || {}
538
+ _opt = options.dup
539
+ persistence = _opt.delete(:persistence)
540
+ notes = _notes(_opt.delete(:notes) || :prime)
541
+ indices = _indices(_opt.delete(:indices), notes)
542
+ _opt.keys.each do |key|
543
+ conditions[key] = _opt[key] unless date.class::FormOptions.include?(key)
544
+ end
545
+ [_to_date_for_note(date), indices, notes, persistence, conditions, options]
546
+ end
547
+
548
+ #
549
+ # 暦注を計算する暦座標の配列
550
+ #
551
+ # @return [Array<Integer>]
552
+ #
553
+ def _indices(indices, notes)
554
+ case indices
555
+ when nil ; (0...notes.size).to_a.reverse.map {|i| -i}
556
+ when Range ; indices.to_a
557
+ when Array ; indices
558
+ else ; [indices.to_i]
559
+ end
560
+ end
561
+
562
+ #
563
+ # notes メソッドの 文字列引数の意味を解釈する
564
+ #
565
+ # @return [Hash] options for note String
566
+ #
567
+ def _find_note(note)
568
+ note = When::EncodingConversion.to_internal_encoding(note)
569
+ _elements.each_index do |index|
570
+ return {:notes=>note, :indices => [-index]} if _elements[-1-index]._pool[note]
571
+ end
572
+ {:value => note}
573
+ end
574
+
575
+ #
576
+ # 計算する暦注
577
+ #
578
+ # @return [Array<Array<String>, Integer>]
579
+ #
580
+ def _notes(notes)
581
+ case notes
582
+ when :all ; _all_keys
583
+ when :prime ; _prime_keys
584
+ when Integer ; [notes]
585
+ when String ; [[notes]]
586
+ else ; notes
587
+ end
588
+ end
589
+
590
+ #
591
+ # 暦注の計算
592
+ #
593
+ # @return [Object] 暦注の値
594
+ #
595
+ def _note_element(note, index, conditions, dates)
596
+ void, event, *parameter = note.split(/\A([^\d]+)/)
597
+ method = event.downcase
598
+ parameter << conditions unless conditions.empty?
599
+ return send(method, dates, *parameter) if respond_to?(method)
600
+ root = _elements[index-1][note]
601
+ parent, leaf = root.iri.split('/Notes') if root.kind_of?(When::Parts::Resource)
602
+ parent = When.Resource(parent) if leaf
603
+ return parent.send(method, dates, *parameter) if parent.respond_to?(method)
604
+ root.send(When::Coordinates::PRECISION_NAME[index].downcase, dates)
605
+ end
606
+
607
+ #
608
+ # イベントを取得する Enumerator
609
+ #
610
+ class Enumerator < When::Parts::Enumerator
611
+
612
+ #
613
+ # 次のイベントを得る
614
+ #
615
+ # @return [When::TM::TemporalPosition]
616
+ #
617
+ def _succ
618
+ @current = (@current==:first) ? @first : event_eval(@current + @delta)
619
+ end
620
+
621
+ # オブジェクトの生成
622
+ #
623
+ # @overload initialize(parent, range, options)
624
+ # @param [When::CalendarNote] parent 暦注アルゴリズム
625
+ # @param [Range, When::Parts::GeometricComplex] range
626
+ # [ 始点 - range.first ]
627
+ # [ 終点 - range.last ]
628
+ # @param [Hash] options 以下の通り
629
+ # @option options [String] :event イベント名
630
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
631
+ #
632
+ # @overload initialize(parent, first, direction, options)
633
+ # @param [When::CalendarNote] parent 暦注アルゴリズム
634
+ # @param [When::TM::TemporalPosition] first 始点
635
+ # @param [Symbol] direction (options[:direction]で渡してもよい)
636
+ # [:forward] 昇順(デフォルト)
637
+ # [:reverse] 降順
638
+ # @param [Hash] options 以下の通り
639
+ # @option options [String] :event イベント名
640
+ # @option options [Integer] :count_limit 繰り返し回数(デフォルトは指定なし)
641
+ #
642
+ def initialize(*args)
643
+ if args[1].kind_of?(Range)
644
+ parent, range, options = args
645
+ first = range.first
646
+ last = range.last
647
+ direction = first < last ? :forward : :reverse
648
+ else
649
+ parent, first, direction, options = args
650
+ end
651
+ direction = options[:direction] if options[:direction]
652
+ @parent = parent
653
+ event = options.delete(:event)
654
+ case event
655
+ when String ; void, @event, @parameter = event.split(/\A([^\d]+)/)
656
+ else ; @event, @parameter = [parent.event, event]
657
+ end
658
+ @delta = @parent.send((@event+'_delta').to_sym, @parameter)
659
+ instance_eval %Q{
660
+ def event_eval(date)
661
+ @parent.#{@event}(date, @parameter)
662
+ end
663
+ }
664
+ date = event_eval(first)
665
+ if direction == :reverse
666
+ @delta = -@delta
667
+ date = event_eval(first + @delta) if first.to_i < date.to_i
668
+ else
669
+ date = event_eval(first + @delta) if first.to_i > date.to_i
670
+ end
671
+ range ?
672
+ super(@parent, range.exclude_end? ? date...last : date..last, options) :
673
+ super(@parent, date, direction, options)
674
+ end
675
+ end
676
+
677
+ #
678
+ # 暦週
679
+ #
680
+ class Week < CalendarNote
681
+
682
+ #
683
+ # 暦週要素
684
+ #
685
+ class DayOfWeek < When::BasicTypes::M17n
686
+
687
+ # 当該暦週要素の標準的な出現間隔
688
+ #
689
+ # @return [Integer]
690
+ #
691
+ attr_reader :delta
692
+
693
+ # 所属する暦週オブジェクト
694
+ #
695
+ # @return [When::CalendarNote]
696
+ #
697
+ def week_note
698
+ @week_note ||= When.CalendarNote(iri.split('/Notes').first)
699
+ end
700
+
701
+ # 当日または直前に当該暦週要素が現れる日付
702
+ #
703
+ # @return [When::TM::TemporalPosition]
704
+ #
705
+ def just_or_last(date)
706
+ date = week_note._to_date_for_note(date)
707
+ ([parent.child.length, @delta[When::DAY]].max*2).times do
708
+ if equal?(week_note.week(date)[:value])
709
+ date.events ||= []
710
+ date.events << self
711
+ return date
712
+ end
713
+ date -= When::P1D
714
+ end
715
+ raise ArgumentError, "#{self} not found"
716
+ end
717
+
718
+ private
719
+
720
+ #
721
+ # オブジェクトの正規化
722
+ #
723
+ def initialize(*args)
724
+ super
725
+ @delta = When::P1D * @delta.to_i if @delta && ! @delta.kind_of?(When::TM::Duration)
726
+ end
727
+ end
728
+
729
+ #
730
+ # 曜日の名前の一覧
731
+ #
732
+ # @param [When::TM::TemporalPosition] date
733
+ #
734
+ # @return [Array<When::CalendarNote::Week::DayOfWeek>]
735
+ #
736
+ def week_labels(date)
737
+ @days_of_week.child[0...week(date)[:position].last]
738
+ end
739
+
740
+ # 七曜
741
+ #
742
+ # @param [When::TM::TemporalPosition] date
743
+ #
744
+ # @return [When::Coordinates::Residue] 七曜
745
+ #
746
+ def common_week(date)
747
+ When.Residue('Week')[date.to_i % 7]
748
+ end
749
+
750
+ #
751
+ # オブジェクトの正規化
752
+ #
753
+ def _normalize(args=[], options={})
754
+ super
755
+ @days_of_week ||= When.CalendarNote("#{self.class.to_s.split('::').last}/Notes::day::Week")
756
+ @days_of_week.child.length.times do |index|
757
+ name = @days_of_week.child[index].translate('en').downcase.gsub(/[- ]/, '_')
758
+ self.class.module_eval %Q{
759
+ def #{name}(date, parameter=nil)
760
+ @days_of_week.child[#{index}].just_or_last(date)
761
+ end
762
+ } unless respond_to?(name)
763
+ self.class.module_eval %Q{
764
+ def #{name}_delta(parameter=nil)
765
+ @days_of_week.child[#{index}].delta
766
+ end
767
+ } unless respond_to?(name + '_delta')
768
+ end
769
+ end
770
+
771
+ #
772
+ # イベントを取得する Enumerator
773
+ #
774
+ class Enumerator < When::CalendarNote::Enumerator
775
+
776
+ #
777
+ # 次のイベントを得る
778
+ #
779
+ # @return [When::TM::TemporalPosition]
780
+ #
781
+ def succ
782
+ value = @current
783
+ plus = @delta.sign > 0
784
+ if @current==:first
785
+ @first = event_eval(@first) unless plus
786
+ @current = @first
787
+ else
788
+ if plus
789
+ @current = event_eval(@current + @delta)
790
+ else
791
+ @last = event_eval(@current - When::P1D)
792
+ @current = event_eval(@current + @delta)
793
+ unless [@current.to_i, value.to_i].include?(@last.to_i)
794
+ @current = @last
795
+ return value
796
+ end
797
+ end
798
+ @current = event_eval(@current + @delta * 2) if @current.to_i == value.to_i
799
+ end
800
+ return value
801
+ end
802
+ end
803
+ end
804
+ end
805
+ end