Class: UniProp::BlockGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/uniprop/metadata_generator.rb

Overview

既存のメタデータを使用し、メタデータ未知のPropFileに関するblocksを作成するクラス

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(using_file, generated_file, using_file_metadata) ⇒ BlockGenerator

Returns a new instance of BlockGenerator.

Parameters:



171
172
173
174
175
176
177
# File 'lib/uniprop/metadata_generator.rb', line 171

def initialize(using_file, generated_file, )
  @using_file = using_file
  @generated_file = generated_file
  @generated_version = generated_file.version
   = 
   = using_file.version.
end

Instance Attribute Details

#generated_fileObject (readonly)

Returns the value of attribute generated_file.



166
167
168
# File 'lib/uniprop/metadata_generator.rb', line 166

def generated_file
  @generated_file
end

#generated_versionObject (readonly)

Returns the value of attribute generated_version.



166
167
168
# File 'lib/uniprop/metadata_generator.rb', line 166

def generated_version
  @generated_version
end

#using_fileObject (readonly)

Returns the value of attribute using_file.



166
167
168
# File 'lib/uniprop/metadata_generator.rb', line 166

def using_file
  @using_file
end

#using_file_metadataObject (readonly)

Returns the value of attribute using_file_metadata.



166
167
168
# File 'lib/uniprop/metadata_generator.rb', line 166

def 
  
end

#using_version_metadataObject (readonly)

Returns the value of attribute using_version_metadata.



166
167
168
# File 'lib/uniprop/metadata_generator.rb', line 166

def 
  
end

Instance Method Details

#block_format_type(content) ⇒ Array<Object>

blockの再生成に使用するフォーマットとして最も適切なものを取得

Parameters:

Returns:

  • (Array<Object>)


226
227
228
# File 'lib/uniprop/metadata_generator.rb', line 226

def block_format_type(content)
  content.map { column_format_type(_1) }
end

#block_format_typesArray<Array<Object>>

Note:

返り値のn番目の要素はblocks内のn番目のblockのフォーマットに対応

using_fileのblocksのフォーマットを取得

Returns:

  • (Array<Array<Object>>)


200
201
202
# File 'lib/uniprop/metadata_generator.rb', line 200

def block_format_types
  @block_format_types ||= .blocks.map { block_format_type(_1.content) }
end

#column_format_type(column) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/uniprop/metadata_generator.rb', line 230

def column_format_type(column)
  # columnがArrayの場合、その列には固有の表記法が使用されており、一様に判定を行う事はできないため、:text を返す

  return Format.new(:text, column) if column.class == Array
  
  # columnがnilの場合、nil (その列には判定を行わない)

  return nil if !column

  if has_multiple_free_value_block?
    # 列挙型以外のプロパティを取るブロックが2つ以上存在する場合、

    # コードポイントと値の対応を調べないと、同じプロパティに関する記述かの判定が不可能

    return column
  else
    # 列挙型以外のプロパティを取るブロックが1つだけの場合、

    # 記述されている値の型を調べるだけで、同じプロパティに関する記述かの判定が可能

    type =  (column.property_value_type==:miscellaneous) ? column.miscellaneous_format : column.property_value_type

    # type==:uniqueの場合、判定時には:textと同様に扱いたいため、:textに変更

    type = :text if type==:unique

    return Format.new(type, column)
  end
end

#generate_raw_blocksArray<RawBlock>

generated_fileのblocksに相当するArray<RawBlock>を作成

Returns:



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

def generate_raw_blocks
  return @raw_blocks if @raw_blocks
  @raw_blocks = []
  
  .blocks.size.times do |block_no|
    range = matched_ranges[block_no]

    if range
      content = .raw_blocks[block_no].content
      @raw_blocks << RawBlock.new(content, range.to_s)
    end
  end

  @raw_blocks
end

#has_multiple_free_value_block?Boolean

using_fileに、binary,enumerated,catalog以外の型のプロパティを含むブロックが2つ以上存在するか判定

Returns:

  • (Boolean)

    存在する場合true



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/uniprop/metadata_generator.rb', line 206

def has_multiple_free_value_block?
  return @multiple_free_value_block_f if @multiple_free_value_block_f

  free_value_exist_f = []
  .blocks.each do |block|
    block_properties = block.content.reject { _1.class == Array }
                                    .compact
    
    free_value_exist_f << !block_properties.all? { _1.property_value_type==:binary ||
                                                   _1.property_value_type==:enumerated ||
                                                  _1.property_value_type==:catalog }
  end
                 
  @multiple_free_value_block_f = free_value_exist_f.filter { _1 }.size > 1
  @multiple_free_value_block_f
end

#line_format(row) ⇒ Array<Integer>

Returns n番目のblockのフォーマットに一致する場合、n。どのブロックにもマッチしない場合、nil。.

Parameters:

  • row (Integer)

Returns:

  • (Array<Integer>)

    n番目のblockのフォーマットに一致する場合、n。どのブロックにもマッチしない場合、nil。



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/uniprop/metadata_generator.rb', line 273

def line_format(row)
  matched_blocks = []
  .blocks.size.times do |block_no|
    codepoint_col_no = .codepoint_column_nos[block_no]

    matched_blocks << block_no if match_format?(row, codepoint_col_no, block_no)
  end
  
  if matched_blocks.size == 1
    # line_formatの結果のサイズが1の場合

    # row行目がマッチするブロックが一意に絞れているため、結果として使用

    return matched_blocks[0]
  else
    # row行目がマッチするブロックが一意に絞れていない場合

    # この場合、ある行がどのプロパティについて記述されているか、データファイルに記述されている場合が多い

    # そのため、row行目の中に含まれるプロパティ名で判定

    
    prop_including_blocks = []
    .blocks.size.times do |block_no|
      .blocks[block_no].content.compact.each do |prop|
        next if prop.class == Array
        prop_including_blocks << block_no if generated_file.has_property_alias?(row, prop)
      end
    end

    if prop_including_blocks.size == 1
      return prop_including_blocks[0]
    else
      # プロパティ名を使用しても一意に絞れない場合、nil

      return nil
    end
  end
end

#lines_formatArray<Integer?>

generated_fileの各行が、何番目のブロックのblock_format_typeにマッチするか判定

Returns:

  • (Array<Integer?>)

    m行目がblocks内のn番目のblockのフォーマットに一致する場合、m番目の要素はn。どのブロックにもマッチしない場合、nil。



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/uniprop/metadata_generator.rb', line 255

def lines_format
  return @lines_format if @lines_format
  @lines_format = []

  generated_file.lines.size.times do |row|
    # n行目がコメントの場合、line_formatによる判定は行わずnilを追加

    if generated_file.comment?(row)
      @lines_format << nil
      next
    end
    @lines_format << line_format(row)
  end

  @lines_format
end

#match_format?(row, codepoint_column_no, block_no) ⇒ Boolean

row行目がformat_typeの形式に一致するか判定

Parameters:

  • row (Integer)
  • codepoint_column_no (Integer)
  • block_no (Integer)

Returns:

  • (Boolean)


311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/uniprop/metadata_generator.rb', line 311

def match_format?(row, codepoint_column_no, block_no)
  shaped_line = generated_file.shaped_lines[row]
  format_type = block_format_types[block_no]

  # row行目の中にcol_formatと異なるフォーマットの列が存在する場合、その時点でfalseを返す

  format_type.each_with_index do |col_format, col_no|
    next if !col_format # formatがnilの場合には判定を行わない


    if col_format.class == Format
      if [:enumerated, :catalog, :binary].include?(col_format.type) && generated_version.has_property?(col_format.property)
        # 列挙型のプロパティ(enumerated, catalog, binary)の場合、 prop.version == generated_version でないと、propに新しいバージョンで追加された値が含まれず、プロパティの範囲の正確な推測が不可能

        prop = generated_version.find_property(col_format.property)
      else
        prop = col_format.property
      end
      
      if generated_file.type_match?(row, col_no, col_format.type, prop)
        next
      else
        return false
      end

    else
      # col_format.class==Property の場合、型での判定はできない

      # generated_fileのrow行目のcodepointがusing_fileで取っている値が、generated_fileと同じであるかを判定する

      bvg = .(using_file).block_value_group(block_no)
      shaped_line_dup = shaped_line.dup
      codepoint = shaped_line_dup.delete_at(codepoint_column_no)

      # codepointがnnnn..mmmm形式の場合、最初のnnnnのみを比較に使用

      # nnnn..mmmmの範囲では同じ値を持つため、nnnnのみを使用すれば十分

      codepoint = codepoint.split("..")[0]
      generated_file_value = generated_file.value_at(row, col_no)
      using_file_value = bvg.values_of(codepoint)
      
      if using_file_value.class==String && using_file_value==generated_file_value || 
        using_file_value.class==Array && using_file_value.include?(generated_file_value)
        next
      else
        return false
      end

    end
  end
  true
end

#matched_rangesArray<Range<Integer>>

Note:

n番目の要素はn番目のブロックがマッチした範囲

各ブロックがgenerated_fileにおいてマッチした行数の範囲を取得

Returns:

  • (Array<Range<Integer>>)


361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/uniprop/metadata_generator.rb', line 361

def matched_ranges
  return @matched_ranges if @matched_ranges

  block_no_to_ranges = Hash.new { |hash,key| hash[key]=[] }

  measured_block_no = nil # 範囲を計測中のblock_no

  start_idx = nil # measured_block_noが最初に現れたidx

  end_idx = nil # measured_block_noが最後に現れたidx

  pre_idx = nil # 前回のblock_no(nil以外のInteger)が現れたidx


  lines_format_dup = lines_format.dup
  lines_format_dup << Float::NAN # 最後の要素まで処理を行うため、最後にNANを入れておく


  lines_format_dup.each_with_index do |block_no, i|
    next if !block_no # i行目がコメント行などの場合


    if block_no != measured_block_no
      # 前回までのmeasured_block_noの計測を終了

      end_idx = pre_idx
      if measured_block_no && start_idx && end_idx
        block_no_to_ranges[measured_block_no] << Range.new(start_idx, end_idx)
      end

      # measured_block_noをblock_noに切り替え

      measured_block_no = block_no
      start_idx = i
    end

    pre_idx = i
  end

  # 同一ブロックが2つ以上に分かれた範囲に記述されていると判定されている場合、

  # 最初の範囲だけをn番目のブロックの範囲として採用

  # その場合、誤ったメタデータが生成されることになるが、検証メソッドにより検出され、ユーザに修正される

  @matched_ranges = []
  .blocks.size.times do |block_no|
    @matched_ranges[block_no] = block_no_to_ranges[block_no][0]
  end        

  @matched_ranges
end