spqr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/spqr/app.rb ADDED
@@ -0,0 +1,299 @@
1
+ # SPQR: Schema Processor for QMF/Ruby agents
2
+ #
3
+ # Application skeleton class
4
+ #
5
+ # Copyright (c) 2009 Red Hat, Inc.
6
+ #
7
+ # Author: William Benton (willb@redhat.com)
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ require 'spqr/spqr'
16
+ require 'qmf'
17
+ require 'logger'
18
+
19
+ module SPQR
20
+ class App < Qmf::AgentHandler
21
+ class ClassMeta < Struct.new(:object_class, :schema_class) ; end
22
+
23
+ def initialize(options=nil)
24
+ defaults = {:logfile=>STDERR, :loglevel=>Logger::WARN}
25
+
26
+ # convenient shorthands for log levels
27
+ loglevels = {:debug => Logger::DEBUG, :info => Logger::INFO, :warn => Logger::WARN, :error => Logger::ERROR, :fatal => Logger::FATAL}
28
+
29
+ options = defaults unless options
30
+
31
+ # set unsupplied options to defaults
32
+ defaults.each do |k,v|
33
+ options[k] = v unless options[k]
34
+ end
35
+
36
+ # fix up shorthands
37
+ options[:loglevel] = loglevels[options[:loglevel]] if loglevels[options[:loglevel]]
38
+
39
+ @log = Logger.new(options[:logfile])
40
+ @log.level = options[:loglevel]
41
+
42
+ @log.info("initializing SPQR app....")
43
+
44
+ @classes_by_name = {}
45
+ @classes_by_id = {}
46
+ end
47
+
48
+ def register(*ks)
49
+ manageable_ks = ks.select {|kl| manageable? kl}
50
+ unmanageable_ks = ks.select {|kl| not manageable? kl}
51
+ manageable_ks.each do |klass|
52
+ @log.info("SPQR will manage registered class #{klass} (#{klass.name})...")
53
+
54
+ schemaclass = schematize(klass)
55
+
56
+ klass.spqr_logger = @log
57
+
58
+ @classes_by_id[klass.class_id] = klass
59
+ @classes_by_name[klass.name] = ClassMeta.new(klass, schemaclass)
60
+ end
61
+
62
+ unmanageable_ks.each do |klass|
63
+ @log.warn("SPQR can't manage #{klass}, which was registered")
64
+ end
65
+ end
66
+
67
+
68
+ def method_call(context, name, obj_id, args, user_id)
69
+ begin
70
+ class_id = obj_id.object_num_high
71
+ obj_id = obj_id.object_num_low
72
+
73
+ @log.debug "calling method: context=#{context} method=#{name} object_id=#{obj_id}, args=#{args}, user=#{user_id}"
74
+
75
+ # Turn the Qmf::Arguments structure into a proper ruby hash
76
+
77
+ # XXX: consider adding appropriate impl method to Manageable
78
+ # to avoid this little dance
79
+ hash_args = qmf_arguments_to_hash(args)
80
+
81
+ managed_object = find_object(context, class_id, obj_id)
82
+ @log.debug("managed object is #{managed_object}")
83
+
84
+ @log.debug("managed_object.respond_to? #{name.to_sym} ==> #{managed_object.respond_to? name.to_sym}")
85
+ managed_object.send(name.to_sym, hash_args)
86
+
87
+ # Copy any out parameters from hash_args from the
88
+ # Qmf::Arguments structure; see XXX above
89
+ hash_args.each do |k,v|
90
+ encoded_val = encode_object(v)
91
+ args[k] = encoded_val
92
+ end
93
+
94
+ @agent.method_response(context, 0, "OK", args)
95
+ rescue Exception => ex
96
+ @log.error "Error calling #{name}: #{ex}"
97
+ @log.error " " + ex.backtrace.join("\n ")
98
+ @agent.method_response(context, 1, "ERROR: #{ex}", args)
99
+ end
100
+ end
101
+
102
+ def get_query(context, query, user_id)
103
+ @log.debug "query: user=#{user_id} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id} details=#{query}"
104
+
105
+ cmeta = @classes_by_name[query.class_name]
106
+ objs = []
107
+
108
+ # XXX: are these cases mutually exclusive?
109
+
110
+ # handle queries for a certain class
111
+ if cmeta
112
+ objs = objs + cmeta.object_class.find_all.collect {|obj| qmfify(obj)}
113
+ end
114
+
115
+ # handle queries for a specific object
116
+ o = find_object(context, query.object_id.object_num_high, query.object_id.object_num_low) rescue nil
117
+ if o
118
+ objs << qmfify(o)
119
+ end
120
+
121
+ objs.each do |obj|
122
+ @log.debug("query_response of: #{obj.inspect}")
123
+ @agent.query_response(context, obj) rescue @log.error($!.inspect)
124
+ end
125
+
126
+ @log.debug("completing query....")
127
+ @agent.query_complete(context)
128
+ end
129
+
130
+ def main
131
+ # XXX: fix and parameterize as necessary
132
+ @log.debug("starting SPQR::App.main...")
133
+
134
+ settings = Qmf::ConnectionSettings.new
135
+ settings.host = 'localhost'
136
+
137
+ @connection = Qmf::Connection.new(settings)
138
+ @log.debug(" +-- @connection created: #{@connection}")
139
+
140
+ @agent = Qmf::Agent.new(self)
141
+ @log.debug(" +-- @agent created: #{@agent}")
142
+
143
+ @agent.set_connection(@connection)
144
+ @log.debug(" +-- @agent.set_connection called")
145
+
146
+ @log.debug(" +-- registering classes...")
147
+ @classes_by_name.values.each do |km|
148
+ @agent.register_class(km.schema_class)
149
+ @log.debug(" +--+-- #{km.schema_class.package_name} #{km.schema_class.class_name} registered")
150
+ end
151
+
152
+ @log.debug("entering orbit....")
153
+ sleep
154
+ end
155
+
156
+ private
157
+
158
+ def qmf_arguments_to_hash(args)
159
+ result = {}
160
+ args.each do |k,v|
161
+ result[k] = v
162
+ end
163
+ result
164
+ end
165
+
166
+ def encode_object(o)
167
+ return o unless o.kind_of? ::SPQR::Manageable
168
+ @agent.alloc_object_id(*(o.qmf_id))
169
+ end
170
+
171
+ def find_object(ctx, c_id, obj_id)
172
+ # XXX: context is currently ignored
173
+ klass = @classes_by_id[c_id]
174
+ klass.find_by_id(obj_id) if klass
175
+ end
176
+
177
+ def schematize(klass)
178
+ @log.info("Making a QMF schema for #{klass}")
179
+
180
+ meta = klass.spqr_meta
181
+ package = meta.package.to_s
182
+ classname = meta.classname.to_s
183
+ @log.info("+-- class #{classname} is in package #{package}")
184
+
185
+ sc = Qmf::SchemaObjectClass.new(package, classname)
186
+
187
+ meta.mmethods.each do |mm|
188
+ @log.info("+-- creating a QMF schema for method #{mm}")
189
+ m_opts = mm.options
190
+ m_opts[:desc] ||= mm.description if mm.description
191
+
192
+ method = Qmf::SchemaMethod.new(mm.name.to_s, m_opts)
193
+
194
+ mm.args.each do |arg|
195
+ @log.info("| +-- creating a QMF schema for arg #{arg}")
196
+
197
+ arg_opts = arg.options
198
+ arg_opts[:desc] ||= arg.description if (arg.description and arg.description.is_a? String)
199
+ arg_opts[:dir] ||= get_xml_constant(arg.direction.to_s, ::SPQR::XmlConstants::Direction)
200
+ arg_name = arg.name.to_s
201
+ arg_type = get_xml_constant(arg.kind.to_s, ::SPQR::XmlConstants::Type)
202
+
203
+ if @log.level <= Logger::DEBUG
204
+ local_variables.grep(/^arg_/).each do |local|
205
+ @log.debug(" #{local} --> #{(eval local).inspect}")
206
+ end
207
+ end
208
+
209
+ method.add_argument(Qmf::SchemaArgument.new(arg_name, arg_type, arg_opts))
210
+ end
211
+
212
+ sc.add_method(method)
213
+ end
214
+
215
+ add_attributes(sc, meta.properties, :add_property, Qmf::SchemaProperty)
216
+ add_attributes(sc, meta.statistics, :add_statistic, Qmf::SchemaStatistic)
217
+
218
+ sc
219
+ end
220
+
221
+ def add_attributes(sc, collection, msg, klass, what=nil)
222
+ what ||= (msg.to_s.split("_").pop rescue "property or statistic")
223
+ collection.each do |basic|
224
+ basic_name = basic.name.to_s
225
+ basic_type = get_xml_constant(basic.kind.to_s, ::SPQR::XmlConstants::Type)
226
+ @log.debug("+-- creating a QMF schema for #{what} #{basic_name} (#{basic_type}) with options #{basic.options.inspect}")
227
+ sc.send(msg, klass.new(basic_name, basic_type, basic.options))
228
+ end
229
+ end
230
+
231
+ def manageable?(k)
232
+ # FIXME: move out of App, into Manageable or a related utils module?
233
+ k.is_a? Class and k.included_modules.include? ::SPQR::Manageable
234
+ end
235
+
236
+ def get_xml_constant(xml_key, dictionary)
237
+ # FIXME: move out of App, into a utils module?
238
+ string_val = dictionary[xml_key]
239
+ return xml_key unless string_val
240
+
241
+ actual_val = const_lookup(string_val)
242
+ return string_val unless actual_val
243
+
244
+ return actual_val
245
+ end
246
+
247
+ # turns a string name of a constant into the value of that
248
+ # constant; returns that value, or nil if fqcn doesn't correspond
249
+ # to a valid constant
250
+ def const_lookup(fqcn)
251
+ # FIXME: move out of App, into a utils module?
252
+ hierarchy = fqcn.split("::")
253
+ const = hierarchy.pop
254
+ mod = Kernel
255
+ hierarchy.each do |m|
256
+ mod = mod.const_get(m)
257
+ end
258
+ mod.const_get(const) rescue nil
259
+ end
260
+
261
+ # turns an instance of a managed object into a QmfObject
262
+ def qmfify(obj)
263
+ @log.debug("trying to qmfify #{obj}: qmf_oid is #{obj.qmf_oid} and class_id is #{obj.class.class_id}")
264
+ cm = @classes_by_name[obj.class.name]
265
+ return nil unless cm
266
+
267
+ qmfobj = Qmf::AgentObject.new(cm.schema_class)
268
+
269
+ set_attrs(qmfobj, obj)
270
+
271
+ @log.debug("calling alloc_object_id(#{obj.qmf_oid}, #{obj.class.class_id})")
272
+ oid = @agent.alloc_object_id(obj.qmf_oid, obj.class.class_id)
273
+
274
+ @log.debug("calling qmfobj.set_object_id(#{oid})")
275
+ qmfobj.set_object_id(oid)
276
+
277
+ @log.debug("returning from qmfify")
278
+ qmfobj
279
+ end
280
+
281
+ def set_attrs(qo, o)
282
+ return unless o.class.respond_to? :spqr_meta
283
+
284
+ attrs = o.class.spqr_meta.properties + o.class.spqr_meta.statistics
285
+
286
+ attrs.each do |a|
287
+ getter = a.name.to_s
288
+ @log.debug("setting property/statistic #{getter} to its value from #{o}: #{o.send(getter) if o.respond_to?(getter)}")
289
+ value = o.send(getter) if o.respond_to?(getter)
290
+ if value
291
+ # XXX: remove this line when/if Manageable includes an
292
+ # appropriate impl method
293
+ value = encode_object(value) if value.kind_of?(::SPQR::Manageable)
294
+ qo[getter] = value
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,434 @@
1
+ # Code generation for SPQR
2
+ #
3
+ # Copyright (c) 2009 Red Hat, Inc.
4
+ #
5
+ # Author: William Benton (willb@redhat.com)
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ require 'spqr/spqr'
14
+
15
+ require 'rexml/document'
16
+ require 'fileutils'
17
+ require 'optparse'
18
+
19
+ module SPQR
20
+
21
+ class SchemaClass
22
+ attr_accessor :name, :package
23
+
24
+ def self.declare(name, package)
25
+ kl = SchemaClass.new(name, package)
26
+ yield kl if block_given?
27
+ kl
28
+ end
29
+
30
+ def declare_property(name, kind, options)
31
+ declare_basic(:property, name, kind, options)
32
+ end
33
+
34
+ def declare_statistic(name, kind, options)
35
+ declare_basic(:statistic, name, kind, options)
36
+ end
37
+
38
+ def declare_method(name, desc, options)
39
+ result = SchemaMethod.new name, desc, options.dup
40
+
41
+ yield result.args if block_given?
42
+ @methods << result
43
+ @methods[-1]
44
+ end
45
+
46
+ def initialize(nm=nil, pkg=nil)
47
+ @name, @package = nm, pkg
48
+ @properties = []
49
+ @statistics = []
50
+ @methods = []
51
+ end
52
+
53
+ def with_each(what)
54
+ source = self.instance_variable_get("@#{what}".to_sym) || []
55
+ source.each do |x|
56
+ yield x if block_given?
57
+ end
58
+ end
59
+
60
+ def member_count(what)
61
+ source = self.instance_variable_get("@#{what}".to_sym) || []
62
+ source.size
63
+ end
64
+
65
+ private
66
+ def declare_basic(what, name, kind, options)
67
+ @wherelocs ||= {:property=>@properties,:statistic=>@statistics}
68
+ where = @wherelocs[what]
69
+
70
+ opts = options.dup
71
+ opts[:index] = true if opts[:index]
72
+ desc = opts.delete(:desc)
73
+
74
+ where << SchemaBasic.new(what, name, desc, kind, opts)
75
+ where[-1]
76
+ end
77
+
78
+ class SchemaBasic
79
+ attr_accessor :name, :kind, :desc, :options
80
+ def initialize(what,nm,desc,knd,opts)
81
+ @what = what
82
+ @name = nm
83
+ @kind = knd
84
+ @options = opts
85
+ @desc = desc.gsub(/\s+/, " ") if desc
86
+ end
87
+
88
+ def property?
89
+ @what == :property
90
+ end
91
+
92
+ def statistic?
93
+ @what == :statistic
94
+ end
95
+ end
96
+
97
+ class SchemaMethod
98
+ attr_accessor :name, :desc, :options, :args
99
+ def initialize(nm,desc,opts=nil)
100
+ @options = (opts && opts.dup) || {}
101
+ @name = nm
102
+ @desc = desc.gsub(/\s+/, " ") if desc
103
+ @args = arg_struct
104
+ end
105
+
106
+ private
107
+ def arg_struct
108
+ @@prototype_args ||= gen_prototype_args
109
+ @@prototype_args.clone
110
+ end
111
+
112
+ def gen_prototype_args
113
+ prototype_args = []
114
+
115
+ def prototype_args.declare(name, kind, dir, desc, options)
116
+ opts = options.dup
117
+
118
+ self << ::SPQR::SchemaClass::SchemaArg.new(name, kind, dir, desc, opts)
119
+ end
120
+
121
+ prototype_args
122
+ end
123
+ end
124
+
125
+ class SchemaArg
126
+ attr_accessor :name, :desc, :kind, :dir, :options
127
+ def initialize(nm, knd, dr, dsc, opts)
128
+ @name = nm
129
+ @desc = dsc.gsub(/\s+/, " ") if dsc
130
+ @kind = knd
131
+ @dir = dr
132
+ @options = opts
133
+ end
134
+
135
+ def inspect
136
+ [:name, :desc, :kind, :dir, :options].map { |sel|"[#{sel}:#{self.send(sel)}]" }.join(" ")
137
+ end
138
+ end
139
+ end
140
+
141
+
142
+ class ModelClassGenerator
143
+ include ::SPQR::PrettyPrinter
144
+
145
+ def ModelClassGenerator.id_registry
146
+ @id_registry ||= {}
147
+ end
148
+
149
+ def ModelClassGenerator.class_registry
150
+ @class_registry ||= {}
151
+ end
152
+
153
+ def initialize(sc)
154
+ @sc = sc
155
+ end
156
+
157
+ def gen
158
+ @package_list = @sc.package.split(".")
159
+ package_dir = "./#{@package_list.join('/')}"
160
+ FileUtils.mkdir_p package_dir
161
+
162
+ filename = "#{$OUTDIR}/#{package_dir}/#{@sc.name}.rb"
163
+ with_output_to filename do
164
+ gen_class
165
+ end
166
+ end
167
+
168
+ private
169
+ def gen_class
170
+ pp "require 'spqr/spqr'"
171
+ pp ""
172
+
173
+ @package_list.map {|pkg| pkg.capitalize}.each do |modname|
174
+ pp "module #{modname}"
175
+ inc_indent
176
+ end
177
+
178
+ pp_decl :class, @sc.name do
179
+ pkgname = (@package_list.map {|pkg| pkg.capitalize}).join("::")
180
+ fqcn = ("#{pkgname}::#{@sc.name}" if pkgname) or @sc.name
181
+
182
+ pp "include ::SPQR::Manageable"
183
+ pp ""
184
+
185
+ pp "spqr_package '#{@package_list.join(".")}'"
186
+ pp "spqr_class '#{@sc.name.split("::")[-1]}'"
187
+
188
+ pp '# Find method (NB: you must implement this)'
189
+ pp_decl :def, "#{@sc.name}.find_by_id", "(objid)" do
190
+ pp "#{@sc.name}.new"
191
+ end
192
+
193
+ pp "\n# Find-all method (NB: you must implement this)"
194
+ pp_decl :def, "#{@sc.name}.find_all" do
195
+ pp "[#{@sc.name}.new]"
196
+ end
197
+
198
+ ModelClassGenerator.id_registry[fqcn.hash] = fqcn
199
+ ModelClassGenerator.class_registry[fqcn] = fqcn.hash
200
+
201
+ pp "\#\#\# Property method declarations" if @sc.member_count(:properties) > 0
202
+ @sc.with_each :properties do |property|
203
+ gen_property property
204
+ end
205
+
206
+ pp "\#\#\# Statistic method declarations" if @sc.member_count(:statistics) > 0
207
+ @sc.with_each :statistics do |statistic|
208
+ gen_statistic statistic
209
+ end
210
+
211
+ pp "\#\#\# Schema method declarations"
212
+ @sc.with_each :methods do |method|
213
+ gen_method method
214
+ end
215
+ end
216
+
217
+ @package_list.size.times do
218
+ dec_indent
219
+ pp "end"
220
+ end
221
+ end
222
+
223
+ def gen_property(property)
224
+ pp ""
225
+ pp "\# property #{property.name} #{property.kind} #{property.desc}"
226
+ pp_decl :def, "#{property.name}" do
227
+ pp "log.debug 'Requested property #{property.name}'"
228
+ pp "nil"
229
+ end
230
+
231
+ pp ""
232
+ pp_decl :def, "#{property.name}=", "(val)" do
233
+ pp "log.debug 'Set property #{property.name} to \#\{val\}'"
234
+ pp "nil"
235
+ end
236
+
237
+ property.options[:desc] = property.desc if property.desc
238
+
239
+ pp ""
240
+ pp "spqr_property #{property.name.to_sym.inspect}, #{property.kind.to_sym.inspect}, #{property.options.inspect.gsub(/[{}]/, '')}"
241
+ end
242
+
243
+ def gen_statistic(statistic)
244
+ pp ""
245
+ pp "\# statistic #{statistic.name}"
246
+ pp_decl :def, "#{statistic.name}" do
247
+ pp "log.debug 'Requested statistic #{statistic.name}'"
248
+ pp "nil"
249
+ end
250
+
251
+ statistic.options[:desc] = statistic.desc if statistic.desc
252
+
253
+ pp ""
254
+ pp "spqr_property #{statistic.name.to_sym.inspect}, #{statistic.kind.to_sym.inspect}, #{statistic.options.inspect.gsub(/[{}]/, '')}"
255
+ end
256
+
257
+ def gen_method(method)
258
+ pp ""
259
+ pp "\# #{method.name} #{method.desc}"
260
+ method.args.each do |arg|
261
+ pp "\# * #{arg.name} (#{arg.kind}/#{arg.dir})"
262
+ pp "\# #{arg.desc}"
263
+ end
264
+
265
+ in_params = method.args.select {|arg| ['in', 'i', 'qmf::dir_in'].include? arg.dir.to_s.downcase }
266
+ out_params = method.args.select {|arg| ['out', 'o', 'qmf::dir_out'].include? arg.dir.to_s.downcase }
267
+ inout_params = method.args.select {|arg| ['inout', 'io', 'qmf::dir_inout'].include? arg.dir.to_s.downcase }
268
+
269
+ pp_decl :def, method.name, "(args)" do
270
+
271
+ if in_params.size + inout_params.size > 0
272
+ what = "in"
273
+
274
+ if in_params.size > 0 and inout_params.size > 0
275
+ what << " and in/out"
276
+ elsif inout_params.size > 0
277
+ what << "/out"
278
+ end
279
+
280
+ pp "\# Print values of #{what} parameters"
281
+ (in_params + inout_params).each do |arg|
282
+ argdisplay = arg.name.to_s.inspect
283
+ pp('log.debug "' + "#{arg.name} => " + '#{args[' + "#{argdisplay}" + ']}"' + " \# #{}")
284
+ end
285
+ end
286
+
287
+ if out_params.size + inout_params.size > 0
288
+ what = "out"
289
+
290
+ if out_params.size > 0 and inout_params.size > 0
291
+ what << " and in/out"
292
+ elsif inout_params.size > 0
293
+ what = "in/out"
294
+ end
295
+
296
+ pp "\# Assign values to #{what} parameters"
297
+
298
+ (out_params + inout_params).each do |arg|
299
+ argdisplay = arg.name.to_s.inspect
300
+ pp "args[#{argdisplay}] = args[#{argdisplay}]"
301
+ end
302
+ end
303
+ end
304
+
305
+
306
+ pp ""
307
+ pp_decl :spqr_expose, "#{method.name.to_sym.inspect} do |args|" do
308
+ {:in => in_params, :inout => inout_params, :out => out_params}.each do |dir,coll|
309
+ coll.each do |arg|
310
+ arg_nm = arg.name
311
+ arg_kd = arg.kind
312
+ arg_opts = arg.options.inspect.gsub(/^[{](.+)[}]$/, '\1')
313
+ pp "args.declare :#{arg_nm}, :#{arg_kd}, :#{dir}, #{arg_opts}"
314
+ end
315
+ end
316
+ end
317
+ end
318
+ end
319
+
320
+ class AppBoilerplateGenerator
321
+ include PrettyPrinter
322
+
323
+ # scs is a list of schemaclass objects, fn is an output filename
324
+ def initialize(scs, fn)
325
+ @scs = scs
326
+ @fn = fn
327
+ end
328
+
329
+ # cc is the name of the variable that will hold a collection of schema classes
330
+ def gen
331
+ with_output_to @fn do
332
+ pp "require 'rubygems'"
333
+ pp "require 'spqr/spqr'"
334
+ pp "require 'spqr/app'"
335
+
336
+ pp ""
337
+
338
+ @scs.each do |sc|
339
+ pp("require '#{sc.package.gsub(/[.]/, '/')}/#{sc.name}'")
340
+ end
341
+
342
+
343
+ pp ""
344
+
345
+ pp "app = SPQR::App.new(:loglevel => :debug)"
346
+
347
+ klass_list = @scs.collect do |sc|
348
+ (sc.package.split(".").collect{|pkg| pkg.capitalize} << sc.name).join("::")
349
+ end
350
+
351
+ pp "app.register #{klass_list.join ','}"
352
+
353
+ pp ""
354
+
355
+ pp "app.main"
356
+ end
357
+ end
358
+ end
359
+
360
+ class QmfSchemaProcessor
361
+ include ::SPQR::MiscUtil
362
+ def initialize(fn)
363
+ @package = nil
364
+ @file = fn
365
+ @doc = nil
366
+ @indent = 0
367
+ @schema_classes = []
368
+ end
369
+
370
+ def main
371
+ File::open(@file, "r") {|infile| @doc = REXML::Document.new(infile)}
372
+
373
+ process_schema
374
+ @schema_classes.each do |klass|
375
+ ModelClassGenerator.new(klass).gen
376
+ end
377
+
378
+ AppBoilerplateGenerator.new(@schema_classes, "#{$OUTDIR}/agent-app.rb").gen
379
+ end
380
+
381
+ private
382
+
383
+ def process_schema
384
+ @package = @doc.root.attributes["package"]
385
+ @package_list = @package.split(".")
386
+
387
+ @package_dir = "#{$OUTDIR}/#{@package_list.join('/')}"
388
+
389
+ FileUtils.mkdir_p @package_dir
390
+
391
+ REXML::XPath.each(@doc.root, "/schema/class") do |elt|
392
+ @schema_classes << process_class(elt)
393
+ end
394
+ end
395
+
396
+ def process_class(elt)
397
+ classname = "#{elt.attributes['name']}"
398
+
399
+ SchemaClass.declare classname, @package do |klass|
400
+ REXML::XPath.each(elt, "property") do |property|
401
+ name = property.attributes['name']
402
+ kind = property.attributes['type']
403
+ opts = symbolize_dict(property.attributes, [:desc, :index, :access, :optional, :min, :max, :maxlen, :unit])
404
+ opts[:index] = (opts[:index].to_s == '1' or opts[:index].to_s.downcase == 'y')
405
+ klass.declare_property name, kind, opts
406
+ end
407
+
408
+ REXML::XPath.each(elt, "statistic") do |statistic|
409
+ name = statistic.attributes['name']
410
+ kind = statistic.attributes['type']
411
+ opts = symbolize_dict(statistic.attributes, [:desc, :unit])
412
+ klass.declare_statistic name, kind, {}
413
+ end
414
+
415
+ REXML::XPath.each(elt, "method") do |method|
416
+ name = method.attributes['name']
417
+ desc = method.attributes['desc']
418
+ klass.declare_method name, desc, {} do |args|
419
+ REXML::XPath.each(method, "arg") do |arg|
420
+ opts = symbolize_dict(arg.attributes, [:name, :type, :refPackage, :refClass, :dir, :unit, :min, :max, :maxlen, :desc, :default, :references])
421
+ name = opts.delete(:name)
422
+ kind = opts.delete(:type)
423
+ dir = opts.delete(:dir)
424
+ desc = opts.delete(:desc)
425
+ args.declare(name, kind, dir, desc, opts)
426
+ end
427
+ end
428
+ end
429
+
430
+ klass
431
+ end
432
+ end
433
+ end
434
+ end