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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +31 -0
- data/README.md +29 -0
- data/Rakefile +12 -0
- data/lib/resources/metadata.json +19899 -0
- data/lib/resources/settings.rb +120 -0
- data/lib/uniprop/consts.rb +31 -0
- data/lib/uniprop/downloader.rb +262 -0
- data/lib/uniprop/dsl.rb +53 -0
- data/lib/uniprop/efficient_elements.rb +40 -0
- data/lib/uniprop/errors.rb +31 -0
- data/lib/uniprop/inspects.rb +122 -0
- data/lib/uniprop/metadata_generator.rb +403 -0
- data/lib/uniprop/metadata_processor.rb +673 -0
- data/lib/uniprop/metadata_validator.rb +282 -0
- data/lib/uniprop/propdata.rb +293 -0
- data/lib/uniprop/unicode_elements.rb +998 -0
- data/lib/uniprop/unicode_manager.rb +277 -0
- data/lib/uniprop/unihanprop.rb +91 -0
- data/lib/uniprop/uniinteger.rb +16 -0
- data/lib/uniprop/unistring.rb +34 -0
- data/lib/uniprop/utils.rb +542 -0
- data/lib/uniprop/value_group.rb +276 -0
- data/lib/uniprop/version.rb +5 -0
- data/lib/uniprop.rb +29 -0
- data/sig/uniprop.rbs +4 -0
- data/uniprop.gemspec +39 -0
- metadata +75 -0
@@ -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
|