norikra 0.0.13-java → 0.0.14-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -52,6 +52,33 @@ module Norikra
52
52
  # "double"],
53
53
  # "10.5"]]]]
54
54
 
55
+ ### SELECT count(*) AS cnt
56
+ ### FROM TestTable.win:time_batch(10 sec)
57
+ ### WHERE params.$$path.$1="/" AND size.$0.bytes > 100 and opts.num.seq.length() > 0
58
+
59
+ # ["EPL_EXPR",
60
+ # ["SELECTION_EXPR", ["SELECTION_ELEMENT_EXPR", "count", "cnt"]],
61
+ # ["STREAM_EXPR",
62
+ # ["EVENT_FILTER_EXPR", "TestTable"],
63
+ # ["VIEW_EXPR", "win", "time_batch", ["TIME_PERIOD", ["SECOND_PART", "10"]]]],
64
+ # ["WHERE_EXPR",
65
+ # ["EVAL_AND_EXPR",
66
+ # ["EVAL_EQUALS_EXPR",
67
+ # ["EVENT_PROP_EXPR",
68
+ # ["EVENT_PROP_SIMPLE", "params"],
69
+ # ["EVENT_PROP_SIMPLE", "$$path"],
70
+ # ["EVENT_PROP_SIMPLE", "$1"]],
71
+ # "\"/\""],
72
+ # [">",
73
+ # ["EVENT_PROP_EXPR",
74
+ # ["EVENT_PROP_SIMPLE", "size"],
75
+ # ["EVENT_PROP_SIMPLE", "$0"],
76
+ # ["EVENT_PROP_SIMPLE", "bytes"]],
77
+ # "100"],
78
+ # [">",
79
+ # ["LIB_FUNC_CHAIN", ["LIB_FUNCTION", "opts.num.seq", "length", "("]],
80
+ # "0"]]]]
81
+
55
82
  def astnode(tree)
56
83
  children = if tree.children
57
84
  tree.children.map{|c| astnode(c)}
@@ -114,23 +141,28 @@ module Norikra
114
141
  result
115
142
  end
116
143
 
117
- def fields(default_target=nil)
118
- @children.map{|c| c.nodetype?(:subquery) ? [] : c.fields(default_target)}.reduce(&:+) || []
144
+ def fields(default_target=nil, known_targets_aliases=[])
145
+ @children.map{|c| c.nodetype?(:subquery) ? [] : c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
119
146
  end
120
147
  end
121
148
 
122
149
  class ASTEventPropNode < ASTNode # EVENT_PROP_EXPR
123
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]
124
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "fraud"], ["EVENT_PROP_SIMPLE", "aaa"]]
150
+ # "bbb" => ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]
151
+ # "fraud.aaa" => ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "fraud"], ["EVENT_PROP_SIMPLE", "aaa"]]
152
+ # "size.$0.bytes" => ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"], ["EVENT_PROP_SIMPLE", "$0"], ["EVENT_PROP_SIMPLE", "bytes"]]
125
153
 
126
154
  def nodetype?(*sym)
127
155
  sym.include?(:prop) || sym.include?(:property)
128
156
  end
129
157
 
130
- def fields(default_target=nil)
158
+ def fields(default_target=nil, known_targets_aliases=[])
131
159
  props = self.listup('EVENT_PROP_SIMPLE')
132
- if props.size > 1 # alias.fieldname
133
- [ {:f => props[1].child.name, :t => props[0].child.name} ]
160
+ if props.size > 1 # alias.fieldname or container_fieldname.key.$1
161
+ if known_targets_aliases.include?(props[0].child.name)
162
+ [ {:f => props[1..-1].map{|n| n.child.name}.join("."), :t => props[0].child.name} ]
163
+ else
164
+ [ {:f => props.map{|n| n.child.name}.join("."), :t => default_target} ]
165
+ end
134
166
  else # fieldname (default target)
135
167
  [ {:f => props[0].child.name, :t => default_target } ]
136
168
  end
@@ -138,42 +170,53 @@ module Norikra
138
170
  end
139
171
 
140
172
  class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
141
- # ["LIB_FUNCTION", "now", "("] #### now()
142
- # ["LIB_FUNCTION", "hoge", "length", "("] # #### hoge.length()
143
- # ["LIB_FUNCTION", "hoge", "substr", "0", "("] #### hoge.substr(0)
144
- # ["LIB_FUNCTION", "substr", "10", "0", "("] #### substr(10,0)
145
- # ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("] #### hoge.substr(0,8)
146
- # ["LIB_FUNCTION", "max", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "("] #### max(size)
173
+ # "now()" => ["LIB_FUNCTION", "now", "("]
174
+ # "hoge.length()" => ["LIB_FUNCTION", "hoge", "length", "("]
175
+ # "hoge.substr(0)" => ["LIB_FUNCTION", "hoge", "substr", "0", "("]
176
+ # "substr(10,0)" => ["LIB_FUNCTION", "substr", "10", "0", "("]
177
+ # "hoge.substr(0,8)" => ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("]
178
+
179
+ # "opts.num.$0.length()" => ["LIB_FUNCTION", "opts.num.$0", "length", "("]
180
+
181
+ # "max(size)" => ["LIB_FUNCTION", "max", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "("]
147
182
 
148
183
  def nodetype?(*sym)
149
184
  sym.include?(:lib) || sym.include?(:libfunc)
150
185
  end
151
186
 
152
- def fields(default_target=nil)
187
+ def fields(default_target=nil, known_targets_aliases=[])
153
188
  if @children.size <= 2
154
189
  # single function like 'now()', function-name and "("
155
190
  []
156
191
 
157
192
  elsif @children[1].nodetype?(:prop, :lib, :subquery)
158
193
  # first element should be func name if second element is property, library call or subqueries
159
- self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
194
+ self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
160
195
 
161
196
  elsif @children[1].name =~ /^(-)?\d+(\.\d+)$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
162
197
  # first element should be func name if secod element is number/string literal
163
- self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
198
+ self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
164
199
 
165
200
  elsif Norikra::Query.imported_java_class?(@children[0].name)
166
201
  # Java imported class name (ex: 'Math.abs(-1)')
167
- self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
202
+ self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
168
203
 
169
204
  else
170
- # first element may be property (simple 'fieldname.funcname()' or fully qualified 'target.fieldname.funcname()')
205
+ # first element may be property
206
+ # * simple 'fieldname.funcname()'
207
+ # * fully qualified 'target.fieldname.funcname()'
208
+ # * simple/fully-qualified container field access 'fieldname.key.$0.funcname()' or 'target.fieldname.$1.funcname()'
171
209
  target,fieldname = if @children[0].name.include?('.')
172
- @children[0].name.split('.', 2)
210
+ parts = @children[0].name.split('.')
211
+ if known_targets_aliases.include?(parts[0])
212
+ [ parts[0], parts[1..-1].join(".") ]
213
+ else
214
+ [ default_target, @children[0].name ]
215
+ end
173
216
  else
174
217
  [default_target,@children[0].name]
175
218
  end
176
- children_list = self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
219
+ children_list = self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
177
220
  [{:f => fieldname, :t => target}] + children_list
178
221
  end
179
222
  end
@@ -202,9 +245,9 @@ module Norikra
202
245
  @children.last.children.size < 1 ? @children.last.name : nil
203
246
  end
204
247
 
205
- def fields(default_target=nil)
248
+ def fields(default_target=nil, known_targets_aliases=[])
206
249
  this_target = self.target
207
- self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target)}.reduce(&:+) || []
250
+ self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target,known_targets_aliases)}.reduce(&:+) || []
208
251
  end
209
252
  end
210
253
 
@@ -32,13 +32,13 @@ class Norikra::RPC::Handler
32
32
 
33
33
  def targets
34
34
  logging(:show, :targets){
35
- @engine.targets
35
+ @engine.targets.map(&:to_hash)
36
36
  }
37
37
  end
38
38
 
39
- def open(target, fields)
39
+ def open(target, fields, auto_field)
40
40
  logging(:manage, :open, target, fields){
41
- r = @engine.open(target, fields)
41
+ r = @engine.open(target, fields, auto_field)
42
42
  !!r
43
43
  }
44
44
  end
@@ -50,6 +50,13 @@ class Norikra::RPC::Handler
50
50
  }
51
51
  end
52
52
 
53
+ def modify(target, auto_field)
54
+ logging(:manage, :modify, target, auto_field){
55
+ r = @engine.modify(target, auto_field)
56
+ !!r
57
+ }
58
+ end
59
+
53
60
  def queries
54
61
  logging(:show, :queries){
55
62
  @engine.queries.map(&:to_hash)
@@ -108,7 +108,7 @@ module Norikra
108
108
  info "loading from stats file"
109
109
  if @stats.targets && @stats.targets.size > 0
110
110
  @stats.targets.each do |target|
111
- @engine.open(target[:name], target[:fields])
111
+ @engine.open(target[:name], target[:fields], target[:auto_field])
112
112
  end
113
113
  end
114
114
  if @stats.queries && @stats.queries.size > 0
@@ -163,7 +163,13 @@ module Norikra
163
163
  port: @port,
164
164
  threads: @thread_conf,
165
165
  log: @log_conf,
166
- targets: @engine.typedef_manager.dump,
166
+ targets: @engine.targets.map{|t|
167
+ {
168
+ :name => t.name,
169
+ :fields => @engine.typedef_manager.dump_target(t.name),
170
+ :auto_field => t.auto_field
171
+ }
172
+ },
167
173
  queries: @engine.queries.map{|q| {:name => q.name, :expression => q.expression}}
168
174
  )
169
175
  stats.dump(@stats_path)
@@ -1,7 +1,31 @@
1
1
  module Norikra
2
2
  class Target
3
+ attr_accessor :name, :fields, :auto_field
4
+
3
5
  def self.valid?(target_name)
4
6
  target_name =~ /^[a-zA-Z]([_a-zA-Z0-9]*[a-zA-Z0-9])?$/
5
7
  end
8
+
9
+ def initialize(name, fields=[], auto_field=true)
10
+ @name = name
11
+ @fields = fields
12
+ @auto_field = !!auto_field
13
+ end
14
+
15
+ def to_s
16
+ @name
17
+ end
18
+
19
+ def to_hash
20
+ {:name => @name, :auto_field => @auto_field}
21
+ end
22
+
23
+ def ==(other)
24
+ self.class == other.class ? self.name == other.name : self.name == other.to_s
25
+ end
26
+
27
+ def auto_field?
28
+ @auto_field
29
+ end
6
30
  end
7
31
  end
@@ -1,238 +1,41 @@
1
- require 'digest'
2
1
  require 'json'
3
2
 
4
3
  require 'norikra/error'
5
4
 
6
- # Norikra::Field, Norikra::FieldSet, Norikra::Typedef
5
+ require 'norikra/field'
6
+ require 'norikra/fieldset'
7
7
 
8
8
  module Norikra
9
- class Field
10
- attr_accessor :name, :type, :optional
11
-
12
- def initialize(name, type, optional=nil)
13
- @name = name.to_s
14
- @type = self.class.valid_type?(type)
15
- @optional = optional
16
- end
17
-
18
- def to_hash(sym=false)
19
- if sym
20
- {name: @name, type: @type, optional: @optional}
21
- else
22
- {'name' => @name, 'type' => @type, 'optional' => @optional}
23
- end
24
- end
25
-
26
- def dup(optional=nil)
27
- self.class.new(@name, @type, optional.nil? ? @optional : optional)
28
- end
29
-
30
- def ==(other)
31
- self.name == other.name && self.type == other.type && self.optional == other.optional
32
- end
33
-
34
- def optional? # used outside of FieldSet
35
- @optional
36
- end
37
-
38
- ### esper types
39
- ### http://esper.codehaus.org/esper-4.9.0/doc/reference/en-US/html/epl_clauses.html#epl-syntax-datatype
40
- # string A single character to an unlimited number of characters.
41
- # boolean A boolean value.
42
- # integer An integer value (4 byte).
43
- # long A long value (8 byte). Use the "L" or "l" (lowercase L) suffix. # select 1L as field1, 1l as field2
44
- # double A double-precision 64-bit IEEE 754 floating point. # select 1.67 as field1, 167e-2 as field2, 1.67d as field3
45
- # float A single-precision 32-bit IEEE 754 floating point. Use the "f" suffix. # select 1.2f as field1, 1.2F as field2
46
- # byte A 8-bit signed two's complement integer. # select 0x10 as field1
47
- #
48
- #### 'integer' in epser document IS WRONG.
49
- #### If 'integer' specified, esper raises this exception:
50
- ### Exception: Nestable type configuration encountered an unexpected property type name 'integer' for property 'status',
51
- ### expected java.lang.Class or java.util.Map or the name of a previously-declared Map or ObjectArray type
52
- #### Correct type name is 'int'. see and run 'junks/esper-test.rb'
53
- def self.valid_type?(type)
54
- case type.to_s.downcase
55
- when 'string' then 'string'
56
- when 'boolean' then 'boolean'
57
- when 'int' then 'int'
58
- when 'long' then 'long'
59
- when 'float' then 'float'
60
- when 'double' then 'double'
61
- else
62
- raise Norikra::ArgumentError, "invalid field type '#{type}'"
63
- end
64
- end
65
-
66
- def format(value)
67
- case @type
68
- when 'string' then value.to_s
69
- when 'boolean' then value =~ /^(true|false)$/i ? ($1.downcase == 'true') : (!!value)
70
- when 'long','int' then value.to_i
71
- when 'double','float' then value.to_f
72
- else
73
- raise RuntimeError, "unknown field type (in format), maybe BUG. name:#{@name},type:#{@type}"
74
- end
75
- end
76
- end
77
-
78
- class FieldSet
79
- attr_accessor :summary, :fields
80
- attr_accessor :target, :level
81
-
82
- def initialize(fields, default_optional=nil, rebounds=0)
83
- @fields = {}
84
- fields.keys.each do |key|
85
- data = fields[key]
86
- type,optional = if data.is_a?(Hash)
87
- [data[:type].to_s, (data.has_key?(:optional) ? data[:optional] : default_optional)]
88
- elsif data.is_a?(String) || data.is_a?(Symbol)
89
- [data.to_s, default_optional]
90
- else
91
- raise ArgumentError, "FieldSet.new argument class unknown: #{fields.class}"
92
- end
93
- @fields[key.to_s] = Field.new(key.to_s, type, optional)
94
- end
95
- self.update_summary
96
-
97
- @target = nil
98
- @level = nil
99
- @rebounds = rebounds
100
- @event_type_name = nil
101
- end
102
-
103
- def dup
104
- fields = Hash[@fields.map{|key,field| [key, {:type => field.type, :optional => field.optional}]}]
105
- self.class.new(fields, nil, @rebounds)
106
- end
107
-
108
- def self.field_names_key(data, fieldset=nil)
109
- if fieldset
110
- keys = []
111
- fieldset.fields.each do |key,field|
112
- unless field.optional?
113
- keys.push(key)
114
- end
115
- end
116
- data.keys.each do |key|
117
- keys.push(key) unless keys.include?(key)
118
- end
119
- keys.sort.join(',')
120
- else
121
- data.keys.sort.join(',')
122
- end
123
- end
124
-
125
- def field_names_key
126
- self.class.field_names_key(@fields)
127
- end
128
-
129
- def update_summary
130
- @summary = @fields.keys.sort.map{|k| @fields[k].name + ':' + @fields[k].type}.join(',')
131
- self
132
- end
133
-
134
- def update(fields, optional_flag)
135
- fields.each do |field|
136
- @fields[field.name] = field.dup(optional_flag)
137
- end
138
- self.update_summary
139
- end
140
-
141
- #TODO: have a bug?
142
- def ==(other)
143
- return false if self.class != other.class
144
- self.summary == other.summary
145
- end
146
-
147
- def definition
148
- d = {}
149
- @fields.each do |key, field|
150
- d[field.name] = field.type
151
- end
152
- d
153
- end
154
-
155
- def subset?(other) # self is subset of other (or not)
156
- (self.fields.keys - other.fields.keys).size == 0
157
- end
158
-
159
- def event_type_name
160
- @event_type_name.dup
161
- end
162
-
163
- def bind(target, level, update_type_name=false)
164
- @target = target
165
- @level = level
166
- prefix = case level
167
- when :base then 'b_'
168
- when :query then 'q_'
169
- when :data then 'e_' # event
170
- else
171
- raise ArgumentError, "unknown fieldset bind level: #{level}, for target #{target}"
172
- end
173
- @rebounds += 1 if update_type_name
174
-
175
- @event_type_name = prefix + Digest::MD5.hexdigest([target, level.to_s, @rebounds.to_s, @summary].join("\t"))
176
- self
177
- end
178
-
179
- def rebind(update_type_name)
180
- self.dup.bind(@target, @level, update_type_name)
181
- end
182
-
183
- def self.simple_guess(data, optional=true)
184
- mapping = Hash[
185
- data.map{|key,value|
186
- type = case value
187
- when TrueClass,FalseClass then 'boolean'
188
- when Integer then 'long'
189
- when Float then 'double'
190
- else
191
- 'string'
192
- end
193
- [key,type]
194
- }
195
- ]
196
- self.new(mapping, optional)
197
- end
198
-
199
- # def self.guess(data, optional=true)
200
- # mapping = Hash[
201
- # data.map{|key,value|
202
- # sval = value.to_s
203
- # type = case
204
- # when val.is_a?(TrueClass) || val.is_a?(FalseClass) || sval =~ /^(?:true|false)$/i
205
- # 'boolean'
206
- # when val.is_a?(Integer) || sval =~ /^-?\d+[lL]?$/
207
- # 'long'
208
- # when val.is_a?(Float) || sval =~ /^-?\d+\.\d+(?:[eE]-?\d+|[dDfF])?$/
209
- # 'double'
210
- # else
211
- # 'string'
212
- # end
213
- # [key,type]
214
- # }
215
- # ]
216
- # self.new(mapping, optional)
217
- # end
218
- end
219
-
220
9
  # Typedef is
221
- # * known field list of target (and these are optional or not)
10
+ # * known field list of target (and these are optional or not), and container fields
222
11
  # * known field-set list of a target
223
12
  # * base set of a target
224
13
  class Typedef
225
- attr_accessor :fields, :baseset, :queryfieldsets, :datafieldsets
14
+ attr_accessor :fields, :container_fields, :waiting_fields ,:baseset, :queryfieldsets, :datafieldsets
226
15
 
227
16
  def initialize(fields=nil)
17
+ @container_fields = {}
18
+
228
19
  if fields && !fields.empty?
229
- @baseset = FieldSet.new(fields, false) # all fields are required
230
- @fields = @baseset.fields.dup
20
+ given_set = FieldSet.new(fields, false) # default_optional=false, but chained access fields are always optional
21
+ @fields = given_set.fields.dup
22
+ base_fields = {}
23
+ @fields.values.each do |field|
24
+ if !field.optional?
25
+ base_fields[field.name] = field
26
+ elsif field.chained_access?
27
+ cname = field.container_name
28
+ @container_fields[cname] = Norikra::Field.new(cname, field.container_type, true)
29
+ end
30
+ end
31
+ @baseset = FieldSet.new(base_fields, false)
231
32
  else
232
33
  @baseset = nil
233
34
  @fields = {}
234
35
  end
235
36
 
37
+ @waiting_fields = []
38
+
236
39
  @queryfieldsets = []
237
40
  @datafieldsets = []
238
41
 
@@ -242,6 +45,8 @@ module Norikra
242
45
  end
243
46
 
244
47
  def field_defined?(list)
48
+ # used for only queries: TypedefManager#ready?(query) and TypedefManager#generate_field_mapping(query)
49
+ # be not needed to think about containers
245
50
  list.reduce(true){|r,f| r && @fields[f]}
246
51
  end
247
52
 
@@ -256,6 +61,9 @@ module Norikra
256
61
  set.fields[fieldname] = field.dup(false)
257
62
  end
258
63
  @baseset = set
64
+ @baseset.fields.each do |name,f|
65
+ @waiting_fields.delete(name) if @waiting_fields.include?(name)
66
+ end
259
67
  @fields = @baseset.fields.merge(@fields)
260
68
  end
261
69
  end
@@ -264,7 +72,15 @@ module Norikra
264
72
  fieldname = fieldname.to_s
265
73
  @mutex.synchronize do
266
74
  return false if @fields[fieldname]
267
- @fields[fieldname] = Field.new(fieldname, type, optional)
75
+ field = Norikra::Field.new(fieldname, type, optional)
76
+ if @waiting_fields.include?(fieldname)
77
+ @waiting_fields.delete(fieldname)
78
+ end
79
+ @fields[fieldname] = field
80
+ if field.chained_access? && !@container_fields[field.container_name]
81
+ container = Norikra::Field.new(field.container_name, field.container_type, true)
82
+ @container_fields[field.container_name] = container
83
+ end
268
84
  end
269
85
  true
270
86
  end
@@ -272,12 +88,13 @@ module Norikra
272
88
  def consistent?(fieldset)
273
89
  fields = fieldset.fields
274
90
  @baseset.subset?(fieldset) &&
275
- @fields.values.select{|f| !f.optional? }.reduce(true){|r,f| r && fields[f.name] && fields[f.name].type == f.type} &&
276
- fields.values.reduce(true){|r,f| r && (@fields[f.name].nil? || @fields[f.name].type == f.type)}
91
+ @fields.values.select{|f| !f.optional? }.reduce(true){|r,f| r && fields.has_key?(f.name) && fields[f.name].type == f.type} &&
92
+ fields.values.reduce(true){|r,f| r && (!@fields.has_key?(f.name) || @fields[f.name].type == f.type)}
277
93
  end
278
94
 
279
95
  def push(level, fieldset)
280
96
  unless self.consistent?(fieldset)
97
+ warn "fieldset mismatch", :self => self, :with => fieldset
281
98
  raise Norikra::ArgumentError, "field definition mismatch with already defined fields"
282
99
  end
283
100
 
@@ -292,6 +109,7 @@ module Norikra
292
109
  @queryfieldsets.push(fieldset)
293
110
 
294
111
  fieldset.fields.each do |fieldname,field|
112
+ @waiting_fields.delete(fieldname) if @waiting_fields.include?(fieldname)
295
113
  @fields[fieldname] = field.dup(true) unless @fields[fieldname]
296
114
  end
297
115
  end
@@ -301,7 +119,15 @@ module Norikra
301
119
  @set_map[fieldset.field_names_key] = fieldset
302
120
 
303
121
  fieldset.fields.each do |fieldname,field|
304
- @fields[fieldname] = field.dup(true) unless @fields[fieldname]
122
+ if @waiting_fields.include?(fieldname)
123
+ @waiting_fields.delete(fieldname)
124
+ end
125
+ unless @fields[fieldname]
126
+ @fields[fieldname] = field.dup(true)
127
+ if field.chained_access? && !@container_fields[field.container_name]
128
+ @container_fields[field.container_name] = Norikra::Field.new(field.container_name, field.container_type, true)
129
+ end
130
+ end
305
131
  end
306
132
  end
307
133
  else
@@ -329,6 +155,7 @@ module Norikra
329
155
 
330
156
  def replace(level, old_fieldset, fieldset)
331
157
  unless self.consistent?(fieldset)
158
+ warn "fieldset mismatch", :self => self, :with => fieldset
332
159
  raise Norikra::ArgumentError, "field definition mismatch with already defined fields"
333
160
  end
334
161
  if level != :data
@@ -345,11 +172,65 @@ module Norikra
345
172
  true
346
173
  end
347
174
 
348
- def refer(data)
349
- field_names_key = FieldSet.field_names_key(data, self)
175
+ def simple_guess(data, optional=true, strict=false)
176
+ flatten_key_value_pairs = []
177
+
178
+ data.each do |key,value|
179
+ next if strict && !(@fields.has_key?(key) || @waiting_fields.include?(key) || value.is_a?(Hash) || value.is_a?(Array))
180
+
181
+ if value.is_a?(Hash) || value.is_a?(Array)
182
+ Norikra::FieldSet.leaves(value).map{|chain| [key] + chain}.each do |chain|
183
+ value = chain.pop
184
+ key = Norikra::Field.regulate_key_chain(chain).join('.')
185
+ next unless @fields.has_key?(key) || @waiting_fields.include?(key)
186
+ flatten_key_value_pairs.push([key, value])
187
+ end
188
+ else
189
+ flatten_key_value_pairs.push([key, value])
190
+ end
191
+ end
192
+
193
+ mapping = Hash[
194
+ flatten_key_value_pairs.map{|key,value|
195
+ type = case value
196
+ when TrueClass,FalseClass then 'boolean'
197
+ when Integer then 'long'
198
+ when Float then 'double'
199
+ else
200
+ 'string'
201
+ end
202
+ [key,type]
203
+ }
204
+ ]
205
+
206
+ FieldSet.new(mapping, optional)
207
+ end
208
+
209
+ # def self.guess(data, optional=true)
210
+ # mapping = Hash[
211
+ # data.map{|key,value|
212
+ # sval = value.to_s
213
+ # type = case
214
+ # when val.is_a?(TrueClass) || val.is_a?(FalseClass) || sval =~ /^(?:true|false)$/i
215
+ # 'boolean'
216
+ # when val.is_a?(Integer) || sval =~ /^-?\d+[lL]?$/
217
+ # 'long'
218
+ # when val.is_a?(Float) || sval =~ /^-?\d+\.\d+(?:[eE]-?\d+|[dDfF])?$/
219
+ # 'double'
220
+ # else
221
+ # 'string'
222
+ # end
223
+ # [key,type]
224
+ # }
225
+ # ]
226
+ # self.new(mapping, optional)
227
+ # end
228
+
229
+ def refer(data, strict=false)
230
+ field_names_key = FieldSet.field_names_key(data, self, strict, @waiting_fields)
350
231
  return @set_map[field_names_key] if @set_map.has_key?(field_names_key)
351
232
 
352
- guessed = FieldSet.simple_guess(data)
233
+ guessed = self.simple_guess(data, false, strict)
353
234
  guessed_fields = guessed.fields
354
235
  @fields.each do |key,field|
355
236
  if guessed_fields.has_key?(key)
@@ -359,23 +240,25 @@ module Norikra
359
240
  guessed_fields[key] = field unless field.optional?
360
241
  end
361
242
  end
362
- guessed.update_summary
243
+ guessed.update_summary #=> guessed
363
244
  end
364
245
 
365
- def format(data)
366
- # all keys of data should be already known at #format (before #format, do #refer)
367
- ret = {}
368
- data.each do |key, value|
369
- ret[key] = @fields[key].format(value)
246
+ def dump_all
247
+ fields = {}
248
+ @fields.each do |key,field|
249
+ fields[field.name] = field.to_hash(true)
370
250
  end
371
- ret
251
+ fields
372
252
  end
373
253
 
374
- def dump
254
+ def dump # to cli display
375
255
  fields = {}
376
- @fields.map{|key,field|
256
+ @fields.each do |key,field|
257
+ fields[key.to_sym] = field.to_hash(true) unless field.chained_access?
258
+ end
259
+ @container_fields.each do |key, field|
377
260
  fields[key.to_sym] = field.to_hash(true)
378
- }
261
+ end
379
262
  fields
380
263
  end
381
264
  end