uniprop 0.1.0

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.
@@ -0,0 +1,673 @@
1
+ module UniProp
2
+ class MetaData
3
+ attr_reader :prop_data, :raw_metadata, :metadata_path
4
+
5
+ # @param [PropData] prop_data
6
+ # @param [Pathname] metadata_path
7
+ def initialize(prop_data, metadata_path)
8
+ @prop_data = prop_data
9
+ @metadata_path = metadata_path
10
+
11
+ if @metadata_path.exist?
12
+ @raw_metadata = JSON.parse(File.read(@metadata_path))
13
+ else
14
+ raise FileNotFoundError, "#{@metadata_path} is not found."
15
+ end
16
+ end
17
+
18
+ # メタデータのversion_names項目の値を取得。メタデータにこの項目が記述されていない場合、Unicode.orgからバージョン一覧を取得
19
+ # @param [Boolean] update_metadata trueの場合、取得した情報がメタデータと異なる際に、メタデータを更新
20
+ # @param [Boolean] confirm trueの場合、Unicode.orgからバージョン名一覧を取得する。メタデータにバージョン一覧が記述されている場合にも、Unicord.orgから取得した情報を優先して返す。メタデータにversion_names項目が無い場合、confirm==falseでもUnicode.orgから情報を取得する
21
+ # @return [Array<String>]
22
+ def version_names(update_metadata: false, confirm: false)
23
+ if confirm || raw_metadata["version_names"].nil? || raw_metadata["version_names"].empty?
24
+ actual_version_names = UniPropUtils::DownloaderWrapper.get_version_names
25
+
26
+ if update_metadata
27
+ raw_metadata["version_names"] = actual_version_names
28
+ prop_data.update_metadata(raw_metadata)
29
+ end
30
+
31
+ return actual_version_names
32
+ else
33
+ return raw_metadata["version_names"]
34
+ end
35
+ end
36
+
37
+ # @return [Array<Hash<String,Object>>]
38
+ def raw_version_metadatas
39
+ @raw_version_metadatas ||= raw_metadata["version_metadatas"].sort { Version.name_to_weight(_1["version_name"]) }
40
+ end
41
+
42
+ # メタデータから、version_nameと同じweightのバージョンに関する記述を取得
43
+ # @param [String] version_name
44
+ # @return [Hash<String,Object>]
45
+ def find_raw_version_metadata(version_name)
46
+ weight = Version.name_to_weight(version_name)
47
+
48
+ result = raw_version_metadatas.find { Version.name_to_weight(_1["version_name"])==weight }
49
+
50
+ if result
51
+ return result
52
+ else
53
+ raise MetaDataNotFoundError, "metadata for #{version_name} is not found."
54
+ end
55
+ end
56
+
57
+ def has_raw_version_metadata?(version_name)
58
+ !!find_raw_version_metadata(version_name)
59
+ rescue
60
+ false
61
+ end
62
+
63
+ # @return [MetaDataValidator]
64
+ def metadata_validator
65
+ @metadata_validator ||= MetaDataValidator.new(self)
66
+ @metadata_validator
67
+ end
68
+ end
69
+
70
+ class VersionMetaData
71
+ attr_reader :version, :raw_metadata
72
+
73
+ def initialize(version, raw_metadata)
74
+ @version = version
75
+ @raw_metadata = raw_metadata
76
+ end
77
+
78
+ # メタデータを元に、各Propertyと、そのPositionを取得
79
+ # @return [Hash<Property,Array<Position>>]
80
+ def property_to_actual_positions
81
+ return @property_to_actual_positions if @property_to_actual_positions
82
+
83
+ @property_to_actual_positions = {}
84
+
85
+ actual_propfiles.each do |propfile|
86
+ if has_propfile_metadata?(propfile)
87
+ propfile_metadata = find_propfile_metadata(propfile)
88
+ else
89
+ next
90
+ end
91
+
92
+ propfile_metadata.blocks.each_with_index do |block, block_no|
93
+ block.content.each_with_index do |col, col_no|
94
+ props = [] # この回のループのblock_no, col_noの箇所に存在するプロパティ
95
+
96
+ if col.class==Array
97
+ props.concat(col)
98
+ else
99
+ props << col
100
+ end
101
+
102
+ props.compact.uniq.each do |prop|
103
+ @property_to_actual_positions[prop] ||= []
104
+ add_f = false # col_noをPositionオブジェクトに追加した時点でtrueに変更
105
+
106
+ # propfile, block_noが同じPositionが存在している場合、列を追加する (新しいPositionオブジェクトは生成しない)
107
+ @property_to_actual_positions[prop].each do |position|
108
+ if position.propfile==propfile && position.block==block_no
109
+ position.columns << col_no
110
+ add_f = true
111
+ break
112
+ end
113
+ end
114
+ if !add_f
115
+ @property_to_actual_positions[prop] << Position.new(propfile, block.range, block_no, [col_no])
116
+ end
117
+
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ @property_to_actual_positions
124
+ end
125
+
126
+ # メタデータを元に、各PropFileと、それに含まれるPropertyを取得
127
+ # @return [Hash<PropFile,Array<Property>>]
128
+ def propfile_to_actual_properties
129
+ return @propfile_to_actual_properties if @propfile_to_actual_properties
130
+
131
+ @propfile_to_actual_properties = Hash.new { |hash,key| hash[key]=[] }
132
+
133
+ actual_propfiles.each do |propfile|
134
+ if has_propfile_metadata?(propfile)
135
+ propfile_metadata = find_propfile_metadata(propfile)
136
+ else
137
+ next
138
+ end
139
+
140
+ propfile_metadata.blocks.each do |block|
141
+ block.content.flatten.each do |col_prop| # 一部ファイルではblockの中に配列が含まれるためflattenにする
142
+ @propfile_to_actual_properties[propfile] << col_prop if version.has_property?(col_prop)
143
+ end
144
+ end
145
+ end
146
+
147
+ @propfile_to_actual_properties
148
+ end
149
+
150
+ # メタデータに含まれるプロパティ名を取得
151
+ # @return [Array<String>]
152
+ def property_names
153
+ return @property_names if @property_names
154
+
155
+ @property_names = []
156
+ propfile_metadatas.each do |propfile_metadata|
157
+ propfile_metadata.raw_blocks.each { @property_names.concat(_1.content.flatten) }
158
+ end
159
+
160
+ @property_names.uniq!
161
+ @property_names
162
+ end
163
+
164
+ # メタデータ内に含まれるプロパティを取得
165
+ # @return [Array<Property>]
166
+ def actual_properties
167
+ return @actual_properties if @actual_properties
168
+
169
+ @actual_properties = propfile_to_actual_properties.values.flatten
170
+ @actual_properties.concat(unihan_properties)
171
+
172
+ @actual_properties
173
+ end
174
+
175
+ # @return [VersionMetaDataValidator]
176
+ def version_metadata_validator
177
+ @version_metadata_validator ||= VersionMetaDataValidator.new(self)
178
+ @version_metadata_validator
179
+ end
180
+
181
+ # メタデータに記述されているfile_formats内の記述を、加工せずそのまま取得
182
+ # @return [Hash<String,Object>] 値の形式はキーによって異なる。
183
+ def find_raw_file_format(propfile)
184
+ raw_metadata["file_formats"].each do |file_format|
185
+ if version.has_file?(file_format["file_name"])
186
+ f = version.find_file(file_format["file_name"])
187
+ return file_format if f==propfile
188
+ end
189
+ end
190
+ raise(MetaDataNotFoundError, "Metadata for #{propfile.basename_prefix} is not found.")
191
+ end
192
+
193
+ # 結び付いているPropFileのフォーマットに関する情報がメタデータにあるかを判定
194
+ # @param [PropFile] propfile
195
+ def has_file_format?(propfile)
196
+ return !!find_raw_file_format(propfile)
197
+ rescue
198
+ return false
199
+ end
200
+
201
+ # バージョンに属する各Propertyと、そのPropertyが実際に記述されているPropFileの関係のHashを取得
202
+ # @return [Hash<Property,Set<PropFile>]
203
+ def property_to_actual_propfiles
204
+ return @property_to_actual_propfiles if @property_to_actual_propfiles
205
+
206
+ @property_to_actual_propfiles = Hash.new { |hash,key| hash[key]=Set.new }
207
+
208
+ propfile_to_actual_properties.each do |propfile, actual_props|
209
+ actual_props.each { @property_to_actual_propfiles[_1] << propfile }
210
+ end
211
+
212
+ @property_to_actual_propfiles
213
+ end
214
+
215
+ # fileのMissingDefオブジェクトを取得
216
+ # @param [PropFile] propfile
217
+ # @return [Array<MissingDef>]
218
+ # @note codepointによって複数のmissingが定義されている場合には返り値は複数のMissingDefオブジェクトになる
219
+ def propfile_missing_defs(propfile)
220
+ @propfile_to_missing_defs ||= {}
221
+ return @propfile_to_missing_defs[propfile] if @propfile_to_missing_defs[propfile]
222
+
223
+ missing_defs = []
224
+
225
+ propfile.shaped_missing_value_lines.each do |shaped_missing_value_line|
226
+ m = shaped_missing_value_line[0].match(/([0-9A-F]{4,6})\.\.([0-9A-F]{4,6})|([0-9A-F]{4,6})/)
227
+
228
+ if m
229
+ # cp..cpでマッチ
230
+ if m && m[2]
231
+ begin_cp = m[1].hex
232
+ end_cp = m[2].hex
233
+ # cpでマッチ
234
+ elsif m && m[1]
235
+ begin_cp = m[1].hex
236
+ end_cp = m[1].hex
237
+ end
238
+ codepoint_range = Range.new(begin_cp, end_cp)
239
+
240
+ # missingについて記述される行は、以下のどちらかの形式を取る
241
+ # (1) @missing: codepoint; property; missing
242
+ # (2) @missing: codepoint; missing
243
+
244
+ # (1) の場合
245
+ if shaped_missing_value_line.size==3
246
+ property_name = shaped_missing_value_line[1]
247
+ missing_value = shaped_missing_value_line[2]
248
+
249
+ if version.has_property?(property_name)
250
+ prop = version.find_property(property_name)
251
+ missing_defs << MissingDef.new(codepoint_range, prop, missing_value)
252
+ end
253
+
254
+ # (2) の場合
255
+ # missingが記述されているファイルからPropertyを特定し使用
256
+ elsif shaped_missing_value_line.size==2
257
+ # ファイルに含まれるプロパティが1種類でない場合、プロパティを特定できない
258
+ next if propfile_to_actual_properties[propfile].size!=1
259
+
260
+ missing_value = shaped_missing_value_line[1]
261
+ prop = propfile_to_actual_properties[propfile].to_a[0]
262
+ missing_defs << MissingDef.new(codepoint_range, prop, missing_value)
263
+ end
264
+ end
265
+ end
266
+
267
+ @propfile_to_missing_defs[propfile] = missing_defs
268
+ @propfile_to_missing_defs[propfile]
269
+ end
270
+
271
+ # propertyのMissingDefオブジェクトを取得
272
+ # @param [Property] property
273
+ # @return [Array<MissingDef>]
274
+ # @note codepointによって複数のmissingが定義されている場合には返り値は複数のMissingDefオブジェクトになる
275
+ def property_missing_defs(property)
276
+ @property_to_missing_defs ||= {}
277
+ return @property_to_missing_defs[property] if @property_to_missing_defs[property]
278
+
279
+ # binaryプロパティのデフォルト値はFalse
280
+ if property.property_value_type==:binary
281
+ @property_to_missing_defs[property]=[MissingDef.new(CODEPOINT_RANGE,property,"False")]
282
+ return @property_to_missing_defs[property]
283
+ end
284
+
285
+ # 最小限のファイルで動くよう、PropertyValueAliases.txtとプロパティが記述されているファイルのみからmissingの記述を探す
286
+ # プロパティ記述ファイルとPropertyValueAliasesのうち、定義されているmissingの種類が最も多い値を返す
287
+ search_files = property_to_actual_propfiles[property]
288
+ search_files << version.property_value_aliases_file
289
+
290
+ # 15.0.0のEastAsianWidth.txtとDerivedEastAsianWidth.txtのように、@missingから始まる定義の内容が異なる場合がある
291
+ # ファイル内で定義される@missingの個数が最も多いファイルの定義を使用する
292
+ search_files.each do |file|
293
+ missing_defs = propfile_missing_defs(file).filter { _1.property==property }
294
+
295
+ if !@property_to_missing_defs[property] || missing_defs.size > @property_to_missing_defs[property].size
296
+ @property_to_missing_defs[property] = missing_defs
297
+ end
298
+ end
299
+
300
+ @property_to_missing_defs[property]
301
+ end
302
+
303
+ # メタデータに記述のあるPropFileを取得
304
+ # @return [Array<PropFile>]
305
+ def actual_propfiles
306
+ return @actual_propfiles if @actual_propfiles
307
+
308
+ @actual_propfiles = []
309
+ propfile_names.each { @actual_propfiles<<version.find_file(_1) if version.has_file?(_1) }
310
+
311
+ @actual_propfiles
312
+ end
313
+
314
+ # メタデータに記述されているPropFileの名前を取得
315
+ # @return [Set<String>]
316
+ def propfile_names
317
+ return @propfile_names if @propfile_names
318
+
319
+ @propfile_names = raw_metadata["file_formats"].map { _1["file_name"] }.to_a
320
+ @propfile_names += unihan_file_names
321
+ @propfile_names
322
+ end
323
+
324
+ # @return [Array<PropFileMetaData>]
325
+ def propfile_metadatas
326
+ return @propfile_metadatas if @propfile_metadatas
327
+
328
+ @propfile_metadatas = []
329
+ actual_propfiles.each { @propfile_metadatas<<PropFileMetaData.new(_1, find_raw_file_format(_1)) if has_file_format?(_1) }
330
+
331
+ @propfile_metadatas
332
+ end
333
+
334
+ # versionに含まれるUnihanのファイル名を取得
335
+ # @return [Array<String>]
336
+ def unihan_file_names
337
+ @unihan_file_names ||= raw_metadata["unihan_files"].to_a
338
+ end
339
+
340
+ # versionに含まれるUnihanのプロパティ名を取得
341
+ # @return [Array<Property>]
342
+ def unihan_property_names
343
+ @unihan_property_names ||= raw_metadata["unihan_properties"].to_a
344
+ end
345
+
346
+ # @return [Array<Property>]
347
+ def unihan_properties
348
+ return @unihan_properties if @unihan_properties
349
+
350
+ @unihan_properties = []
351
+ unihan_property_names.each do |prop_name|
352
+ if version.has_property?(prop_name)
353
+ prop = version.find_property(prop_name)
354
+ else
355
+ prop = UniProp::Property.new(version, prop_name)
356
+ end
357
+ @unihan_properties << prop
358
+ end
359
+
360
+ @unihan_properties
361
+ end
362
+
363
+ # @return [VersionMetaData]
364
+ def find_propfile_metadata(propfile)
365
+ propfile_metadata = propfile_metadatas.find { _1.propfile==propfile }
366
+
367
+ if propfile_metadata
368
+ return propfile_metadata
369
+ else
370
+ raise MetaDataNotFoundError, "metadata for #{propfile} is not found."
371
+ end
372
+ end
373
+
374
+ def has_propfile_metadata?(propfile)
375
+ !!find_propfile_metadata(propfile)
376
+ rescue
377
+ false
378
+ end
379
+ end
380
+
381
+ class PropFileMetaData
382
+ attr_reader :propfile, :raw_file_format
383
+
384
+ def initialize(propfile, raw_file_format)
385
+ @propfile = propfile
386
+ @raw_file_format = raw_file_format
387
+ end
388
+
389
+ # raw_file_formatからRawBlockオブジェクトを作成
390
+ # @return [Array<RawBlock>]
391
+ def raw_blocks
392
+ return @raw_blocks if @raw_blocks
393
+ @raw_blocks = []
394
+ raw_file_format["blocks"].each { @raw_blocks << RawBlock.new(_1["content"], _1["range"])}
395
+ @raw_blocks
396
+ end
397
+
398
+ # @return [Array<Block>]
399
+ def blocks
400
+ return @blocks if @blocks
401
+ @blocks = []
402
+
403
+ raw_blocks.each do |raw_block|
404
+ content = version.convert_property(raw_block.content)
405
+ range = UniPropUtils::RangeProcessor.str_to_range(raw_block.range)
406
+ @blocks << Block.new(content, range)
407
+ end
408
+
409
+ @blocks
410
+ end
411
+
412
+ # ファイル内に含まれるプロパティを取得
413
+ # @return [Array<Property>]
414
+ def actual_properties
415
+ return @actual_properties if @actual_properties
416
+ @actual_properties = []
417
+
418
+ blocks.each { @actual_properties.concat(_1.content.flatten.compact) }
419
+
420
+ @actual_properties.uniq!
421
+ @actual_properties
422
+ end
423
+
424
+ # プロパティが記述されている範囲を取得
425
+ # @return [Array<Range<Integer>>]
426
+ def property_written_ranges
427
+ @property_written_ranges ||= blocks.map { _1.range }
428
+ end
429
+
430
+ # :nocov:
431
+ # ファイル内にプロパティが1つ以上含まれるかを判定
432
+ def has_any_properties?
433
+ !actual_properties.empty?
434
+ end
435
+ # :nocov:
436
+
437
+ # 各ブロックの、codepointが記述されている列番号を取得
438
+ # @return [Array<Integer?>] selfのブロック数と同じサイズのArray。n番目の要素にはn番目のブロックにおいてcodepointが記述されている列数を表すIntegerが入る。n番目のブロックにcodepointが記述されていない場合、n番目の要素はnil
439
+ def codepoint_column_nos
440
+ return @codepoint_column_nos if @codepoint_column_nos
441
+ @codepoint_column_nos = Array.new(raw_blocks.size)
442
+
443
+ raw_blocks.each_with_index do |raw_block, block_no|
444
+ raw_block.content.each_with_index do |col, col_no|
445
+ if (
446
+ col.class==Array && col.map{Alias.canonical(_1)}.include?("codepoint") ||
447
+ col.class==String && Alias.canonical(col)=="codepoint"
448
+ )
449
+ @codepoint_column_nos[block_no] = col_no
450
+ end
451
+ end
452
+ end
453
+
454
+ @codepoint_column_nos
455
+ end
456
+
457
+ # 各ブロックの、propが記述されている列番号を取得
458
+ # @param [Property] prop
459
+ # @return [Array<Array<Integer>>] selfのブロック数と同じサイズのArray。n番目の要素にはn番目のブロックにおいてpropが記述されている列数を表すIntegerが入る。
460
+ def property_column_nos(prop)
461
+ property_column_nos = Array.new(blocks.size) { [] }
462
+
463
+ blocks.each_with_index do |block, block_no|
464
+ block.content.each_with_index do |col, col_no|
465
+ if (
466
+ col.class==Array && col.include?(prop) ||
467
+ col.class==Property && col==prop
468
+ )
469
+ property_column_nos[block_no] << col_no
470
+ end
471
+ end
472
+ end
473
+
474
+ property_column_nos
475
+ end
476
+
477
+ # propfile内に複数のプロパティを含む列が存在するかを判定
478
+ def has_multiple_properties_column?
479
+ blocks.each do |block|
480
+ return true if block.content.any? { _1.class==Array }
481
+ end
482
+ return false
483
+ end
484
+
485
+ # block_no番目のブロックに関するBlockValueGroupオブジェクトを取得
486
+ # @param [Integer] block_no
487
+ def block_value_group(block_no)
488
+ return nil if block_no<0 || blocks.size<=block_no
489
+ @block_value_groups ||= {}
490
+ @block_value_groups[block_no] ||= BlockValueGroup.new(propfile, block_no)
491
+ @block_value_groups[block_no]
492
+ end
493
+
494
+ # :nocov:
495
+ # @return [VersionMetaData]
496
+ def version_metadata
497
+ propfile.version.version_metadata
498
+ end
499
+ # :nocov:
500
+
501
+ # :nocov:
502
+ # @return [Version]
503
+ def version
504
+ propfile.version
505
+ end
506
+ # :nocov:
507
+ end
508
+
509
+ class PropertyMetaData
510
+ attr_reader :prop_data, :raw_metadata, :metadata_path
511
+
512
+ # @param [PropData] prop_data
513
+ # @param [Pathname] metadata_path
514
+ def initialize(prop_data, metadata_path)
515
+ @prop_data = prop_data
516
+ @metadata_path = metadata_path
517
+
518
+ if @metadata_path.exist?
519
+ @raw_metadata = JSON.parse(File.read(@metadata_path))
520
+ else
521
+ raise FileNotFoundError, "#{@metadata_path} is not found."
522
+ end
523
+ end
524
+
525
+ # @return [Array<Hash<String,Object>>]
526
+ def raw_version_metadatas
527
+ @raw_version_metadatas ||= raw_metadata.sort { Version.name_to_weight(_1["version_name"]) }
528
+ end
529
+
530
+ # メタデータから、version_nameと同じweightのバージョンに関する記述を取得
531
+ # @param [String] version_name
532
+ # @return [Hash<String,Object>]
533
+ def find_raw_version_metadata(version_name)
534
+ weight = Version.name_to_weight(version_name)
535
+ result = raw_version_metadatas.find { Version.name_to_weight(_1["version_name"])==weight }
536
+
537
+ if result
538
+ return result
539
+ else
540
+ raise MetaDataNotFoundError, "metadata for #{version_name} is not found."
541
+ end
542
+ end
543
+
544
+ def has_raw_version_metadata?(version_name)
545
+ !!find_raw_version_metadata(version_name)
546
+ rescue
547
+ false
548
+ end
549
+
550
+ # @param [EfficientVersion] version
551
+ # @return [Hash<String,Object>]
552
+ def find_version_property_metadata(version)
553
+ @version_property_metadatas ||= []
554
+
555
+ vpm = @version_property_metadatas.find { _1.version==version }
556
+ return vpm if vpm
557
+
558
+ # プロパティ中心のメタデータが存在しない場合、生成を試みる
559
+ if !has_raw_version_metadata?(version.version_name)
560
+ prop_data.generate_property_metadata(metadata_path, version)
561
+ end
562
+
563
+ vpm = VersionPropertyMetaData.new(version, find_raw_version_metadata(version.version_name))
564
+ @version_property_metadatas << vpm
565
+ vpm
566
+ end
567
+ end
568
+
569
+ class VersionPropertyMetaData
570
+ attr_reader :version, :raw_metadata
571
+
572
+ # @param [EfficientVersion] version
573
+ # @param [Array<Hash<String,Object>>] プロパティ中心のメタデータのproperties項
574
+ def initialize(version, raw_metadata)
575
+ @version = version
576
+ @raw_metadata = raw_metadata
577
+ end
578
+
579
+ # プロパティ中心のメタデータのproperties項をUniPropのデータ構造に変換して取得
580
+ # @return [Array<Hash<String,Object>>]
581
+ def property_datas
582
+ return @property_datas if @property_datas
583
+
584
+ @property_datas = []
585
+ raw_metadata["properties"].each do |raw_prop_data|
586
+
587
+ positions = []
588
+ raw_prop_data["positions"].each do |raw_position|
589
+ positions << Position.new(
590
+ version.find_file(raw_position["file_name"]),
591
+ UniPropUtils::RangeProcessor.str_to_range(raw_position["range"]),
592
+ raw_position["block"],
593
+ raw_position["columns"],
594
+ )
595
+ end
596
+
597
+ @property_datas << PropertyData.new(
598
+ version.find_property(raw_prop_data["property_name"]),
599
+ positions,
600
+ raw_prop_data["unihan"],
601
+ raw_prop_data["derived"],
602
+ )
603
+ end
604
+
605
+ @property_datas
606
+ end
607
+
608
+ # @param [Property] property
609
+ # @return [PropertyData]
610
+ def find_property_data(property)
611
+ prop = property_datas.find { _1.property==property }
612
+ if prop
613
+ return prop
614
+ else
615
+ raise MetaDataNotFoundError, "MetaData for #{property.longest_alias} is not found."
616
+ end
617
+ end
618
+ end
619
+
620
+ class PropertyData
621
+ attr_reader :property, :positions, :version
622
+
623
+ # プロパティメタデータに含まれる、プロパティ1つあたりのデータ
624
+ # @param [Property] property
625
+ # @param [Array<Position>] positions
626
+ # @param [Boolean] unihan
627
+ # @param [Boolean] derived
628
+ def initialize(property, positions, unihan, derived)
629
+ @property = property
630
+ @version = @property.version
631
+ @positions = positions
632
+ @unihan = unihan
633
+ @derived = derived
634
+ end
635
+
636
+ def is_unihan?
637
+ @unihan
638
+ end
639
+
640
+ def is_derived?
641
+ @derived
642
+ end
643
+
644
+ def type
645
+ property.property_value_type
646
+ end
647
+
648
+ # プロパティの解析に最も適したPositionオブジェクトをを取得
649
+ # @return [Position]
650
+ def position
651
+ if is_derived?
652
+ # Derivedファイルが存在する場合、使用
653
+ return positions.find { _1.propfile.basename_prefix=~/Derived/ }
654
+ else
655
+ # Derivedファイルが存在しない場合、
656
+ # 記述される列が最も小さいファイルが解析しやすいため使用
657
+ return positions.sort { _1.columns.size }.first
658
+ end
659
+ end
660
+
661
+ # propertyの情報を取得するのに最適なPropertyValueGroupオブジェクトを生成
662
+ # @return [PropertyValueGroup/UnihanValueGroup]
663
+ def property_value_group
664
+ return @property_value_group if @property_value_group
665
+
666
+ if is_unihan?
667
+ @property_value_group = version.unihanprop.unihan_value_group(property)
668
+ else
669
+ @property_value_group = PropertyValueGroup.new(position.propfile, property, position.block)
670
+ end
671
+ end
672
+ end
673
+ end