Created
September 29, 2012 10:36
-
-
Save pocari/3803659 to your computer and use it in GitHub Desktop.
FixedLayoutMapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#coding: Windows-31J | |
module FixedLayoutMapper | |
class Mapper | |
class ColumnMapper | |
attr_accessor :layout_mapper, :converter | |
def initialize(layout_mapper, converter = nil) | |
@layout_mapper = layout_mapper | |
@converter = converter | |
end | |
def convert(value) | |
if @converter | |
@converter.(value) | |
else | |
value | |
end | |
end | |
end | |
class SimpleMapper < ColumnMapper | |
def initialize(layout_mapper, len, converter = nil) | |
super layout_mapper, converter | |
@len = len | |
end | |
def map(bytes) | |
value = bytes.take(@len).pack("C*").force_encoding(layout_mapper.encoding) | |
#value = converter.(value) if converter | |
[convert(value), bytes.drop(@len)] | |
end | |
end | |
class SubLayoutMapper < ColumnMapper | |
def initialize(layout_mapper, layout_id, converter = nil) | |
super layout_mapper, converter | |
@layout_id = layout_id | |
end | |
def map(bytes) | |
value, rest = layout_mapper.get_layout(@layout_id).map(bytes, layout_mapper.get_result_class(@layout_id)) | |
[convert(value), rest] | |
end | |
end | |
class ArrayMapper < ColumnMapper | |
def initialize(layout_mapper, mappers, converter = nil) | |
super layout_mapper, converter | |
@mappers = mappers | |
end | |
def map(bytes) | |
ret = [] | |
rest = @mappers.inject(bytes) do |acc, mapper| | |
value, acc = mapper.map(acc) | |
ret << value | |
acc | |
end | |
[convert(ret), rest] | |
end | |
end | |
class Layout | |
def initialize | |
@layout = [] | |
end | |
def syms | |
@layout.map{|e| e[0]} | |
end | |
def add(sym, mapper) | |
@layout << [sym, mapper] | |
end | |
def map(bytes, result_class) | |
obj = result_class.new | |
rest = @layout.inject(bytes) do |acc, (sym, mapper)| | |
value, acc = mapper.map(acc) | |
obj[sym] = value | |
acc | |
end | |
[obj, rest] | |
end | |
end | |
class << self | |
def define_layout(opts = {:encoding => Encoding.default_external}, &block) | |
obj = Mapper.new(opts) | |
obj.define_layout(&block) | |
obj | |
end | |
end | |
attr_reader :encoding | |
def initialize(opts = {:encoding => Encoding.default_external}) | |
@current_layout = :default_layout | |
@layouts = {} | |
@result_class = {} | |
@encoding = opts[:encoding] | |
end | |
def define_layout(&block) | |
instance_eval(&block) | |
build_layout | |
end | |
def map(data, layout_id = @current_layout) | |
obj, = @layouts[layout_id].map(data.unpack("C*"), @result_class[layout_id]) | |
obj | |
end | |
def get_layout(layout_id) | |
@layouts[layout_id] | |
end | |
def get_result_class(layout_id) | |
@result_class[layout_id] | |
end | |
private | |
def build_layout | |
@layouts.each do |layout_id, layout| | |
@result_class[layout_id] = Struct.new(*layout.syms) do | |
def to_hash | |
each_pair.inject({}) do |acc, (key, value)| | |
case | |
when value.respond_to?(:to_hash) | |
acc[key] = value.to_hash | |
when Array === value | |
acc[key] = value.map{|e| | |
if e.respond_to?(:to_hash) | |
e.to_hash | |
else | |
e | |
end | |
} | |
else | |
acc[key] = value | |
end | |
acc | |
end | |
end | |
end | |
end | |
end | |
def layout(layout_id, &block) | |
change_current_layout(layout_id) do | |
instance_eval(&block) | |
end | |
end | |
def col(sym, map_info, layout_id = @current_layout, &block) | |
case map_info | |
when Numeric | |
col_len(sym, map_info, layout_id, block) | |
when Symbol | |
col_from_sub_layout(sym, map_info, layout_id, block) | |
when Array | |
col_array(sym, map_info, layout_id, block) | |
end | |
end | |
def col_len(sym, len, layout_id = @current_layout, block) | |
@layouts[layout_id] ||= Layout.new | |
@layouts[layout_id].add(sym, SimpleMapper.new(self, len, block)) | |
end | |
def col_from_sub_layout(sym, sub_layout, layout_id = @current_layout, block) | |
@layouts[layout_id] ||= Layout.new | |
@layouts[layout_id].add(sym, SubLayoutMapper.new(self, sub_layout, block)) | |
end | |
def col_array(sym, array_param, layout_id = @current_layout, block) | |
@layouts[layout_id] ||= Layout.new | |
@layouts[layout_id].add(sym, ArrayMapper.new(self, | |
array_param.map{|arg| | |
case arg | |
when Numeric | |
mapper_class = SimpleMapper | |
when Symbol | |
mapper_class = SubLayoutMapper | |
else | |
raise "elements must be Numeric or Symbol" | |
end | |
mapper_class.new(self, arg) | |
}, block) | |
) | |
end | |
def change_current_layout(layout) | |
tmp = @current_layout | |
@current_layout = layout | |
yield | |
@current_layout = tmp | |
end | |
end | |
end | |
if $0 == __FILE__ | |
divider = FixedLayoutMapper::Mapper.define_layout do | |
layout :sub2 do | |
col :sub2_1, 1 | |
col :sub2_2, 3 | |
end | |
layout :sub3 do | |
col :sub3_1, 2 | |
end | |
layout :sub1 do | |
col :f1, 5 do |v| | |
v * 2 | |
end | |
col :f2, 6 | |
col :f3, [2, :sub3, 2] | |
col :f4, :sub2 | |
end | |
col :f1, :sub1 do |sub1| | |
sub1.f1 *= 2 | |
sub1 | |
end | |
col :f2, [:sub3] * 4 do |sub3_ary| | |
sub3_ary.map{|e| e.sub3_1 *= 2; e} | |
end | |
end | |
data = [ | |
%w(12345 123456 12 12 12 1 123 あ い う え) | |
] | |
obj = divider.map(data[0].join) | |
p obj | |
p obj.to_hash | |
require 'yaml' | |
puts obj.to_yaml | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment