Class: UniProp::PropFile

Inherits:
Object
  • Object
show all
Defined in:
lib/uniprop/inspects.rb,
lib/uniprop/unicode_elements.rb

Direct Known Subclasses

PropertyAliases, PropertyValueAliases, UnihanFile

Defined Under Namespace

Classes: PropertyAliases, PropertyValueAliases, UnihanFile

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, version, strip_regexp: /\s+/, split_regexp: /;/) ⇒ PropFile

Note:

fileをPathnameで指定する場合、絶対パスでの指定が必要

Returns a new instance of PropFile.

Parameters:

  • path (Pathname/String)

    キャッシュのPathnameまたはbasename_prefixに相当するString

  • strip_regexp (Regexp) (defaults to: /\s+/)

    対応するファイル内で使われる空白文字の正規表現

  • split_regexp (Regexp) (defaults to: /;/)

    対応するファイル内で使われる区切り文字の正規表現



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/uniprop/unicode_elements.rb', line 180

def initialize(path, version, strip_regexp: /\s+/, split_regexp: /;/)
  if path.class==Pathname
    @cache_path = path
    @basename_prefix = UniPropUtils::FileManager.prefix(@cache_path)
  else
    @basename_prefix = path
  end

  # # strip_regexp, split_regexpが引数で指定されていない場合、settings.rbの記述を使用
  # file_format = version.prop_data.find_settings_value(version.prop_data.unihan_files_information, "file_format", version.version_name)

  # strip_regexp ||= file_format[:strip]
  # split_regexp ||= file_format[:split]

  @version = version
  @strip_regexp = strip_regexp
  @split_regexp = split_regexp
end

Instance Attribute Details

#basename_prefixObject

Returns the value of attribute basename_prefix.



174
175
176
# File 'lib/uniprop/unicode_elements.rb', line 174

def basename_prefix
  @basename_prefix
end

#split_regexpObject

Returns the value of attribute split_regexp.



174
175
176
# File 'lib/uniprop/unicode_elements.rb', line 174

def split_regexp
  @split_regexp
end

#strip_regexpObject

Returns the value of attribute strip_regexp.



174
175
176
# File 'lib/uniprop/unicode_elements.rb', line 174

def strip_regexp
  @strip_regexp
end

#versionObject

Returns the value of attribute version.



174
175
176
# File 'lib/uniprop/unicode_elements.rb', line 174

def version
  @version
end

Instance Method Details

#==(other) ⇒ Object

Parameters:



249
250
251
252
253
254
255
256
257
258
# File 'lib/uniprop/unicode_elements.rb', line 249

def ==(other)
  if other.class == self.class
    # cache_pathで判定したほうが簡潔に書けるが、キャッシュにファイルが存在しない場合にも判定を行うため、このような実装にしてある
    return version==other.version && basename_prefix==other.basename_prefix
  elsif other.class == Pathname
    return @cache_path==other
  else
    return false
  end
end

#actual_propertiesSet<Property>

修正済みメタデータを参照し、ファイル内に含まれるプロパティを取得

Returns:



504
505
506
# File 'lib/uniprop/unicode_elements.rb', line 504

def actual_properties
  @actual_properties ||= version..propfile_to_actual_properties[self]
end

#cache_pathPathname

Returns:

  • (Pathname)

Raises:

  • (FileNotFoundError)

    キャッシュが存在せず、ダウンロードにも失敗した場合に発生



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/uniprop/unicode_elements.rb', line 201

def cache_path
  return @cache_path if @cache_path

  # キャッシュの中にbasename_prefixと同名のファイルがある場合、それを使用
  if version.has_cache_file?(basename_prefix)
    @cache_path = version.find_cache_file_path(basename_prefix)
    return @cache_path
  end

  # キャッシュが保存されていない場合、ダウンロードを試みる
  download_myself
  if version.has_cache_file?(basename_prefix)
    @cache_path = version.find_cache_file_path(basename_prefix)
    return @cache_path
  else
    raise FileNotFoundError, "#{basename_prefix} does not exist in cache and download failed."
  end
end

#column_size(row) ⇒ Integer

row行目の列数を取得

Parameters:

  • row (Integer)

Returns:

  • (Integer)


473
474
475
476
# File 'lib/uniprop/unicode_elements.rb', line 473

def column_size(row)
  shaped_line = shaped_lines[row].to_a
  shaped_line[-1]&.empty? ? shaped_line.size-1 : shaped_line.size
end

#comment?(row) ⇒ Boolean

Note:

空行もコメント行とみなす

rowがコメントのみから成る行かを判定

Parameters:

  • row (Integer)

Returns:

  • (Boolean)


361
362
363
364
365
366
367
# File 'lib/uniprop/unicode_elements.rb', line 361

def comment?(row)
  if 0 <= row && row <= lines.size-1
    lines_without_comment[row].match?(/^\s*$/)
  else
    false
  end
end

#comment_rangesArray<Range>

コメントのみから成る行の範囲を取得

Returns:

  • (Array<Range>)


371
372
373
374
375
376
377
378
379
380
381
# File 'lib/uniprop/unicode_elements.rb', line 371

def comment_ranges
  return @comment_ranges if @comment_ranges
  
  comment_rows = []
  lines.size.times do |row|
    comment_rows << row if comment?(row)
  end
  @comment_ranges = UniPropUtils::RangeProcessor.array_to_ranges(comment_rows)

  @comment_ranges
end

#contentsArray<Set<String>>

Note:

返り値の配列のインデックスnは、n列目(最初の列を0列目とする)に含まれるすべての値を含む配列。

各列に含まれる値(codepointを含む)の配列の配列を取得。

Returns:

  • (Array<Set<String>>)


294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/uniprop/unicode_elements.rb', line 294

def contents
  return @contents if @contents

  @contents = []

  shaped_lines.each do |shaped_line|
    shaped_line.each_with_index do |col_value, i|
      @contents[i] ||= Set.new
      @contents[i] << Alias.canonical(col_value)
    end
  end

  # valuesでは区切り文字で区切られたそれぞれの部分を列とみなす。(1行に区切り文字がn個あれば、n+1列あるとみなされる)
  # しかし実際には、最後の区切り文字の右側にコメントしか記述されない事が多いので、その場合は最終列を削除。
  # contentsでは、最後の列に実際に値が無い場合(空orコメントのみの場合)には列とみなさない。
  if @contents[-1].empty? || @contents[-1] == Set.new([""])
    @contents = @contents[...-1]
  end
  
  @contents
end

#download_myselfObject

:nocov: versionの、basename_prefixに該当するファイル名のファイルをUnicode.orgからダウンロード



222
223
224
# File 'lib/uniprop/unicode_elements.rb', line 222

def download_myself
  version.download_file(basename_prefix)
end

#has_property_alias?(row, prop) ⇒ Boolean

rowの中の1つ以上の列に、propのエイリアスが含まれるかを判定

Parameters:

  • row (Integer)

    検索する行の番号

  • prop (Property)

Returns:

  • (Boolean)


341
342
343
# File 'lib/uniprop/unicode_elements.rb', line 341

def has_property_alias?(row, prop)
  !!shaped_lines[row]&.any? { prop.has_alias?(_1) }
end

#information_containing_rangesArray<Range>

空行・コメントのみ以外の行の範囲を取得

Returns:

  • (Array<Range>)


391
392
393
# File 'lib/uniprop/unicode_elements.rb', line 391

def information_containing_ranges
  UniPropUtils::RangeProcessor.sub(Range.new(0, lines.size-1), comment_ranges)
end

#inspectObject



5
6
7
# File 'lib/uniprop/inspects.rb', line 5

def inspect
  "#<#{self.class.name} #{basename_prefix}>"
end

#is_derived?Boolean

Returns:

  • (Boolean)


521
522
523
# File 'lib/uniprop/unicode_elements.rb', line 521

def is_derived?
  basename_prefix.start_with?(/Derived/)
end

#is_meta_file?Boolean

:nocov:

Returns:

  • (Boolean)


227
# File 'lib/uniprop/unicode_elements.rb', line 227

def is_meta_file?() false end

#is_unihan_file?Boolean

Returns:

  • (Boolean)


229
# File 'lib/uniprop/unicode_elements.rb', line 229

def is_unihan_file?() false end

#linesArray

ファイルコンテンツを改行で区切った配列を取得

Returns:

  • (Array)


233
# File 'lib/uniprop/unicode_elements.rb', line 233

def lines()  @lines ||= cache_path.readlines.map(&:chomp)  end

#lines_without_commentArray

Note:

コメントのみからなる行は空文字列に変換されるだけであり、要素は削除されない (行数がインデックスと対応)

ファイルコンテンツからコメントを削除したものを改行で区切った配列を取得

Returns:

  • (Array)


238
239
240
# File 'lib/uniprop/unicode_elements.rb', line 238

def lines_without_comment
  @lines_without_comment ||= lines.map { |l| l.gsub(/#.*/,'') }
end

#max_column_size(range) ⇒ Integer

rangeで指定された行の範囲内のうち、最大の列数を取得

Parameters:

  • (Range<Integer>)

Returns:

  • (Integer)


481
482
483
# File 'lib/uniprop/unicode_elements.rb', line 481

def max_column_size(range)
  range.map{column_size(_1)}.max
end

#missing_value_linesArray<String>

missing valueについて記述された行のみを取得

Returns:

  • (Array<String>)


385
386
387
# File 'lib/uniprop/unicode_elements.rb', line 385

def missing_value_lines
  @missing_value_lines ||= lines.filter { _1.match?(/@missing/) }
end

#netto_linesArray

lines_without_commentのうち、空文字列となった要素を削除した配列を取得

Returns:

  • (Array)


244
245
246
# File 'lib/uniprop/unicode_elements.rb', line 244

def netto_lines
  @netto_lines ||= lines_without_comment.reject { |l| l.match(/^\s*$/) }
end

#netto_shaped_linesArray

Returns valuesから空の配列を削除したArray.

Returns:

  • (Array)

    valuesから空の配列を削除したArray



287
288
289
# File 'lib/uniprop/unicode_elements.rb', line 287

def netto_shaped_lines
  @netto_shaped_lines ||= shaped_lines.reject { _1.empty? }
end

#property_alias_including_ranges(prop) ⇒ Array<Range>

propのエイリアスが含まれる行の範囲を取得

Parameters:

Returns:

  • (Array<Range>)


348
349
350
351
352
353
354
355
356
# File 'lib/uniprop/unicode_elements.rb', line 348

def property_alias_including_ranges(prop)
  property_alias_including_rows = []

  lines.size.times do |row|
    property_alias_including_rows << row if has_property_alias?(row, prop)
  end

  UniPropUtils::RangeProcessor.array_to_ranges(property_alias_including_rows)
end

#property_value_type_match?(row, column, prop) ⇒ Boolean

Note:

Miscellaneousプロパティの判定方法はsettings.rbで指定可能

row, columnの値がprop.property_value_typeの型にマッチする値かを判定

Parameters:

  • prop (Property)
  • row (Integer)
  • column (Integer)

Returns:

  • (Boolean)


401
402
403
404
405
406
407
# File 'lib/uniprop/unicode_elements.rb', line 401

def property_value_type_match?(row, column, prop)
  if prop.property_value_type == :miscellaneous
    return type_match?(row, column, prop.miscellaneous_format, prop)
  else
    return type_match?(row, column, prop.property_value_type, prop)
  end
end

#property_value_type_match_ranges(column, prop) ⇒ Array<Range>

column列目の中で、propが取りうる値の範囲を取得

Parameters:

Returns:

  • (Array<Range>)


448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/uniprop/unicode_elements.rb', line 448

def property_value_type_match_ranges(column, prop)
  # miscellanesou_format==unqueの場合、ファイル内の全範囲をreturn
  if prop.property_value_type==:miscellaneous && prop.miscellaneous_format==:unique
    if unique_column?(column, prop.unique_threshold)
      return information_containing_ranges
    else
      return []
    end
  end

  # それ以外の場合、property_value_type_match? がtrueとなる行の範囲をreturn
  property_value_including_rows = []

  lines.size.times do |row|
    if property_value_type_match?(row, column, prop)
      property_value_including_rows << row
    end
  end

  UniPropUtils::RangeProcessor.array_to_ranges(property_value_including_rows)
end

#propfile_value_groupPropFileValueGroup

Returns:



517
518
519
# File 'lib/uniprop/unicode_elements.rb', line 517

def propfile_value_group
  @propfile_value_group ||= PropFileValueGroup.new(self)
end

#shaped_linesArray

Note:

strip_regexp==/s+/ の場合であっても、各列の最初と最後の空白しか除去されない。「0000; 1111 2222; 3333;」の、1111と2222の間の空白は除去されない。

lines_without_commentをstrip_regexpとsplit_regexpで処理した配列を取得def values

Returns:

  • (Array)


264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/uniprop/unicode_elements.rb', line 264

def shaped_lines
  return @shaped_lines if @shaped_lines

  @shaped_lines = []

  # String#splitはlimit==0(デフォルト)の場合、配列末尾の空文字列が削除される
  # それを防ぐため、limit==-1としてある。これはlimit<0にする事が目的であり、-1という値に意味は無い
  if strip_regexp == /\s+/
    lines_without_comment.each do |line|
      @shaped_lines << line.split(sep=split_regexp, limit=-1)
                    .map { _1.gsub(/^\s+/, '') }
                    .map { _1.gsub(/\s+$/, '') }
    end
  else
    lines_without_comment.each do |line|
      @shaped_lines << line.split(sep=split_regexp, limit=-1)
                    .map { _1.gsub(strip_regexp, '') }
    end
  end
  @shaped_lines
end

#shaped_missing_value_linesArray<Array<String>>

Returns:

  • (Array<Array<String>>)


509
510
511
512
513
514
# File 'lib/uniprop/unicode_elements.rb', line 509

def shaped_missing_value_lines
  @shaped_missing_value_lines ||= missing_value_lines.map {
    _1.gsub(/\s/, '')
      .split(/;/)
  }
end

#type_match?(row, column, type, prop) ⇒ Boolean

row, columnの値がtypeの型にマッチする値かを判定

Parameters:

  • row (Integer)
  • column (Integer)
  • type (Symbol)
  • prop (Property)

Returns:

  • (Boolean)


415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/uniprop/unicode_elements.rb', line 415

def type_match?(row, column, type, prop)
  # データファイルには、一番右の;の右側に情報が記述されるフォーマットと、コメントのみが記述されるフォーマットが存在
  # row行目だけを取り出し、一番右側の列が値を持たない(value_atがnil)場合、たまたまrow行目に値が記述されていない(値が空文字列)だけか、ファイル全体として一番右の列に値が記述されていないのか、判定不可能
  # そのため、ここでは値が存在しない列に対しては、空文字列を値として持つと仮定して判定を行う
  value = value_at(row, column) || ""

  case type
  when :catalog, :enumerated
    return UniPropUtils::TypeJudgementer.validate_enumerative(value, prop)
  when :binary
    return UniPropUtils::TypeJudgementer.validate_binary(value, prop)
  when :string
    return UniPropUtils::TypeJudgementer.validate_string(value)
  when :numeric
    return UniPropUtils::TypeJudgementer.validate_numeric(value)
  when :jamo_short_name
    # Jamo_Short_Nameは、プロパティ値のエイリアス1つか、空文字列(missing)を値として取る(15.0.0でコードポイント110Bの値が空文字列として明示的に記述されている)
    return prop.property_values.any? { _1.has_alias?(value) } || value.empty?
  when :script_extensions
    # Script_Extensionsは1つ以上のScriptプロパティのプロパティ値を取る。2つ以上取る場合、ファイル内では半角スペース区切りで記述される。
    return value.split.all? { version.find_property("Script").has_property_value?(_1) }
  when :text
    # 任意の文字列(空文字列も含む)である事を表すtextでは常にtrueを返す
    return true
  else
    return false
  end
end

#unique_column?(column, unique_threshold) ⇒ Boolean

:nocov: 引数の列がユニーク列(行数に対し、記述されている値の割合が閾値以上の列。Nameプロパティなど、それぞれのcodepointが異なる値を取る傾向にあるプロパティが該当)であるかを判定

Parameters:

  • column (Integer)
  • unique_threshold (Float)

Returns:

  • (Boolean)

    ユニーク列であればtrue



327
328
329
# File 'lib/uniprop/unicode_elements.rb', line 327

def unique_column?(column, unique_threshold)
  (contents[column].size.to_f / netto_lines.size) > unique_threshold
end

#value_at(row, column) ⇒ String

引数の行・列の値を取得

Returns:

  • (String)


334
335
336
# File 'lib/uniprop/unicode_elements.rb', line 334

def value_at(row, column)
  shaped_lines.dig(row, column)
end

#valuesSet<String>

ファイル内に含まれるすべての値(codepointを含む)を取得。

Returns:

  • (Set<String>)


318
319
320
# File 'lib/uniprop/unicode_elements.rb', line 318

def values
  @values ||= contents.reduce(Set.new, :merge)
end

#verbose_property_value_type_match_ranges(column, prop) ⇒ Array<Range<Integer>>

property_value_type_match_rangesの最小値を下限、最大値を上限とする範囲の中で、空行、コメント行、column列目の値がpropの行のいずれかに該当する範囲を取得

Returns:

  • (Array<Range<Integer>>)


487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/uniprop/unicode_elements.rb', line 487

def verbose_property_value_type_match_ranges(column, prop)
  prop_ranges = property_value_type_match_ranges(column, prop)

  if prop_ranges.empty?
    return prop_ranges
  else
    prop_begin_col = UniPropUtils::RangeProcessor.min(prop_ranges)
    prop_end_col = UniPropUtils::RangeProcessor.max(prop_ranges)

    return UniPropUtils::RangeProcessor.sum_up(
      comment_ranges.map { UniPropUtils::RangeProcessor.cut_external(_1, prop_begin_col, prop_end_col) }.compact + prop_ranges
    )
  end
end