def _build_internal(props1, props2=nil)
_add_metamodel_description(props1)
if props1.many?
_build_many_methods(props1, props2)
else
_build_one_methods(props1, props2)
end
if props2
props1.opposite, props2.opposite = props2, props1
other_class = props1.impl_type
other_class._add_metamodel_description(props2)
raise "Internal error: second description must be a reference description" \
unless props2.reference?
if props2.many?
other_class._build_many_methods(props2, props1)
else
other_class._build_one_methods(props2, props1)
end
end
end
def _build_one_methods(props, other_props=nil)
name = props.value(:name)
other_role = other_props && other_props.value(:name)
if props.value(:derived)
build_derived_method(name, props, :one)
else
@@one_read_builder ||= ERB.new "\ndef get<%= firstToUpper(name) %>\n<% if !props.reference? && props.value(:defaultValueLiteral) %>\n<% defVal = props.value(:defaultValueLiteral) %>\n<% check_default_value_literal(defVal, props) %>\n<% defVal = '\"'+defVal+'\"' if props.impl_type == String %>\n<% defVal = ':'+defVal if props.impl_type.is_a?(DataTypes::Enum) && props.impl_type != DataTypes::Boolean %>\n(defined? @<%= name %>) ? @<%= name %> : <%= defVal %>\n<% else %>\n@<%= name %>\n<% end %>\nend\n<% if name != \"class\" %>\nalias <%= name %> get<%= firstToUpper(name) %>\n<% end %>\n\n"
self::ClassModule.module_eval(@@one_read_builder.result(binding))
end
if props.value(:changeable)
@@one_write_builder ||= ERB.new "\ndef set<%= firstToUpper(name) %>(val)\nreturn if (defined? @<%= name %>) && val == @<%= name %>\n<%= type_check_code(\"val\", props) %>\noldval = @<%= name %>\n@<%= name %> = val\n<% if other_role %>\noldval._unregister<%= firstToUpper(other_role) %>(self) unless oldval.nil? || oldval.is_a?(MMGeneric)\nval._register<%= firstToUpper(other_role) %>(self) unless val.nil? || val.is_a?(MMGeneric)\n<% end %>\n<% if props.reference? && props.value(:containment) %>\nval._set_container(self, :<%= name %>) unless val.nil?\noldval._set_container(nil, nil) unless oldval.nil?\n<% end %>\nend\nalias <%= name %>= set<%= firstToUpper(name) %>\n\ndef _register<%= firstToUpper(name) %>(val)\n<% if other_role %>\n@<%= name %>._unregister<%= firstToUpper(other_role) %>(self) unless @<%= name %>.nil? || @<%= name %>.is_a?(MMGeneric)\n<% end %>\n<% if props.reference? && props.value(:containment) %>\n@<%= name %>._set_container(nil, nil) unless @<%= name %>.nil?\nval._set_container(self, :<%= name %>) unless val.nil?\n<% end %>\n@<%= name %> = val\nend\n\ndef _unregister<%= firstToUpper(name) %>(val)\n<% if props.reference? && props.value(:containment) %>\n@<%= name %>._set_container(nil, nil) unless @<%= name %>.nil?\n<% end %>\n@<%= name %> = nil\nend\n\n"
self::ClassModule.module_eval(@@one_write_builder.result(binding))
end
end
def _build_many_methods(props, other_props=nil)
name = props.value(:name)
other_role = other_props && other_props.value(:name)
if props.value(:derived)
build_derived_method(name, props, :many)
else
@@many_read_builder ||= ERB.new "\ndef get<%= firstToUpper(name) %>\n( @<%= name %> ? @<%= name %>.dup : [] )\nend\n<% if name != \"class\" %>\nalias <%= name %> get<%= firstToUpper(name) %>\n<% end %>\n\n"
self::ClassModule.module_eval(@@many_read_builder.result(binding))
end
if props.value(:changeable)
@@many_write_builder ||= ERB.new "\ndef add<%= firstToUpper(name) %>(val, index=-1)\n@<%= name %> = [] unless @<%= name %>\nreturn if val.nil? || (@<%= name %>.any?{|e| e.object_id == val.object_id} && (val.is_a?(MMBase) || val.is_a?(MMGeneric)))\n<%= type_check_code(\"val\", props) %>\n@<%= name %>.insert(index, val)\n<% if other_role %>\nval._register<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMGeneric)\n<% end %>\n<% if props.reference? && props.value(:containment) %>\nval._set_container(self, :<%= name %>)\n<% end %>\nend\n\ndef remove<%= firstToUpper(name) %>(val)\n@<%= name %> = [] unless @<%= name %>\n@<%= name %>.each_with_index do |e,i|\nif e.object_id == val.object_id\n@<%= name %>.delete_at(i)\n<% if props.reference? && props.value(:containment) %>\nval._set_container(nil, nil)\n<% end %>\n<% if other_role %>\nval._unregister<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMGeneric)\n<% end %>\nreturn\nend\nend\nend\n\ndef set<%= firstToUpper(name) %>(val)\nreturn if val.nil?\nraise _assignmentTypeError(self, val, Enumerable) unless val.is_a? Enumerable\nget<%= firstToUpper(name) %>.each {|e|\nremove<%= firstToUpper(name) %>(e)\n}\nval.each {|v|\nadd<%= firstToUpper(name) %>(v)\n}\nend\nalias <%= name %>= set<%= firstToUpper(name) %>\n\ndef _register<%= firstToUpper(name) %>(val)\n@<%= name %> = [] unless @<%= name %>\n@<%= name %>.push val\n<% if props.reference? && props.value(:containment) %>\nval._set_container(self, :<%= name %>)\n<% end %>\nend\n\ndef _unregister<%= firstToUpper(name) %>(val)\n@<%= name %>.delete val\n<% if props.reference? && props.value(:containment) %>\nval._set_container(nil, nil)\n<% end %>\nend\n\n"
self::ClassModule.module_eval(@@many_write_builder.result(binding))
end
end
private
def build_derived_method(name, props, kind)
raise "Implement method #{name}_derived instead of method #{name}" \
if (public_instance_methods+protected_instance_methods+private_instance_methods).include?(name)
@@derived_builder ||= ERB.new "\ndef get<%= firstToUpper(name) %>\nraise \"Derived feature requires public implementation of method <%= name %>_derived\" \\\nunless respond_to?(:<%= name+\"_derived\" %>)\nval = <%= name %>_derived\n<% if kind == :many %>\nraise _assignmentTypeError(self,val,Enumerable) unless val && val.is_a?(Enumerable)\nval.each do |v|\n<%= type_check_code(\"v\", props) %>\nend\n<% else %>\n<%= type_check_code(\"val\", props) %>\n<% end %>\nval\nend\n<% if name != \"class\" %>\nalias <%= name %> get<%= firstToUpper(name) %>\n<% end %>\n#TODO final_method :<%= name %>\n\n"
self::ClassModule.module_eval(@@derived_builder.result(binding))
end
def check_default_value_literal(literal, props)
return if literal.nil? || props.impl_type == String
if props.impl_type == Integer
unless literal =~ /^\d+$/
raise StandardError.new("Property #{props.value(:name)} can not take value #{literal}, expected an Integer")
end
elsif props.impl_type == Float
unless literal =~ /^\d+\.\d+$/
raise StandardError.new("Property #{props.value(:name)} can not take value #{literal}, expected a Float")
end
elsif props.impl_type == RGen::MetamodelBuilder::DataTypes::Boolean
unless ["true", "false"].include?(literal)
raise StandardError.new("Property #{props.value(:name)} can not take value #{literal}, expected true or false")
end
elsif props.impl_type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum)
unless props.impl_type.literals.include?(literal.to_sym)
raise StandardError.new("Property #{props.value(:name)} can not take value #{literal}, expected one of #{props.impl_type.literals_as_strings.join(', ')}")
end
else
raise StandardError.new("Unkown type "+props.impl_type.to_s)
end
end
def type_check_code(varname, props)
code = ""
if props.impl_type.is_a?(Class)
code << "unless #{varname}.nil? || #{varname}.is_a?(#{props.impl_type}) || #{varname}.is_a?(MMGeneric)"
code << " || #{varname}.is_a?(BigDecimal)" if props.impl_type == Float && defined?(BigDecimal)
code << "\n"
expected = props.impl_type.to_s
elsif props.impl_type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum)
code << "unless #{varname}.nil? || [#{props.impl_type.literals_as_strings.join(',')}].include?(#{varname}) || #{varname}.is_a?(MMGeneric)\n"
expected = "["+props.impl_type.literals_as_strings.join(',')+"]"
else
raise StandardError.new("Unkown type "+props.impl_type.to_s)
end
code << "raise _assignmentTypeError(self,#{varname},\"#{expected}\")\n"
code << "end"
code
end
def _ownProps(props)
Hash[*(props.select{|k,v| !(k.to_s =~ /^opposite_/)}.flatten)]
end
def _oppositeProps(props)
r = {}
props.each_pair do |k,v|
if k.to_s =~ /^opposite_(.*)$/
r[$1.to_sym] = v
end
end
r
end
def _setManyUpperBound(props)
props[:upperBound] = -1 unless props[:upperBound].is_a?(Integer) && props[:upperBound] > 1
props
end
end
end