spqr 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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