pax_global_header 0000666 0000000 0000000 00000000064 14560401624 0014514 g ustar 00root root 0000000 0000000 52 comment=1ebaf70ce25c32d85e87f5c7b1287a3a4f551ac5
rgen-0.10.2/ 0000775 0000000 0000000 00000000000 14560401624 0012527 5 ustar 00root root 0000000 0000000 rgen-0.10.2/.gitignore 0000664 0000000 0000000 00000000315 14560401624 0014516 0 ustar 00root root 0000000 0000000 .eprj
test/metamodel_roundtrip_test/TestModel_Regenerated.rb
test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore
test/model_builder/ecore_internal.rb
test/testmodel/ea_testmodel_regenerated.xml
rgen-0.10.2/.project 0000664 0000000 0000000 00000000607 14560401624 0014201 0 ustar 00root root 0000000 0000000
rgen-head
org.rubypeople.rdt.core.rubybuilder
org.rubypeople.rdt.core.rubynature
rgen-0.10.2/CHANGELOG 0000775 0000000 0000000 00000023767 14560401624 0013763 0 ustar 00root root 0000000 0000000 =0.1.0 (August 3rd, 2006)
* First public release
=0.2.0 (September 3rd, 2006)
* Added model transformation language (Transformer)
* Now RGen is distributed as a gem
* More complete documentation
=0.3.0 (October 9th, 2006)
* Improved XML Instantiator (Namespaces, Resolver, Customization)
* Added many_to_one builder method
* Added attribute reflection to MMBase (one_attributes, many_attributes)
* Added +copy+ method to Transformer
* Added simple model dumper module
* Fixed mmgen/mmgen.rb
=0.4.0 (Aug 8th, 2007)
* Added ECore metamodel and use it as the core metametamodel
* Revised and extended MetamodelBuilder language
* There is an ECore instance describing each metamodel built using MetamodelBuilder now
* Metamodel generator is now ECore based
* Added Ruby implementation of Boolean and Enum types
* Switched XML Instantiator to xmlscan for performance reasons
* Cleaned up instantiator file structure
* Renamed RGen::XMLInstantiator into RGen::Instantiator::DefaultXMLInstantiator
* Included xmlscan as a redistributed module
* Added support for chardata within XML tags
* Added (Enterprise Architect) XMI to ECore instantiator
* Some minor fixes in NameHelper
* Some fixes to template language
* Added UML1.3 Metamodel
* Added tranformation from UML1.3 to ECore
=0.4.1 (Nov 25th, 2007)
* Template language performance improvement
* Bugfix: use true/false instead of symbols for boolean attribute default values in metamodel classes
* Minor fixes on metamodel generator and ecore primitive type handling
* Made transformer implementation non-recursive to prevent "stack level too deep" exception for large models
* Minor fixes on EAInstantiator
* Made transformer search for matching rules for superclasses
* Bugfix: Enums are now added to EPackages created using the "ecore" method on a module
* Bugfix: Metamodel generator now writes enum names
* Performance improvement: don't require ecore transformer every time someone calls "ecore"
* Major performance improvement of template engine (no Regexps to check \n at end of line)
* Major performance improvement: AbstractXMLInstantiator optionally controls the garbage collector
* Major performance improvement: ERB templates are reused in metamodel_builder
* Added delete method to Environment
=0.4.2 (Mar 2nd, 2008)
* Performance improvement: collection feature of array extension uses hashes now to speed up array union
* Performance improvement: find on environment hashes elements by class
* Extended Transformer to allow sharing of result maps between several Transformer instances
* Bugfix: User defined upper bound values are no longer overwritten by -1 in all "many" metamodel builder methods
=0.4.3 (Aug 12th, 2008)
* Performance improvement: significant speed up of metamodel reverse registration
* Bugfix: Use object identity for metamodel to-many add/remove methods
* Bugfix: If expand's :for expression evaluates to nil an error is generated (silently used current context before)
* Template language indentation string can be set on DirectoryTemplateContainer and with the "file" command
=0.4.4 (Sep 10th, 2008)
* Added "abstract" metamodel DSL command
* Added ecore_ext.rb with convenience methods
* Added XMI1.1 serializer, revised XMLSerializer super class
=0.4.5 (Nov 17th, 2008)
* Updated XMI1.1 serializer to support explicit placement of elements on content level of the XMI file
=0.4.6 (Mar 1st, 2009)
* Bugfix: expand :foreach silently assumed current context if :foreach evalutated to nil
* Bugfix: fixed unit test for non-Windows plattforms (\r\n)
* Bugfix: depending on the Ruby version and/or platform constants used in templates could not be resolved
* Added automatic line ending detection (\n or \r\n) for template language +nl+ command
=0.5.0 (Jun 8th, 2009)
* Added ModelBuilder and ModelSerializer
* Added template language "define_local" command
* Added template language "evaluate" command
* Fixed template language bug: indentation problem when expand continues a non-empty line
* Fixed template language bug: template content expands several times when a template container is called recursively
* Fixed template language bug: template resolution problem if a template file has the same name as a template directory
* Cleaned up EA support
* Added method to clear ecore metamodel reflection cache
* Improved overriding of metamodel features in reopened classes
=0.5.1 (Nov 10th, 2009)
* Fixed metamodel builder bug: _register at one-side did not unregister from the element referenced by the old value
* Added helper class for building simple model comparators
=0.5.2 (Jun 13th, 2010)
* Added has_many_attr to metamodel builder, support for "many" attributes
* Added JSON support (json instantiator and serializer)
* Added QualifiedNameResolver instantiation helper
* Added reference proxy support
* Added more generic access methods on metaclasses
* Added ReferenceResolver resolver mixin
* Fixed ecore xml instantiator and serializer to handle references to builtin datatypes correctly
* Fixed bug in ecore xml serializer to not output references which are opposites of containment references
=0.5.3 (Aug 13th, 2010)
* Fixed string escaping in JSON instantiator and serializer
* Fixed order of eClassifiers and eSubpackages within an EPackage created by reflection on a RGen module
=0.5.4
* Fixed undeterministic order of child elements in ModelSerializer
* Fixed undeterministic order of attributes in XMI serializers
* Fixed ModelSerializer to always serialize the to-one part of bidirectional 1:N references
* Fixed ModelSerializer to add :as => in case of ambiguous child roles
* Made JsonInstantiator search subpackages for unqualified class names
=0.6.0
* Added exception when trying to instantiate abstract class
* Replaced xmlscan by dependency to nokogiri
* Made RGen work with Ruby 1.9
* Cleaned up intermediate attribute and reference description, improvement of metamodel load time
* Added optional data property for MMProxy
* Added ECoreToRuby which can create Ruby classes and modules from ECore models in memory (without metamodel generator)
* Refactored out QualifiedNameProvider and OppositeReferenceFilter
* Added model fragment/fragmented models support
* Extended Instantiator::ReferenceResolver and changed it into a class
* Moved utilities into util folder/module
* Added FileCacheMap
* Fixed template language bug: indenting not correct after callback into same template container and iinc/idec
* Added support for fragmented models
* Added FileChangeDetector utility
* Added CachedGlob utility
* Added index parameter to model element add methods
* Added MMGeneric
* Modified has_many_attr to allow the same value in the same attribute multiple times
* Made Environment#delete faster on large models
* Added type check of ecore defaultValueLiteral content in MetamodelBuilder
* Many-feature setters can work with an Enumerable instead of an Array
* Added pattern matcher utility
* Fixed problem of Ruby hanging when exceptions occur
* Fixed metamodel generator to quote illegal enum literal symbols
* Imporved UML to ECore transformer and EA support
=0.6.1
* Fixed metamodel builder to not overwrite a model element's 'class' method
* Added enum type transformation to ECoreToUML13 transformer, primitive type mapping based on instanceClassName
* Fixed default value appearing on read after setting a feature value to nil
* Added eIsSet and eUnset methods
* Added eContainer and eContainingFeature methods
* Fixed ModelFragment#elements not containing root elements
* Added optional output of invalidation reason to FileCacheMap#load_data
=0.6.2
* Made qualified name provider work with unidirectional containment references
* Fixed array_extension breaking the Hash[] method
=0.6.3
* Added BigDecimal support
=0.6.4
* Made FileChangeDetector and FileCacheMap robust against missing files
=0.6.5
* Fixed missing default argument of FragmentedModel#resolve
* Added to_str to methods which aren't forwarded by array extension on empty arrays
=0.6.6
* Added ModelFragment#mark_resolved and ResolutionHelper
* Added ReferenceResolver option to output failed resolutions
* Major performance improvement of FragmentedModel#resolve
* Fixed a Ruby 2.0 related warning
=0.7.0
* Enforce unique container rule by automatically disconnecting elements from other containers
* Added support for long typed values (ELong), thanks to Thomas Hallgren;
Note that this is merely an EMF compatibility thing, RGen could already handle big integers before
* Added eContents and eAllContents methods
* Added setNilOrRemoveGeneric and setNilOrRemoveAllGeneric methods
* Added disconnectContainer method
=0.8.0
* Fixed missing indentation when template file is not terminated by a newline
* Fixed missing indentation when expand in same line expands sub templates
* Fixed DefaultXMLInstantiator naming error with a tag named 'File' (issue #19, pull request #21 from jkugs)
* Simplified ECoreToRuby and optionally let it create modules with non-temporary names
* Improved performance of output handler
* Improved performance of setXXX and addXXX methods (pull request #22 from thallgren)
* Use a value larger than Fixnum max to test Bignum support (pull request #18 from graaff)
=0.8.1
* Improved performance of ECoreToRuby
* Fixed <%ws%> command to trigger indentation if starting a new line
=0.8.2
* Added helper methods
=0.8.3
* Performance improvement: getGeneric made a lot faster
=0.8.4
* Add early loading for types which have attributes conflicting with Ruby reserved words.
* Change type checking code to use ObjectSpace to find class objects. This is to find classes with an unbound name.
=0.9.0
* Update to support Ruby 2.7, drop support for older Rubies
=0.9.1
* fixes in ecore model (make ENamedElement required; set default for EClass::{abstract, interface})
=0.10.0
* Added support for Rubies 3.0, 3.1, 3.2
=0.10.1
* Updated dependencies
=0.10.2
* Fixed issue with Kernel methods name clash with features
rgen-0.10.2/Gemfile 0000664 0000000 0000000 00000005062 14560401624 0014025 0 ustar 00root root 0000000 0000000 # THIS FILE IS AUTOGENERATED, DO NOT EDIT
require 'yaml'
require 'pathname'
def source_gems(gem_source, gems)
workspace_config_path = File.join(File.dirname(__FILE__), '..', 'Workspace.yaml')
workspace = File.file?(workspace_config_path) ? YAML.load_file(workspace_config_path) : {}
abort 'Workspace syntax error' unless workspace.is_a?(Hash)
gems.each do |gem_args|
found = false
gem_args = [gem_args] if gem_args.is_a?(String)
name = gem_args.first
begin gem *(gem_args << {:source => gem_source}); next end if workspace.nil?
env = (workspace['environment'] || 'production')
((workspace['common'] || []) + ((workspace['environments'] || {})[env] || [])).each do |source_info|
(source_info['gems'] || []).each do |gem_info|
next unless name == (gem_info.is_a?(String) ? gem_info : gem_info['name'])
if !source_info['url'].nil?
gem *(gem_args << {:source => source_info['url']})
elsif !source_info['path'].nil?
path = gem_info['path'] || name
source_path = Pathname(source_info['path']).absolute? ? source_info['path'] : File.join('..', source_info['path'])
gem_path = Pathname(path).absolute? ? path : File.join(source_path, path)
Dir.chdir(gem_path) { abort "Can't bundle dependency from #{gem_path}" unless system('bundle') }
gem *(gem_args << {:path => gem_path})
elsif !source_info['git'].nil?
gem *(gem_args << {:git => (source_info['git'] + '/' + (gem_info['path'] || name)), :ref => gem_info['ref'],
:branch => gem_info['branch'], :tag => gem_info['tag']})
else
abort "Unknown source type #{source_info.to_s}"
end
found = true
break
end
break if found
end
gem *(gem_args << {:source => gem_source}) unless found
end
end
require 'yaml'
project = YAML.load(File.read(File.join(File.dirname(__FILE__), 'Project.yaml')))
deps = project['dependencies'] || {}
deps =
if deps.is_a?(Hash)
deps
else
{'http://rubygems.org' => deps}
end
is_gem = project['is_gem'].nil? ? true : project['is_gem']
deps.each do |source, gems|
source_gems(source, gems.map do |d|
if d.is_a?(String)
d
else
if is_gem
d['name']
else
dep_args = [d['name']]
dep_args.concat(if d['version'].nil?; []; else d['version'].is_a?(String) ? [d['version']] : d['version'] end)
dep_args
end
end
end)
end
if is_gem
source "https://rubygems.org"
gemspec :name => project['name'], :path => File.dirname(__FILE__)
end
rgen-0.10.2/MIT-LICENSE 0000664 0000000 0000000 00000002041 14560401624 0014160 0 ustar 00root root 0000000 0000000 Copyright (c) 2013 Martin Thiede
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
rgen-0.10.2/Project.yaml 0000775 0000000 0000000 00000002345 14560401624 0015030 0 ustar 00root root 0000000 0000000 name: rgen
gemspec: rgen.gemspec
git: https://github.com/mthiede/rgen.git
version: 0.10.2
summary: Ruby Modelling and Generator Framework
email: martin dot thiede at gmx de
homepage: http://ruby-gen.org
rubyforge_project: rgen
description: RGen is a framework for Model Driven Software Development (MDSD) in Ruby. This means that it helps you build Metamodels, instantiate Models, modify and transform Models and finally generate arbitrary textual content from it.
authors: [Martin Thiede]
rdoc_options: [--main, README.rdoc, -x, test, -x, metamodels, -x, ea_support/uml13*]
extra_rdoc_files: [README.rdoc, CHANGELOG, MIT-LICENSE]
include_files: ['lib/**/*', 'test/**/*', README.rdoc, CHANGELOG, MIT-LICENSE, Rakefile]
exclude_files: ['**/*.bak']
encrypt_sources: false
dependencies:
https://rubygems.org:
- {name: nokogiri, version: ['>= 1.15.4', '< 1.16'], development: true}
- {name: rake, version: ['>= 13.0.0', '< 14.0'], development: true}
- {name: minitest, version: ['>= 5.20.0', '< 6.0'], development: true}
- {name: minitest-fail-fast, version: ['>= 0.1.0', '< 0.2'], development: true}
- {name: andand, version: ['>= 1.3.3', '< 1.4'], development: true}
rgen-0.10.2/README.rdoc 0000664 0000000 0000000 00000004074 14560401624 0014342 0 ustar 00root root 0000000 0000000 = RGen - Ruby Modelling and Generator Framework
RGen is a framework for Model Driven Software Development (MDSD)in Ruby.
This means that it helps you build Metamodels, instantiate Models, modify
and transform Models and finally generate arbitrary textual content from it.
RGen features include:
* Supporting Ruby 1.8.7, 1.9.x, 2.0, 2.1, 2.2
* Metamodel definition language (internal Ruby DSL)
* ECore Meta-metamodel with an ECore instance available for every Metamodel
* Generator creating the Ruby metamodel definition from an ECore instance
* Transformer creating Ruby metamodel classes/modules from an ECore instance
* Instantiation of Metamodels, i.e. creation of Models (e.g. from XML)
* Model builder, internal Ruby DSL
* Model fragmentation over several several files and per-fragment caching
* Model Transformation language (internal Ruby DSL)
* Powerful template based generator language (internal Ruby DSL inside of ERB)
* UML 1.3 metamodel and XMI 1.1 instantiator included
* ECore XML support (XMI 2.0)
* UML-to-ECore and ECore-to-UML transformation (UML class models)
* Enterprise Architect support (UML1.3/XMI1.1)
== Download
Get the latest release from Github: https://github.com/mthiede/rgen
== Installation
Install RGen as a Ruby gem:
gem install rgen
== Running the Tests
Change to the 'test' folder and run the test suite:
cd test
ruby rgen_test.rb
== Documentation
RDoc documentation is available at Github: http://mthiede.github.com/rgen/
Find the main documentation parts for:
* RGen::MetamodelBuilder
* RGen::Transformer
* RGen::TemplateLanguage
* RGen::Fragment::FragmentedModel
== Examples
There are several examples of using RGen within the framework itself.
Metamodel Definition:
lib/rgen/ecore/ecore.rb
lib/metamodels/uml13_metamodel.rb
Instantiation:
lib/rgen/instantiator/xmi11_instantiator.rb
lib/rgen/instantiator/ecore_xml_instantiator.rb
Transformations:
lib/rgen/ecore/ruby_to_ecore.rb
lib/transformers/uml13_to_ecore.rb
Generators:
lib/mmgen/metamodel_generator.rb
== License
RGen is released under the MIT license.
rgen-0.10.2/Rakefile 0000664 0000000 0000000 00000001752 14560401624 0014201 0 ustar 00root root 0000000 0000000 require 'rubygems/package_task'
require 'rdoc/task'
require 'rake/testtask'
RGenGemSpec = eval(File.read('rgen.gemspec'))
RDoc::Task.new do |rd|
rd.main = 'README.rdoc'
rd.rdoc_files.include('README.rdoc', 'CHANGELOG', 'MIT-LICENSE', 'lib/**/*.rb')
rd.rdoc_files.exclude('lib/metamodels/*')
rd.rdoc_files.exclude('lib/ea_support/uml13*')
rd.rdoc_dir = 'doc'
end
RGenPackageTask = Gem::PackageTask.new(RGenGemSpec) do |p|
p.need_zip = false
end
::Rake::TestTask.new(:test) do |t|
t.test_files = ['test/rgen_test.rb']
t.warning = false
end
task :prepare_package_rdoc => :rdoc do
RGenPackageTask.package_files.include('doc/**/*')
end
task :release => [:prepare_package_rdoc, :package]
task :clobber => [:clobber_rdoc, :clobber_package]
task :ecore_to_json do
require 'rgen/ecore/ecore_to_json'
exporter = RGen::ECore::ECoreToJson.new
File.write('ecore.json', exporter.epackage_to_json_string(RGen.ecore, exporter.ecore_datatypes))
end
rgen-0.10.2/TODO 0000664 0000000 0000000 00000004327 14560401624 0013225 0 ustar 00root root 0000000 0000000 =Known Bugs
* <% expand ... :indent => 0 %> seems to change behaviour of active template not only expanded subtemplate
* Ecore build in types (EString, ...) do not work in ECore instantiator, define your own EDatatype instead
* ECore datatypes in RGen::ECore should use Java like instanceClassNames
* overloading of transformation rules not working correctly
* with \r\n in templates, empty lines appear in output
* <%nl%> after <%nows%> creates no indentation (<%nl%> in another template in same file)
=Major issues
* XML instantiator documentation
* revise builder datatypes, especially enum implementation using Enum objects as types,
also revise ecore metamodel at this point
* revise documentation of BuilderExtensions
* further cleanup EA UML import/export
- The differences between EA UML and uml13_metamodel.rb seem to be violations by EA, ArgoUML follows the standard much more closely
- Enums should be instances of Enumeration class with EnumerationLiterals (UML Standard),
for EA convert to Classes with stereotype "enumeration" and attributes as literals
(this is what EA 7 creates when clicking on the "New Enumeration" button, EA will reference these classes as type)
This is whats missing for Pragma MM generators.
- Support primitive types as instances of DataType (which basically have a name) instead of tagged values
(this should also be working with EA 7, the tagged values are just add on)
- Support more UML metamodel features in the transformers
* Model Serializer:
- make "name" attribute configurable
- convert chars in string into something Ruby compatible (e.g newline to \n)
=Minor Issues
* allow definition of templates from within regular code
* indexed find in environment
* XMI Instantiator fixmap: add element names to make feature names unique
* no error for expand '..', :forach => (foreach misspelled)
* With JRuby (1.3.1) exceptions raised in templates have a short or no backtrace
* extended constraint checks (feature bounds)
* class filter in RText language
* root classes for RText language
* command/class aliases in RText language
* language variants (different root classes depending on file type)
* reference name in reference_qualifier
rgen-0.10.2/anounce.txt 0000664 0000000 0000000 00000011306 14560401624 0014721 0 ustar 00root root 0000000 0000000 =RGen - Ruby Modelling and Generator Framework
RGen is a framework to support the "Model Driven Software Development (MDSD)" approach. Some people may want to call just about the same thing "Domain Specific Languages".
The essence is to have a Metamodel which imposes a structure as well as certain other constraints on the Models to be instantiated from it. The Metamodel can be part of the software which is being developed.
From a formal language point of view, the metamodel could be regarded as a grammer with the model being a sentence of the grammer.
One possible application of MDSD are large domain specific software systems like banking systems. In this case one would define a metamodel which reflects the application domain (e.g. accounts, customers, their relations, ...).
The metamodel can then serve as a common means of communication between users from the application domain (e.g. the customer) and software developers, as well as between software developers working on different subsystems. In addition, the metamodel can be used to generate recurring parts of the software instead of writing it by hand
(e.g. database interfaces, application server glue code, etc). In a particular project a lot more usescases will typically show up.
A very good framework implementing the MDSD approach is the open source Java framework OpenArchitectureWare (http://www.openarchitectureware.org/). Actually OpenArchitectureWare inspired the development of RGen.
RGen implements many features also provided by OpenArchitectureWare:
* Programmatic (textual) definition of Metamodels
* Instantiation of models from various sources (XML, UML Models, ...)
* Support of Model Transformations
* Powerful template language (based on ERB) to generate arbitrary textual content
In contrast to the mentioned Java framework, RGen is a more lightweight approach.
It can be used to write powerful generator or transformation scripts quickly.
However I believe RGen can also be useful in large software development projects.
Here are some example usecases of RGen:
Example 1: Generating C Code from a XML description
* Directly instantiate the XML file using RGen's XMLInstantiator
An implicit Metamodel (Ruby classes) will be created automatically
The model is now available as Ruby objects in memory
* Use the template language to navigate the instantiated model and generate C code
Example 2: UML Defined Application specific Metamodel
* Specify your metamodel as a UML Class Diagram
* If the tool you use is Enterprise Architect, the UML Class Diagram can directly be instantiated using RGen's XMIClassInstantiator
* If not, support for your tool should be added. The existing XMI instantiator is basically a transformation from an XMI model to an UML Class model. For a tool producing different XMI output the transformation has to be adapted.
* Use the included MetamodelGenerator to generate Ruby classes from the UML model. These classes act as your application specific metamodel. Of course the generated classes can be extended by hand written code, either by subclassing or by just adding methods using Ruby's open classes. The generated code itself should not be touched.
* Extend RGen with an own instantiator which reads your specific file format and instantiates your application specific metamodel
* Then go on doing transformations on your model(s) or generate output.
RGen could also be useful in combination with Rails.
One application to Rails could be to generate not only the model, view and controller classes, but also the database schemas and code reflecting associations between Rails model elements (i.e. the has_many, belongs_to, .. code in the ActiveRecord subclasses)
The base model for such a generation could be a description of the model elements and its associations as an UML class model.
Another application could be to base new Rails generators (like the Login generator, etc) on RGen.
=Major Performance Improvement
RGen 0.4.1 features major performance improvements.
All applications using the template language will be faster now.
Applications using the AbstractXMLInstantiator can benefit if explicit garbage collection is enabled.
(see documentation of AbstractXMLInstantiator)
Another improvement makes the "ecore" class method of classes and modules much faster.
Last but not least the loading of metamodels takes about half the time as before.
For large models the metamodel generator is about 20 times faster.
Reading a 50 000 lines ecore file now takes 9 seconds on a Centrino Duo 2GHz (85s with RGen 0.4.0)
Generating the RGen metamodel code for the ecore model takes 5 seconds (200s with RGen 0.4.0)
Requiring this RGen metamodel takes about 3 seconds (about 6s with RGen 0.4.0).
rgen-0.10.2/design_rationale.txt 0000664 0000000 0000000 00000005477 14560401624 0016614 0 ustar 00root root 0000000 0000000 =ElementSet vs. Array
Subject:
Use a special array-like class "ElementSet" with the following properties:
* can call methods of elements by . notation
* can use all set and enumerable methods of Array
* enforce constraints regarding type of elements
* auto register/unregister with counterpart of an association
Dependencies:
* Without the constraint and register/unregister functionality of the ElementSet,
the API of model elements built by MetamodelBuilder has to be different:
instead of "e.myelements << newel" would be "e.addMyelements(newel)"
However this can also be an advantage (see Metamodel Many Assoc API)
A1. ElementSet:
+ nice notation for calling methods of elements (.)
+ nice notation for adding/removing elements from a model element
(e.myelements << newel; e.myelements.delete newel)
- complicated to realize
if ElementSet inherits from Array:
constraints/registration can not be garanteed for all add/remove operations
input and output of Array methods must be wrapped into ElementSet objects
if ElementSet delegates to an Array:
all (relevant) methods have to be delegated (methods from including
Enumerable do not automatically return ElementSet objects)
- dot notation for calling methods of elements my lead to errors which are difficult
to find
A2. Array:
+ a separate operator like >> makes calling methods of elements more explicit
+ very easy to implement
+ easy to understand by users (no "magic" going on)
Decision: (2006-06-08)
A2. Array
Simplicity of implementation and ease of use are more important than a nice notation
= Metamodel Many Assoc API
Subject:
How to implement the API to deal with to-many associations of model elements.
One option is an array like object which is held by the model element for each to-many
association and which is given to the user for modification (external array).
The other option is an internal array which is only accessed via add and remove
methods
Dependencies:
If an external array is used, this array must check the association's constraints
and register/unregister with the other side of the association.
(see ElementSet vs. Array)
A1.External Array
+ nice API (e.myassocs << newel; e. myassocs.delete newel)
+ this is a Rails like API
- a reference to the array might be stored somewhere else in the program and
accidentially be modified, this would modify the model element it belongs to
as well as register/unregister with other model elements leading to errors
which are hard to find
- an external array is complicated to implement (see ElementSet vs. Array)
A2.Internal Array
+ easy to understand for non Ruby/Rails aware users
+ simple implementation
Decision: (2006-06-09)
A2. Internal Array
Simplicity of implementation and ease of use are more important than a nice notation
rgen-0.10.2/lib/ 0000775 0000000 0000000 00000000000 14560401624 0013275 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/ea_support/ 0000775 0000000 0000000 00000000000 14560401624 0015456 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/ea_support/ea_support.rb 0000664 0000000 0000000 00000003324 14560401624 0020166 0 ustar 00root root 0000000 0000000 require 'ea_support/uml13_ea_metamodel'
require 'ea_support/uml13_ea_metamodel_ext'
require 'ea_support/uml13_to_uml13_ea'
require 'ea_support/uml13_ea_to_uml13'
require 'ea_support/id_store'
require 'rgen/serializer/xmi11_serializer'
require 'rgen/instantiator/xmi11_instantiator'
require 'rgen/environment'
module EASupport
FIXMAP = {
:tags => {
"EAStub" => proc { |tag, attr|
UML13EA::Class.new(:name => attr["name"]) if attr["UMLType"] == "Class"
}
}
}
INFO = XMI11Instantiator::INFO
WARN = XMI11Instantiator::WARN
ERROR = XMI11Instantiator::ERROR
def self.instantiateUML13FromXMI11(envUML, fileName, options={})
envUMLEA = RGen::Environment.new
xmiInst = XMI11Instantiator.new(envUMLEA, FIXMAP, options[:loglevel] || ERROR)
xmiInst.add_metamodel("omg.org/UML1.3", UML13EA)
File.open(fileName) do |f|
xmiInst.instantiate(f.read)
end
trans = UML13EAToUML13.new(envUMLEA, envUML)
trans.transform
trans.cleanModel if options[:clean_model]
end
def self.serializeUML13ToXMI11(envUML, fileName, options={})
envUMLEA = RGen::Environment.new
UML13EA.idStore = options[:keep_ids] ?
IdStore.new(File.dirname(fileName)+"/"+File.basename(fileName)+".ids") : IdStore.new
UML13ToUML13EA.new(envUML, envUMLEA).transform
File.open(fileName, "w") do |f|
xmiSer = RGen::Serializer::XMI11Serializer.new(f)
xmiSer.setNamespace("UML","omg.org/UML1.3")
xmiSer.serialize(envUMLEA.find(:class => UML13EA::Model).first,
{:documentation => {:exporter => "Enterprise Architect", :exporterVersion => "2.5"}})
end
UML13EA.idStore.store
end
end rgen-0.10.2/lib/ea_support/id_store.rb 0000664 0000000 0000000 00000001176 14560401624 0017620 0 ustar 00root root 0000000 0000000 require 'yaml'
class IdStore
def initialize(fileName=nil)
if fileName
raise "Base directory does not exist: #{File.dirname(fileName)}" \
unless File.exist?(File.dirname(fileName))
@idsFileName = fileName
end
@idHash = nil
end
def idHash
load unless @idHash
@idHash
end
def load
if @idsFileName && File.exist?(@idsFileName)
@idHash = YAML.load_file(@idsFileName) || {}
else
@idHash = {}
end
end
def store
return unless @idsFileName
File.open(@idsFileName,"w") do |f|
YAML.dump(@idHash, f)
end
end
end rgen-0.10.2/lib/ea_support/uml13_ea_metamodel.rb 0000664 0000000 0000000 00000052055 14560401624 0021447 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder'
module UML13EA
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
OperationDirectionKind = Enum.new(:name => 'OperationDirectionKind', :literals =>[ ])
MessageDirectionKind = Enum.new(:name => 'MessageDirectionKind', :literals =>[ ])
ChangeableKind = Enum.new(:name => 'ChangeableKind', :literals =>[ :changeable, :none, :addOnly ])
PseudostateKind = Enum.new(:name => 'PseudostateKind', :literals =>[ :initial, :deepHistory, :shallowHistory, :join, :fork, :branch, :junction, :final ])
ParameterDirectionKind = Enum.new(:name => 'ParameterDirectionKind', :literals =>[ :in, :inout, :out, :return ])
ScopeKind = Enum.new(:name => 'ScopeKind', :literals =>[ :instance, :classifier ])
OrderingKind = Enum.new(:name => 'OrderingKind', :literals =>[ :unordered, :ordered, :sorted ])
CallConcurrencyKind = Enum.new(:name => 'CallConcurrencyKind', :literals =>[ :sequential, :guarded, :concurrent ])
AggregationKind = Enum.new(:name => 'AggregationKind', :literals =>[ :none, :aggregate, :composite, :shared ])
VisibilityKind = Enum.new(:name => 'VisibilityKind', :literals =>[ :public, :protected, :private ])
end
class UML13EA::Expression < RGen::MetamodelBuilder::MMBase
has_attr 'language', String
has_attr 'body', String
end
class UML13EA::ActionExpression < UML13EA::Expression
end
class UML13EA::Element < RGen::MetamodelBuilder::MMBase
end
class UML13EA::ModelElement < UML13EA::Element
has_attr 'name', String
has_attr 'visibility', UML13EA::VisibilityKind, :defaultValueLiteral => "public"
has_attr 'isSpecification', Boolean
end
class UML13EA::Namespace < UML13EA::ModelElement
end
class UML13EA::GeneralizableElement < UML13EA::ModelElement
has_attr 'isRoot', Boolean
has_attr 'isLeaf', Boolean
has_attr 'isAbstract', Boolean
end
class UML13EA::Classifier < RGen::MetamodelBuilder::MMMultiple(UML13EA::GeneralizableElement, UML13EA::Namespace)
end
class UML13EA::ClassifierRole < UML13EA::Classifier
end
class UML13EA::PresentationElement < UML13EA::Element
end
class UML13EA::DiagramElement < UML13EA::PresentationElement
has_attr 'geometry', String
has_attr 'style', String
end
class UML13EA::Feature < UML13EA::ModelElement
has_attr 'ownerScope', UML13EA::ScopeKind, :defaultValueLiteral => "instance"
end
class UML13EA::BehavioralFeature < UML13EA::Feature
has_attr 'isQuery', Boolean
end
class UML13EA::Method < UML13EA::BehavioralFeature
end
class UML13EA::Actor < UML13EA::Classifier
end
class UML13EA::DataType < UML13EA::Classifier
end
class UML13EA::Primitive < UML13EA::DataType
end
class UML13EA::Action < UML13EA::ModelElement
has_attr 'isAsynchronous', Boolean
end
class UML13EA::SendAction < UML13EA::Action
end
class UML13EA::Interface < UML13EA::Classifier
end
class UML13EA::Event < UML13EA::ModelElement
end
class UML13EA::ChangeEvent < UML13EA::Event
end
class UML13EA::Partition < UML13EA::ModelElement
end
class UML13EA::Comment < UML13EA::ModelElement
has_attr 'body', String
end
class UML13EA::ProgrammingLanguageType < UML13EA::DataType
end
class UML13EA::StateMachine < UML13EA::ModelElement
end
class UML13EA::Call < RGen::MetamodelBuilder::MMBase
end
class UML13EA::Operation < UML13EA::BehavioralFeature
has_attr 'concurrency', UML13EA::CallConcurrencyKind, :defaultValueLiteral => "sequential"
has_attr 'isRoot', Boolean
has_attr 'isLeaf', Boolean
has_attr 'isAbstract', Boolean
end
class UML13EA::XmiIdProvider < RGen::MetamodelBuilder::MMBase
end
class UML13EA::StateVertex < RGen::MetamodelBuilder::MMMultiple(UML13EA::ModelElement, UML13EA::XmiIdProvider)
end
class UML13EA::SynchState < UML13EA::StateVertex
has_attr 'bound', Integer
end
class UML13EA::ClassifierInState < UML13EA::Classifier
end
class UML13EA::Link < UML13EA::ModelElement
end
class UML13EA::ProcedureExpression < UML13EA::Expression
end
class UML13EA::CallEvent < UML13EA::Event
end
class UML13EA::AssignmentAction < UML13EA::Action
end
class UML13EA::Relationship < UML13EA::ModelElement
end
class UML13EA::Association < RGen::MetamodelBuilder::MMMultiple(UML13EA::GeneralizableElement, UML13EA::Relationship, UML13EA::XmiIdProvider)
end
class UML13EA::AssociationRole < UML13EA::Association
end
class UML13EA::Diagram < UML13EA::PresentationElement
has_attr 'name', String
has_attr 'toolName', String
has_attr 'diagramType', String
has_attr 'style', String
end
class UML13EA::MultiplicityRange < RGen::MetamodelBuilder::MMBase
has_attr 'lower', String
has_attr 'upper', String
end
class UML13EA::ActionSequence < UML13EA::Action
end
class UML13EA::Constraint < UML13EA::ModelElement
end
class UML13EA::Instance < UML13EA::ModelElement
end
class UML13EA::UseCaseInstance < UML13EA::Instance
end
class UML13EA::State < UML13EA::StateVertex
end
class UML13EA::CompositeState < UML13EA::State
has_attr 'isConcurrent', Boolean
end
class UML13EA::SubmachineState < UML13EA::CompositeState
end
class UML13EA::SubactivityState < UML13EA::SubmachineState
has_attr 'isDynamic', Boolean
end
class UML13EA::StructuralFeature < UML13EA::Feature
has_attr 'changeable', UML13EA::ChangeableKind, :defaultValueLiteral => "changeable"
has_attr 'targetScope', UML13EA::ScopeKind, :defaultValueLiteral => "instance"
end
class UML13EA::Attribute < UML13EA::StructuralFeature
end
class UML13EA::Flow < UML13EA::Relationship
end
class UML13EA::Class < RGen::MetamodelBuilder::MMMultiple(UML13EA::Classifier, UML13EA::XmiIdProvider)
has_attr 'isActive', Boolean
end
class UML13EA::Guard < UML13EA::ModelElement
end
class UML13EA::CreateAction < UML13EA::Action
end
class UML13EA::IterationExpression < UML13EA::Expression
end
class UML13EA::ReturnAction < UML13EA::Action
end
class UML13EA::Parameter < UML13EA::ModelElement
has_attr 'kind', UML13EA::ParameterDirectionKind, :defaultValueLiteral => "inout"
end
class UML13EA::Dependency < UML13EA::Relationship
end
class UML13EA::Binding < UML13EA::Dependency
end
class UML13EA::Package < RGen::MetamodelBuilder::MMMultiple(UML13EA::Namespace, UML13EA::GeneralizableElement, UML13EA::XmiIdProvider)
end
class UML13EA::ObjectSetExpression < UML13EA::Expression
end
class UML13EA::StubState < UML13EA::StateVertex
has_attr 'referenceState', String
end
class UML13EA::Stereotype < UML13EA::GeneralizableElement
has_attr 'icon', String
has_attr 'baseClass', String
end
class UML13EA::Object < UML13EA::Instance
end
class UML13EA::LinkObject < RGen::MetamodelBuilder::MMMultiple(UML13EA::Link, UML13EA::Object)
end
class UML13EA::ComponentInstance < UML13EA::Instance
end
class UML13EA::Usage < UML13EA::Dependency
end
class UML13EA::SignalEvent < UML13EA::Event
end
class UML13EA::Structure < UML13EA::DataType
end
class UML13EA::AssociationEnd < RGen::MetamodelBuilder::MMMultiple(UML13EA::ModelElement, UML13EA::XmiIdProvider)
has_attr 'isNavigable', Boolean, :defaultValueLiteral => "false"
has_attr 'isOrdered', Boolean, :defaultValueLiteral => "false"
has_attr 'aggregation', UML13EA::AggregationKind, :defaultValueLiteral => "none"
has_attr 'targetScope', UML13EA::ScopeKind, :defaultValueLiteral => "instance"
has_attr 'changeable', UML13EA::ChangeableKind, :defaultValueLiteral => "changeable"
has_attr 'multiplicity', String
end
class UML13EA::AssociationEndRole < UML13EA::AssociationEnd
end
class UML13EA::Signal < UML13EA::Classifier
end
class UML13EA::Exception < UML13EA::Signal
end
class UML13EA::Extend < UML13EA::Relationship
end
class UML13EA::Argument < UML13EA::ModelElement
end
class UML13EA::TemplateParameter < RGen::MetamodelBuilder::MMBase
end
class UML13EA::PseudoState < UML13EA::StateVertex
has_attr 'kind', UML13EA::PseudostateKind, :defaultValueLiteral => "initial"
end
class UML13EA::SimpleState < UML13EA::State
end
class UML13EA::ActionState < UML13EA::SimpleState
has_attr 'isDynamic', Boolean
end
class UML13EA::TypeExpression < UML13EA::Expression
end
class UML13EA::DestroyAction < UML13EA::Action
end
class UML13EA::TerminateAction < UML13EA::Action
end
class UML13EA::Generalization < RGen::MetamodelBuilder::MMMultiple(UML13EA::Relationship, UML13EA::XmiIdProvider)
has_attr 'discriminator', String
end
class UML13EA::FinalState < UML13EA::State
end
class UML13EA::Subsystem < RGen::MetamodelBuilder::MMMultiple(UML13EA::Package, UML13EA::Classifier)
has_attr 'isInstantiable', Boolean
end
class UML13EA::TimeExpression < UML13EA::Expression
end
class UML13EA::TaggedValue < UML13EA::Element
has_attr 'tag', String
has_attr 'value', String
end
class UML13EA::DataValue < UML13EA::Instance
end
class UML13EA::Transition < UML13EA::ModelElement
end
class UML13EA::NodeInstance < UML13EA::Instance
end
class UML13EA::Component < UML13EA::Classifier
end
class UML13EA::Message < UML13EA::ModelElement
end
class UML13EA::Enumeration < UML13EA::DataType
end
class UML13EA::Reception < UML13EA::BehavioralFeature
has_attr 'isPolymorphic', Boolean
has_attr 'specification', String
end
class UML13EA::Include < UML13EA::Relationship
end
class UML13EA::CallState < UML13EA::ActionState
end
class UML13EA::ElementResidence < RGen::MetamodelBuilder::MMBase
has_attr 'visibility', UML13EA::VisibilityKind, :defaultValueLiteral => "public"
end
class UML13EA::UninterpretedAction < UML13EA::Action
end
class UML13EA::ArgListsExpression < UML13EA::Expression
end
class UML13EA::Stimulus < UML13EA::ModelElement
end
class UML13EA::AssociationClass < RGen::MetamodelBuilder::MMMultiple(UML13EA::Class, UML13EA::Association)
end
class UML13EA::Node < UML13EA::Classifier
end
class UML13EA::ElementImport < RGen::MetamodelBuilder::MMBase
has_attr 'visibility', UML13EA::VisibilityKind, :defaultValueLiteral => "public"
has_attr 'alias', String
end
class UML13EA::BooleanExpression < UML13EA::Expression
end
class UML13EA::Collaboration < RGen::MetamodelBuilder::MMMultiple(UML13EA::GeneralizableElement, UML13EA::Namespace)
end
class UML13EA::CallAction < UML13EA::Action
end
class UML13EA::UseCase < UML13EA::Classifier
end
class UML13EA::ActivityModel < UML13EA::StateMachine
end
class UML13EA::Permission < UML13EA::Dependency
end
class UML13EA::Interaction < UML13EA::ModelElement
end
class UML13EA::EnumerationLiteral < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
end
class UML13EA::Model < UML13EA::Package
end
class UML13EA::LinkEnd < UML13EA::ModelElement
end
class UML13EA::ExtensionPoint < UML13EA::ModelElement
has_attr 'location', String
end
class UML13EA::Multiplicity < RGen::MetamodelBuilder::MMBase
end
class UML13EA::ObjectFlowState < UML13EA::SimpleState
has_attr 'isSynch', Boolean
end
class UML13EA::AttributeLink < UML13EA::ModelElement
end
class UML13EA::MappingExpression < UML13EA::Expression
end
class UML13EA::TimeEvent < UML13EA::Event
end
class UML13EA::Abstraction < UML13EA::Dependency
end
class UML13EA::ActionInstance < RGen::MetamodelBuilder::MMBase
end
UML13EA::ClassifierRole.contains_one_uni 'multiplicity', UML13EA::Multiplicity
UML13EA::ClassifierRole.has_many 'availableContents', UML13EA::ModelElement
UML13EA::ClassifierRole.has_many 'availableFeature', UML13EA::Feature
UML13EA::ClassifierRole.has_one 'base', UML13EA::Classifier, :lowerBound => 1
UML13EA::Diagram.contains_many 'element', UML13EA::DiagramElement, 'diagram'
UML13EA::Method.many_to_one 'specification', UML13EA::Operation, 'method'
UML13EA::Method.contains_one_uni 'body', UML13EA::ProcedureExpression
UML13EA::SendAction.has_one 'signal', UML13EA::Signal, :lowerBound => 1
UML13EA::ChangeEvent.contains_one_uni 'changeExpression', UML13EA::BooleanExpression
UML13EA::Partition.has_many 'contents', UML13EA::ModelElement
UML13EA::Comment.many_to_many 'annotatedElement', UML13EA::ModelElement, 'comment'
UML13EA::ProgrammingLanguageType.contains_one_uni 'type', UML13EA::TypeExpression
UML13EA::Action.contains_one_uni 'recurrence', UML13EA::IterationExpression
UML13EA::Action.contains_one_uni 'target', UML13EA::ObjectSetExpression
UML13EA::Action.contains_one_uni 'script', UML13EA::ActionExpression
UML13EA::Action.contains_many_uni 'actualArgument', UML13EA::Argument
UML13EA::StateMachine.many_to_one 'context', UML13EA::ModelElement, 'behavior'
UML13EA::StateMachine.contains_many_uni 'transitions', UML13EA::Transition
UML13EA::StateMachine.contains_one_uni 'top', UML13EA::State, :lowerBound => 1
UML13EA::Operation.one_to_many 'occurrence', UML13EA::CallEvent, 'operation'
UML13EA::ClassifierInState.has_one 'type', UML13EA::Classifier, :lowerBound => 1
UML13EA::ClassifierInState.has_many 'inState', UML13EA::State
UML13EA::Link.contains_many_uni 'connection', UML13EA::LinkEnd, :lowerBound => 2
UML13EA::Link.has_one 'association', UML13EA::Association, :lowerBound => 1
UML13EA::PresentationElement.many_to_many 'subject', UML13EA::ModelElement, 'presentation'
UML13EA::AssociationRole.contains_one_uni 'multiplicity', UML13EA::Multiplicity
UML13EA::AssociationRole.has_one 'base', UML13EA::Association
UML13EA::Diagram.has_one 'owner', UML13EA::ModelElement, :lowerBound => 1
UML13EA::ActionSequence.contains_many_uni 'action', UML13EA::Action
UML13EA::Constraint.contains_one_uni 'body', UML13EA::BooleanExpression
UML13EA::Constraint.many_to_many 'constrainedElement', UML13EA::ModelElement, 'constraint', :lowerBound => 1
UML13EA::SubactivityState.contains_one_uni 'dynamicArguments', UML13EA::ArgListsExpression
UML13EA::AssociationEnd.contains_many 'qualifier', UML13EA::Attribute, 'associationEnd'
UML13EA::Attribute.contains_one_uni 'initialValue', UML13EA::Expression
UML13EA::Flow.many_to_many 'source', UML13EA::ModelElement, 'sourceFlow'
UML13EA::Flow.many_to_many 'target', UML13EA::ModelElement, 'targetFlow'
UML13EA::Guard.contains_one_uni 'expression', UML13EA::BooleanExpression
UML13EA::CreateAction.has_one 'instantiation', UML13EA::Classifier, :lowerBound => 1
UML13EA::Namespace.contains_many 'ownedElement', UML13EA::ModelElement, 'namespace'
UML13EA::Parameter.contains_one_uni 'defaultValue', UML13EA::Expression
UML13EA::Parameter.many_to_many 'state', UML13EA::ObjectFlowState, 'parameter'
UML13EA::Parameter.has_one 'type', UML13EA::Classifier, :lowerBound => 1
UML13EA::Binding.has_many 'argument', UML13EA::ModelElement, :lowerBound => 1
UML13EA::Event.contains_many_uni 'parameters', UML13EA::Parameter
UML13EA::Dependency.many_to_many 'supplier', UML13EA::ModelElement, 'supplierDependency', :opposite_lowerBound => 1
UML13EA::Dependency.many_to_many 'client', UML13EA::ModelElement, 'clientDependency', :opposite_lowerBound => 1
UML13EA::Package.contains_many 'importedElement', UML13EA::ElementImport, 'package'
UML13EA::Classifier.contains_many 'feature', UML13EA::Feature, 'owner'
UML13EA::Stereotype.one_to_many 'extendedElement', UML13EA::ModelElement, 'stereotype'
UML13EA::Stereotype.has_many 'requiredTag', UML13EA::TaggedValue
UML13EA::ComponentInstance.has_many 'resident', UML13EA::Instance
UML13EA::SignalEvent.many_to_one 'signal', UML13EA::Signal, 'occurrence', :lowerBound => 1
UML13EA::Instance.contains_many_uni 'slot', UML13EA::AttributeLink
UML13EA::Instance.one_to_many 'linkEnd', UML13EA::LinkEnd, 'instance'
UML13EA::Instance.has_many 'classifier', UML13EA::Classifier, :lowerBound => 1
UML13EA::AssociationEndRole.has_many 'availableQualifier', UML13EA::Attribute
UML13EA::AssociationEndRole.has_one 'base', UML13EA::AssociationEnd
UML13EA::Extend.many_to_one 'extension', UML13EA::UseCase, 'extend'
UML13EA::Extend.contains_one_uni 'condition', UML13EA::BooleanExpression
UML13EA::Extend.has_many 'extensionPoint', UML13EA::ExtensionPoint, :lowerBound => 1
UML13EA::Extend.has_one 'base', UML13EA::UseCase, :lowerBound => 1
UML13EA::Argument.contains_one_uni 'value', UML13EA::Expression
UML13EA::TemplateParameter.has_one 'modelElement', UML13EA::ModelElement
UML13EA::TemplateParameter.has_one 'defaultElement', UML13EA::ModelElement
UML13EA::ActionState.contains_one_uni 'dynamicArguments', UML13EA::ArgListsExpression
UML13EA::GeneralizableElement.one_to_many 'specialization', UML13EA::Generalization, 'supertype'
UML13EA::GeneralizableElement.one_to_many 'generalization', UML13EA::Generalization, 'subtype'
UML13EA::StateVertex.one_to_many 'incoming', UML13EA::Transition, 'target', :opposite_lowerBound => 1
UML13EA::StateVertex.one_to_many 'outgoing', UML13EA::Transition, 'source', :opposite_lowerBound => 1
UML13EA::CompositeState.contains_many 'substate', UML13EA::StateVertex, 'container', :lowerBound => 1
UML13EA::ModelElement.contains_many 'taggedValue', UML13EA::TaggedValue, 'modelElement'
UML13EA::StructuralFeature.contains_one_uni 'multiplicity', UML13EA::Multiplicity
UML13EA::StructuralFeature.has_one 'type', UML13EA::Classifier, :lowerBound => 1
UML13EA::Transition.has_one 'trigger', UML13EA::Event
UML13EA::Transition.contains_one_uni 'effect', UML13EA::Action
UML13EA::Transition.contains_one_uni 'guard', UML13EA::Guard
UML13EA::NodeInstance.has_many 'resident', UML13EA::ComponentInstance
UML13EA::Component.contains_many 'residentElement', UML13EA::ElementResidence, 'implementationLocation'
UML13EA::Component.many_to_many 'deploymentLocation', UML13EA::Node, 'resident'
UML13EA::Message.has_one 'action', UML13EA::Action, :lowerBound => 1
UML13EA::Message.has_one 'communicationConnection', UML13EA::AssociationRole
UML13EA::Message.has_many 'predecessor', UML13EA::Message
UML13EA::Message.has_one 'receiver', UML13EA::ClassifierRole, :lowerBound => 1
UML13EA::Message.has_one 'sender', UML13EA::ClassifierRole, :lowerBound => 1
UML13EA::Message.has_one 'activator', UML13EA::Message
UML13EA::Interaction.contains_many 'message', UML13EA::Message, 'interaction', :lowerBound => 1
UML13EA::ModelElement.one_to_many 'elementResidence', UML13EA::ElementResidence, 'resident'
UML13EA::ModelElement.contains_many_uni 'templateParameter', UML13EA::TemplateParameter
UML13EA::ModelElement.one_to_many 'elementImport', UML13EA::ElementImport, 'modelElement'
UML13EA::Enumeration.contains_many_uni 'literal', UML13EA::EnumerationLiteral, :lowerBound => 1
UML13EA::Reception.many_to_one 'signal', UML13EA::Signal, 'reception'
UML13EA::Association.contains_many 'connection', UML13EA::AssociationEnd, 'association', :lowerBound => 2
UML13EA::Include.many_to_one 'base', UML13EA::UseCase, 'include'
UML13EA::Include.has_one 'addition', UML13EA::UseCase, :lowerBound => 1
UML13EA::Classifier.many_to_many 'participant', UML13EA::AssociationEnd, 'specification'
UML13EA::Classifier.one_to_many 'associationEnd', UML13EA::AssociationEnd, 'type'
UML13EA::Stimulus.has_one 'dispatchAction', UML13EA::Action, :lowerBound => 1
UML13EA::Stimulus.has_one 'communicationLink', UML13EA::Link
UML13EA::Stimulus.has_one 'receiver', UML13EA::Instance, :lowerBound => 1
UML13EA::Stimulus.has_one 'sender', UML13EA::Instance, :lowerBound => 1
UML13EA::Stimulus.has_many 'argument', UML13EA::Instance
UML13EA::State.contains_one_uni 'doActivity', UML13EA::Action
UML13EA::State.contains_many_uni 'internalTransition', UML13EA::Transition
UML13EA::State.has_many 'deferrableEvent', UML13EA::Event
UML13EA::State.contains_one_uni 'exit', UML13EA::Action
UML13EA::State.contains_one_uni 'entry', UML13EA::Action
UML13EA::Collaboration.has_one 'representedOperation', UML13EA::Operation
UML13EA::Collaboration.has_one 'representedClassifier', UML13EA::Classifier
UML13EA::Collaboration.has_many 'constrainingElement', UML13EA::ModelElement
UML13EA::Collaboration.contains_many 'interaction', UML13EA::Interaction, 'context'
UML13EA::CallAction.has_one 'operation', UML13EA::Operation, :lowerBound => 1
UML13EA::UseCase.has_many 'extensionPoint', UML13EA::ExtensionPoint
UML13EA::ActivityModel.contains_many_uni 'partition', UML13EA::Partition
UML13EA::Interaction.contains_many_uni 'link', UML13EA::Link
UML13EA::LinkEnd.has_one 'associationEnd', UML13EA::AssociationEnd, :lowerBound => 1
UML13EA::LinkEnd.has_one 'participant', UML13EA::Instance, :lowerBound => 1
UML13EA::BehavioralFeature.many_to_many 'raisedSignal', UML13EA::Signal, 'context'
UML13EA::BehavioralFeature.contains_many_uni 'parameter', UML13EA::Parameter
UML13EA::SubmachineState.has_one 'submachine', UML13EA::StateMachine, :lowerBound => 1
UML13EA::Multiplicity.contains_many_uni 'range', UML13EA::MultiplicityRange, :lowerBound => 1
UML13EA::ObjectFlowState.has_one 'type', UML13EA::Classifier, :lowerBound => 1
UML13EA::ObjectFlowState.has_one 'available', UML13EA::Parameter, :lowerBound => 1
UML13EA::AttributeLink.has_one 'value', UML13EA::Instance, :lowerBound => 1
UML13EA::AttributeLink.has_one 'attribute', UML13EA::Attribute, :lowerBound => 1
UML13EA::TimeEvent.contains_one_uni 'when', UML13EA::TimeExpression
UML13EA::Abstraction.contains_one_uni 'mapping', UML13EA::MappingExpression
rgen-0.10.2/lib/ea_support/uml13_ea_metamodel_ext.rb 0000664 0000000 0000000 00000002203 14560401624 0022315 0 ustar 00root root 0000000 0000000 module UML13EA
class << self
attr_accessor :idStore
end
module ModelElement::ClassModule
def qualifiedName
_name = (respond_to?(:_name) ? self._name : name) || "unnamed"
_namespace = respond_to?(:_namespace) ? self._namespace : namespace
_namespace && _namespace.qualifiedName ? _namespace.qualifiedName+"::"+_name : _name
end
end
module XmiIdProvider::ClassModule
def _xmi_id
UML13EA.idStore.idHash[qualifiedName] ||= "EAID_"+object_id.to_s
end
end
module Package::ClassModule
def _xmi_id
UML13EA.idStore.idHash[qualifiedName] ||= "EAPK_"+object_id.to_s
end
end
module Generalization::ClassModule
def _name
"#{subtype.name}_#{supertype.name}"
end
end
module Association::ClassModule
def _name
connection.collect{|c| "#{c.getType.name}_#{c.name}"}.sort.join("_")
end
end
module AssociationEnd::ClassModule
def _name
"#{getType.name}_#{name}"
end
def _namespace
association
end
end
module StateVertex::ClassModule
def _namespace
container
end
end
end
rgen-0.10.2/lib/ea_support/uml13_ea_metamodel_generator.rb 0000664 0000000 0000000 00000003623 14560401624 0023512 0 ustar 00root root 0000000 0000000 require 'metamodels/uml13_metamodel'
require 'mmgen/metamodel_generator'
require 'rgen/transformer'
require 'rgen/environment'
require 'rgen/ecore/ecore'
include MMGen::MetamodelGenerator
class ECoreCopyTransformer < RGen::Transformer
copy_all RGen::ECore
end
eaMMRoot = ECoreCopyTransformer.new.trans(UML13.ecore)
eaMMRoot.name = "UML13EA"
eaMMRoot.eClassifiers.find{|c| c.name == "ActivityGraph"}.name = "ActivityModel"
eaMMRoot.eClassifiers.find{|c| c.name == "Pseudostate"}.name = "PseudoState"
compositeState = eaMMRoot.eClassifiers.find{|c| c.name == "CompositeState"}
compositeState.eReferences.find{|r| r.name == "subvertex"}.name = "substate"
generalization = eaMMRoot.eClassifiers.find{|c| c.name == "Generalization"}
generalization.eReferences.find{|r| r.name == "parent"}.name = "supertype"
generalization.eReferences.find{|r| r.name == "child"}.name = "subtype"
assocEnd = eaMMRoot.eClassifiers.find{|c| c.name == "AssociationEnd"}
assocEnd.eAttributes.find{|r| r.name == "ordering"}.name = "isOrdered"
assocEnd.eAttributes.find{|r| r.name == "changeability"}.name = "changeable"
assocEnd.eAttributes.find{|r| r.name == "isOrdered"}.eType = RGen::ECore::EBoolean
assocEnd.eAttributes.find{|r| r.name == "changeable"}.eType.eLiterals.find{|l| l.name == "frozen"}.name = "none"
multRef = assocEnd.eStructuralFeatures.find{|f| f.name == "multiplicity"}
multRef.eType = nil
assocEnd.removeEStructuralFeatures(multRef)
assocEnd.addEStructuralFeatures(RGen::ECore::EAttribute.new(:name => "multiplicity", :eType => RGen::ECore::EString))
xmiIdProvider = RGen::ECore::EClass.new(:name => "XmiIdProvider", :ePackage => eaMMRoot)
eaMMRoot.eClassifiers.each do |c|
if %w(Package Class Generalization Association AssociationEnd StateVertex).include?(c.name)
c.addESuperTypes(xmiIdProvider)
end
end
generateMetamodel(eaMMRoot, File.dirname(__FILE__)+"/uml13_ea_metamodel.rb")
rgen-0.10.2/lib/ea_support/uml13_ea_to_uml13.rb 0000664 0000000 0000000 00000006355 14560401624 0021145 0 ustar 00root root 0000000 0000000 require 'rgen/transformer'
require 'metamodels/uml13_metamodel'
require 'ea_support/uml13_ea_metamodel'
class UML13EAToUML13 < RGen::Transformer
include UML13EA
def transform
trans(:class => Package)
trans(:class => Class)
@env_out.find(:class => UML13::Attribute).each do |me|
# remove tagged vales internally used by EA which have been converted to UML
me.taggedValue = me.taggedValue.reject{|tv| ["lowerBound", "upperBound"].include?(tv.tag)}
end
end
def cleanModel
@env_out.find(:class => UML13::ModelElement).each do |me|
me.taggedValue = []
end
end
copy_all UML13EA, :to => UML13, :except => %w(
XmiIdProvider
AssociationEnd AssociationEndRole
StructuralFeature
Attribute
Generalization
ActivityModel
CompositeState
PseudoState
Dependency
)
transform AssociationEndRole, :to => UML13::AssociationEndRole do
copyAssociationEnd
end
transform AssociationEnd, :to => UML13::AssociationEnd do
copyAssociationEnd
end
def copyAssociationEnd
copy_features :except => [:isOrdered, :changeable] do
{:ordering => isOrdered ? :ordered : :unordered,
:changeability => {:none => :frozen}[changeable] || changeable,
:aggregation => {:shared => :aggregate}[aggregation] || aggregation,
:multiplicity => UML13::Multiplicity.new(
:range => [UML13::MultiplicityRange.new(
:lower => multiplicity && multiplicity.split("..").first,
:upper => multiplicity && multiplicity.split("..").last)])}
end
end
transform StructuralFeature, :to => UML13::StructuralFeature,
:if => lambda{|c| !@current_object.is_a?(UML13EA::Attribute)} do
copy_features :except => [:changeable] do
{:changeability => {:none => :frozen}[changeable] }
end
end
transform StructuralFeature, :to => UML13::Attribute,
:if => lambda{|c| @current_object.is_a?(UML13EA::Attribute)} do
_lowerBound = taggedValue.find{|tv| tv.tag == "lowerBound"}
_upperBound = taggedValue.find{|tv| tv.tag == "upperBound"}
if _lowerBound || _upperBound
_multiplicity = UML13::Multiplicity.new(
:range => [UML13::MultiplicityRange.new(
:lower => (_lowerBound && _lowerBound.value) || "0",
:upper => (_upperBound && _upperBound.value) || "1"
)])
end
copy_features :except => [:changeable] do
{:changeability => {:none => :frozen}[changeable],
:multiplicity => _multiplicity }
end
end
transform Generalization, :to => UML13::Generalization do
copy_features :except => [:subtype, :supertype] do
{ :child => trans(subtype),
:parent => trans(supertype) }
end
end
copy ActivityModel, :to => UML13::ActivityGraph
transform CompositeState, :to => UML13::CompositeState do
copy_features :except => [:substate] do
{ :subvertex => trans(substate) }
end
end
copy PseudoState, :to => UML13::Pseudostate
transform Dependency, :to => UML13::Dependency do
_name_tag = taggedValue.find{|tv| tv.tag == "dst_name"}
copy_features do
{ :name => (_name_tag && _name_tag.value) || "Anonymous" }
end
end
end
rgen-0.10.2/lib/ea_support/uml13_to_uml13_ea.rb 0000664 0000000 0000000 00000006057 14560401624 0021144 0 ustar 00root root 0000000 0000000 require 'rgen/transformer'
require 'metamodels/uml13_metamodel'
require 'ea_support/uml13_ea_metamodel'
require 'ea_support/uml13_ea_metamodel_ext'
class UML13ToUML13EA < RGen::Transformer
include UML13
def transform
trans(:class => Package)
trans(:class => Class)
end
copy_all UML13, :to => UML13EA, :except => %w(
ActivityGraph
CompositeState SimpleState
Class
Association AssociationEnd AssociationEndRole
Generalization
Pseudostate
Attribute
)
copy ActivityGraph, :to => UML13EA::ActivityModel
copy Pseudostate, :to => UML13EA::PseudoState
transform CompositeState, :to => UML13EA::CompositeState do
copy_features :except => [:subvertex] do
{ :substate => trans(subvertex) }
end
end
transform SimpleState, :to => UML13EA::SimpleState do
copy_features :except => [:container] do
{ :taggedValue => trans(taggedValue) +
[@env_out.new(UML13EA::TaggedValue, :tag => "ea_stype", :value => "State")] +
(container ? [ @env_out.new(UML13EA::TaggedValue, :tag => "owner", :value => trans(container)._xmi_id)] : []) }
end
end
transform Class, :to => UML13EA::Class do
copy_features do
{ :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_stype", :value => "Class")]}
end
end
transform Association, :to => UML13EA::Association do
copy_features do
{ :connection => trans(connection[1].isNavigable ? [connection[0], connection[1]] : [connection[1], connection[0]]),
:taggedValue => trans(taggedValue) + [
@env_out.new(UML13EA::TaggedValue, :tag => "ea_type", :value => "Association"),
@env_out.new(UML13EA::TaggedValue, :tag => "direction", :value =>
connection.all?{|c| c.isNavigable} ? "Bi-Directional" : "Source -> Destination")] }
end
end
transform AssociationEnd, :to => UML13EA::AssociationEnd do
copyAssociationEnd
end
transform AssociationEndRole, :to => UML13EA::AssociationEndRole do
copyAssociationEnd
end
def copyAssociationEnd
_lower = multiplicity && multiplicity.range.first.lower
_upper = multiplicity && multiplicity.range.first.upper
copy_features :except => [:multiplicity, :ordering, :changeability] do
{ :multiplicity => _lower == _upper ? _lower : "#{_lower}..#{_upper}",
:isOrdered => ordering == :ordered,
:changeable => :none } #{:frozen => :none}[changeability] || changeability}
end
end
transform Attribute, :to => UML13EA::Attribute do
copy_features :except => [:changeability] do
{ :changeable => {:frozen => :none}[changeability] }
end
end
transform Generalization, :to => UML13EA::Generalization do
copy_features :except => [:child, :parent] do
{ :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_type", :value => "Generalization")],
:subtype => trans(child),
:supertype => trans(parent)}
end
end
end
rgen-0.10.2/lib/metamodels/ 0000775 0000000 0000000 00000000000 14560401624 0015427 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/metamodels/uml13_metamodel.rb 0000664 0000000 0000000 00000045451 14560401624 0020755 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder'
module UML13
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
AggregationKind = Enum.new(:name => "AggregationKind", :literals =>[ :none, :aggregate, :composite ])
ChangeableKind = Enum.new(:name => "ChangeableKind", :literals =>[ :changeable, :frozen, :addOnly ])
OperationDirectionKind = Enum.new(:name => "OperationDirectionKind", :literals =>[ ])
ParameterDirectionKind = Enum.new(:name => "ParameterDirectionKind", :literals =>[ :in, :inout, :out, :return ])
MessageDirectionKind = Enum.new(:name => "MessageDirectionKind", :literals =>[ ])
ScopeKind = Enum.new(:name => "ScopeKind", :literals =>[ :instance, :classifier ])
VisibilityKind = Enum.new(:name => "VisibilityKind", :literals =>[ :public, :protected, :private ])
PseudostateKind = Enum.new(:name => "PseudostateKind", :literals =>[ :initial, :deepHistory, :shallowHistory, :join, :fork, :branch, :junction, :final ])
CallConcurrencyKind = Enum.new(:name => "CallConcurrencyKind", :literals =>[ :sequential, :guarded, :concurrent ])
OrderingKind = Enum.new(:name => "OrderingKind", :literals =>[ :unordered, :ordered, :sorted ])
class Element < RGen::MetamodelBuilder::MMBase
end
class ModelElement < Element
has_attr 'name', String
has_attr 'visibility', UML13::VisibilityKind, :defaultValueLiteral => "public"
has_attr 'isSpecification', Boolean
end
class Namespace < ModelElement
end
class GeneralizableElement < ModelElement
has_attr 'isRoot', Boolean
has_attr 'isLeaf', Boolean
has_attr 'isAbstract', Boolean
end
class Classifier < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Namespace)
end
class Class < Classifier
has_attr 'isActive', Boolean
end
class DataType < Classifier
end
class Feature < ModelElement
has_attr 'ownerScope', UML13::ScopeKind, :defaultValueLiteral => "instance"
end
class StructuralFeature < Feature
has_attr 'changeability', UML13::ChangeableKind, :defaultValueLiteral => "changeable"
has_attr 'targetScope', UML13::ScopeKind, :defaultValueLiteral => "instance"
end
class AssociationEnd < ModelElement
has_attr 'isNavigable', Boolean, :defaultValueLiteral => "false"
has_attr 'ordering', UML13::OrderingKind, :defaultValueLiteral => "unordered"
has_attr 'aggregation', UML13::AggregationKind, :defaultValueLiteral => "none"
has_attr 'targetScope', UML13::ScopeKind, :defaultValueLiteral => "instance"
has_attr 'changeability', UML13::ChangeableKind, :defaultValueLiteral => "changeable"
end
class Interface < Classifier
end
class Constraint < ModelElement
end
class Relationship < ModelElement
end
class Association < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Relationship)
end
class Attribute < StructuralFeature
end
class BehavioralFeature < Feature
has_attr 'isQuery', Boolean
end
class Operation < BehavioralFeature
has_attr 'concurrency', UML13::CallConcurrencyKind, :defaultValueLiteral => "sequential"
has_attr 'isRoot', Boolean
has_attr 'isLeaf', Boolean
has_attr 'isAbstract', Boolean
end
class Parameter < ModelElement
has_attr 'kind', UML13::ParameterDirectionKind, :defaultValueLiteral => "inout"
end
class Method < BehavioralFeature
end
class Generalization < Relationship
has_attr 'discriminator', String
end
class AssociationClass < RGen::MetamodelBuilder::MMMultiple(Class, Association)
end
class Dependency < Relationship
end
class Abstraction < Dependency
end
class PresentationElement < Element
end
class Usage < Dependency
end
class Binding < Dependency
end
class Component < Classifier
end
class Node < Classifier
end
class Permission < Dependency
end
class Comment < ModelElement
has_attr 'body', String
end
class Flow < Relationship
end
class TemplateParameter < RGen::MetamodelBuilder::MMBase
end
class ElementResidence < RGen::MetamodelBuilder::MMBase
has_attr 'visibility', UML13::VisibilityKind, :defaultValueLiteral => "public"
end
class Multiplicity < RGen::MetamodelBuilder::MMBase
end
class Expression < RGen::MetamodelBuilder::MMBase
has_attr 'language', String
has_attr 'body', String
end
class ObjectSetExpression < Expression
end
class TimeExpression < Expression
end
class BooleanExpression < Expression
end
class ActionExpression < Expression
end
class MultiplicityRange < RGen::MetamodelBuilder::MMBase
has_attr 'lower', String
has_attr 'upper', String
end
class Structure < DataType
end
class Primitive < DataType
end
class Enumeration < DataType
end
class EnumerationLiteral < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
end
class ProgrammingLanguageType < DataType
end
class IterationExpression < Expression
end
class TypeExpression < Expression
end
class ArgListsExpression < Expression
end
class MappingExpression < Expression
end
class ProcedureExpression < Expression
end
class Stereotype < GeneralizableElement
has_attr 'icon', String
has_attr 'baseClass', String
end
class TaggedValue < Element
has_attr 'tag', String
has_attr 'value', String
end
class UseCase < Classifier
end
class Actor < Classifier
end
class Instance < ModelElement
end
class UseCaseInstance < Instance
end
class Extend < Relationship
end
class Include < Relationship
end
class ExtensionPoint < ModelElement
has_attr 'location', String
end
class StateMachine < ModelElement
end
class Event < ModelElement
end
class StateVertex < ModelElement
end
class State < StateVertex
end
class TimeEvent < Event
end
class CallEvent < Event
end
class SignalEvent < Event
end
class Transition < ModelElement
end
class CompositeState < State
has_attr 'isConcurrent', Boolean
end
class ChangeEvent < Event
end
class Guard < ModelElement
end
class Pseudostate < StateVertex
has_attr 'kind', UML13::PseudostateKind, :defaultValueLiteral => "initial"
end
class SimpleState < State
end
class SubmachineState < CompositeState
end
class SynchState < StateVertex
has_attr 'bound', Integer
end
class StubState < StateVertex
has_attr 'referenceState', String
end
class FinalState < State
end
class Collaboration < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Namespace)
end
class ClassifierRole < Classifier
end
class AssociationRole < Association
end
class AssociationEndRole < AssociationEnd
end
class Message < ModelElement
end
class Interaction < ModelElement
end
class Signal < Classifier
end
class Action < ModelElement
has_attr 'isAsynchronous', Boolean
end
class CreateAction < Action
end
class DestroyAction < Action
end
class UninterpretedAction < Action
end
class AttributeLink < ModelElement
end
class Object < Instance
end
class Link < ModelElement
end
class LinkObject < RGen::MetamodelBuilder::MMMultiple(Object, Link)
end
class DataValue < Instance
end
class CallAction < Action
end
class SendAction < Action
end
class ActionSequence < Action
end
class Argument < ModelElement
end
class Reception < BehavioralFeature
has_attr 'isPolymorphic', Boolean
has_attr 'specification', String
end
class LinkEnd < ModelElement
end
class Call < RGen::MetamodelBuilder::MMBase
end
class ReturnAction < Action
end
class TerminateAction < Action
end
class Stimulus < ModelElement
end
class ActionInstance < RGen::MetamodelBuilder::MMBase
end
class Exception < Signal
end
class AssignmentAction < Action
end
class ComponentInstance < Instance
end
class NodeInstance < Instance
end
class ActivityGraph < StateMachine
end
class Partition < ModelElement
end
class SubactivityState < SubmachineState
has_attr 'isDynamic', Boolean
end
class ActionState < SimpleState
has_attr 'isDynamic', Boolean
end
class CallState < ActionState
end
class ObjectFlowState < SimpleState
has_attr 'isSynch', Boolean
end
class ClassifierInState < Classifier
end
class Package < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Namespace)
end
class Model < Package
end
class Subsystem < RGen::MetamodelBuilder::MMMultiple(Classifier, Package)
has_attr 'isInstantiable', Boolean
end
class ElementImport < RGen::MetamodelBuilder::MMBase
has_attr 'visibility', UML13::VisibilityKind, :defaultValueLiteral => "public"
has_attr 'alias', String
end
class DiagramElement < PresentationElement
has_attr 'geometry', String
has_attr 'style', String
end
class Diagram < PresentationElement
has_attr 'name', String
has_attr 'toolName', String
has_attr 'diagramType', String
has_attr 'style', String
end
end
UML13::Classifier.many_to_many 'participant', UML13::AssociationEnd, 'specification'
UML13::Classifier.one_to_many 'associationEnd', UML13::AssociationEnd, 'type'
UML13::Classifier.contains_many 'feature', UML13::Feature, 'owner'
UML13::StructuralFeature.contains_one_uni 'multiplicity', UML13::Multiplicity
UML13::StructuralFeature.has_one 'type', UML13::Classifier, :lowerBound => 1
UML13::Namespace.contains_many 'ownedElement', UML13::ModelElement, 'namespace'
UML13::AssociationEnd.contains_one_uni 'multiplicity', UML13::Multiplicity
UML13::AssociationEnd.contains_many 'qualifier', UML13::Attribute, 'associationEnd'
UML13::Association.contains_many 'connection', UML13::AssociationEnd, 'association', :lowerBound => 2
UML13::Constraint.contains_one_uni 'body', UML13::BooleanExpression
UML13::Constraint.many_to_many 'constrainedElement', UML13::ModelElement, 'constraint', :lowerBound => 1
UML13::GeneralizableElement.one_to_many 'specialization', UML13::Generalization, 'parent'
UML13::GeneralizableElement.one_to_many 'generalization', UML13::Generalization, 'child'
UML13::Attribute.contains_one_uni 'initialValue', UML13::Expression
UML13::Operation.one_to_many 'occurrence', UML13::CallEvent, 'operation'
UML13::Operation.one_to_many 'method', UML13::Method, 'specification'
UML13::Parameter.contains_one_uni 'defaultValue', UML13::Expression
UML13::Parameter.many_to_many 'state', UML13::ObjectFlowState, 'parameter'
UML13::Parameter.has_one 'type', UML13::Classifier, :lowerBound => 1
UML13::Method.contains_one_uni 'body', UML13::ProcedureExpression
UML13::BehavioralFeature.many_to_many 'raisedSignal', UML13::Signal, 'context'
UML13::BehavioralFeature.contains_many_uni 'parameter', UML13::Parameter
UML13::ModelElement.one_to_many 'behavior', UML13::StateMachine, 'context'
UML13::ModelElement.many_to_one 'stereotype', UML13::Stereotype, 'extendedElement'
UML13::ModelElement.one_to_many 'elementResidence', UML13::ElementResidence, 'resident'
UML13::ModelElement.many_to_many 'sourceFlow', UML13::Flow, 'source'
UML13::ModelElement.many_to_many 'targetFlow', UML13::Flow, 'target'
UML13::ModelElement.many_to_many 'presentation', UML13::PresentationElement, 'subject'
UML13::ModelElement.many_to_many 'supplierDependency', UML13::Dependency, 'supplier', :lowerBound => 1
UML13::ModelElement.contains_many 'taggedValue', UML13::TaggedValue, 'modelElement'
UML13::ModelElement.contains_many_uni 'templateParameter', UML13::TemplateParameter
UML13::ModelElement.many_to_many 'clientDependency', UML13::Dependency, 'client', :lowerBound => 1
UML13::ModelElement.many_to_many 'comment', UML13::Comment, 'annotatedElement'
UML13::ModelElement.one_to_many 'elementImport', UML13::ElementImport, 'modelElement'
UML13::Abstraction.contains_one_uni 'mapping', UML13::MappingExpression
UML13::Binding.has_many 'argument', UML13::ModelElement, :lowerBound => 1
UML13::Component.contains_many 'residentElement', UML13::ElementResidence, 'implementationLocation'
UML13::Component.many_to_many 'deploymentLocation', UML13::Node, 'resident'
UML13::TemplateParameter.has_one 'modelElement', UML13::ModelElement
UML13::TemplateParameter.has_one 'defaultElement', UML13::ModelElement
UML13::Multiplicity.contains_many_uni 'range', UML13::MultiplicityRange, :lowerBound => 1
UML13::Enumeration.contains_many_uni 'literal', UML13::EnumerationLiteral, :lowerBound => 1
UML13::ProgrammingLanguageType.contains_one_uni 'type', UML13::TypeExpression
UML13::Stereotype.has_many 'requiredTag', UML13::TaggedValue
UML13::UseCase.has_many 'extensionPoint', UML13::ExtensionPoint
UML13::UseCase.one_to_many 'include', UML13::Include, 'base'
UML13::UseCase.one_to_many 'extend', UML13::Extend, 'extension'
UML13::Extend.contains_one_uni 'condition', UML13::BooleanExpression
UML13::Extend.has_many 'extensionPoint', UML13::ExtensionPoint, :lowerBound => 1
UML13::Extend.has_one 'base', UML13::UseCase, :lowerBound => 1
UML13::Include.has_one 'addition', UML13::UseCase, :lowerBound => 1
UML13::StateMachine.contains_many_uni 'transitions', UML13::Transition
UML13::StateMachine.contains_one_uni 'top', UML13::State, :lowerBound => 1
UML13::Event.contains_many_uni 'parameters', UML13::Parameter
UML13::State.contains_one_uni 'doActivity', UML13::Action
UML13::State.contains_many_uni 'internalTransition', UML13::Transition
UML13::State.has_many 'deferrableEvent', UML13::Event
UML13::State.contains_one_uni 'exit', UML13::Action
UML13::State.contains_one_uni 'entry', UML13::Action
UML13::TimeEvent.contains_one_uni 'when', UML13::TimeExpression
UML13::SignalEvent.many_to_one 'signal', UML13::Signal, 'occurrence', :lowerBound => 1
UML13::Transition.many_to_one 'target', UML13::StateVertex, 'incoming', :lowerBound => 1
UML13::Transition.many_to_one 'source', UML13::StateVertex, 'outgoing', :lowerBound => 1
UML13::Transition.has_one 'trigger', UML13::Event
UML13::Transition.contains_one_uni 'effect', UML13::Action
UML13::Transition.contains_one_uni 'guard', UML13::Guard
UML13::CompositeState.contains_many 'subvertex', UML13::StateVertex, 'container', :lowerBound => 1
UML13::ChangeEvent.contains_one_uni 'changeExpression', UML13::BooleanExpression
UML13::Guard.contains_one_uni 'expression', UML13::BooleanExpression
UML13::SubmachineState.has_one 'submachine', UML13::StateMachine, :lowerBound => 1
UML13::Collaboration.has_one 'representedOperation', UML13::Operation
UML13::Collaboration.has_one 'representedClassifier', UML13::Classifier
UML13::Collaboration.has_many 'constrainingElement', UML13::ModelElement
UML13::Collaboration.contains_many 'interaction', UML13::Interaction, 'context'
UML13::ClassifierRole.contains_one_uni 'multiplicity', UML13::Multiplicity
UML13::ClassifierRole.has_many 'availableContents', UML13::ModelElement
UML13::ClassifierRole.has_many 'availableFeature', UML13::Feature
UML13::ClassifierRole.has_one 'base', UML13::Classifier, :lowerBound => 1
UML13::AssociationRole.contains_one_uni 'multiplicity', UML13::Multiplicity
UML13::AssociationRole.has_one 'base', UML13::Association
UML13::AssociationEndRole.has_many 'availableQualifier', UML13::Attribute
UML13::AssociationEndRole.has_one 'base', UML13::AssociationEnd
UML13::Message.has_one 'action', UML13::Action, :lowerBound => 1
UML13::Message.has_one 'communicationConnection', UML13::AssociationRole
UML13::Message.has_many 'predecessor', UML13::Message
UML13::Message.has_one 'receiver', UML13::ClassifierRole, :lowerBound => 1
UML13::Message.has_one 'sender', UML13::ClassifierRole, :lowerBound => 1
UML13::Message.has_one 'activator', UML13::Message
UML13::Interaction.contains_many 'message', UML13::Message, 'interaction', :lowerBound => 1
UML13::Interaction.contains_many_uni 'link', UML13::Link
UML13::Instance.contains_many_uni 'slot', UML13::AttributeLink
UML13::Instance.one_to_many 'linkEnd', UML13::LinkEnd, 'instance'
UML13::Instance.has_many 'classifier', UML13::Classifier, :lowerBound => 1
UML13::Signal.one_to_many 'reception', UML13::Reception, 'signal'
UML13::CreateAction.has_one 'instantiation', UML13::Classifier, :lowerBound => 1
UML13::Action.contains_one_uni 'recurrence', UML13::IterationExpression
UML13::Action.contains_one_uni 'target', UML13::ObjectSetExpression
UML13::Action.contains_one_uni 'script', UML13::ActionExpression
UML13::Action.contains_many_uni 'actualArgument', UML13::Argument
UML13::AttributeLink.has_one 'value', UML13::Instance, :lowerBound => 1
UML13::AttributeLink.has_one 'attribute', UML13::Attribute, :lowerBound => 1
UML13::CallAction.has_one 'operation', UML13::Operation, :lowerBound => 1
UML13::SendAction.has_one 'signal', UML13::Signal, :lowerBound => 1
UML13::ActionSequence.contains_many_uni 'action', UML13::Action
UML13::Argument.contains_one_uni 'value', UML13::Expression
UML13::Link.contains_many_uni 'connection', UML13::LinkEnd, :lowerBound => 2
UML13::Link.has_one 'association', UML13::Association, :lowerBound => 1
UML13::LinkEnd.has_one 'associationEnd', UML13::AssociationEnd, :lowerBound => 1
UML13::LinkEnd.has_one 'participant', UML13::Instance, :lowerBound => 1
UML13::Stimulus.has_one 'dispatchAction', UML13::Action, :lowerBound => 1
UML13::Stimulus.has_one 'communicationLink', UML13::Link
UML13::Stimulus.has_one 'receiver', UML13::Instance, :lowerBound => 1
UML13::Stimulus.has_one 'sender', UML13::Instance, :lowerBound => 1
UML13::Stimulus.has_many 'argument', UML13::Instance
UML13::ComponentInstance.has_many 'resident', UML13::Instance
UML13::NodeInstance.has_many 'resident', UML13::ComponentInstance
UML13::ActivityGraph.contains_many_uni 'partition', UML13::Partition
UML13::Partition.has_many 'contents', UML13::ModelElement
UML13::SubactivityState.contains_one_uni 'dynamicArguments', UML13::ArgListsExpression
UML13::ObjectFlowState.has_one 'type', UML13::Classifier, :lowerBound => 1
UML13::ObjectFlowState.has_one 'available', UML13::Parameter, :lowerBound => 1
UML13::ClassifierInState.has_one 'type', UML13::Classifier, :lowerBound => 1
UML13::ClassifierInState.has_many 'inState', UML13::State
UML13::ActionState.contains_one_uni 'dynamicArguments', UML13::ArgListsExpression
UML13::Package.contains_many 'importedElement', UML13::ElementImport, 'package'
UML13::Diagram.contains_many 'element', UML13::DiagramElement, 'diagram'
UML13::Diagram.has_one 'owner', UML13::ModelElement, :lowerBound => 1
rgen-0.10.2/lib/metamodels/uml13_metamodel_ext.rb 0000664 0000000 0000000 00000001164 14560401624 0021626 0 ustar 00root root 0000000 0000000 # Some handy extensions to the UML13 metamodel
#
module UML13
module AssociationEnd::ClassModule
def otherEnd
association.connection.find{|c| c != self}
end
end
module Classifier::ClassModule
def localCompositeEnd
associationEnd.select{|e| e.aggregation == :composite}
end
def remoteCompositeEnd
associationEnd.otherEnd.select{|e| e.aggregation == :composite}
end
def localNavigableEnd
associationEnd.select{|e| e.isNavigable}
end
def remoteNavigableEnd
associationEnd.otherEnd.select{|e| e.isNavigable}
end
end
end
rgen-0.10.2/lib/mmgen/ 0000775 0000000 0000000 00000000000 14560401624 0014400 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/mmgen/metamodel_generator.rb 0000664 0000000 0000000 00000001044 14560401624 0020741 0 ustar 00root root 0000000 0000000 require 'rgen/environment'
require 'rgen/template_language'
require 'rgen/ecore/ecore'
require 'rgen/ecore/ecore_ext'
require 'mmgen/mm_ext/ecore_mmgen_ext'
module MMGen
module MetamodelGenerator
def generateMetamodel(rootPackage, out_file)
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new(RGen::ECore, File.dirname(out_file))
tpl_path = File.dirname(__FILE__) + '/templates'
tc.load(tpl_path)
tc.expand('metamodel_generator::GenerateMetamodel', File.basename(out_file), :for => rootPackage)
end
end
end rgen-0.10.2/lib/mmgen/mm_ext/ 0000775 0000000 0000000 00000000000 14560401624 0015671 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/mmgen/mm_ext/ecore_mmgen_ext.rb 0000664 0000000 0000000 00000004733 14560401624 0021365 0 ustar 00root root 0000000 0000000 require 'rgen/util/name_helper'
module RGen
module ECore
module EPackage::ClassModule
include RGen::Util::NameHelper
def moduleName
firstToUpper(name)
end
def qualifiedModuleName(rootPackage)
return moduleName unless eSuperPackage and self != rootPackage
eSuperPackage.qualifiedModuleName(rootPackage) + "::" + moduleName
end
def ancestorPackages
return [] unless eSuperPackage
[eSuperPackage] + eSuperPackage.ancestorPackages
end
def ownClasses
eClassifiers.select{|c| c.is_a?(EClass)}
end
def classesInGenerationOrdering
ownClasses + eSubpackages.collect{|s| s.classesInGenerationOrdering}.flatten
end
def needClassReorder?
classesInGenerationOrdering != inheritanceOrderClasses(classesInGenerationOrdering)
end
def allClassesSorted
inheritanceOrderClasses(classesInGenerationOrdering)
end
def inheritanceOrderClasses(cls)
sortArray = cls.dup
i1 = 0
while i1 < sortArray.size-1
again = false
for i2 in i1+1..sortArray.size-1
e2 = sortArray[i2]
if sortArray[i1].eSuperTypes.include?(e2)
sortArray.delete(e2)
sortArray.insert(i1,e2)
again = true
break
end
end
i1 += 1 unless again
end
sortArray
end
end
module EClassifier::ClassModule
include RGen::Util::NameHelper
def classifierName
firstToUpper(name)
end
def qualifiedClassifierName(rootPackage)
(ePackage ? ePackage.qualifiedModuleName(rootPackage) + "::" : "") + classifierName
end
def ancestorPackages
return [] unless ePackage
[ePackage] + ePackage.ancestorPackages
end
def qualifiedClassifierNameIfRequired(package)
if ePackage != package
commonSuper = (package.ancestorPackages & ancestorPackages).first
qualifiedClassifierName(commonSuper)
else
classifierName
end
end
end
module EAttribute::ClassModule
def RubyType
typeMap = {'float' => 'Float', 'int' => 'Integer'}
(self.getType && typeMap[self.getType.downcase]) || 'String'
end
end
end
end
rgen-0.10.2/lib/mmgen/mmgen.rb 0000664 0000000 0000000 00000001346 14560401624 0016034 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..")
require 'ea/xmi_ecore_instantiator'
require 'mmgen/metamodel_generator'
include MMGen::MetamodelGenerator
unless ARGV.length >= 2
puts "Usage: mmgen.rb ()*"
exit
else
file_name = ARGV.shift
root_package_name = ARGV.shift
modules = ARGV
out_file = file_name.gsub(/\.\w+$/,'.rb')
puts out_file
end
env = RGen::Environment.new
File.open(file_name) { |f|
puts "instantiating ..."
XMIECoreInstantiator.new.instantiateECoreModel(env, f.read)
}
rootPackage = env.find(:class => RGen::ECore::EPackage, :name => root_package_name).first
puts "generating ..."
generateMetamodel(rootPackage, out_file, modules)
rgen-0.10.2/lib/mmgen/templates/ 0000775 0000000 0000000 00000000000 14560401624 0016376 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/mmgen/templates/annotations.tpl 0000664 0000000 0000000 00000002353 14560401624 0021457 0 ustar 00root root 0000000 0000000 <% define 'Annotations', :for => EPackage do %>
<% for a in eAnnotations %>
annotation <% expand 'AnnotationArgs', :for => a %>
<% end %>
<% end %>
<% define 'Annotations', :for => EClass do %>
<% for a in eAnnotations %>
annotation <% expand 'AnnotationArgs', :for => a %>
<% end %>
<% end %>
<% define 'Annotations', :for => EStructuralFeature do %>
<% oppositeAnnotations = (this.respond_to?(:eOpposite) && eOpposite && eOpposite.eAnnotations) || [] %>
<% if eAnnotations.size > 0 || oppositeAnnotations.size > 0 %>
do<%iinc%>
<% for a in eAnnotations %>
annotation <% expand 'AnnotationArgs', :for => a %>
<% end %>
<% for a in oppositeAnnotations %>
opposite_annotation <% expand 'AnnotationArgs', :for => a %>
<% end %><%idec%>
end<%nows%>
<% end %>
<% end %>
<% define 'AnnotationArgs', :for => EAnnotation do %>
<% if source.nil? %>
<% expand 'Details' %>
<% else %>
:source => "<%= source.to_s %>", :details => {<% expand 'Details' %>}<%nows%>
<% end %>
<% end %>
<% define 'Details', :for => EAnnotation do %>
<%= details.sort{|a,b| a.key<=>b.key}.collect{ |d| "\'" + d.key + "\' => \'"+ (d.value || "").gsub('\'','\\\'').to_s + "\'"}.join(', ') %><%nows%>
<% end %> rgen-0.10.2/lib/mmgen/templates/metamodel_generator.tpl 0000664 0000000 0000000 00000015461 14560401624 0023143 0 ustar 00root root 0000000 0000000
<% define 'GenerateMetamodel', :for => EPackage do |filename| %>
<% file filename do %>
require 'rgen/metamodel_builder'
<%nl%>
<% if needClassReorder? %>
<% expand 'GeneratePackagesOnly' %>
<% expand 'GenerateClassesReordered' %>
<% else %>
<% expand 'GeneratePackage' %>
<% end %>
<%nl%>
<% expand 'GenerateAssocs' %>
<% end %>
<% end %>
<% define 'GeneratePackage', :for => EPackage do %>
module <%= moduleName %><% iinc %>
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
<% expand 'annotations::Annotations' %>
<%nl%>
<% expand 'EnumTypes' %>
<% for c in ownClasses %><%nl%>
<% expand 'ClassHeader', this, :for => c %><%iinc%>
<% if c.abstract %>abstract<% end %>
<% expand 'annotations::Annotations', :for => c %>
<% expand 'Attribute', this, :foreach => c.eAttributes %>
<%idec%>
end
<% end %><%nl%>
<% for p in eSubpackages %>
<%nl%><% expand 'GeneratePackage', :for => p %>
<% end %><%idec%>
end
<% end %>
<% define 'GenerateClassesReordered', :for => EPackage do %>
<% for c in allClassesSorted %><%nl%>
<% expand 'ClassHeaderFullyQualified', this, :for => c %><%iinc%>
<% if c.abstract %>abstract<% end %>
<% expand 'annotations::Annotations', :for => c %>
<% expand 'Attribute', this, :foreach => c.eAttributes %>
<%idec%>
end
<% end %><%nl%>
<% end %>
<% define 'GeneratePackagesOnly', :for => EPackage do %>
module <%= moduleName %><% iinc %>
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
<% expand 'annotations::Annotations' %>
<%nl%>
<% expand 'EnumTypes' %>
<% for p in eSubpackages %>
<%nl%><% expand 'GeneratePackagesOnly', :for => p %>
<% end %><%idec%>
end
<% end %>
<% define 'Attribute', :for => EAttribute do |rootp| %>
<% if upperBound == 1%>has_attr<% else %>has_many_attr<% end %> '<%= name %>', <%nows%>
<% if eType.is_a?(EEnum) %><%nows%>
<%= eType.qualifiedClassifierName(rootp) %><%nows%>
<% else %><%nows%>
<%= eType && eType.instanceClass.to_s %><%nows%>
<% end %><%nows%>
<% for p in RGen::MetamodelBuilder::Intermediate::Attribute.properties %>
<% unless p == :name || (p == :upperBound && (upperBound == 1 || upperBound == -1)) ||
RGen::MetamodelBuilder::Intermediate::Attribute.default_value(p) == getGeneric(p) %>
, :<%=p%> => <%nows%>
<% if getGeneric(p).is_a?(String) %>
"<%= getGeneric(p) %>"<%nows%>
<% elsif getGeneric(p).is_a?(Symbol) %>
:<%= getGeneric(p) %><%nows%>
<% else %>
<%= getGeneric(p) %><%nows%>
<% end %>
<% end %>
<% end %>
<%ws%><% expand 'annotations::Annotations' %><%nl%>
<% end %>
<% define 'EnumTypes', :for => EPackage do %>
<% for enum in eClassifiers.select{|c| c.is_a?(EEnum)} %>
<%= enum.name %> = Enum.new(:name => '<%= enum.name %>', :literals =>[ <%nows%>
<%= enum.eLiterals.collect { |lit| ":"+(lit.name =~ /^\d|\W/ ? "'"+lit.name+"'" : lit.name) }.join(', ') %> ])
<% end %>
<% end %>
<% define 'GenerateAssocs', :for => EPackage do %>
<% refDone = {} %>
<% for ref in eAllClassifiers.select{|c| c.is_a?(EClass)}.eReferences %>
<% if !refDone[ref] && ref.eOpposite %>
<% ref = ref.eOpposite if ref.eOpposite.containment %>
<% refDone[ref] = refDone[ref.eOpposite] = true %>
<% if !ref.many && !ref.eOpposite.many %>
<% if ref.containment %>
<% expand 'Reference', "contains_one", this, :for => ref %>
<% else %>
<% expand 'Reference', "one_to_one", this, :for => ref %>
<% end %>
<% elsif !ref.many && ref.eOpposite.many %>
<% expand 'Reference', "many_to_one", this, :for => ref %>
<% elsif ref.many && !ref.eOpposite.many %>
<% if ref.containment %>
<% expand 'Reference', "contains_many", this, :for => ref %>
<% else %>
<% expand 'Reference', "one_to_many", this, :for => ref %>
<% end %>
<% elsif ref.many && ref.eOpposite.many %>
<% expand 'Reference', "many_to_many", this, :for => ref %>
<% end %>
<% expand 'annotations::Annotations', :for => ref %><%nl%>
<% elsif !refDone[ref] %>
<% refDone[ref] = true %>
<% if !ref.many %>
<% if ref.containment %>
<% expand 'Reference', "contains_one_uni", this, :for => ref %>
<% else %>
<% expand 'Reference', "has_one", this, :for => ref %>
<% end %>
<% elsif ref.many %>
<% if ref.containment %>
<% expand 'Reference', "contains_many_uni", this, :for => ref %>
<% else %>
<% expand 'Reference', "has_many", this, :for => ref %>
<% end %>
<% end %>
<% expand 'annotations::Annotations', :for => ref %><%nl%>
<% end %>
<% end %>
<% end %>
<% define 'Reference', :for => EReference do |cmd, rootpackage| %>
<%= eContainingClass.qualifiedClassifierName(rootpackage) %>.<%= cmd %> '<%= name %>', <%= eType && eType.qualifiedClassifierName(rootpackage) %><%nows%>
<% if eOpposite %><%nows%>
, '<%= eOpposite.name%>'<%nows%>
<% end %><%nows%>
<% pset = RGen::MetamodelBuilder::Intermediate::Reference.properties.reject{|p| p == :name || p == :upperBound || p == :containment} %>
<% for p in pset.reject{|p| RGen::MetamodelBuilder::Intermediate::Reference.default_value(p) == getGeneric(p)} %>
, :<%=p%> => <%=getGeneric(p)%><%nows%>
<% end %>
<% if eOpposite %>
<% for p in pset.reject{|p| RGen::MetamodelBuilder::Intermediate::Reference.default_value(p) == eOpposite.getGeneric(p)} %>
, :opposite_<%=p%> => <%=eOpposite.getGeneric(p)%><%nows%>
<% end %>
<% end %><%ws%>
<% end %>
<% define 'ClassHeader', :for => EClass do |rootp| %>
class <%= classifierName %> < <% nows %>
<% if eSuperTypes.size > 1 %><% nows %>
RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.qualifiedClassifierNameIfRequired(rootp)}.join(', ') %>)
<% elsif eSuperTypes.size > 0 %><% nows %>
<%= eSuperTypes.first.qualifiedClassifierNameIfRequired(rootp) %>
<% else %><% nows %>
RGen::MetamodelBuilder::MMBase
<% end %>
<% end %>
<% define 'ClassHeaderFullyQualified', :for => EClass do |rootp| %>
class <%= qualifiedClassifierName(rootp) %> < <% nows %>
<% if eSuperTypes.size > 1 %><% nows %>
RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.qualifiedClassifierName(rootp)}.join(', ') %>)
<% elsif eSuperTypes.size > 0 %><% nows %>
<%= eSuperTypes.first.qualifiedClassifierName(rootp) %>
<% else %><% nows %>
RGen::MetamodelBuilder::MMBase
<% end %>
<% end %>
rgen-0.10.2/lib/rgen/ 0000775 0000000 0000000 00000000000 14560401624 0014230 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/array_extensions.rb 0000664 0000000 0000000 00000003024 14560401624 0020151 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/metamodel_builder'
class Array
def >>(method)
compact.inject([]) { |r,e| r | ( (o=e.send(method)).is_a?(Array) ? o : [o] ) }
end
def method_missing(m, *args)
# This extensions has the side effect that it allows to call any method on any
# empty array with an empty array as the result. This behavior is required for
# navigating models.
#
# This is a problem for Hash[] called with an (empty) array of tupels.
# It will call to_hash expecting a Hash as the result. When it gets an array instead,
# it fails with an exception. Make sure it gets a NoMethodException as without this
# extension and it will catch that and return an empty hash as expected.
#
# Similar problems exist for other Ruby built-in methods which are expected to fail.
#
return super unless (size == 0 &&
m != :to_hash && m != :to_str) ||
compact.any?{|e| e.is_a? RGen::MetamodelBuilder::MMBase}
# use an array to build the result to achiev similar ordering
result = []
inResult = {}
compact.each do |e|
if e.is_a? RGen::MetamodelBuilder::MMBase
((o=e.send(m)).is_a?(Array) ? o : [o] ).each do |v|
next if inResult[v.object_id]
inResult[v.object_id] = true
result << v
end
else
raise StandardError.new("Trying to call a method on an array element not a RGen MMBase")
end
end
result.compact
end
end
rgen-0.10.2/lib/rgen/ecore/ 0000775 0000000 0000000 00000000000 14560401624 0015325 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/ecore/ecore.rb 0000664 0000000 0000000 00000021072 14560401624 0016751 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder'
module RGen
extend RGen::MetamodelBuilder::ModuleExtension
# This is the ECore metamodel described using the RGen::MetamodelBuilder language.
#
# Known differences to the Java/EMF implementation are:
# * Attributes can not be "many"
#
module ECore
extend RGen::MetamodelBuilder::ModuleExtension
class EObject < RGen::MetamodelBuilder::MMBase
abstract
end
class EModelElement < RGen::MetamodelBuilder::MMBase
abstract
end
class EAnnotation < RGen::MetamodelBuilder::MMMultiple(EModelElement, EObject)
has_attr 'source', String
end
class ENamedElement < EModelElement
abstract
has_attr 'name', String, :lowerBound => 1
end
class ETypedElement < ENamedElement
abstract
has_attr 'lowerBound', Integer, :defaultValueLiteral => "0"
has_attr 'ordered', Boolean, :defaultValueLiteral => "true"
has_attr 'unique', Boolean, :defaultValueLiteral => "true"
has_attr 'upperBound', Integer, :defaultValueLiteral => "1"
has_attr 'many', Boolean, :derived=>true
has_attr 'required', Boolean, :derived=>true
module ClassModule
def many_derived
upperBound > 1 || upperBound == -1
end
def required_derived
lowerBound > 0
end
end
end
class EStructuralFeature < ETypedElement
abstract
has_attr 'changeable', Boolean, :defaultValueLiteral => "true"
has_attr 'defaultValue', Object, :derived=>true
has_attr 'defaultValueLiteral', String
has_attr 'derived', Boolean, :defaultValueLiteral => "false"
has_attr 'transient', Boolean, :defaultValueLiteral => "false"
has_attr 'unsettable', Boolean, :defaultValueLiteral => "false"
has_attr 'volatile', Boolean, :defaultValueLiteral => "false"
module ClassModule
def defaultValue_derived
return nil if defaultValueLiteral.nil?
case eType
when EInt, ELong
defaultValueLiteral.to_i
when EFloat
defaultValueLiteral.to_f
when EEnum
defaultValueLiteral.to_sym
when EBoolean
defaultValueLiteral == "true"
when EString
defaultValueLiteral
else
raise "Unhandled type"
end
end
end
end
class EAttribute < EStructuralFeature
has_attr 'iD', Boolean, :defaultValueLiteral => "false"
end
class EClassifier < ENamedElement
abstract
has_attr 'defaultValue', Object, :derived=>true
has_attr 'instanceClass', Object, :derived=>true
has_attr 'instanceClassName', String
module ClassModule
def instanceClass_derived
map = {"java.lang.string" => "String",
"boolean" => "RGen::MetamodelBuilder::DataTypes::Boolean",
"int" => "Integer",
"long" => "RGen::MetamodelBuilder::DataTypes::Long"}
icn = instanceClassName
icn = "NilClass" if icn.nil?
icn = map[icn.downcase] if map[icn.downcase]
eval(icn)
end
end
end
class EDataType < EClassifier
has_attr 'serializable', Boolean
end
class EGenericType < EDataType
has_one 'eClassifier', EDataType
end
class ETypeArgument < EModelElement
has_one 'eClassifier', EDataType
end
class EEnum < EDataType
end
class EEnumLiteral < ENamedElement
# instance may point to a "singleton object" (e.g. a Symbol) representing the literal
# has_attr 'instance', Object, :eType=>:EEnumerator, :transient=>true
has_attr 'literal', String
has_attr 'value', Integer
end
# TODO: check if required
class EFactory < EModelElement
end
class EOperation < ETypedElement
end
class EPackage < ENamedElement
has_attr 'nsPrefix', String
has_attr 'nsURI', String
end
class EParameter < ETypedElement
end
class EReference < EStructuralFeature
has_attr 'container', Boolean, :derived=>true
has_attr 'containment', Boolean, :defaultValueLiteral => "false"
has_attr 'resolveProxies', Boolean, :defaultValueLiteral => "true"
end
class EStringToStringMapEntry < RGen::MetamodelBuilder::MMBase
has_attr 'key', String
has_attr 'value', String
end
class EClass < EClassifier
has_attr 'abstract', Boolean, :defaultValueLiteral => "false"
has_attr 'interface', Boolean, :defaultValueLiteral => "false"
has_one 'eIDAttribute', ECore::EAttribute, :derived=>true, :resolveProxies=>false
has_many 'eAllAttributes', ECore::EAttribute, :derived=>true
has_many 'eAllContainments', ECore::EReference, :derived=>true
has_many 'eAllOperations', ECore::EOperation, :derived=>true
has_many 'eAllReferences', ECore::EReference, :derived=>true
has_many 'eAllStructuralFeatures', ECore::EStructuralFeature, :derived=>true
has_many 'eAllSuperTypes', ECore::EClass, :derived=>true
has_many 'eAttributes', ECore::EAttribute, :derived=>true
has_many 'eReferences', ECore::EReference, :derived=>true
module ClassModule
def eAllAttributes_derived
eAttributes + eSuperTypes.eAllAttributes
end
def eAllContainments_derived
eReferences.select{|r| r.containment} + eSuperTypes.eAllContainments
end
def eAllReferences_derived
eReferences + eSuperTypes.eAllReferences
end
def eAllStructuralFeatures_derived
eStructuralFeatures + eSuperTypes.eAllStructuralFeatures
end
def eAllSuperTypes_derived
eSuperTypes + eSuperTypes.eAllSuperTypes
end
def eAttributes_derived
eStructuralFeatures.select{|f| f.is_a?(EAttribute)}
end
def eReferences_derived
eStructuralFeatures.select{|f| f.is_a?(EReference)}
end
end
end
# predefined datatypes
EString = EDataType.new(:name => "EString", :instanceClassName => "String")
EInt = EDataType.new(:name => "EInt", :instanceClassName => "Integer")
ELong = EDataType.new(:name => "ELong", :instanceClassName => "Long")
EBoolean = EDataType.new(:name => "EBoolean", :instanceClassName => "Boolean")
EFloat = EDataType.new(:name => "EFloat", :instanceClassName => "Float")
ERubyObject = EDataType.new(:name => "ERubyObject", :instanceClassName => "Object")
EJavaObject = EDataType.new(:name => "EJavaObject")
ERubyClass = EDataType.new(:name => "ERubyClass", :instanceClassName => "Class")
EJavaClass = EDataType.new(:name => "EJavaClass")
end
ECore::EModelElement.contains_many 'eAnnotations', ECore::EAnnotation, 'eModelElement', :resolveProxies=>false
ECore::EAnnotation.contains_many_uni 'details', ECore::EStringToStringMapEntry, :resolveProxies=>false
ECore::EAnnotation.contains_many_uni 'contents', ECore::EObject, :resolveProxies=>false
ECore::EAnnotation.has_many 'references', ECore::EObject
ECore::EPackage.contains_many 'eClassifiers', ECore::EClassifier, 'ePackage'
ECore::EPackage.contains_many 'eSubpackages', ECore::EPackage, 'eSuperPackage'
ECore::ETypedElement.has_one 'eType', ECore::EClassifier
ECore::EClass.contains_many 'eOperations', ECore::EOperation, 'eContainingClass', :resolveProxies=>false
ECore::EClass.contains_many 'eStructuralFeatures', ECore::EStructuralFeature, 'eContainingClass', :resolveProxies=>false
ECore::EClass.has_many 'eSuperTypes', ECore::EClass
ECore::EEnum.contains_many 'eLiterals', ECore::EEnumLiteral, 'eEnum', :resolveProxies=>false
ECore::EFactory.one_to_one 'ePackage', ECore::EPackage, 'eFactoryInstance', :lowerBound=>1, :transient=>true, :resolveProxies=>false
ECore::EOperation.contains_many 'eParameters', ECore::EParameter, 'eOperation', :resolveProxies=>false
ECore::EOperation.has_many 'eExceptions', ECore::EClassifier
ECore::EReference.has_one 'eOpposite', ECore::EReference
ECore::EAttribute.has_one 'eAttributeType', ECore::EDataType, :lowerBound=>1, :derived=>true
ECore::EReference.has_one 'eReferenceType', ECore::EClass, :lowerBound=>1, :derived=>true
ECore::EParameter.contains_one 'eGenericType', ECore::EGenericType, 'eParameter'
ECore::EGenericType.contains_many 'eTypeArguments', ECore::ETypeArgument, 'eGenericType'
end
rgen-0.10.2/lib/rgen/ecore/ecore_builder_methods.rb 0000664 0000000 0000000 00000005452 14560401624 0022206 0 ustar 00root root 0000000 0000000 module RGen
module ECore
module ECoreBuilderMethods
def eAttr(name, type, argHash={}, &block)
eAttribute(name, {:eType => type}.merge(argHash), &block)
end
def eRef(name, type, argHash={}, &block)
eReference(name, {:eType => type}.merge(argHash), &block)
end
# create bidirectional reference at once
def eBiRef(name, type, oppositeName, argHash={})
raise BuilderError.new("eOpposite attribute not allowed for bidirectional references") \
if argHash[:eOpposite] || argHash[:opposite_eOpposite]
eReference(name, {:eType => type}.merge(argHash.reject{|k,v| k.to_s =~ /^opposite_/})) do
eReference oppositeName, {:eContainingClass => type, :eType => _context(2),
:as => :eOpposite, :eOpposite => _context(1)}.
merge(Hash[*(argHash.select{|k,v| k.to_s =~ /^opposite_/}.
collect{|p| [p[0].to_s.sub(/^opposite_/,"").to_sym, p[1]]}.flatten)])
end
end
# reference shortcuts
alias references_1 eRef
alias references_one eRef
def references_N(name, type, argHash={})
eRef(name, type, {:upperBound => -1}.merge(argHash))
end
alias references_many references_N
def references_1to1(name, type, oppositeName, argHash={})
eBiRef(name, type, oppositeName, {:upperBound => 1, :opposite_upperBound => 1}.merge(argHash))
end
alias references_one_to_one references_1to1
def references_1toN(name, type, oppositeName, argHash={})
eBiRef(name, type, oppositeName, {:upperBound => -1, :opposite_upperBound => 1}.merge(argHash))
end
alias references_one_to_many references_1toN
def references_Nto1(name, type, oppositeName, argHash={})
eBiRef(name, type, oppositeName, {:upperBound => 1, :opposite_upperBound => -1}.merge(argHash))
end
alias references_many_to_one references_Nto1
def references_MtoN(name, type, oppositeName, argHash={})
eBiRef(name, type, oppositeName, {:upperBound => -1, :opposite_upperBound => -1}.merge(argHash))
end
alias references_many_to_many references_MtoN
# containment reference shortcuts
def contains_1(name, type, argHash={})
references_1(name, type, {:containment => true}.merge(argHash))
end
alias contains_one contains_1
def contains_N(name, type, argHash={})
references_N(name, type, {:containment => true}.merge(argHash))
end
alias contains_many contains_N
def contains_1to1(name, type, oppositeName, argHash={})
references_1to1(name, type, oppositeName, {:containment => true}.merge(argHash))
end
alias contains_one_to_one contains_1to1
def contains_1toN(name, type, oppositeName, argHash={})
references_1toN(name, type, oppositeName, {:containment => true}.merge(argHash))
end
alias contains_one_to_many contains_1toN
end
end
end rgen-0.10.2/lib/rgen/ecore/ecore_ext.rb 0000664 0000000 0000000 00000003176 14560401624 0017636 0 ustar 00root root 0000000 0000000 require 'rgen/array_extensions'
require 'rgen/ecore/ecore'
module RGen
module ECore
# make super type reference bidirectional
EClass.many_to_many 'eSuperTypes', ECore::EClass, 'eSubTypes'
module EModelElement::ClassModule
def annotationValue(source, tag)
detail = eAnnotations.select{ |a| a.source == source }.details.find{ |d| d.key == tag }
detail && detail.value
end
end
module EPackage::ClassModule
def qualifiedName
if eSuperPackage
eSuperPackage.qualifiedName+"::"+name
else
name
end
end
def eAllClassifiers
eClassifiers + eSubpackages.eAllClassifiers
end
def eAllSubpackages
eSubpackages + eSubpackages.eAllSubpackages
end
def eClasses
eClassifiers.select{|c| c.is_a?(ECore::EClass)}
end
def eAllClasses
eClasses + eSubpackages.eAllClasses
end
def eDataTypes
eClassifiers.select{|c| c.is_a?(ECore::EDataType)}
end
def eAllDataTypes
eDataTypes + eSubpackages.eAllDataTypes
end
end
module EClass::ClassModule
def qualifiedName
if ePackage
ePackage.qualifiedName+"::"+name
else
name
end
end
def eAllSubTypes
eSubTypes + eSubTypes.eAllSubTypes
end
def concrete
!(abstract || interface)
end
def isAssignableFrom(cls)
cls == self || cls.eAllSuperTypes.any? { |super_type| super_type == self }
end
end
end
end
rgen-0.10.2/lib/rgen/ecore/ecore_interface.rb 0000664 0000000 0000000 00000002403 14560401624 0020766 0 ustar 00root root 0000000 0000000 module RGen
module ECore
# Mixin to provide access to the ECore model describing a Ruby class or module
# built using MetamodelBuilder.
# The module should be used to +extend+ a class or module, i.e. to make its
# methods class methods.
#
module ECoreInterface
# This method will lazily build to ECore model element belonging to the calling
# class or module using RubyToECore.
# Alternatively, the ECore model element can be provided up front. This is used
# when the Ruby metamodel classes and modules are created from ECore.
#
def ecore
if defined?(@ecore)
@ecore
else
unless defined?(@@transformer)
require 'rgen/ecore/ruby_to_ecore'
@@transformer = RubyToECore.new
end
@@transformer.trans(self)
end
end
# This method can be used to clear the ecore cache after the metamodel classes
# or modules have been changed; the ecore model will be recreated on next access
# to the +ecore+ method
# Beware, the ecore cache is global, i.e. for all metamodels.
#
def self.clear_ecore_cache
require 'rgen/ecore/ruby_to_ecore'
@@transformer = RubyToECore.new
end
def _set_ecore_internal(ecore) # :nodoc:
@ecore = ecore
end
end
end
end
rgen-0.10.2/lib/rgen/ecore/ecore_to_json.rb 0000664 0000000 0000000 00000010360 14560401624 0020502 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore'
require 'json'
require 'andand'
module RGen
module ECore
# ECoreToJson can turn ECore models into their JSON metamodel representations
class ECoreToJson
def initialize
end
def root_elements_to_json_string(root_elements)
JSON.pretty_generate(root_elements.map do |el|
if el.is_a?(RGen::ECore::EPackage)
epackage(el)
elsif el.is_a?(RGen::ECore::EClass)
eclass(el)
else
raise "Not implemented for #{el}"
end
end)
end
def epackage_to_json(package)
epackage(package)
end
def ecore_datatypes
[RGen::ECore::EString, RGen::ECore::EInt, RGen::ECore::ELong, RGen::ECore::EBoolean, RGen::ECore::EFloat,
RGen::ECore::ERubyObject, RGen::ECore::EJavaObject, RGen::ECore::ERubyClass, RGen::ECore::EJavaClass]
.map {|dt| edatatype(dt)}
end
def epackage_to_json_pretty_string(package, append = [])
JSON.pretty_generate([epackage_to_json(package)] + append)
end
def epackage_to_json_string(package, append = [])
JSON.generate([epackage_to_json(package)] + append)
end
def emodelelement(me)
{
:eAnnotations => me.eAnnotations.map { |e| eannotation(e) }
}
end
def enamedelement(ne)
merge(emodelelement(ne), {:name => ne.name})
end
def epackage(package)
merge(enamedelement(package), {
:_class_ref => 'RGen.ECore.EPackage',
:eClassifiers => package.eClassifiers.map do |classifier|
if classifier.is_a?(RGen::ECore::EClass)
eclass(classifier)
elsif classifier.is_a?(RGen::ECore::EEnum)
eenum(classifier)
else
edatatype(classifier)
end
end,
:eSubpackages => package.eSubpackages.map { |sp| epackage(sp) },
:nsURI => package.nsURI,
:nsPrefix => package.nsPrefix
})
end
def eclassifier(classifier)
enamedelement(classifier).merge({
# omit :instanceClassName => classifier.instanceClassName
})
end
def eclass(_class)
merge(eclassifier(_class), {
:_class_ref => 'RGen.ECore.EClass',
:abstract => _class.abstract,
:interface => _class.interface,
:eStructuralFeatures => _class.eStructuralFeatures.map do |sf|
if sf.is_a?(RGen::ECore::EReference)
ereference(sf)
else
eattribute(sf)
end
end,
:eSuperTypes => _class.eSuperTypes.map { |st| {:_ref => ref_id(st)} }
})
end
def edatatype(_datatype)
merge(eclassifier(_datatype), {
:_class_ref => 'RGen.ECore.EDataType',
:serializable => _datatype.serializable,
:instanceClassName => _datatype.instanceClassName
})
end
def eenum(enum)
merge(edatatype(enum), {
:_class_ref => 'RGen.ECore.EEnum',
:eLiterals => enum.eLiterals.map do |l|
merge(enamedelement(l), {
:_class_ref => 'RGen.ECore.EEnumLiteral',
:value => l.value,
})
end
})
end
def eannotation(e)
merge(emodelelement(e), {
:_class_ref => 'RGen.ECore.EAnnotation',
:source => e.source,
:details => e.details.map do |d|
merge({}, {
:_class_ref => 'RGen.ECore.EStringToStringMapEntry',
:key => d.key,
:value => d.value
})
end
})
end
def etypedelement(te)
merge(enamedelement(te), {
:ordered => te.ordered,
:unique => te.unique,
:lowerBound => te.lowerBound,
:upperBound => te.upperBound,
:eType => {:_ref => te.eType ? ref_id(te.eType) : nil}
})
end
def estructuralfeature(sf)
merge(etypedelement(sf), {
:changeable => sf.changeable,
:volatile => sf.volatile,
:transient => sf.transient,
:defaultValueLiteral => sf.defaultValueLiteral,
:unsettable => sf.unsettable,
:derived => sf.derived,
})
end
def eattribute(attr)
merge(estructuralfeature(attr), {
:_class_ref => 'RGen.ECore.EAttribute',
:iD => attr.iD
})
end
def ereference(ref)
merge(estructuralfeature(ref), {
:_class_ref => 'RGen.ECore.EReference',
:containment => ref.containment,
:resolveProxies => ref.resolveProxies,
:eOpposite => ref.eOpposite ? {:_ref => "#{ref_id(ref.eOpposite.eContainer)}.#{ref.eOpposite.name}"} : nil
})
end
def ref_id(obj)
res = ref_parts(obj)
res.join('.')
end
def ref_parts(obj)
return [obj.name] unless obj.andand.eContainer
ref_parts(obj.eContainer) << obj.name
end
def merge(hash, values)
values.each { |k, v| hash[k] = v unless v.nil? }
hash
end
end
end
end rgen-0.10.2/lib/rgen/ecore/ecore_to_ruby.rb 0000775 0000000 0000000 00000016611 14560401624 0020522 0 ustar 00root root 0000000 0000000 require 'set'
require 'rgen/ecore/ecore'
module RGen
module ECore
# ECoreToRuby can turn ECore models into their Ruby metamodel representations
class ECoreToRuby
def initialize
@modules = {}
@classifiers = {}
@features_added = {}
@reserved = Set.new(Object.methods).merge(Kernel.methods)
end
# Create a Ruby module representing +epackage+.
# This includes all nested modules/packages, classes and enums.
#
# If a parent module is provided with the "under" parameter,
# the new module will be nested under the parent module.
#
# If the parent module has a non-temporary name,
# (more precisely: a non-temporary classpath) i.e. if it is reachable
# via a path of constant names from the root, then the nested
# modules and classes will also have non-temporary names.
# In particular, this means that they will keep their names even
# if they are assigned to new constants.
#
# If no parent module is provided or the parent module has a
# temporary name by itself, then the nested modules and classes will
# also have temporary names. This means that their name will stay
# 'volatile' until they are assigned to constants reachable from
# the root and the Module#name method is called for the first time.
#
# While the second approach is more flexible, it can come with a major
# performance impact. The reason is that Ruby searches the space of
# all known non-temporary classes/modules every time the name
# of a class/module with a temporary name is queried.
#
def create_module(epackage, under=Module.new)
with_empty_constant_order_helper do
temp = under.to_s.start_with?("#")
mod = create_module_internal(epackage, under, temp)
epackage.eAllClassifiers.each do |c|
if c.is_a?(RGen::ECore::EClass)
create_class(c, temp)
elsif c.is_a?(RGen::ECore::EEnum)
create_enum(c)
end
end
load_classes_with_reserved_keywords(epackage)
mod
end
end
private
def load_classes_with_reserved_keywords(epackage)
epackage.eAllClassifiers.each do |eclass|
# we early load classes which have ruby reserved keywords
if eclass.is_a?(RGen::ECore::EClass)
reserved_used = eclass.eStructuralFeatures.any? { |f| @reserved.include?(f.name.to_sym) }
add_features(eclass) if reserved_used
end
end
end
def create_module_internal(epackage, under, temp)
return @modules[epackage] if @modules[epackage]
if temp
mod = Module.new do
extend RGen::MetamodelBuilder::ModuleExtension
end
under.const_set(epackage.name, mod)
else
under.module_eval <<-END
module #{epackage.name}
extend RGen::MetamodelBuilder::ModuleExtension
end
END
mod = under.const_get(epackage.name)
end
@modules[epackage] = mod
epackage.eSubpackages.each{|p| create_module_internal(p, mod, temp)}
mod._set_ecore_internal(epackage)
mod
end
def create_class(eclass, temp)
return @classifiers[eclass] if @classifiers[eclass]
mod = @modules[eclass.ePackage]
if temp
cls = Class.new(super_class(eclass, temp)) do
abstract if eclass.abstract
class << self
attr_accessor :_ecore_to_ruby
end
end
mod.const_set(eclass.name, cls)
else
mod.module_eval <<-END
class #{eclass.name} < #{super_class(eclass, temp)}
#{eclass.abstract ? 'abstract' : ''}
class << self
attr_accessor :_ecore_to_ruby
end
end
END
cls = mod.const_get(eclass.name)
end
class << eclass
attr_accessor :instanceClass
def instanceClassName
instanceClass.to_s
end
end
eclass.instanceClass = cls
cls::ClassModule.module_eval do
alias _method_missing method_missing
def method_missing(m, *args)
if self.class._ecore_to_ruby.add_features(self.class.ecore)
send(m, *args)
else
_method_missing(m, *args)
end
end
alias _respond_to respond_to?
def respond_to?(m, include_all=false)
self.class._ecore_to_ruby.add_features(self.class.ecore)
_respond_to(m)
end
end
@classifiers[eclass] = cls
cls._set_ecore_internal(eclass)
cls._ecore_to_ruby = self
cls
end
def create_enum(eenum)
return @classifiers[eenum] if @classifiers[eenum]
e = RGen::MetamodelBuilder::DataTypes::Enum.new(eenum.eLiterals.collect{|l| l.name.to_sym})
@classifiers[eenum] = e
@modules[eenum.ePackage].const_set(eenum.name, e)
e
end
class FeatureWrapper
def initialize(efeature, classifiers)
@efeature = efeature
@classifiers = classifiers
end
def value(prop)
return false if prop == :containment && @efeature.is_a?(RGen::ECore::EAttribute)
@efeature.send(prop)
end
def many?
@efeature.many
end
def reference?
@efeature.is_a?(RGen::ECore::EReference)
end
def opposite
@efeature.eOpposite
end
def impl_type
etype = @efeature.eType
if etype.is_a?(RGen::ECore::EClass) || etype.is_a?(RGen::ECore::EEnum)
@classifiers[etype]
else
ic = etype.instanceClass
if ic
ic
else
raise "unknown type: #{etype.name}"
end
end
end
end
def super_class(eclass, temp)
super_types = eclass.eSuperTypes
if temp
case super_types.size
when 0
RGen::MetamodelBuilder::MMBase
when 1
create_class(super_types.first, temp)
else
RGen::MetamodelBuilder::MMMultiple(*super_types.collect{|t| create_class(t, temp)})
end
else
case super_types.size
when 0
"RGen::MetamodelBuilder::MMBase"
when 1
create_class(super_types.first, temp).name
else
"RGen::MetamodelBuilder::MMMultiple(" +
super_types.collect{|t| create_class(t, temp).name}.join(",") + ")"
end
end
end
class EmptyConstantOrderHelper
def classCreated(c); end
def moduleCreated(m); end
def enumCreated(e); end
end
def with_empty_constant_order_helper
orig_coh = RGen::MetamodelBuilder::ConstantOrderHelper
RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, EmptyConstantOrderHelper.new)
begin
result = yield
ensure
RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, orig_coh)
end
result
end
public
def add_features(eclass)
return false if @features_added[eclass]
c = @classifiers[eclass]
eclass.eStructuralFeatures.each do |f|
w1 = FeatureWrapper.new(f, @classifiers)
w2 = FeatureWrapper.new(f.eOpposite, @classifiers) if f.is_a?(RGen::ECore::EReference) && f.eOpposite
c.module_eval do
if w1.many?
_build_many_methods(w1, w2)
else
_build_one_methods(w1, w2)
end
end
end
@features_added[eclass] = true
eclass.eSuperTypes.each do |t|
add_features(t)
end
true
end
end
end
end
rgen-0.10.2/lib/rgen/ecore/ruby_to_ecore.rb 0000664 0000000 0000000 00000006132 14560401624 0020514 0 ustar 00root root 0000000 0000000 require 'rgen/transformer'
require 'rgen/ecore/ecore'
module RGen
module ECore
# This transformer creates an ECore model from Ruby classes built
# by RGen::MetamodelBuilder.
#
class RubyToECore < Transformer
transform Class, :to => EClass, :if => :convert? do
{ :name => name.gsub(/.*::(\w+)$/,'\1'),
:abstract => _abstract_class,
:interface => false,
:eStructuralFeatures => trans(_metamodel_description),
:ePackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil),
:eSuperTypes => trans(superclasses),
:instanceClassName => name,
:eAnnotations => trans(_annotations)
}
end
method :superclasses do
if superclass.respond_to?(:multiple_superclasses) && superclass.multiple_superclasses
superclass.multiple_superclasses
else
[ superclass ]
end
end
transform Module, :to => EPackage, :if => :convert? do
@enumParentModule ||= {}
_constants = _constantOrder + (constants - _constantOrder)
_constants.select {|c| const_get(c).is_a?(MetamodelBuilder::DataTypes::Enum)}.
each {|c| @enumParentModule[const_get(c)] = @current_object}
{ :name => name.gsub(/.*::(\w+)$/,'\1'),
:eClassifiers => trans(_constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Class) ||
(c.is_a?(MetamodelBuilder::DataTypes::Enum) && c != MetamodelBuilder::DataTypes::Boolean) }),
:eSuperPackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil),
:eSubpackages => trans(_constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Module) && !c.is_a?(Class)}),
:eAnnotations => trans(_annotations)
}
end
method :convert? do
@current_object.respond_to?(:ecore) && @current_object != RGen::MetamodelBuilder::MMBase
end
transform MetamodelBuilder::Intermediate::Attribute, :to => EAttribute do
Hash[*MetamodelBuilder::Intermediate::Attribute.properties.collect{|p| [p, value(p)]}.flatten].merge({
:eType => (etype == :EEnumerable ? trans(impl_type) : RGen::ECore.const_get(etype)),
:eAnnotations => trans(annotations)
})
end
transform MetamodelBuilder::Intermediate::Reference, :to => EReference do
Hash[*MetamodelBuilder::Intermediate::Reference.properties.collect{|p| [p, value(p)]}.flatten].merge({
:eType => trans(impl_type),
:eOpposite => trans(opposite),
:eAnnotations => trans(annotations)
})
end
transform MetamodelBuilder::Intermediate::Annotation, :to => EAnnotation do
{ :source => source,
:details => details.keys.collect do |k|
e = RGen::ECore::EStringToStringMapEntry.new
e.key = k
e.value = details[k]
e
end
}
end
transform MetamodelBuilder::DataTypes::Enum, :to => EEnum do
{ :name => name,
:instanceClassName => @enumParentModule && @enumParentModule[@current_object] && @enumParentModule[@current_object].name+"::"+name,
:eLiterals => literals.collect do |l|
lit = RGen::ECore::EEnumLiteral.new
lit.name = l.to_s
lit
end }
end
end
end
end
rgen-0.10.2/lib/rgen/environment.rb 0000664 0000000 0000000 00000005632 14560401624 0017127 0 ustar 00root root 0000000 0000000 module RGen
# An Environment is used to hold model elements.
#
class Environment
def initialize
@elements = {}
@subClasses = {}
@subClassesUpdated = {}
@deleted = {}
@deletedClasses = {}
end
# Add a model element. Returns the environment so <<
can be chained.
#
def <<(el)
clazz = el.class
@elements[clazz] ||= []
@elements[clazz] << el
updateSubClasses(clazz)
self
end
# Removes model element from environment.
def delete(el)
@deleted[el] = true
@deletedClasses[el.class] = true
end
# Iterates each element
#
def each(&b)
removeDeleted
@elements.values.flatten.each(&b)
end
# Return the elements of the environment as an array
#
def elements
removeDeleted
@elements.values.flatten
end
# This method can be used to instantiate a class and automatically put it into
# the environment. The new instance is returned.
#
def new(clazz, *args)
obj = clazz.new(*args)
self << obj
obj
end
# Finds and returns model elements in the environment.
#
# The search description argument must be a hash specifying attribute/value pairs.
# Only model elements are returned which respond to the specified attribute methods
# and return the specified values as result of these attribute methods.
#
# As a special hash key :class can be used to look for model elements of a specific
# class. In this case an array of possible classes can optionally be given.
#
def find(desc)
removeDeleted
result = []
classes = desc[:class] if desc[:class] and desc[:class].is_a?(Array)
classes = [ desc[:class] ] if !classes and desc[:class]
if classes
hashKeys = classesWithSubClasses(classes)
else
hashKeys = @elements.keys
end
hashKeys.each do |clazz|
next unless @elements[clazz]
@elements[clazz].each do |e|
failed = false
desc.each_pair { |k,v|
failed = true if k != :class and ( !e.respond_to?(k) or e.send(k) != v )
}
result << e unless failed
end
end
result
end
private
def removeDeleted
@deletedClasses.keys.each do |c|
@elements[c].reject!{|e| @deleted[e]}
end
@deletedClasses.clear
@deleted.clear
end
def updateSubClasses(clazz)
return if @subClassesUpdated[clazz]
if clazz.respond_to?( :ecore )
superClasses = clazz.ecore.eAllSuperTypes.collect{|c| c.instanceClass}
else
superClasses = superclasses(clazz)
end
superClasses.each do |c|
next if c == Object
@subClasses[c] ||= []
@subClasses[c] << clazz
end
@subClassesUpdated[clazz] = true
end
def classesWithSubClasses(classes)
result = classes
classes.each do |c|
result += @subClasses[c] if @subClasses[c]
end
result.uniq
end
def superclasses(clazz)
if clazz == Object
[]
else
superclasses(clazz.superclass) << clazz.superclass
end
end
end
end rgen-0.10.2/lib/rgen/fragment/ 0000775 0000000 0000000 00000000000 14560401624 0016033 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/fragment/dump_file_cache.rb 0000664 0000000 0000000 00000003555 14560401624 0021457 0 ustar 00root root 0000000 0000000 module RGen
module Fragment
# Caches model fragments in Ruby dump files.
#
# Dump files are created per each fragment file.
#
# The main goal is to support fast loading and joining of fragments. Therefore the cache
# stores additional information which makes the joining process faster (adding to
# environment, resolving references)
#
class DumpFileCache
# +cache_map+ must be an object responding to +load_data+ and +store_data+
# for loading or storing data associated with a file;
# this can be an instance of Util::FileCacheMap
def initialize(cache_map)
@cache_map = cache_map
end
# Note that the fragment must not be connected to other fragments by resolved references
# unresolve the fragment if necessary
def store(fragment)
fref = fragment.fragment_ref
# temporarily remove the reference to the fragment to avoid dumping the fragment
fref.fragment = nil
@cache_map.store_data(fragment.location,
Marshal.dump({
:root_elements => fragment.root_elements,
:elements => fragment.elements,
:index => fragment.index,
:unresolved_refs => fragment.unresolved_refs,
:fragment_ref => fref,
:data => fragment.data
}))
fref.fragment = fragment
end
def load(fragment)
dump = @cache_map.load_data(fragment.location)
return :invalid if dump == :invalid
header = Marshal.load(dump)
fragment.set_root_elements(header[:root_elements],
:elements => header[:elements],
:index => header[:index],
:unresolved_refs => header[:unresolved_refs])
fragment.data = header[:data]
if header[:fragment_ref]
fragment.fragment_ref = header[:fragment_ref]
fragment.fragment_ref.fragment = fragment
else
raise "no fragment_ref in fragment loaded from cache"
end
end
end
end
end
rgen-0.10.2/lib/rgen/fragment/fragmented_model.rb 0000664 0000000 0000000 00000010576 14560401624 0021665 0 ustar 00root root 0000000 0000000 require 'rgen/instantiator/reference_resolver'
module RGen
module Fragment
# A FragmentedModel represents a model which consists of fragments (ModelFragment).
#
# The main purpose of this class is to resolve references across fragments and
# to keep the references consistent while fragments are added or removed.
# This way it also plays an important role in keeping the model fragments consistent
# and thus ModelFragment objects should only be accessed via this interface.
# Overall unresolved references after the resolution step are also maintained.
#
# A FragmentedModel can also keep an RGen::Environment object up to date while fragments
# are added or removed. The environment must be registered with the constructor.
#
# Reference resolution is based on arbitrary identifiers. The identifiers must be
# provided in the fragments' indices. The FragmentedModel takes care to maintain
# the overall index.
#
class FragmentedModel
attr_reader :fragments
attr_reader :environment
# Creates a fragmented model. Options:
#
# :env
# environment which will be updated as model elements are added and removed
#
def initialize(options={})
@environment = options[:env]
@fragments = []
@index = nil
@fragment_change_listeners = []
@fragment_index = {}
end
# Adds a proc which is called when a fragment is added or removed
# The proc receives the fragment and one of :added, :removed
#
def add_fragment_change_listener(listener)
@fragment_change_listeners << listener
end
def remove_fragment_change_listener(listener)
@fragment_change_listeners.delete(listener)
end
# Add a fragment.
#
def add_fragment(fragment)
invalidate_cache
@fragments << fragment
fragment.elements.each{|e| @environment << e} if @environment
@fragment_change_listeners.each{|l| l.call(fragment, :added)}
end
# Removes the fragment. The fragment will be unresolved using unresolve_fragment.
#
def remove_fragment(fragment)
raise "fragment not part of model" unless @fragments.include?(fragment)
invalidate_cache
@fragments.delete(fragment)
@fragment_index.delete(fragment)
unresolve_fragment(fragment)
fragment.elements.each{|e| @environment.delete(e)} if @environment
@fragment_change_listeners.each{|l| l.call(fragment, :removed)}
end
# Resolve references between fragments.
# It is assumed that references within fragments have already been resolved.
# This method can be called several times. It will update the overall unresolved references.
#
# Options:
#
# :fragment_provider:
# Only if a +fragment_provider+ is given, the resolve step can be reverted later on
# by a call to unresolve_fragment. The fragment provider is a proc which receives a model
# element and must return the fragment in which the element is contained.
#
# :use_target_type:
# reference resolver uses the expected target type to narrow the set of possible targets
#
def resolve(options={})
local_index = index
@fragments.each do |f|
f.resolve_external(local_index, options)
end
end
# Remove all references between this fragment and all other fragments.
# The references will be replaced with unresolved references (MMProxy objects).
#
def unresolve_fragment(fragment)
fragment.unresolve_external
@fragments.each do |f|
if f != fragment
f.unresolve_external_fragment(fragment)
end
end
end
# Returns the overall unresolved references.
#
def unresolved_refs
@fragments.collect{|f| f.unresolved_refs}.flatten
end
# Returns the overall index.
# This is a Hash mapping identifiers to model elements accessible via the identifier.
#
def index
# Invalidate the cache when any fragment's local index changes.
# Assumption: If the local index content changes, there is a new index object.
fragments.each do |f|
if !@fragment_index[f] || (@fragment_index[f].object_id != f.index.object_id)
@fragment_index[f] = f.index
invalidate_cache
end
end
return @index if @index
@index = {}
fragments.each do |f|
f.index.each do |i|
(@index[i[0]] ||= []) << i[1]
end
end
@index
end
private
def invalidate_cache
@index = nil
end
end
end
end
rgen-0.10.2/lib/rgen/fragment/model_fragment.rb 0000664 0000000 0000000 00000023530 14560401624 0021346 0 ustar 00root root 0000000 0000000 require 'rgen/instantiator/reference_resolver'
module RGen
module Fragment
# A model fragment is a list of root model elements associated with a location (e.g. a file).
# It also stores a list of unresolved references as well as a list of unresolved references
# which have been resolved. Using the latter, a fragment can undo reference resolution.
#
# Optionally, an arbitrary data object may be associated with the fragment. The data object
# will also be stored in the cache.
#
# If an element within the fragment changes this must be indicated to the fragment by calling
# +mark_changed+.
#
# Note: the fragment knows how to resolve references (+resolve_local+, +resolve_external+).
# However considering a fragment a data structure, this functionality might be removed in the
# future. Instead the fragment should be told about each resolution taking place. Use
# method +mark_resolved+ for this purpose.
#
class ModelFragment
attr_reader :root_elements
attr_accessor :location, :fragment_ref, :data
# A FragmentRef serves as a single target object for elements which need to reference the
# fragment they are contained in. The FragmentRef references the fragment it is contained in.
# The FragmentRef is separate from the fragment itself to allow storing it in a marshal dump
# independently of the fragment.
#
class FragmentRef
attr_accessor :fragment
end
# A ResolvedReference wraps an unresolved reference after it has been resolved.
# It also holds the target element to which it has been resolved, i.e. with which the proxy
# object has been replaced.
#
class ResolvedReference
attr_reader :uref, :target
def initialize(uref, target)
@uref, @target = uref, target
end
end
# Create a model fragment
#
# :data
# data object associated with this fragment
#
# :identifier_provider
# identifier provider to be used when resolving references
# it must be a proc which receives a model element and must return
# that element's identifier or nil if the element has no identifier
#
def initialize(location, options={})
@location = location
@fragment_ref = FragmentRef.new
@fragment_ref.fragment = self
@data = options[:data]
@resolved_refs = nil
@changed = false
@identifier_provider = options[:identifier_provider]
end
# Set the root elements, normally done by an instantiator.
#
# For optimization reasons the instantiator of the fragment may provide data explicitly which
# is normally derived by the fragment itself. In this case it is essential that this
# data is consistent with the fragment.
#
def set_root_elements(root_elements, options={})
@root_elements = root_elements
@elements = options[:elements]
@index = options[:index]
@unresolved_refs = options[:unresolved_refs]
@resolved_refs = nil
# new unresolved refs, reset removed_urefs
@removed_urefs = nil
@changed = false
end
# Must be called when any of the elements in this fragment has been changed
#
def mark_changed
@changed = true
@elements = nil
@index = nil
@unresolved_refs = nil
# unresolved refs will be recalculated, no need to keep removed_urefs
@removed_urefs = nil
@resolved_refs = :dirty
end
# Can be used to reset the change status to unchanged.
#
def mark_unchanged
@changed = false
end
# Indicates whether the fragment has been changed or not
#
def changed?
@changed
end
# Returns all elements within this fragment
#
def elements
return @elements if @elements
@elements = []
@root_elements.each do |e|
@elements << e
@elements.concat(e.eAllContents)
end
@elements
end
# Returns the index of the element contained in this fragment.
#
# FragmentedModel's index caching depends on the fact that any change
# of a fragment's index contents implies a new index object.
#
def index
build_index unless @index
@index
end
# Returns all unresolved references within this fragment, i.e. references to MMProxy objects
#
def unresolved_refs
@unresolved_refs ||= collect_unresolved_refs
if @removed_urefs
@unresolved_refs -= @removed_urefs
@removed_urefs = nil
end
@unresolved_refs
end
# Builds the index of all elements within this fragment having an identifier
# the index is an array of 2-element arrays holding the identifier and the element
#
def build_index
raise "cannot build index without an identifier provider" unless @identifier_provider
@index = elements.collect { |e|
ident = @identifier_provider.call(e, nil)
ident && !ident.empty? ? [ident, e] : nil
}.compact
end
# Resolves local references (within this fragment) as far as possible
#
# Options:
#
# :use_target_type:
# reference resolver uses the expected target type to narrow the set of possible targets
#
def resolve_local(options={})
resolver = RGen::Instantiator::ReferenceResolver.new
index.each do |i|
resolver.add_identifier(i[0], i[1])
end
@unresolved_refs = resolver.resolve(unresolved_refs, :use_target_type => options[:use_target_type])
end
# Resolves references to external fragments using the external_index provided.
# The external index must be a Hash mapping identifiers uniquely to model elements.
#
# Options:
#
# :fragment_provider:
# If a +fragment_provider+ is given, the resolve step can be reverted later on
# by a call to unresolve_external or unresolve_external_fragment. The fragment provider
# is a proc which receives a model element and must return the fragment in which it is
# contained.
#
# :use_target_type:
# reference resolver uses the expected target type to narrow the set of possible targets
#
#
def resolve_external(external_index, options)
fragment_provider = options[:fragment_provider]
resolver = RGen::Instantiator::ReferenceResolver.new(
:identifier_resolver => proc {|ident| external_index[ident] })
if fragment_provider
@resolved_refs = {} if @resolved_refs.nil? || @resolved_refs == :dirty
on_resolve = proc { |ur, target|
target_fragment = fragment_provider.call(target)
target_fragment ||= :unknown
raise "can not resolve local reference in resolve_external, call resolve_local first" \
if target_fragment == self
@resolved_refs[target_fragment] ||= []
@resolved_refs[target_fragment] << ResolvedReference.new(ur, target)
}
@unresolved_refs = resolver.resolve(unresolved_refs, :on_resolve => on_resolve, :use_target_type => options[:use_target_type])
else
@unresolved_refs = resolver.resolve(unresolved_refs, :use_target_type => options[:use_target_type])
end
end
# Marks a particular unresolved reference +uref+ as resolved to +target+ in +target_fragment+.
#
def mark_resolved(uref, target_fragment, target)
@resolved_refs = {} if @resolved_refs.nil? || @resolved_refs == :dirty
target_fragment ||= :unknown
if target_fragment != self
@resolved_refs[target_fragment] ||= []
@resolved_refs[target_fragment] << ResolvedReference.new(uref, target)
end
@removed_urefs ||= []
@removed_urefs << uref
end
# Unresolve outgoing references to all external fragments, i.e. references which used to
# be represented by an unresolved reference from within this fragment.
# Note, that there may be more references to external fragments due to references which
# were represented by unresolved references from within other fragments.
#
def unresolve_external
return if @resolved_refs.nil?
raise "can not unresolve, missing fragment information" if @resolved_refs == :dirty || @resolved_refs[:unknown]
rrefs = @resolved_refs.values.flatten
@resolved_refs = {}
unresolve_refs(rrefs)
end
# Like unresolve_external but only unresolve references to external fragment +fragment+
#
def unresolve_external_fragment(fragment)
return if @resolved_refs.nil?
raise "can not unresolve, missing fragment information" if @resolved_refs == :dirty || @resolved_refs[:unknown]
rrefs = @resolved_refs[fragment]
@resolved_refs.delete(fragment)
unresolve_refs(rrefs) if rrefs
end
private
# Turns resolved references +rrefs+ back into unresolved references
#
def unresolve_refs(rrefs)
# make sure any removed_urefs have been removed,
# otherwise they will be removed later even if this method actually re-added them
unresolved_refs
rrefs.each do |rr|
ur = rr.uref
refs = ur.element.getGeneric(ur.feature_name)
if refs.is_a?(Array)
index = refs.index(rr.target)
ur.element.removeGeneric(ur.feature_name, rr.target)
ur.element.addGeneric(ur.feature_name, ur.proxy, index)
else
ur.element.setGeneric(ur.feature_name, ur.proxy)
end
@unresolved_refs << ur
end
end
def collect_unresolved_refs
unresolved_refs = []
elements.each do |e|
each_reference_target(e) do |r, t|
if t.is_a?(RGen::MetamodelBuilder::MMProxy)
unresolved_refs <<
RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(e, r.name, t)
end
end
end
unresolved_refs
end
def each_reference_target(element)
non_containment_references(element.class).each do |r|
element.getGenericAsArray(r.name).each do |t|
yield(r, t)
end
end
end
def non_containment_references(clazz)
@@non_containment_references_cache ||= {}
@@non_containment_references_cache[clazz] ||=
clazz.ecore.eAllReferences.select{|r| !r.containment}
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/ 0000775 0000000 0000000 00000000000 14560401624 0016747 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/instantiator/abstract_instantiator.rb 0000664 0000000 0000000 00000003275 14560401624 0023705 0 ustar 00root root 0000000 0000000 module RGen
module Instantiator
class AbstractInstantiator
ResolverDescription = Struct.new(:from, :attribute, :block) # :nodoc:
class << self
attr_accessor :resolver_descs
end
def initialize(env)
@env = env
end
# Specifies that +attribute+ should be resolved. If +:class+ is specified,
# resolve +attribute+ only for objects of type class.
# The block must return the value to which the attribute should be assigned.
# The object for which the attribute is to be resolved will be accessible
# in the current context within the block.
#
def self.resolve(attribute, desc=nil, &block)
from = (desc.is_a?(Hash) && desc[:class])
self.resolver_descs ||= []
self.resolver_descs << ResolverDescription.new(from, attribute, block)
end
# Resolves +attribute+ to a model element which has attribute +:id+ set to the
# value currently in attribute +:src+
#
def self.resolve_by_id(attribute, desc)
id_attr = (desc.is_a?(Hash) && desc[:id])
src_attr = (desc.is_a?(Hash) && desc[:src])
raise StandardError.new("No id attribute given.") unless id_attr
resolve(attribute) do
@env.find(id_attr => @current_object.send(src_attr)).first
end
end
private
def method_missing(m, *args) #:nodoc:
if @current_object
@current_object.send(m)
else
super
end
end
def resolve
self.class.resolver_descs ||= []
self.class.resolver_descs.each { |desc|
@env.find(:class => desc.from).each { |e|
old_object, @current_object = @current_object, e
e.send("#{desc.attribute}=", instance_eval(&desc.block)) if e.respond_to?("#{desc.attribute}=")
@current_object = old_object
}
}
end
end
end
end rgen-0.10.2/lib/rgen/instantiator/abstract_xml_instantiator.rb 0000664 0000000 0000000 00000003760 14560401624 0024564 0 ustar 00root root 0000000 0000000 require 'nokogiri'
class AbstractXMLInstantiator
class Visitor < Nokogiri::XML::SAX::Document
def initialize(inst, gcSuspendCount)
@instantiator = inst
@gcSuspendCount = gcSuspendCount
@namespaces = {}
end
def start_element_namespace(tag, attributes, prefix, uri, ns)
controlGC
ns.each{|n| @namespaces[n[0]] = n[1]}
attrs = attributes.collect{|a| [a.prefix ? a.prefix+":"+a.localname : a.localname, a.value]}
@instantiator.start_tag(prefix, tag, @namespaces, Hash[*(attrs.flatten)])
attrs.each { |pair| @instantiator.set_attribute(pair[0], pair[1]) }
end
def end_element_namespace(tag, prefix, uri)
@instantiator.end_tag(prefix, tag)
end
def characters(str)
@instantiator.text(str)
end
def controlGC
return unless @gcSuspendCount > 0
@gcCounter ||= 0
@gcCounter += 1
if @gcCounter == @gcSuspendCount
@gcCounter = 0
GC.enable
ObjectSpace.garbage_collect
GC.disable
end
end
end
# Parses str and calls start_tag, end_tag, set_attribute and text methods of a subclass.
#
# If gcSuspendCount is specified, the garbage collector will be disabled for that
# number of start or end tags. After that period it will clean up and then be disabled again.
# A value of about 1000 can significantly improve overall performance.
# The memory usage normally does not increase.
# Depending on the work done for every xml tag the value might have to be adjusted.
#
def instantiate(str, gcSuspendCount=0)
gcDisabledBefore = GC.disable
gcSuspendCount = 0 if gcDisabledBefore
begin
visitor = Visitor.new(self, gcSuspendCount)
parser = Nokogiri::XML::SAX::Parser.new(visitor)
parser.parse(str) do |ctx|
@parserContext = ctx
end
ensure
GC.enable unless gcDisabledBefore
end
end
def text(str)
end
end
rgen-0.10.2/lib/rgen/instantiator/default_xml_instantiator.rb 0000664 0000000 0000000 00000006233 14560401624 0024403 0 ustar 00root root 0000000 0000000 require 'rgen/instantiator/nodebased_xml_instantiator'
module RGen
module Instantiator
# A default XML instantiator.
# Derive your own instantiator from this class or use it as is.
#
class DefaultXMLInstantiator < NodebasedXMLInstantiator
include Util::NameHelper
NamespaceDescriptor = Struct.new(:prefix, :target)
class << self
def map_tag_ns(from, to, prefix="")
tag_ns_map[from] = NamespaceDescriptor.new(prefix, to)
end
def tag_ns_map # :nodoc:
@tag_ns_map ||={}
@tag_ns_map
end
end
def initialize(env, default_module, create_mm=false)
super(env)
@default_module = default_module
@create_mm = create_mm
end
def on_descent(node)
obj = new_object(node)
@env << obj unless obj.nil?
node.object = obj
node.attributes.each_pair { |k,v| set_attribute(node, k, v) }
end
def on_ascent(node)
node.children.each { |c| assoc_p2c(node, c) }
node.object.class.has_attr 'chardata', Object unless node.object.respond_to?(:chardata)
set_attribute(node, "chardata", node.chardata)
end
def class_name(str)
saneClassName(str)
end
def new_object(node)
ns_desc = self.class.tag_ns_map[node.namespace]
class_name = class_name(ns_desc.nil? ? node.qtag : ns_desc.prefix+node.tag)
mod = (ns_desc && ns_desc.target) || @default_module
build_on_error(NameError, :build_class, class_name, mod) do
begin
mod.const_get(class_name, false).new
rescue ArgumentError
# Ruby 1.8
mod.const_get(class_name).new
end
end
end
def build_class(name, mod)
mod.const_set(name, Class.new(RGen::MetamodelBuilder::MMBase))
end
def method_name(str)
saneMethodName(str)
end
def assoc_p2c(parent, child)
return unless parent.object && child.object
method_name = method_name(className(child.object))
build_on_error(NoMethodError, :build_p2c_assoc, parent, child, method_name) do
parent.object.addGeneric(method_name, child.object)
child.object.setGeneric("parent", parent.object)
end
end
def build_p2c_assoc(parent, child, method_name)
parent.object.class.has_many(method_name, child.object.class)
child.object.class.has_one("parent", RGen::MetamodelBuilder::MMBase)
end
def set_attribute(node, attr, value)
return unless node.object
build_on_error(NoMethodError, :build_attribute, node, attr, value) do
node.object.setGeneric(method_name(attr), value)
end
end
def build_attribute(node, attr, value)
node.object.class.has_attr(method_name(attr))
end
protected
# Helper method for implementing classes.
# This method yields the given block.
# If the metamodel should be create automatically (see constructor)
# rescues +error+ and calls +builder_method+ with +args+, then
# yields the block again.
def build_on_error(error, builder_method, *args)
begin
yield
rescue error
if @create_mm
send(builder_method, *args)
yield
else
raise
end
end
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/ecore_xml_instantiator.rb 0000664 0000000 0000000 00000012125 14560401624 0024051 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore'
require 'rgen/instantiator/abstract_xml_instantiator'
require 'rgen/array_extensions'
class ECoreXMLInstantiator < AbstractXMLInstantiator
include RGen::ECore
INFO = 0
WARN = 1
ERROR = 2
def initialize(env, loglevel=ERROR)
@env = env
@rolestack = []
@elementstack = []
@element_by_id = {}
@loglevel = loglevel
end
def start_tag(prefix, tag, namespaces, attributes)
eRef = nil
if @elementstack.last
eRef = eAllReferences(@elementstack.last).find{|r|r.name == tag}
if eRef
if attributes["xsi:type"] && attributes["xsi:type"] =~ /ecore:(\w+)/
class_name = $1
attributes.delete("xsi:type")
else
class_name = eRef.eType.name
end
else
raise "Reference not found: #{tag} on #{@elementstack.last}"
end
else
class_name = tag
end
eClass = RGen::ECore.ecore.eClassifiers.find{|c| c.name == class_name}
if eClass
obj = RGen::ECore.const_get(class_name).new
if attributes["xmi:id"]
@element_by_id[attributes["xmi:id"]] = obj
attributes.delete("xmi:id")
end
if eRef
if eRef.many
@elementstack.last.addGeneric(eRef.name, obj)
else
@elementstack.last.setGeneric(eRef.name, obj)
end
end
@env << obj
@elementstack.push obj
else
log WARN, "Class not found: #{class_name}"
@elementstack.push nil
end
attributes.each_pair do |attr, value|
set_attribute_internal(attr, value)
end
end
def end_tag(prefix, tag)
@elementstack.pop
end
ResolverDescription = Struct.new(:object, :attribute, :value)
def set_attribute(attr, value)
# do nothing, already handled by start_tag/set_attribute_internal
end
def set_attribute_internal(attr, value)
return unless @elementstack.last
eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr}
if eFeat.is_a?(EReference)
rd = ResolverDescription.new
rd.object = @elementstack.last
rd.attribute = attr
rd.value = value
@resolver_descs << rd
elsif eFeat
value = true if value == "true" && eFeat.eType == EBoolean
value = false if value == "false" && eFeat.eType == EBoolean
value = value.to_i if eFeat.eType == EInt || eFeat.eType == ELong
@elementstack.last.setGeneric(attr, value)
else
log WARN, "Feature not found: #{attr} on #{@elementstack.last}"
end
end
def instantiate(str)
@resolver_descs = []
# puts "Instantiating ..."
super(str, 1000)
rootpackage = @env.find(:class => EPackage).first
# puts "Resolving ..."
@resolver_descs.each do |rd|
refed = find_referenced(rootpackage, rd.value)
feature = eAllStructuralFeatures(rd.object).find{|f| f.name == rd.attribute}
raise StandardError.new("StructuralFeature not found: #{rd.attribute}") unless feature
if feature.many
rd.object.setGeneric(feature.name, refed)
else
rd.object.setGeneric(feature.name, refed.first)
end
end
end
def eAllReferences(element)
@eAllReferences ||= {}
@eAllReferences[element.class] ||= element.class.ecore.eAllReferences
end
def eAllAttributes(element)
@eAllAttributes ||= {}
@eAllAttributes[element.class] ||= element.class.ecore.eAllAttributes
end
def eAllStructuralFeatures(element)
@eAllStructuralFeatures ||= {}
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
end
def find_referenced(context, desc)
desc.split(/\s+/).collect do |r|
if r =~ /^#([^\/]+)$/
@element_by_id[$1]
elsif r =~ /^#\/\d*\/([\w\/]+)/
find_in_context(context, $1.split('/'))
elsif r =~ /#\/\/(\w+)$/
case $1
when "EString"; RGen::ECore::EString
when "EInt"; RGen::ECore::EInt
when "ELong"; RGen::ECore::ELong
when "EBoolean"; RGen::ECore::EBoolean
when "EFloat"; RGen::ECore::EFloat
when "EJavaObject"; RGen::ECore::EJavaObject
when "EJavaClass"; RGen::ECore::EJavaClass
end
end
end.compact
end
def find_in_context(context, desc_elements)
if context.is_a?(EPackage)
r = (context.eClassifiers + context.eSubpackages).find{|c| c.name == desc_elements.first}
elsif context.is_a?(EClass)
r = context.eStructuralFeatures.find{|s| s.name == desc_elements.first}
else
raise StandardError.new("Don't know how to find #{desc_elements.join('/')} in context #{context}")
end
if r
if desc_elements.size > 1
find_in_context(r, desc_elements[1..-1])
else
r
end
else
log WARN, "Can not follow path, element #{desc_elements.first} not found within #{context}(#{context.name})"
end
end
def log(level, msg)
puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel
end
end
rgen-0.10.2/lib/rgen/instantiator/json_instantiator.rb 0000664 0000000 0000000 00000010154 14560401624 0023045 0 ustar 00root root 0000000 0000000 require 'rgen/instantiator/qualified_name_resolver'
require 'rgen/instantiator/json_parser'
module RGen
module Instantiator
# JsonInstantiator is used to create RGen models from JSON.
#
# Each JSON object needs to have an attribute "_class" which is used to find
# the metamodel class to instantiate. The value of "_class" should be the
# the relative qualified class name within the root package as a string.
#
# If the option "short_class_names" is set to true, unqualified class names can be used.
# In this case, metamodel classes are searched in the metamodel root package first.
# If this search is not successful, all subpackages will be searched for the class name.
#
class JsonInstantiator
# Model elements will be created in evironment +env+,
# classes are looked for in metamodel package module +mm+,
# +options+ include:
# short_class_names: if true subpackages will be searched for unqualifed class names (default: true)
# ignore_keys: an array of json object key names which are to be ignored (default: none)
#
# The options are also passed to the underlying QualifiedNameResolver.
#
def initialize(env, mm, options={})
@env = env
@mm = mm
@options = options
@short_class_names = !@options.has_key?(:short_class_names) || @options[:short_class_names]
@ignore_keys = @options[:ignore_keys] || []
@unresolvedReferences = []
@classes = {}
@classes_flat = {}
mm.ecore.eAllClasses.each do |c|
@classes[c.instanceClass.name.sub(mm.name+"::","")] = c
@classes_flat[c.name] = c
end
@parser = JsonParser.new(self)
end
# Creates the elements described by the json string +str+.
# Returns an array of ReferenceResolver::UnresolvedReference
# describing the references which could not be resolved
#
# Options:
# :root_elements: if an array is provided, it will be filled with the root elements
#
def instantiate(str, options={})
root = @parser.parse(str)
if options[:root_elements].is_a?(Array)
options[:root_elements].clear
root.each{|r| options[:root_elements] << r}
end
resolver = QualifiedNameResolver.new(root, @options)
resolver.resolveReferences(@unresolvedReferences)
end
def createObject(hash)
className = hash["_class"]
# hashes without a _class key are returned as is
return hash unless className
if @classes[className]
clazz = @classes[className].instanceClass
elsif @short_class_names && @classes_flat[className]
clazz = @classes_flat[className].instanceClass
else
raise "class not found: #{className}"
end
hash.delete("_class")
@ignore_keys.each do |k|
hash.delete(k)
end
urefs = []
hash.keys.each do |k|
f = eFeature(k, clazz)
hash[k] = [hash[k]] if f.many && !hash[k].is_a?(Array)
if f.is_a?(RGen::ECore::EReference) && !f.containment
if f.many
idents = hash[k]
hash[k] = idents.collect do |i|
proxy = RGen::MetamodelBuilder::MMProxy.new(i)
urefs << ReferenceResolver::UnresolvedReference.new(nil, k, proxy)
proxy
end
else
ident = hash[k]
ident = ident.first if ident.is_a?(Array)
proxy = RGen::MetamodelBuilder::MMProxy.new(ident)
hash[k] = proxy
urefs << ReferenceResolver::UnresolvedReference.new(nil, k, proxy)
end
elsif f.eType.is_a?(RGen::ECore::EEnum)
hash[k] = hash[k].to_sym
elsif f.eType.instanceClassName == "Float"
hash[k] = hash[k].to_f
end
end
obj = @env.new(clazz, hash)
urefs.each do |r|
r.element = obj
@unresolvedReferences << r
end
obj
end
private
def eFeature(name, clazz)
@eFeature ||= {}
@eFeature[clazz] ||= {}
unless @eFeature[clazz][name]
feature = clazz.ecore.eAllStructuralFeatures.find{|f| f.name == name}
raise "feature '#{name}' not found in class '#{clazz}'" unless feature
end
@eFeature[clazz][name] ||= feature
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/json_parser.rb 0000664 0000000 0000000 00000015625 14560401624 0021632 0 ustar 00root root 0000000 0000000 #
# DO NOT MODIFY!!!!
# This file is automatically generated by racc 1.4.5
# from racc grammer file "json_parser.y".
#
require 'racc/parser'
module RGen
module Instantiator
class JsonParser < Racc::Parser
module_eval <<'..end json_parser.y modeval..id3d5fb611e2', 'json_parser.y', 38
ParserToken = Struct.new(:line, :file, :value)
def initialize(instantiator)
@instantiator = instantiator
end
def parse(str, file=nil)
@q = []
line = 1
until str.empty?
case str
when /\A\n/
str = $'
line +=1
when /\A\s+/
str = $'
when /\A([-+]?\d+\.\d+)/
str = $'
@q << [:FLOAT, ParserToken.new(line, file, $1)]
when /\A([-+]?\d+)/
str = $'
@q << [:INTEGER, ParserToken.new(line, file, $1)]
when /\A"((?:[^"\\]|\\"|\\\\|\\[^"\\])*)"/
str = $'
sval = $1
sval.gsub!('\\\\','\\')
sval.gsub!('\\"','"')
sval.gsub!('\\n',"\n")
sval.gsub!('\\r',"\r")
sval.gsub!('\\t',"\t")
sval.gsub!('\\f',"\f")
sval.gsub!('\\b',"\b")
@q << [:STRING, ParserToken.new(line, file, sval)]
when /\A(\{|\}|\[|\]|,|:|true|false)/
str = $'
@q << [$1, ParserToken.new(line, file, $1)]
else
raise "parse error in line #{line} on "+str[0..20].inspect+"..."
end
end
@q.push [false, ParserToken.new(line, file, '$end')]
do_parse
end
def next_token
r = @q.shift
r
end
..end json_parser.y modeval..id3d5fb611e2
##### racc 1.4.5 generates ###
racc_reduce_table = [
0, 0, :racc_error,
1, 14, :_reduce_1,
3, 16, :_reduce_2,
2, 16, :_reduce_3,
1, 17, :_reduce_4,
3, 17, :_reduce_5,
3, 18, :_reduce_6,
2, 18, :_reduce_7,
1, 19, :_reduce_8,
3, 19, :_reduce_9,
3, 20, :_reduce_10,
1, 15, :_reduce_11,
1, 15, :_reduce_12,
1, 15, :_reduce_13,
1, 15, :_reduce_14,
1, 15, :_reduce_15,
1, 15, :_reduce_16,
1, 15, :_reduce_17 ]
racc_reduce_n = 18
racc_shift_n = 29
racc_action_table = [
3, 16, 17, 7, 22, 8, 21, 10, 11, 1,
2, 3, 12, 23, 7, 24, 8, 25, 10, 11,
1, 2, 3, 20, 15, 7, 17, 8, nil, 10,
11, 1, 2, 3, nil, nil, 7, nil, 8, nil,
10, 11, 1, 2 ]
racc_action_check = [
0, 7, 7, 0, 15, 0, 14, 0, 0, 0,
0, 3, 3, 17, 3, 18, 3, 19, 3, 3,
3, 3, 20, 13, 4, 20, 25, 20, nil, 20,
20, 20, 20, 23, nil, nil, 23, nil, 23, nil,
23, 23, 23, 23 ]
racc_action_pointer = [
-2, nil, nil, 9, 24, nil, nil, -5, nil, nil,
nil, nil, nil, 19, 3, 4, nil, 5, 9, 13,
20, nil, nil, 31, nil, 19, nil, nil, nil ]
racc_action_default = [
-18, -16, -17, -18, -18, -1, -11, -18, -13, -12,
-14, -15, -3, -4, -18, -18, -7, -18, -18, -8,
-18, -2, 29, -18, -6, -18, -5, -10, -9 ]
racc_goto_table = [
5, 18, 4, 14, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, 28,
26, nil, nil, 27 ]
racc_goto_check = [
2, 6, 1, 4, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, 6,
4, nil, nil, 2 ]
racc_goto_pointer = [
nil, 2, 0, nil, 0, nil, -6, nil ]
racc_goto_default = [
nil, nil, 13, 6, nil, 9, nil, 19 ]
racc_token_table = {
false => 0,
Object.new => 1,
"[" => 2,
"]" => 3,
"," => 4,
"{" => 5,
"}" => 6,
:STRING => 7,
":" => 8,
:INTEGER => 9,
:FLOAT => 10,
"true" => 11,
"false" => 12 }
racc_use_result_var = true
racc_nt_base = 13
Racc_arg = [
racc_action_table,
racc_action_check,
racc_action_default,
racc_action_pointer,
racc_goto_table,
racc_goto_check,
racc_goto_default,
racc_goto_pointer,
racc_nt_base,
racc_reduce_table,
racc_token_table,
racc_shift_n,
racc_reduce_n,
racc_use_result_var ]
Racc_token_to_s_table = [
'$end',
'error',
'"["',
'"]"',
'","',
'"{"',
'"}"',
'STRING',
'":"',
'INTEGER',
'FLOAT',
'"true"',
'"false"',
'$start',
'json',
'value',
'array',
'valueList',
'object',
'memberList',
'member']
Racc_debug_parser = false
##### racc system variables end #####
# reduce 0 omitted
module_eval <<'.,.,', 'json_parser.y', 4
def _reduce_1( val, _values, result )
result = val[0]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 6
def _reduce_2( val, _values, result )
result = val[1]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 7
def _reduce_3( val, _values, result )
result = []
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 9
def _reduce_4( val, _values, result )
result = [ val[0] ]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 10
def _reduce_5( val, _values, result )
result = [ val[0] ] + val[2]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 12
def _reduce_6( val, _values, result )
result = @instantiator.createObject(val[1])
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 13
def _reduce_7( val, _values, result )
result = nil
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 15
def _reduce_8( val, _values, result )
result = val[0]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 16
def _reduce_9( val, _values, result )
result = val[0].merge(val[2])
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 18
def _reduce_10( val, _values, result )
result = {val[0].value => val[2]}
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 20
def _reduce_11( val, _values, result )
result = val[0]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 21
def _reduce_12( val, _values, result )
result = val[0]
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 22
def _reduce_13( val, _values, result )
result = val[0].value
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 23
def _reduce_14( val, _values, result )
result = val[0].value.to_i
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 24
def _reduce_15( val, _values, result )
result = val[0].value.to_f
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 25
def _reduce_16( val, _values, result )
result = true
result
end
.,.,
module_eval <<'.,.,', 'json_parser.y', 26
def _reduce_17( val, _values, result )
result = false
result
end
.,.,
def _reduce_none( val, _values, result )
result
end
end # class JsonParser
end
end
rgen-0.10.2/lib/rgen/instantiator/json_parser.y 0000664 0000000 0000000 00000004113 14560401624 0021465 0 ustar 00root root 0000000 0000000 class JsonParser
rule
json: value { result = val[0] }
array: "[" valueList "]" { result = val[1] }
| "[" "]" { result = [] }
valueList: value { result = [ val[0] ] }
| value "," valueList { result = [ val[0] ] + val[2] }
object: "{" memberList "}" { result = @instantiator.createObject(val[1]) }
| "{" "}" { result = nil }
memberList: member { result = val[0] }
| member "," memberList { result = val[0].merge(val[2]) }
member: STRING ":" value { result = {val[0].value => val[2]} }
value: array { result = val[0] }
| object { result = val[0] }
| STRING { result = val[0].value }
| INTEGER { result = val[0].value.to_i }
| FLOAT { result = val[0].value.to_f }
| "true" { result = true }
| "false" { result = false }
end
---- header
module RGen
module Instantiator
---- inner
ParserToken = Struct.new(:line, :file, :value)
def initialize(instantiator)
@instantiator = instantiator
end
def parse(str, file=nil)
@q = []
line = 1
until str.empty?
case str
when /\A\n/
str = $'
line +=1
when /\A\s+/
str = $'
when /\A([-+]?\d+\.\d+)/
str = $'
@q << [:FLOAT, ParserToken.new(line, file, $1)]
when /\A([-+]?\d+)/
str = $'
@q << [:INTEGER, ParserToken.new(line, file, $1)]
when /\A"((?:[^"\\]|\\"|\\\\|\\[^"\\])*)"/
str = $'
sval = $1
sval.gsub!('\\\\','\\')
sval.gsub!('\\"','"')
sval.gsub!('\\n',"\n")
sval.gsub!('\\r',"\r")
sval.gsub!('\\t',"\t")
sval.gsub!('\\f',"\f")
sval.gsub!('\\b',"\b")
@q << [:STRING, ParserToken.new(line, file, sval)]
when /\A(\{|\}|\[|\]|,|:|true|false)/
str = $'
@q << [$1, ParserToken.new(line, file, $1)]
else
raise "parse error in line #{line} on "+str[0..20].inspect+"..."
end
end
@q.push [false, ParserToken.new(line, file, '$end')]
do_parse
end
def next_token
r = @q.shift
r
end
---- footer
end
end
rgen-0.10.2/lib/rgen/instantiator/nodebased_xml_instantiator.rb 0000664 0000000 0000000 00000006762 14560401624 0024712 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder'
require 'rgen/instantiator/abstract_instantiator'
require 'nokogiri'
module RGen
module Instantiator
class NodebasedXMLInstantiator < AbstractInstantiator
class << self
# The prune level is the number of parent/children associations which
# is kept when the instantiator ascents the XML tree.
# If the level is 2, information for the node's children and the childrens'
# children will be available as an XMLNodeDescriptor object.
# If the level is 0 no pruning will take place, i.e. the whole information
# is kept until the end of the instantiation process. 0 is default.
def set_prune_level(level)
@prune_level = level
end
def prune_level # :nodoc:
@prune_level ||= 0
end
end
class XMLNodeDescriptor
attr_reader :namespace, :qtag, :prefix, :tag, :parent, :attributes, :chardata
attr_accessor :object, :children
def initialize(ns, qtag, prefix, tag, parent, children, attributes)
@namespace, @qtag, @prefix, @tag, @parent, @children, @attributes =
ns, qtag, prefix, tag, parent, children, attributes
@parent.children << self if @parent
@chardata = []
end
end
class Visitor < Nokogiri::XML::SAX::Document
attr_reader :namespaces
def initialize(inst)
@instantiator = inst
@namespaces = {}
end
def start_element_namespace(tag, attributes, prefix, uri, ns)
ns.each{|n| @namespaces[n[0]] = n[1]}
attrs = {}
attributes.each{|a| attrs[a.prefix ? a.prefix+":"+a.localname : a.localname] = a.value}
qname = prefix ? prefix+":"+tag : tag
@instantiator.start_element(uri, qname, prefix, tag, attrs)
end
def end_element(name)
@instantiator.end_element
end
def characters(str)
@instantiator.on_chardata(str)
end
end
def initialize(env)
super
@env = env
@stack = []
end
def instantiate_file(file)
File.open(file) { |f| parse(f.read)}
resolve
end
def instantiate(text)
parse(text)
resolve
end
def parse(src)
@visitor = Visitor.new(self)
parser = Nokogiri::XML::SAX::Parser.new(@visitor)
parser.parse(src)
@visitor = nil
end
def start_element(ns, qtag, prefix, tag, attributes)
node = XMLNodeDescriptor.new(ns, qtag, prefix, tag, @stack[-1], [], attributes)
@stack.push node
on_descent(node)
end
def end_element
node = @stack.pop
on_ascent(node)
prune_children(node, self.class.prune_level - 1) if self.class.prune_level > 0
end
def on_chardata(str)
node = @stack.last
node.chardata << str
end
# This method is called when the XML parser goes down the tree.
# An XMLNodeDescriptor +node+ describes the current node.
# Implementing classes must overwrite this method.
def on_descent(node)
raise "Overwrite this method !"
end
# This method is called when the XML parser goes up the tree.
# An XMLNodeDescriptor +node+ describes the current node.
# Implementing classes must overwrite this method.
def on_ascent(node)
raise "Overwrite this method !"
end
def namespaces
@visitor.namespaces if @visitor
end
private
def prune_children(node, level)
if level == 0
node.children = nil
else
node.children.each { |c| prune_children(c, level-1) }
end
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/qualified_name_resolver.rb 0000664 0000000 0000000 00000005545 14560401624 0024171 0 ustar 00root root 0000000 0000000 require 'rgen/instantiator/reference_resolver'
module RGen
module Instantiator
# This is a resolver resolving element identifiers which are qualified names.
class QualifiedNameResolver
attr_reader :nameAttribute
attr_reader :separator
attr_reader :leadingSeparator
def initialize(rootElements, options={})
@rootElements = rootElements
@nameAttribute = options[:nameAttribute] || "name"
@separator = options[:separator] || "/"
@leadingSeparator = options.has_key?(:leadingSeparator) ? options[:leadingSeparator] : true
@elementByQName = {}
@visitedQName = {}
@childReferences = {}
@resolverDelegate = ReferenceResolver.new(:identifier_resolver => method(:resolveIdentifier))
end
def resolveIdentifier(qualifiedName)
return @elementByQName[qualifiedName] if @elementByQName.has_key?(qualifiedName)
path = qualifiedName.split(separator).reject{|s| s == ""}
if path.size > 1
parentQName = (leadingSeparator ? separator : "") + path[0..-2].join(separator)
parents = resolveIdentifier(parentQName)
parents = [parents].compact unless parents.is_a?(Array)
children = parents.collect{|p| allNamedChildren(p)}.flatten
elsif path.size == 1
parentQName = ""
children = allRootNamedChildren
else
return @elementByQName[qualifiedName] = nil
end
# if the parent was already visited all matching elements are the hash
if !@visitedQName[parentQName]
children.each do |c|
name = c.send(nameAttribute)
if name
qname = parentQName + ((parentQName != "" || leadingSeparator) ? separator : "") + name
existing = @elementByQName[qname]
if existing
@elementByQName[qname] = [existing] unless existing.is_a?(Array)
@elementByQName[qname] << c
else
@elementByQName[qname] = c
end
end
end
# all named children of praent have been checked and hashed
@visitedQName[parentQName] = true
end
@elementByQName[qualifiedName] ||= nil
end
def resolveReferences(unresolvedReferences, problems=[])
@resolverDelegate.resolve(unresolvedReferences, :problems => problems)
end
private
def allNamedChildren(element)
childReferences(element.class).collect do |r|
element.getGenericAsArray(r.name).collect do |c|
if c.respond_to?(nameAttribute)
c
else
allNamedChildren(c)
end
end
end.flatten
end
def allRootNamedChildren
@rootElements.collect do |e|
if e.respond_to?(nameAttribute)
e
else
allNamedChildren(e)
end
end.flatten
end
def childReferences(clazz)
@childReferences[clazz] ||= clazz.ecore.eAllReferences.select{|r| r.containment}
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/reference_resolver.rb 0000664 0000000 0000000 00000011144 14560401624 0023154 0 ustar 00root root 0000000 0000000 require 'rgen/instantiator/resolution_helper'
module RGen
module Instantiator
# The ReferenceResolver can be used to resolve unresolved references, i.e. instances
# of class UnresolvedReference
#
# There are two ways how this can be used:
# 1. the identifiers and associated model elements are added upfront using +add_identifier+
# 2. register an :identifier_resolver with the constructor, which will be invoked
# for every unresolved identifier
#
class ReferenceResolver
# Instances of this class represent information about not yet resolved references.
# This consists of the +element+ and metamodel +feature_name+ which hold/is to hold the
# reference and the +proxy+ object which is the placeholder for the reference.
# If the reference could not be resolved because the target type does not match the
# feature type, the flag +target_type_error+ will be set.
#
class UnresolvedReference
attr_reader :feature_name, :proxy
attr_accessor :element, :target_type_error
def initialize(element, feature_name, proxy)
@element = element
@feature_name = feature_name
@proxy = proxy
end
end
# Create a reference resolver, options:
#
# :identifier_resolver:
# a proc which is called with an identifier and which should return the associated element
# in case the identifier is not uniq, the proc may return multiple values
# default: lookup element in internal map
#
def initialize(options={})
@identifier_resolver = options[:identifier_resolver]
@identifier_map = {}
end
# Add an +identifer+ / +element+ pair which will be used for looking up unresolved identifers
def add_identifier(ident, element)
map_entry = @identifier_map[ident]
if map_entry
if map_entry.is_a?(Array)
map_entry << element
else
@identifier_map[ident] = [map_entry, element]
end
else
@identifier_map[ident] = element
end
end
# Tries to resolve the given +unresolved_refs+. If resolution is successful, the proxy object
# will be removed, otherwise there will be an error description in the problems array.
# In case the resolved target element's type is not valid for the given feature, the
# +target_type_error+ flag will be set on the unresolved reference.
# Returns an array of the references which are still unresolved. Options:
#
# :problems
# an array to which problems will be appended
#
# :on_resolve
# a proc which will be called for every sucessful resolution, receives the unresolved
# reference as well as to new target element
#
# :use_target_type
# use the expected target type to narrow the set of possible targets
# (i.e. ignore targets with wrong type)
#
# :failed_resolutions
# a Hash which will receive an entry for each failed resolution for which at least one
# target element was found (wrong target type, or target not unique).
# hash key is the uref, hash value is the target element or the Array of target elements
#
def resolve(unresolved_refs, options={})
problems = options[:problems] || []
still_unresolved_refs = []
failed_resolutions = options[:failed_resolutions] || {}
unresolved_refs.each do |ur|
if @identifier_resolver
target = @identifier_resolver.call(ur.proxy.targetIdentifier)
else
target = @identifier_map[ur.proxy.targetIdentifier]
end
target = [target].compact unless target.is_a?(Array)
if options[:use_target_type]
feature = ur.element.class.ecore.eAllReferences.find{|r| r.name == ur.feature_name}
target = target.select{|e| e.is_a?(feature.eType.instanceClass)}
end
if target.size == 1
status = ResolutionHelper.set_uref_target(ur, target[0])
if status == :success
options[:on_resolve] && options[:on_resolve].call(ur, target[0])
elsif status == :type_error
ur.target_type_error = true
problems << type_error_message(target[0])
still_unresolved_refs << ur
failed_resolutions[ur] = target[0]
end
elsif target.size > 1
problems << "identifier #{ur.proxy.targetIdentifier} not uniq"
still_unresolved_refs << ur
failed_resolutions[ur] = target
else
problems << "identifier #{ur.proxy.targetIdentifier} not found"
still_unresolved_refs << ur
end
end
still_unresolved_refs
end
private
def type_error_message(target)
"invalid target type #{target.class}"
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/resolution_helper.rb 0000664 0000000 0000000 00000002073 14560401624 0023040 0 ustar 00root root 0000000 0000000 module RGen
module Instantiator
module ResolutionHelper
# sets the target of an unresolved reference in the model
# returns :type_error if the target is of wrong type, otherwise :success
#
def self.set_uref_target(uref, target)
refs = uref.element.getGeneric(uref.feature_name)
if refs.is_a?(Array)
index = refs.index(uref.proxy)
uref.element.removeGeneric(uref.feature_name, uref.proxy)
begin
uref.element.addGeneric(uref.feature_name, target, index)
rescue StandardError => e
if is_type_error?(e)
uref.element.addGeneric(uref.feature_name, uref.proxy, index)
return :type_error
else
raise
end
end
else
begin
# this will replace the proxy
uref.element.setGeneric(uref.feature_name, target)
rescue StandardError => e
if is_type_error?(e)
return :type_error
else
raise
end
end
end
:success
end
def self.is_type_error?(e)
e.message =~ /Can not use a .* where a .* is expected/
end
end
end
end
rgen-0.10.2/lib/rgen/instantiator/xmi11_instantiator.rb 0000664 0000000 0000000 00000011477 14560401624 0023044 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore'
require 'rgen/instantiator/abstract_xml_instantiator'
require 'rgen/array_extensions'
class XMI11Instantiator < AbstractXMLInstantiator
include RGen::ECore
ResolverDescription = Struct.new(:object, :attribute, :value, :many)
INFO = 0
WARN = 1
ERROR = 2
def initialize(env, fix_map={}, loglevel=ERROR)
@env = env
@fix_map = fix_map
@loglevel = loglevel
@rolestack = []
@elementstack = []
end
def add_metamodel(ns, mod)
@ns_module_map ||={}
@ns_module_map[ns] = mod
end
def instantiate(str)
@resolver_descs = []
@element_by_id = {}
super(str, 1000)
@resolver_descs.each do |rd|
if rd.many
newval = rd.value.split(" ").collect{|v| @element_by_id[v]}
else
newval = @element_by_id[rd.value]
end
log WARN, "Could not resolve reference #{rd.attribute} on #{rd.object}" unless newval
begin
rd.object.setGeneric(rd.attribute,newval)
rescue Exception
log WARN, "Could not set reference #{rd.attribute} on #{rd.object}"
end
end
end
def start_tag(prefix, tag, namespaces, attributes)
if tag =~ /\w+\.(\w+)/
# XMI role
role_name = map_feature_name($1) || $1
eRef = @elementstack.last && eAllReferences(@elementstack.last).find{|r|r.name == role_name}
log WARN, "No reference found for #{role_name} on #{@elementstack.last}" unless eRef
@rolestack.push eRef
elsif attributes["xmi.idref"]
# reference
rd = ResolverDescription.new
rd.object = @elementstack.last
rd.attribute = @rolestack.last.name
rd.value = attributes["xmi.idref"]
rd.many = @rolestack.last.many
@resolver_descs << rd
@elementstack.push nil
else
# model element
value = map_tag(tag, attributes) || tag
if value.is_a?(String)
mod = @ns_module_map[namespaces[prefix]]
unless mod
log WARN, "Ignoring tag #{tag}"
return
end
value = mod.const_get(value).new
end
@env << value
eRef = @rolestack.last
if eRef && eRef.many
@elementstack.last.addGeneric(eRef.name, value)
elsif eRef
@elementstack.last.setGeneric(eRef.name, value)
end
@elementstack.push value
end
end
def end_tag(prefix, tag)
if tag =~ /\w+\.(\w+)/
@rolestack.pop
else
@elementstack.pop
end
end
def set_attribute(attr, value)
return unless @elementstack.last
if attr == "xmi.id"
@element_by_id[value] = @elementstack.last
else
attr_name = map_feature_name(attr) || attr
eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr_name}
unless eFeat
log WARN, "No structural feature found for #{attr_name} on #{@elementstack.last}"
return
end
if eFeat.is_a?(RGen::ECore::EReference)
if map_feature_value(attr_name, value).is_a?(eFeat.eType.instanceClass)
@elementstack.last.setGeneric(attr_name, map_feature_value(attr_name, value))
else
rd = ResolverDescription.new
rd.object = @elementstack.last
rd.attribute = attr_name
rd.value = value
rd.many = eFeat.many
@resolver_descs << rd
end
else
value = map_feature_value(attr_name, value) || value
value = true if value == "true" && eFeat.eType == EBoolean
value = false if value == "false" && eFeat.eType == EBoolean
value = value.to_i if eFeat.eType == EInt || eFeat.eType == ELong
value = value.to_f if eFeat.eType == EFloat
value = value.to_sym if eFeat.eType.is_a?(EEnum)
@elementstack.last.setGeneric(attr_name, value)
end
end
end
private
def map_tag(tag, attributes)
tag_map = @fix_map[:tags] || {}
value = tag_map[tag]
if value.is_a?(Proc)
value.call(tag, attributes)
else
value
end
end
def map_feature_name(name)
name_map = @fix_map[:feature_names] || {}
name_map[name]
end
def map_feature_value(attr_name, value)
value_map = @fix_map[:feature_values] || {}
map = value_map[attr_name]
if map.is_a?(Hash)
map[value]
elsif map.is_a?(Proc)
map.call(value)
end
end
def log(level, msg)
puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel
end
def eAllReferences(element)
@eAllReferences ||= {}
@eAllReferences[element.class] ||= element.class.ecore.eAllReferences
end
def eAllStructuralFeatures(element)
@eAllStructuralFeatures ||= {}
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
end
end
rgen-0.10.2/lib/rgen/metamodel_builder.rb 0000664 0000000 0000000 00000017125 14560401624 0020240 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/metamodel_builder/constant_order_helper'
require 'rgen/metamodel_builder/builder_runtime'
require 'rgen/metamodel_builder/builder_extensions'
require 'rgen/metamodel_builder/module_extension'
require 'rgen/metamodel_builder/data_types'
require 'rgen/metamodel_builder/mm_multiple'
require 'rgen/ecore/ecore_interface'
module RGen
# MetamodelBuilder can be used to create a metamodel, i.e. Ruby classes which
# act as metamodel elements.
#
# To create a new metamodel element, create a Ruby class which inherits from
# MetamodelBuilder::MMBase
#
# class Person < RGen::MetamodelBuilder::MMBase
# end
#
# This way a couple of class methods are made available to the new class.
# These methods can be used to:
# * add attributes to the class
# * add associations with other classes
#
# Here is an example:
#
# class Person < RGen::MetamodelBuilder::MMBase
# has_attr 'name', String
# has_attr 'age', Integer
# end
#
# class House < RGen::MetamodelBuilder::MMBase
# has_attr 'address' # String is default
# end
#
# Person.many_to_many 'homes', House, 'inhabitants'
#
# See BuilderExtensions for details about the available class methods.
#
# =Attributes
#
# The example above creates two classes 'Person' and 'House'. Person has the attributes
# 'name' and 'age', House has the attribute 'address'. The attributes can be
# accessed on instances of the classes in the following way:
#
# p = Person.new
# p.name = "MyName"
# p.age = 22
# p.name # => "MyName"
# p.age # => 22
#
# Note that the class Person takes care of the type of its attributes. As
# declared above, a 'name' can only be a String, an 'age' must be an Integer.
# So the following would return an exception:
#
# p.name = :myName # => exception: can not put a Symbol where a String is expected
#
# If the type of an attribute should be left undefined, use Object as type.
#
# =Associations
#
# As well as attributes show up as instance methods, associations bring their own
# accessor methods. For the Person-to-House association this would be:
#
# h1 = House.new
# h1.address = "Street1"
# h2 = House.new
# h2.address = "Street2"
# p.addHomes(h1)
# p.addHomes(h2)
# p.removeHomes(h1)
# p.homes # => [ h2 ]
#
# The Person-to-House association is _bidirectional_. This means that with the
# addition of a House to a Person, the Person is also added to the House. Thus:
#
# h1.inhabitants # => []
# h2.inhabitants # => [ p ]
#
# Note that the association is defined between two specific classes, instances of
# different classes can not be added. Thus, the following would result in an
# exception:
#
# p.addHomes(:justASymbol) # => exception: can not put a Symbol where a House is expected
#
# =ECore Metamodel description
#
# The class methods described above are used to create a Ruby representation of the metamodel
# we have in mind in a very simple and easy way. We don't have to care about all the details
# of a metamodel at this point (e.g. multiplicities, changeability, etc).
#
# At the same time however, an instance of the ECore metametamodel (i.e. a ECore based
# description of our metamodel) is provided for all the Ruby classes and modules we create.
# Since we did not provide the nitty-gritty details of the metamodel, defaults are used to
# fully complete the ECore metamodel description.
#
# In order to access the ECore metamodel description, just call the +ecore+ method on a
# Ruby class or module object belonging to your metamodel.
#
# Here is the example continued from above:
#
# Person.ecore.eAttributes.name # => ["name", "age"]
# h2pRef = House.ecore.eReferences.first
# h2pRef.eType # => Person
# h2pRef.eOpposite.eType # => House
# h2pRef.lowerBound # => 0
# h2pRef.upperBound # => -1
# h2pRef.many # => true
# h2pRef.containment # => false
#
# Note that the use of array_extensions.rb is assumed here to make model navigation convenient.
#
# The following metamodel builder methods are supported, see individual method description
# for details:
#
# Attributes:
# * BuilderExtensions#has_attr
#
# Unidirectional references:
# * BuilderExtensions#has_one
# * BuilderExtensions#has_many
# * BuilderExtensions#contains_one_uni
# * BuilderExtensions#contains_many_uni
#
# Bidirectional references:
# * BuilderExtensions#one_to_one
# * BuilderExtensions#one_to_many
# * BuilderExtensions#many_to_one
# * BuilderExtensions#many_to_many
# * BuilderExtensions#contains_one
# * BuilderExtensions#contains_many
#
# Every builder command can optionally take a specification of further ECore properties.
# Additional properties for Attributes and References are (with defaults in brackets):
# * :ordered (true),
# * :unique (true),
# * :changeable (true),
# * :volatile (false),
# * :transient (false),
# * :unsettable (false),
# * :derived (false),
# * :lowerBound (0),
# * :resolveProxies (true) references only,
#
# Using these additional properties, the above example can be refined as follows:
#
# class Person < RGen::MetamodelBuilder::MMBase
# has_attr 'name', String, :lowerBound => 1
# has_attr 'yearOfBirth', Integer,
# has_attr 'age', Integer, :derived => true
# def age_derived
# Time.now.year - yearOfBirth
# end
# end
#
# Person.many_to_many 'homes', House, 'inhabitants', :upperBound => 5
#
# Person.ecore.eReferences.find{|r| r.name == 'homes'}.upperBound # => 5
#
# This way we state that there must be a name for each person, we introduce a new attribute
# 'yearOfBirth' and make 'age' a derived attribute. We also say that a person can
# have at most 5 houses in our metamodel.
#
# ==Derived attributes and references
#
# If the attribute 'derived' of an attribute or reference is set to true, a method +attributeName_derived+
# has to be provided. This method is called whenever the original attribute is accessed. The
# original attribute can not be written if it is derived.
#
#
module MetamodelBuilder
# Use this class as a start for new metamodel elements (i.e. Ruby classes)
# by inheriting for it.
#
# See MetamodelBuilder for an example.
class MMBase
include BuilderRuntime
include DataTypes
extend BuilderExtensions
extend ModuleExtension
extend RGen::ECore::ECoreInterface
def initialize(arg=nil)
raise StandardError.new("Class #{self.class} is abstract") if self.class._abstract_class
arg.each_pair { |k,v| setGeneric(k, v) } if arg.is_a?(Hash)
end
# Object#inspect causes problems on most models
def inspect
self.class.name
end
def self.method_added(m)
raise "Do not add methods to model classes directly, add them to the ClassModule instead"
end
end
# Instances of MMGeneric can be used as values of any attribute are reference
class MMGeneric
# empty implementation so we don't have to check if a value is a MMGeneriv before setting the container
def _set_container(container, containing_feature_name)
end
end
# MMProxy objects can be used instead of real target elements in case references should be resolved later on
class MMProxy < MMGeneric
# The +targetIdentifer+ is an object identifying the element the proxy represents
attr_accessor :targetIdentifier
# +data+ is optional additional information to be associated with the proxy
attr_accessor :data
def initialize(ident=nil, data=nil)
@targetIdentifier = ident
@data = data
end
end
end
end
rgen-0.10.2/lib/rgen/metamodel_builder/ 0000775 0000000 0000000 00000000000 14560401624 0017705 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/metamodel_builder/builder_extensions.rb 0000664 0000000 0000000 00000055421 14560401624 0024146 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'erb'
require 'rgen/metamodel_builder/intermediate/feature'
module RGen
module MetamodelBuilder
# This module provides methods which can be used to setup a metamodel element.
# The module is used to +extend+ MetamodelBuilder::MMBase, i.e. add the module's
# methods as class methods.
#
# MetamodelBuilder::MMBase should be used as a start for new metamodel elements.
# See MetamodelBuilder for an example.
#
module BuilderExtensions
include Util::NameHelper
class FeatureBlockEvaluator
def self.eval(block, props1, props2=nil)
return unless block
e = self.new(props1, props2)
e.instance_eval(&block)
end
def initialize(props1, props2)
@props1, @props2 = props1, props2
end
def annotation(hash)
@props1.annotations << Intermediate::Annotation.new(hash)
end
def opposite_annotation(hash)
raise "No opposite available" unless @props2
@props2.annotations << Intermediate::Annotation.new(hash)
end
end
# Add an attribute which can hold a single value.
# 'role' specifies the name which is used to access the attribute.
# 'target_class' specifies the type of objects which can be held by this attribute.
# If no target class is given, String will be default.
#
# This class method adds the following instance methods, where 'role' is to be
# replaced by the given role name:
# class#role # getter
# class#role=(value) # setter
def has_attr(role, target_class=nil, raw_props={}, &block)
props = Intermediate::Attribute.new(target_class, _ownProps(raw_props).merge({
:name=>role, :upperBound=>1}))
raise "No opposite available" unless _oppositeProps(raw_props).empty?
FeatureBlockEvaluator.eval(block, props)
_build_internal(props)
end
# Add an attribute which can hold multiple values.
# 'role' specifies the name which is used to access the attribute.
# 'target_class' specifies the type of objects which can be held by this attribute.
# If no target class is given, String will be default.
#
# This class method adds the following instance methods, where 'role' is to be
# replaced by the given role name:
# class#addRole(value, index=-1)
# class#removeRole(value)
# class#role # getter, returns an array
# class#role= # setter, sets multiple values at once
# Note that the first letter of the role name is turned into an uppercase
# for the add and remove methods.
def has_many_attr(role, target_class=nil, raw_props={}, &block)
props = Intermediate::Attribute.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
:name=>role})))
raise "No opposite available" unless _oppositeProps(raw_props).empty?
FeatureBlockEvaluator.eval(block, props)
_build_internal(props)
end
# Add a single unidirectional association.
# 'role' specifies the name which is used to access the association.
# 'target_class' specifies the type of objects which can be held by this association.
#
# This class method adds the following instance methods, where 'role' is to be
# replaced by the given role name:
# class#role # getter
# class#role=(value) # setter
#
def has_one(role, target_class=nil, raw_props={}, &block)
props = Intermediate::Reference.new(target_class, _ownProps(raw_props).merge({
:name=>role, :upperBound=>1, :containment=>false}))
raise "No opposite available" unless _oppositeProps(raw_props).empty?
FeatureBlockEvaluator.eval(block, props)
_build_internal(props)
end
# Add an unidirectional _many_ association.
# 'role' specifies the name which is used to access the attribute.
# 'target_class' is optional and can be used to fix the type of objects which
# can be referenced by this association.
#
# This class method adds the following instance methods, where 'role' is to be
# replaced by the given role name:
# class#addRole(value, index=-1)
# class#removeRole(value)
# class#role # getter, returns an array
# Note that the first letter of the role name is turned into an uppercase
# for the add and remove methods.
#
def has_many(role, target_class=nil, raw_props={}, &block)
props = Intermediate::Reference.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
:name=>role, :containment=>false})))
raise "No opposite available" unless _oppositeProps(raw_props).empty?
FeatureBlockEvaluator.eval(block, props)
_build_internal(props)
end
def contains_one_uni(role, target_class=nil, raw_props={}, &block)
props = Intermediate::Reference.new(target_class, _ownProps(raw_props).merge({
:name=>role, :upperBound=>1, :containment=>true}))
raise "No opposite available" unless _oppositeProps(raw_props).empty?
FeatureBlockEvaluator.eval(block, props)
_build_internal(props)
end
def contains_many_uni(role, target_class=nil, raw_props={}, &block)
props = Intermediate::Reference.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
:name=>role, :containment=>true})))
raise "No opposite available" unless _oppositeProps(raw_props).empty?
FeatureBlockEvaluator.eval(block, props)
_build_internal(props)
end
# Add a bidirectional one-to-many association between two classes.
# The class this method is called on is refered to as _own_class_ in
# the following.
#
# Instances of own_class can use 'own_role' to access _many_ associated instances
# of type 'target_class'. Instances of 'target_class' can use 'target_role' to
# access _one_ associated instance of own_class.
#
# This class method adds the following instance methods where 'ownRole' and
# 'targetRole' are to be replaced by the given role names:
# own_class#addOwnRole(value, index=-1)
# own_class#removeOwnRole(value)
# own_class#ownRole
# target_class#targetRole
# target_class#targetRole=(value)
# Note that the first letter of the role name is turned into an uppercase
# for the add and remove methods.
#
# When an element is added/set on either side, this element also receives the element
# is is added to as a new element.
#
def one_to_many(target_role, target_class, own_role, raw_props={}, &block)
props1 = Intermediate::Reference.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
:name=>target_role, :containment=>false})))
props2 = Intermediate::Reference.new(self, _oppositeProps(raw_props).merge({
:name=>own_role, :upperBound=>1, :containment=>false}))
FeatureBlockEvaluator.eval(block, props1, props2)
_build_internal(props1, props2)
end
def contains_many(target_role, target_class, own_role, raw_props={}, &block)
props1 = Intermediate::Reference.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
:name=>target_role, :containment=>true})))
props2 = Intermediate::Reference.new(self, _oppositeProps(raw_props).merge({
:name=>own_role, :upperBound=>1, :containment=>false}))
FeatureBlockEvaluator.eval(block, props1, props2)
_build_internal(props1, props2)
end
# This is the inverse of one_to_many provided for convenience.
def many_to_one(target_role, target_class, own_role, raw_props={}, &block)
props1 = Intermediate::Reference.new(target_class, _ownProps(raw_props).merge({
:name=>target_role, :upperBound=>1, :containment=>false}))
props2 = Intermediate::Reference.new(self, _setManyUpperBound(_oppositeProps(raw_props).merge({
:name=>own_role, :containment=>false})))
FeatureBlockEvaluator.eval(block, props1, props2)
_build_internal(props1, props2)
end
# Add a bidirectional many-to-many association between two classes.
# The class this method is called on is refered to as _own_class_ in
# the following.
#
# Instances of own_class can use 'own_role' to access _many_ associated instances
# of type 'target_class'. Instances of 'target_class' can use 'target_role' to
# access _many_ associated instances of own_class.
#
# This class method adds the following instance methods where 'ownRole' and
# 'targetRole' are to be replaced by the given role names:
# own_class#addOwnRole(value, index=-1)
# own_class#removeOwnRole(value)
# own_class#ownRole
# target_class#addTargetRole
# target_class#removeTargetRole=(value)
# target_class#targetRole
# Note that the first letter of the role name is turned into an uppercase
# for the add and remove methods.
#
# When an element is added on either side, this element also receives the element
# is is added to as a new element.
#
def many_to_many(target_role, target_class, own_role, raw_props={}, &block)
props1 = Intermediate::Reference.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
:name=>target_role, :containment=>false})))
props2 = Intermediate::Reference.new(self, _setManyUpperBound(_oppositeProps(raw_props).merge({
:name=>own_role, :containment=>false})))
FeatureBlockEvaluator.eval(block, props1, props2)
_build_internal(props1, props2)
end
# Add a bidirectional one-to-one association between two classes.
# The class this method is called on is refered to as _own_class_ in
# the following.
#
# Instances of own_class can use 'own_role' to access _one_ associated instance
# of type 'target_class'. Instances of 'target_class' can use 'target_role' to
# access _one_ associated instance of own_class.
#
# This class method adds the following instance methods where 'ownRole' and
# 'targetRole' are to be replaced by the given role names:
# own_class#ownRole
# own_class#ownRole=(value)
# target_class#targetRole
# target_class#targetRole=(value)
#
# When an element is set on either side, this element also receives the element
# is is added to as the new element.
#
def one_to_one(target_role, target_class, own_role, raw_props={}, &block)
props1 = Intermediate::Reference.new(target_class, _ownProps(raw_props).merge({
:name=>target_role, :upperBound=>1, :containment=>false}))
props2 = Intermediate::Reference.new(self, _oppositeProps(raw_props).merge({
:name=>own_role, :upperBound=>1, :containment=>false}))
FeatureBlockEvaluator.eval(block, props1, props2)
_build_internal(props1, props2)
end
def contains_one(target_role, target_class, own_role, raw_props={}, &block)
props1 = Intermediate::Reference.new(target_class, _ownProps(raw_props).merge({
:name=>target_role, :upperBound=>1, :containment=>true}))
props2 = Intermediate::Reference.new(self, _oppositeProps(raw_props).merge({
:name=>own_role, :upperBound=>1, :containment=>false}))
FeatureBlockEvaluator.eval(block, props1, props2)
_build_internal(props1, props2)
end
def _metamodel_description # :nodoc:
@metamodel_description ||= []
end
def _add_metamodel_description(desc) # :nodoc
@metamodel_description ||= []
@metamodelDescriptionByName ||= {}
@metamodel_description.delete(@metamodelDescriptionByName[desc.value(:name)])
@metamodel_description << desc
@metamodelDescriptionByName[desc.value(:name)] = desc
end
def abstract
@abstract = true
end
def _abstract_class
@abstract || false
end
def inherited(c)
c.send(:include, c.const_set(:ClassModule, Module.new))
MetamodelBuilder::ConstantOrderHelper.classCreated(c)
end
protected
# Central builder method
#
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
# this is a bidirectional reference
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
# To-One association methods
#
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 <<-CODE
def get<%= firstToUpper(name) %>
<% if !props.reference? && props.value(:defaultValueLiteral) %>
<% defVal = props.value(:defaultValueLiteral) %>
<% check_default_value_literal(defVal, props) %>
<% defVal = '"'+defVal+'"' if props.impl_type == String %>
<% defVal = ':'+defVal if props.impl_type.is_a?(DataTypes::Enum) && props.impl_type != DataTypes::Boolean %>
(defined? @<%= name %>) ? @<%= name %> : <%= defVal %>
<% else %>
@<%= name %>
<% end %>
end
<% if name != "class" %>
alias <%= name %> get<%= firstToUpper(name) %>
<% else %>
def getGeneric(role)
send("get\#{firstToUpper(role.to_s)}")
end
<% end %>
CODE
self::ClassModule.module_eval(@@one_read_builder.result(binding))
end
if props.value(:changeable)
@@one_write_builder ||= ERB.new <<-CODE
def set<%= firstToUpper(name) %>(val)
return if (defined? @<%= name %>) && val == @<%= name %>
<%= type_check_code("val", props) %>
oldval = @<%= name %>
@<%= name %> = val
<% if other_role %>
oldval._unregister<%= firstToUpper(other_role) %>(self) unless oldval.nil? || oldval.is_a?(MMGeneric)
val._register<%= firstToUpper(other_role) %>(self) unless val.nil? || val.is_a?(MMGeneric)
<% end %>
<% if props.reference? && props.value(:containment) %>
val._set_container(self, :<%= name %>) unless val.nil?
oldval._set_container(nil, nil) unless oldval.nil?
<% end %>
end
alias <%= name %>= set<%= firstToUpper(name) %>
def _register<%= firstToUpper(name) %>(val)
<% if other_role %>
@<%= name %>._unregister<%= firstToUpper(other_role) %>(self) unless @<%= name %>.nil? || @<%= name %>.is_a?(MMGeneric)
<% end %>
<% if props.reference? && props.value(:containment) %>
@<%= name %>._set_container(nil, nil) unless @<%= name %>.nil?
val._set_container(self, :<%= name %>) unless val.nil?
<% end %>
@<%= name %> = val
end
def _unregister<%= firstToUpper(name) %>(val)
<% if props.reference? && props.value(:containment) %>
@<%= name %>._set_container(nil, nil) unless @<%= name %>.nil?
<% end %>
@<%= name %> = nil
end
CODE
self::ClassModule.module_eval(@@one_write_builder.result(binding))
end
end
# To-Many association methods
#
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 <<-CODE
def get<%= firstToUpper(name) %>
( defined?(@<%= name %>) ? @<%= name %>.dup : [] )
end
<% if name != "class" %>
alias <%= name %> get<%= firstToUpper(name) %>
<% else %>
def getGeneric(role)
send("get\#{firstToUpper(role.to_s)}")
end
<% end %>
CODE
self::ClassModule.module_eval(@@many_read_builder.result(binding))
end
if props.value(:changeable)
@@many_write_builder ||= ERB.new <<-CODE
def add<%= firstToUpper(name) %>(val, index=-1)
@<%= name %> = [] unless defined?(@<%= name %>)
return if val.nil? || (val.is_a?(MMBase) || val.is_a?(MMGeneric)) && @<%= name %>.any? {|e| e.equal?(val)}
<%= type_check_code("val", props) %>
@<%= name %>.insert(index, val)
<% if other_role %>
val._register<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMGeneric)
<% end %>
<% if props.reference? && props.value(:containment) %>
val._set_container(self, :<%= name %>)
<% end %>
end
def remove<%= firstToUpper(name) %>(val)
@<%= name %> = [] unless defined?(@<%= name %>)
@<%= name %>.each_with_index do |e,i|
if e.equal?(val)
@<%= name %>.delete_at(i)
<% if props.reference? && props.value(:containment) %>
val._set_container(nil, nil)
<% end %>
<% if other_role %>
val._unregister<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMGeneric)
<% end %>
return
end
end
end
def set<%= firstToUpper(name) %>(val)
return if val.nil?
raise _assignmentTypeError(self, val, Enumerable) unless val.is_a? Enumerable
get<%= firstToUpper(name) %>.each {|e|
remove<%= firstToUpper(name) %>(e)
}
@<%= name %> = [] unless defined?(@<%= name %>)
<% if props.reference? %>
val.uniq {|elem| elem.object_id }.each {|elem|
next if elem.nil?
<%= type_check_code("elem", props) %>
@<%= name %> << elem
<% if other_role %>
elem._register<%= firstToUpper(other_role) %>(self) unless elem.is_a?(MMGeneric)
<% end %>
<% if props.value(:containment) %>
elem._set_container(self, :<%= name %>)
<% end %>
}
<% else %>
val.each {|elem|
<%= type_check_code("elem", props) %>
@<%= name %> << elem
}
<% end %>
end
alias <%= name %>= set<%= firstToUpper(name) %>
def _register<%= firstToUpper(name) %>(val)
@<%= name %> = [] unless defined?(@<%= name %>)
@<%= name %>.push val
<% if props.reference? && props.value(:containment) %>
val._set_container(self, :<%= name %>)
<% end %>
end
def _unregister<%= firstToUpper(name) %>(val)
@<%= name %>.delete val
<% if props.reference? && props.value(:containment) %>
val._set_container(nil, nil)
<% end %>
end
CODE
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 <<-CODE
def get<%= firstToUpper(name) %>
raise "Derived feature requires public implementation of method <%= name %>_derived" \
unless respond_to?(:<%= name+"_derived" %>)
val = <%= name %>_derived
<% if kind == :many %>
raise _assignmentTypeError(self,val,Enumerable) unless val && val.is_a?(Enumerable)
val.each do |v|
<%= type_check_code("v", props) %>
end
<% else %>
<%= type_check_code("val", props) %>
<% end %>
val
end
<% if name != "class" %>
alias <%= name %> get<%= firstToUpper(name) %>
<% end %>
#TODO final_method :<%= name %>
CODE
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 || props.impl_type == RGen::MetamodelBuilder::DataTypes::Long
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 == RGen::MetamodelBuilder::DataTypes::Long
code << "unless #{varname}.nil? || #{varname}.is_a?(Integer) || #{varname}.is_a?(MMGeneric)"
code << "\n"
expected = "Integer"
elsif props.impl_type.is_a?(Class)
code << "unless #{varname}.nil? || #{varname}.is_a?(ObjectSpace._id2ref(#{props.impl_type.object_id})) || #{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
end
rgen-0.10.2/lib/rgen/metamodel_builder/builder_runtime.rb 0000664 0000000 0000000 00000011334 14560401624 0023425 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/util/name_helper'
module RGen
module MetamodelBuilder
# This module is mixed into MetamodelBuilder::MMBase.
# The methods provided by this module are used by the methods generated
# by the class methods of MetamodelBuilder::BuilderExtensions
module BuilderRuntime
include Util::NameHelper
def is_a?(c)
return super unless c.const_defined?(:ClassModule)
kind_of?(c::ClassModule)
end
def addGeneric(role, value, index=-1)
send("add#{firstToUpper(role.to_s)}",value, index)
end
def removeGeneric(role, value)
send("remove#{firstToUpper(role.to_s)}",value)
end
def setGeneric(role, value)
send("set#{firstToUpper(role.to_s)}",value)
end
def hasManyMethods(role)
respond_to?("add#{firstToUpper(role.to_s)}")
end
def setOrAddGeneric(role, value)
if hasManyMethods(role)
addGeneric(role, value)
else
setGeneric(role, value)
end
end
def setNilOrRemoveGeneric(role, value)
if hasManyMethods(role)
removeGeneric(role, value)
else
setGeneric(role, nil)
end
end
def setNilOrRemoveAllGeneric(role)
if hasManyMethods(role)
setGeneric(role, [])
else
setGeneric(role, nil)
end
end
alias getGeneric send
def getGenericAsArray(role)
result = getGeneric(role)
if result.nil?
[]
elsif result.is_a?(Array)
result
else
[result]
end
end
def eIsSet(role)
eval("defined? @#{role}") != nil
end
def eUnset(role)
if respond_to?("add#{firstToUpper(role.to_s)}")
setGeneric(role, [])
else
setGeneric(role, nil)
end
remove_instance_variable("@#{role}")
end
def eContainer
@_container
end
def eContainingFeature
@_containing_feature_name
end
# returns the contained elements in no particular order
def eContents
if @_contained_elements
@_contained_elements.dup
else
[]
end
end
# if a block is given, calls the block on every contained element in depth first order.
# if the block returns :prune, recursion will stop at this point.
#
# BEWARE of concurrent modification of contained elements while iterating!
# (adding/removing containers or contained elements)
# if you need to do such modifications, use the variant without a block instead.
#
# if no block is given builds and returns a list of all contained elements.
#
def eAllContents(&block)
if block
if @_contained_elements
@_contained_elements.each do |e|
res = block.call(e)
e.eAllContents(&block) if res != :prune
end
end
nil
else
result = []
if @_contained_elements
@_contained_elements.each do |e|
result << e
result.concat(e.eAllContents)
end
end
result
end
end
def disconnectContainer
eContainer.setNilOrRemoveGeneric(eContainingFeature, self) if eContainer
end
def _set_container(container, containing_feature_name)
# if a new container is set, make sure to disconnect from the old one.
# note that _set_container will never be called for the container and the role
# which are currently set because the accessor methods in BuilderExtensions
# block setting/adding a value which is already present.
# (it may be called for the same container with a different role, a different container
# with the same role and a different container with a different role, though)
# this ensures, that disconnecting for the current container doesn't break
# a new connection which has just been set up in the accessor methods.
disconnectContainer if container
@_container._remove_contained_element(self) if @_container
container._add_contained_element(self) if container
@_container = container
@_containing_feature_name = containing_feature_name
end
def _add_contained_element(element)
@_contained_elements ||= []
@_contained_elements << element
end
def _remove_contained_element(element)
@_contained_elements.delete(element) if @_contained_elements
end
def _assignmentTypeError(target, value, expected)
text = ""
if target
targetId = target.class.name
targetId += "(" + target.name + ")" if target.respond_to?(:name) and target.name
text += "In #{targetId} : "
end
valueId = value.class.name
valueId += "(" + value.name + ")" if value.respond_to?(:name) and value.name
valueId += "(:" + value.to_s + ")" if value.is_a?(Symbol)
text += "Can not use a #{valueId} where a #{expected} is expected"
StandardError.new(text)
end
end
end
end
rgen-0.10.2/lib/rgen/metamodel_builder/constant_order_helper.rb 0000664 0000000 0000000 00000006641 14560401624 0024624 0 ustar 00root root 0000000 0000000 module RGen
module MetamodelBuilder
# The purpose of the ConstantOrderHelper is to capture the definition order of RGen metamodel builder
# classes, modules and enums. The problem is that Ruby doesn't seem to track the order of
# constants being created in a module. However the order is important because it defines the order
# of eClassifiers and eSubpackages in a EPackage.
#
# It would be helpful here if Ruby provided a +const_added+ callback, but this is not the case up to now.
#
# The idea for capturing is that all events of creating a RGen class, module or enum are reported to the
# ConstantOrderHelper singleton.
# For classes and modules it tries to add their names to the parent's +_constantOrder+ array.
# The parent module is derived from the class's or module's name. However, the new name is only added
# if the respective parent module has a new constant (which is not yet in +_constantOrder+) which
# points to the new class or module.
# For enums it is a bit more complicated, because at the time the enum is created, the parent
# module does not yet contain the constant to which the enum is assigned. Therefor, the enum is remembered
# and it is tried to be stored on the next event (class, module or enum) within the module which was
# created last (which was last extended with ModuleExtension). If it can not be found in that module,
# all parent modules of the last module are searched. This way it should also be correctly entered in
# case it was defined outside of the last created module.
# Note that an enum is not stored to the constant order array unless another event occurs. That's why
# it is possible that one enum is missing at the enum. This needs to be taken care of by the ECore transformer.
#
# This way of capturing should be sufficient for the regular use cases of the RGen metamodel builder language.
# However, it is possible to write code which messes this up, see unit tests for details.
# In the worst case, the new classes, modules or enums will just not be found in a parent module and thus be ignored.
#
ConstantOrderHelper = Class.new do
def initialize
@currentModule = nil
@pendingEnum = nil
end
def classCreated(c)
handlePendingEnum
cont = containerModule(c)
name = (c.name || "").split("::").last
return unless cont.respond_to?(:_constantOrder) && !cont._constantOrder.include?(name)
cont._constantOrder << name
end
def moduleCreated(m)
handlePendingEnum
cont = containerModule(m)
name = (m.name || "").split("::").last
return unless cont.respond_to?(:_constantOrder) && !cont._constantOrder.include?(name)
cont._constantOrder << name
@currentModule = m
end
def enumCreated(e)
handlePendingEnum
@pendingEnum = e
end
private
def containerModule(m)
containerName = (m.name || "").split("::")[0..-2].join("::")
containerName.empty? ? nil : eval(containerName, TOPLEVEL_BINDING)
end
def handlePendingEnum
return unless @pendingEnum
m = @currentModule
while m
if m.respond_to?(:_constantOrder)
newConstants = m.constants - m._constantOrder
const = newConstants.find{|c| m.const_get(c).object_id == @pendingEnum.object_id}
if const
m._constantOrder << const.to_s
break
end
end
m = containerModule(m)
end
@pendingEnum = nil
end
end.new
end
end
rgen-0.10.2/lib/rgen/metamodel_builder/data_types.rb 0000664 0000000 0000000 00000004442 14560401624 0022373 0 ustar 00root root 0000000 0000000 module RGen
module MetamodelBuilder
module DataTypes
# An enum object is used to describe possible attribute values within a
# MetamodelBuilder attribute definition. An attribute defined this way can only
# take the values specified when creating the Enum object.
# Literal values can only be symbols or true or false.
# Optionally a name may be specified for the enum object.
#
# Examples:
#
# Enum.new(:name => "AnimalEnum", :literals => [:cat, :dog])
# Enum.new(:literals => [:cat, :dog])
# Enum.new([:cat, :dog])
#
class Enum
attr_reader :name, :literals
# Creates a new named enum type object consisting of the elements passed as arguments.
def initialize(params)
MetamodelBuilder::ConstantOrderHelper.enumCreated(self)
if params.is_a?(Array)
@literals = params
@name = "anonymous"
elsif params.is_a?(Hash)
raise StandardError.new("Hash entry :literals is missing") unless params[:literals]
@literals = params[:literals]
@name = params[:name] || "anonymous"
else
raise StandardError.new("Pass an Array or a Hash")
end
end
# This method can be used to check if an object can be used as value for
# variables having this enum object as type.
def validLiteral?(l)
literals.include?(l)
end
def literals_as_strings
literals.collect do |l|
if l.is_a?(Symbol)
if l.to_s =~ /^\d|\W/
":'"+l.to_s+"'"
else
":"+l.to_s
end
elsif l.is_a?(TrueClass) || l.is_a?(FalseClass)
l.to_s
else
raise StandardError.new("Literal values can only be symbols or true/false")
end
end
end
def to_s # :nodoc:
name
end
end
# Boolean is a predefined enum object having Ruby's true and false singletons
# as possible values.
Boolean = Enum.new(:name => "Boolean", :literals => [true, false])
# Long represents a 64-bit Integer
# This constant is merely a marker for keeping this information in the Ruby version of the metamodel,
# values of this type will always be instances of Integer or Bignum;
# Setting it to a string value ensures that it responds to "to_s" which is used in the metamodel generator
Long = "Long"
end
end
end
rgen-0.10.2/lib/rgen/metamodel_builder/intermediate/ 0000775 0000000 0000000 00000000000 14560401624 0022357 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/metamodel_builder/intermediate/annotation.rb 0000664 0000000 0000000 00000001270 14560401624 0025056 0 ustar 00root root 0000000 0000000 module RGen
module MetamodelBuilder
module Intermediate
class Annotation
attr_reader :details, :source
def initialize(hash)
if hash[:source] || hash[:details]
restKeys = hash.keys - [:source, :details]
raise "Hash key #{restKeys.first} not allowed." unless restKeys.empty?
raise "Details not provided, key :details is missing" unless hash[:details]
raise "Details must be provided as a hash" unless hash[:details].is_a?(Hash)
@details = hash[:details]
@source = hash[:source]
else
raise "Details must be provided as a hash" unless hash.is_a?(Hash)
@details = hash
end
end
end
end
end
end
rgen-0.10.2/lib/rgen/metamodel_builder/intermediate/feature.rb 0000664 0000000 0000000 00000006710 14560401624 0024343 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder/data_types'
module RGen
module MetamodelBuilder
module Intermediate
class Feature
attr_reader :etype, :impl_type
def value(prop)
@props[prop]
end
def annotations
@annotations ||= []
end
def many?
value(:upperBound) > 1 || value(:upperBound) == -1
end
def reference?
is_a?(Reference)
end
protected
def check(props)
@props.keys.each do |p|
kind = props[p]
raise StandardError.new("invalid property #{p}") unless kind
raise StandardError.new("property '#{p}' not set") if value(p).nil? && kind == :required
end
end
end
class Attribute < Feature
Properties = {
:name => :required,
:ordered => :required,
:unique => :required,
:changeable => :required,
:volatile => :required,
:transient => :required,
:unsettable => :required,
:derived => :required,
:lowerBound => :required,
:upperBound => :required,
:defaultValueLiteral => :optional
}
Defaults = {
:ordered => true,
:unique => true,
:changeable => true,
:volatile => false,
:transient => false,
:unsettable => false,
:derived => false,
:lowerBound => 0
}
Types = {
String => :EString,
Integer => :EInt,
RGen::MetamodelBuilder::DataTypes::Long => :ELong,
Float => :EFloat,
RGen::MetamodelBuilder::DataTypes::Boolean => :EBoolean,
Object => :ERubyObject,
Class => :ERubyClass
}
def self.default_value(prop)
Defaults[prop]
end
def self.properties
Properties.keys.sort{|a,b| a.to_s <=> b.to_s}
end
def initialize(type, props)
@props = Defaults.merge(props)
type ||= String
@etype = Types[type]
if @etype
@impl_type = type
elsif type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum)
@etype = :EEnumerable
@impl_type = type
else
raise ArgumentError.new("invalid type '#{type}'")
end
if @props[:derived]
@props[:changeable] = false
@props[:volatile] = true
@props[:transient] = true
end
check(Properties)
end
end
class Reference < Feature
attr_accessor :opposite
Properties = {
:name => :required,
:ordered => :required,
:unique => :required,
:changeable => :required,
:volatile => :required,
:transient => :required,
:unsettable => :required,
:derived => :required,
:lowerBound => :required,
:upperBound => :required,
:resolveProxies => :required,
:containment => :required
}
Defaults = {
:ordered => true,
:unique => true,
:changeable => true,
:volatile => false,
:transient => false,
:unsettable => false,
:derived => false,
:lowerBound => 0,
:resolveProxies => true
}
def self.default_value(prop)
Defaults[prop]
end
def self.properties
Properties.keys.sort{|a,b| a.to_s <=> b.to_s}
end
def initialize(type, props)
@props = Defaults.merge(props)
if type.respond_to?(:_metamodel_description)
@etype = nil
@impl_type = type
else
raise ArgumentError.new("'#{type}' (#{type.class}) is not a MMBase in reference #{props[:name]}")
end
if @props[:derived]
@props[:changeable] = false
@props[:volatile] = true
@props[:transient] = true
end
check(Properties)
end
end
end
end
end
rgen-0.10.2/lib/rgen/metamodel_builder/mm_multiple.rb 0000664 0000000 0000000 00000000650 14560401624 0022557 0 ustar 00root root 0000000 0000000
module RGen
module MetamodelBuilder
def self.MMMultiple(*superclasses)
c = Class.new(MMBase)
class << c
attr_reader :multiple_superclasses
end
c.instance_variable_set(:@multiple_superclasses, superclasses)
superclasses.collect{|sc| sc.ancestors}.flatten.
reject{|m| m.is_a?(Class)}.each do |arg|
c.instance_eval do
include arg
end
end
return c
end
end
end rgen-0.10.2/lib/rgen/metamodel_builder/module_extension.rb 0000664 0000000 0000000 00000001466 14560401624 0023622 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore_interface'
require 'rgen/metamodel_builder/intermediate/annotation'
module RGen
module MetamodelBuilder
# This module is used to extend modules which should be
# part of RGen metamodels
module ModuleExtension
include RGen::ECore::ECoreInterface
def annotation(hash)
_annotations << Intermediate::Annotation.new(hash)
end
def _annotations
@_annotations ||= []
end
def _constantOrder
@_constantOrder ||= []
end
def final_method(m)
@final_methods ||= []
@final_methods << m
end
def method_added(m)
raise "Method #{m} can not be redefined" if @final_methods && @final_methods.include?(m)
end
def self.extended(m)
MetamodelBuilder::ConstantOrderHelper.moduleCreated(m)
end
end
end
end
rgen-0.10.2/lib/rgen/model_builder.rb 0000664 0000000 0000000 00000001746 14560401624 0017373 0 ustar 00root root 0000000 0000000 require 'rgen/model_builder/builder_context'
require 'rgen/util/method_delegation'
#require 'ruby-prof'
module RGen
module ModelBuilder
def self.build(package, env=nil, builderMethodsModule=nil, &block)
resolver = ReferenceResolver.new
bc = BuilderContext.new(package, builderMethodsModule, resolver, env)
contextModule = eval("Module.nesting", block.binding).first
Util::MethodDelegation.registerDelegate(bc, contextModule, "const_missing")
BuilderContext.currentBuilderContext = bc
begin
#RubyProf.start
bc.instance_eval(&block)
#prof = RubyProf.stop
#File.open("profile_flat.txt","w+") do |f|
# RubyProf::FlatPrinter.new(prof).print(f, 0)
# end
ensure
BuilderContext.currentBuilderContext = nil
end
Util::MethodDelegation.unregisterDelegate(bc, contextModule, "const_missing")
#puts "Resolving..."
resolver.resolve(bc.toplevelElements)
bc.toplevelElements
end
end
end
rgen-0.10.2/lib/rgen/model_builder/ 0000775 0000000 0000000 00000000000 14560401624 0017036 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/model_builder/builder_context.rb 0000664 0000000 0000000 00000025517 14560401624 0022567 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore_ext'
require 'rgen/model_builder/reference_resolver'
module RGen
module ModelBuilder
class BuilderContext
attr_reader :toplevelElements
def initialize(package, extensionsModule, resolver, env=nil)
package = package.ecore unless package.is_a?(RGen::ECore::EPackage)
raise "First argument must be a metamodel package" \
unless package.is_a?(RGen::ECore::EPackage)
@rootPackage, @env = package, env
@commandResolver = CommandResolver.new(package, extensionsModule, self)
@package = @rootPackage
@resolver = resolver
@contextStack = []
@toplevelElements = []
@helperNames = {}
end
def const_missing_delegated(delegator, const)
ConstPathElement.new(const, self)
end
# in Ruby 1.9.0 and 1.9.1 #instance_eval looks up constants in the calling scope
# that's why const_missing needs to be prepared in BuilderContext, too
class << self
def currentBuilderContext=(bc)
@@currentBuilderContext = bc
end
def const_missing(name)
if @@currentBuilderContext
ConstPathElement.new(name, @@currentBuilderContext)
else
super
end
end
end
class CommandResolver
def initialize(rootPackage, extensionsModule, builderContext)
@extensionFactory = ExtensionContainerFactory.new(rootPackage, extensionsModule, builderContext)
@packageResolver = PackageResolver.new(rootPackage, @extensionFactory)
@resolveCommand = {}
end
def resolveCommand(cmd, parentPackage)
return @resolveCommand[[parentPackage, cmd]] if @resolveCommand.has_key?([parentPackage, cmd])
package = @packageResolver.packageByCommand(parentPackage, cmd)
result = nil
if package
extensionContainer = @extensionFactory.extensionContainer(package)
if extensionContainer.respond_to?(cmd)
result = extensionContainer
else
className = cmd.to_s[0..0].upcase + cmd.to_s[1..-1]
result = package.eClasses.find{|c| c.name == className}
end
end
@resolveCommand[[parentPackage, cmd]] = [package, result]
end
end
def method_missing(m, *args, &block)
package, classOrContainer = @commandResolver.resolveCommand(m, @package)
return super if package.nil?
return classOrContainer.send(m, *args, &block) if classOrContainer.is_a?(ExtensionContainerFactory::ExtensionContainer)
eClass = classOrContainer
nameArg, argHash = self.class.processArguments(args)
internalName = nameArg || argHash[:name]
argHash[:name] ||= nameArg if nameArg && self.class.hasNameAttribute(eClass)
resolverJobs, asRole, helperName = self.class.filterArgHash(argHash, eClass)
element = eClass.instanceClass.new(argHash)
@resolver.setElementName(element, internalName)
@env << element if @env
contextElement = @contextStack.last
if contextElement
self.class.associateWithContextElement(element, contextElement, asRole)
else
@toplevelElements << element
end
resolverJobs.each do |job|
job.receiver = element
job.namespace = contextElement
@resolver.addJob(job)
end
# process block
if block
@contextStack.push(element)
@package, oldPackage = package, @package
instance_eval(&block)
@package = oldPackage
@contextStack.pop
end
element
end
def _using(constPathElement, &block)
@package, oldPackage =
self.class.resolvePackage(@package, @rootPackage, constPathElement.constPath), @package
instance_eval(&block)
@package = oldPackage
end
def _context(depth=1)
@contextStack[-depth]
end
class ExtensionContainerFactory
class ExtensionContainer
def initialize(builderContext)
@builderContext = builderContext
end
def method_missing(m, *args, &block)
@builderContext.send(m, *args, &block)
end
end
def initialize(rootPackage, extensionsModule, builderContext)
@rootPackage, @extensionsModule, @builderContext = rootPackage, extensionsModule, builderContext
@extensionContainer = {}
end
def moduleForPackage(package)
qName = package.qualifiedName
rqName = @rootPackage.qualifiedName
raise "Package #{qName} is not contained within #{rqName}" unless qName.index(rqName) == 0
path = qName.sub(rqName,'').split('::')
path.shift if path.first == ""
mod = @extensionsModule
path.each do |p|
if mod && mod.const_defined?(p)
mod = mod.const_get(p)
else
mod = nil
break
end
end
mod
end
def extensionContainer(package)
return @extensionContainer[package] if @extensionContainer[package]
container = ExtensionContainer.new(@builderContext)
extensionModule = moduleForPackage(package)
container.extend(extensionModule) if extensionModule
@extensionContainer[package] = container
end
end
class PackageResolver
def initialize(rootPackage, extensionFactory)
@rootPackage = rootPackage
@extensionFactory = extensionFactory
@packageByCommand = {}
end
def packageByCommand(contextPackage, name)
return @packageByCommand[[contextPackage, name]] if @packageByCommand.has_key?([contextPackage, name])
if @extensionFactory.extensionContainer(contextPackage).respond_to?(name)
result = contextPackage
else
className = name.to_s[0..0].upcase + name.to_s[1..-1]
eClass = contextPackage.eClasses.find{|c| c.name == className}
if eClass
result = contextPackage
elsif contextPackage != @rootPackage
result = packageByCommand(contextPackage.eSuperPackage, name)
else
result = nil
end
end
@packageByCommand[[contextPackage, name]] = result
end
end
class ConstPathElement < Module
def initialize(name, builderContext, parent=nil)
@name = name.to_s
@builderContext = builderContext
@parent = parent
end
def const_missing(const)
ConstPathElement.new(const, @builderContext, self)
end
def method_missing(m, *args, &block)
@builderContext._using(self) do
send(m, *args, &block)
end
end
def constPath
if @parent
@parent.constPath << @name
else
[@name]
end
end
end
# helper methods put in the class object to be out of the way of
# method evaluation in the builder context
class << self
class PackageNotFoundException < Exception
end
def resolvePackage(contextPackage, rootPackage, path)
begin
return resolvePackageDownwards(contextPackage, path)
rescue PackageNotFoundException
if contextPackage.eSuperPackage && contextPackage != rootPackage
return resolvePackage(contextPackage.eSuperPackage, rootPackage, path)
else
raise
end
end
end
def resolvePackageDownwards(contextPackage, path)
first, *rest = path
package = contextPackage.eSubpackages.find{|p| p.name == first}
raise PackageNotFoundException.new("Could not resolve package: #{first} is not a subpackage of #{contextPackage.name}") unless package
if rest.empty?
package
else
resolvePackageDownwards(package, rest)
end
end
def processArguments(args)
unless (args.size == 2 && args.first.is_a?(String) && args.last.is_a?(Hash)) ||
(args.size == 1 && (args.first.is_a?(String) || args.first.is_a?(Hash))) ||
args.size == 0
raise "Provide a Hash to set feature values, " +
"optionally the first argument may be a String specifying " +
"the value of the \"name\" attribute."
end
if args.last.is_a?(Hash)
argHash = args.last
else
argHash = {}
end
nameArg = args.first if args.first.is_a?(String)
[nameArg, argHash]
end
def filterArgHash(argHash, eClass)
resolverJobs = []
asRole, helperName = nil, nil
refByName = {}
eAllReferences(eClass).each {|r| refByName[r.name] = r}
argHash.each_pair do |k,v|
if k == :as
asRole = v
argHash.delete(k)
elsif k == :name && !hasNameAttribute(eClass)
helperName = v
argHash.delete(k)
elsif v.is_a?(String)
ref = refByName[k.to_s]#eAllReferences(eClass).find{|r| r.name == k.to_s}
if ref
argHash.delete(k)
resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, v)
end
elsif v.is_a?(Array)
ref = refByName[k.to_s] #eAllReferences(eClass).find{|r| r.name == k.to_s}
ref && v.dup.each do |e|
if e.is_a?(String)
v.delete(e)
resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, e)
end
end
end
end
[ resolverJobs, asRole, helperName ]
end
def hasNameAttribute(eClass)
@hasNameAttribute ||= {}
@hasNameAttribute[eClass] ||= eClass.eAllAttributes.any?{|a| a.name == "name"}
end
def eAllReferences(eClass)
@eAllReferences ||= {}
@eAllReferences[eClass] ||= eClass.eAllReferences
end
def containmentRefs(contextClass, eClass)
@containmentRefs ||= {}
@containmentRefs[[contextClass, eClass]] ||=
eAllReferences(contextClass).select do |r|
r.containment && (eClass.eAllSuperTypes << eClass).include?(r.eType)
end
end
def associateWithContextElement(element, contextElement, asRole)
return unless contextElement
contextClass = contextElement.class.ecore
if asRole
asRoleRef = eAllReferences(contextClass).find{|r| r.name == asRole.to_s}
raise "Context class #{contextClass.name} has no reference named #{asRole}" unless asRoleRef
ref = asRoleRef
else
possibleContainmentRefs = containmentRefs(contextClass, element.class.ecore)
if possibleContainmentRefs.size == 1
ref = possibleContainmentRefs.first
elsif possibleContainmentRefs.size == 0
raise "Context class #{contextClass.name} can not contain a #{element.class.ecore.name}"
else
raise "Context class #{contextClass.name} has several containment references to a #{element.class.ecore.name}." +
" Clearify using \":as => \""
end
end
if ref.many
contextElement.addGeneric(ref.name, element)
else
contextElement.setGeneric(ref.name, element)
end
end
end
end
end
end
rgen-0.10.2/lib/rgen/model_builder/model_serializer.rb 0000664 0000000 0000000 00000017306 14560401624 0022723 0 ustar 00root root 0000000 0000000 require 'rgen/array_extensions'
require 'rgen/ecore/ecore_ext'
module RGen
module ModelBuilder
class ModelSerializer
def initialize(writable, rootPackage)
@writable = writable
@currentPackage = rootPackage
@qualifiedElementName = {}
@internalElementName = {}
@relativeQualifiedElementName = {}
end
def serialize(elements)
calcQualifiedElementNames(elements)
unifyQualifiedElementNames
elements = [elements] unless elements.is_a?(Enumerable)
elements.each do |e|
serializeElement(e)
end
end
private
def serializeElement(element, viaRef=nil, namePath=[], indent=0)
className = element.class.ecore.name
cmd = className[0..0].downcase+className[1..-1]
args = ["\"#{@internalElementName[element]}\""]
namePath = namePath + [@internalElementName[element]]
childs = []
eAllStructuralFeatures(element).each do |f|
next if f.derived
if f.is_a?(RGen::ECore::EAttribute)
next if f.name == "name" && element.name == @internalElementName[element]
val = element.getGeneric(f.name)
#puts f.defaultValue.inspect if f.name == "isRoot"
args << ":#{f.name} => #{serializeAttribute(val)}" unless val == f.defaultValue || val.nil?
elsif !f.containment
next if f.eOpposite && f.eOpposite == viaRef
val = element.getGeneric(f.name)
refString = serializeReference(element, f, val)
args << ":#{f.name} => #{refString}" if refString
else
cs = element.getGeneric(f.name)
refString = nil
if cs.is_a?(Array)
cs.compact!
rcs = cs.select{|c| serializeChild?(c, namePath)}
childs << [f, rcs] unless rcs.empty?
refString = serializeReference(element, f, cs-rcs)
else
if cs && serializeChild?(cs, namePath)
childs << [f, [cs]]
else
refString = serializeReference(element, f, cs)
end
end
args << ":#{f.name} => #{refString}" if refString
end
end
args << ":as => :#{viaRef.name}" if viaRef && containmentRefs(viaRef.eContainingClass, element.class.ecore).size > 1
cmd = elementPackage(element)+"."+cmd if elementPackage(element).size > 0
@writable.write " " * indent + cmd + " " + args.join(", ")
if childs.size > 0
@writable.write " do\n"
oldPackage, @currentPackage = @currentPackage, element.class.ecore.ePackage
childs.each do |pair|
f, cs = pair
cs.each {|c| serializeElement(c, f, namePath, indent+1) }
end
@currentPackage = oldPackage
@writable.write " " * indent + "end\n"
else
@writable.write "\n"
end
end
def serializeChild?(child, namePath)
@qualifiedElementName[child][0..-2] == namePath
end
def serializeAttribute(value)
if value.is_a?(String)
"\"#{value.gsub("\"","\\\"")}\""
elsif value.is_a?(Symbol)
":#{value}"
elsif value.nil?
"nil"
else
value.to_s
end
end
def serializeReference(element, ref, value)
if value.is_a?(Array)
value = value.compact
value = value.select{|v| compareWithOppositeReference(ref, element, v) > 0} if ref.eOpposite
qualNames = value.collect do |v|
relativeQualifiedElementName(v, element).join(".")
end
!qualNames.empty? && ("[" + qualNames.collect { |v| "\"#{v}\"" }.join(", ") + "]")
elsif value && (!ref.eOpposite || compareWithOppositeReference(ref, element, value) > 0)
qualName = relativeQualifiedElementName(value, element).join(".")
("\"#{qualName}\"")
end
end
# descide which part of a bidirectional reference get serialized
def compareWithOppositeReference(ref, element, target)
result = 0
# first try to make the reference from the many side to the one side
result = -1 if ref.many && !ref.eOpposite.many
result = 1 if !ref.many && ref.eOpposite.many
return result if result != 0
# for 1:1 or many:many perfer, shorter references
result = relativeQualifiedElementName(element, target).size <=>
relativeQualifiedElementName(target, element).size
return result if result != 0
# there just needs to be a descision, use class name or object_id
result = element.class.name <=> target.class.name
return result if result != 0
element.object_id <=> target.object_id
end
def elementPackage(element)
@elementPackage ||= {}
return @elementPackage[element] if @elementPackage[element]
eNames = element.class.ecore.ePackage.qualifiedName.split("::")
rNames = @currentPackage.qualifiedName.split("::")
while eNames.first == rNames.first && !eNames.first.nil?
eNames.shift
rNames.shift
end
@elementPackage[element] = eNames.join("::")
end
def relativeQualifiedElementName(element, context)
return @relativeQualifiedElementName[[element, context]] if @relativeQualifiedElementName[[element, context]]
# elements which are not in the @qualifiedElementName Hash are not in the scope
# of this serialization and will be ignored
return [] if element.nil? || @qualifiedElementName[element].nil?
return [] if context.nil? || @qualifiedElementName[context].nil?
eNames = @qualifiedElementName[element].dup
cNames = @qualifiedElementName[context].dup
while eNames.first == cNames.first && eNames.size > 1
eNames.shift
cNames.shift
end
@relativeQualifiedElementName[[element, context]] = eNames
end
def calcQualifiedElementNames(elements, prefix=[], takenNames=[])
elements = [elements] unless elements.is_a?(Array)
elements.compact!
elements.each do |element|
qualifiedNamePath = prefix + [calcInternalElementName(element, takenNames)]
@qualifiedElementName[element] ||= []
@qualifiedElementName[element] << qualifiedNamePath
takenChildNames = []
eAllStructuralFeatures(element).each do |f|
if f.is_a?(RGen::ECore::EReference) && f.containment
childs = element.getGeneric(f.name)
calcQualifiedElementNames(childs, qualifiedNamePath, takenChildNames)
end
end
end
end
def unifyQualifiedElementNames
@qualifiedElementName.keys.each do |k|
@qualifiedElementName[k] = @qualifiedElementName[k].sort{|a,b| a.size <=> b.size}.first
end
end
def calcInternalElementName(element, takenNames)
return @internalElementName[element] if @internalElementName[element]
name = if element.respond_to?(:name) && element.name && !element.name.empty?
element.name
else
nextElementHelperName(element)
end
while takenNames.include?(name)
name = nextElementHelperName(element)
end
takenNames << name
@internalElementName[element] = name
end
def nextElementHelperName(element)
eClass = element.class.ecore
@nextElementNameId ||= {}
@nextElementNameId[eClass] ||= 1
result = "_#{eClass.name}#{@nextElementNameId[eClass]}"
@nextElementNameId[eClass] += 1
result
end
def eAllStructuralFeatures(element)
@eAllStructuralFeatures ||= {}
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
end
def eAllReferences(eClass)
@eAllReferences ||= {}
@eAllReferences[eClass] ||= eClass.eAllReferences
end
def containmentRefs(contextClass, eClass)
@containmentRefs ||= {}
@containmentRefs[[contextClass, eClass]] ||=
eAllReferences(contextClass).select do |r|
r.containment && (eClass.eAllSuperTypes << eClass).include?(r.eType)
end
end
end
end
end
rgen-0.10.2/lib/rgen/model_builder/reference_resolver.rb 0000664 0000000 0000000 00000010503 14560401624 0023241 0 ustar 00root root 0000000 0000000 require 'rgen/array_extensions'
module RGen
module ModelBuilder
class ReferenceResolver
ResolverJob = Struct.new(:receiver, :reference, :namespace, :string)
class ResolverException < Exception
end
class ToplevelNamespace
def initialize(ns)
raise "Namespace must be an Enumerable" unless ns.is_a?(Enumerable)
@ns = ns
end
def elements
@ns
end
end
def initialize
@jobs = []
@elementName = {}
end
def addJob(job)
@jobs << job
end
def setElementName(element, name)
@elementName[element] = name
end
def resolve(ns=[])
@toplevelNamespace = ToplevelNamespace.new(ns)
(@jobs || []).each_with_index do |job, i|
target = resolveReference(job.namespace || @toplevelNamespace, job.string.split("."))
raise ResolverException.new("Can not resolve reference #{job.string}") unless target
if job.reference.many
job.receiver.addGeneric(job.reference.name, target)
else
job.receiver.setGeneric(job.reference.name, target)
end
end
end
private
# TODO: if a reference can not be fully resolved, but a prefix can be found,
# the exception reported is that its first path element can not be found on
# toplevel
def resolveReference(namespace, nameParts)
element = resolveReferenceDownwards(namespace, nameParts)
if element.nil? && parentNamespace(namespace)
element = resolveReference(parentNamespace(namespace), nameParts)
end
element
end
def resolveReferenceDownwards(namespace, nameParts)
firstPart, *restParts = nameParts
element = namespaceElementByName(namespace, firstPart)
return nil unless element
if restParts.size > 0
resolveReferenceDownwards(element, restParts)
else
element
end
end
def namespaceElementByName(namespace, name)
@namespaceElementsByName ||= {}
return @namespaceElementsByName[namespace][name] if @namespaceElementsByName[namespace]
hash = {}
namespaceElements(namespace).each do |e|
raise ResolverException.new("Multiple elements named #{elementName(e)} found in #{nsToS(namespace)}") if hash[elementName(e)]
hash[elementName(e)] = e if elementName(e)
end
@namespaceElementsByName[namespace] = hash
hash[name]
end
def parentNamespace(namespace)
if namespace.class.respond_to?(:ecore)
parents = elementParents(namespace)
raise ResolverException.new("Element #{nsToS(namespace)} has multiple parents") \
if parents.size > 1
parents.first || @toplevelNamespace
else
nil
end
end
def namespaceElements(namespace)
if namespace.is_a?(ToplevelNamespace)
namespace.elements
elsif namespace.class.respond_to?(:ecore)
elementChildren(namespace)
else
raise ResolverException.new("Element #{nsToS(namespace)} can not be used as a namespace")
end
end
def nsToS(namespace)
if namespace.is_a?(ToplevelNamespace)
"toplevel namespace"
else
result = namespace.class.name
result += ":\"#{elementName(namespace)}\"" if elementName(namespace)
result
end
end
def elementName(element)
@elementName[element]
end
def elementChildren(element)
@elementChildren ||= {}
return @elementChildren[element] if @elementChildren[element]
children = containmentRefs(element).collect do |r|
element.getGeneric(r.name)
end.flatten.compact
@elementChildren[element] = children
end
def elementParents(element)
@elementParents ||= {}
return @elementParents[element] if @elementParents[element]
parents = parentRefs(element).collect do |r|
element.getGeneric(r.name)
end.flatten.compact
@elementParents[element] = parents
end
def containmentRefs(element)
@containmentRefs ||= {}
@containmentRefs[element.class] ||= eAllReferences(element).select{|r| r.containment}
end
def parentRefs(element)
@parentRefs ||= {}
@parentRefs[element.class] ||= eAllReferences(element).select{|r| r.eOpposite && r.eOpposite.containment}
end
def eAllReferences(element)
@eAllReferences ||= {}
@eAllReferences[element.class] ||= element.class.ecore.eAllReferences
end
end
end
end rgen-0.10.2/lib/rgen/serializer/ 0000775 0000000 0000000 00000000000 14560401624 0016401 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/serializer/json_serializer.rb 0000664 0000000 0000000 00000007203 14560401624 0022132 0 ustar 00root root 0000000 0000000 module RGen
module Serializer
class JsonSerializer
def initialize(writer, opts={})
@writer = writer
@elementIdentifiers = {}
@identAttrName = opts[:identAttrName] || "name"
@separator = opts[:separator] || "/"
@leadingSeparator = opts.has_key?(:leadingSeparator) ? opts[:leadingSeparator] : true
@featureFilter = opts[:featureFilter]
@identifierProvider = opts[:identifierProvider]
end
def elementIdentifier(element)
ident = @identifierProvider && @identifierProvider.call(element)
ident || (element.is_a?(RGen::MetamodelBuilder::MMProxy) && element.targetIdentifier) || qualifiedElementName(element)
end
# simple identifier calculation based on qualified names
# prerequisits:
# * containment relations must be bidirectionsl
# * local name stored in single attribute +@identAttrName+ for all classes
#
def qualifiedElementName(element)
return @elementIdentifiers[element] if @elementIdentifiers[element]
localIdent = ((element.respond_to?(@identAttrName) && element.getGeneric(@identAttrName)) || "").strip
parentRef = element.class.ecore.eAllReferences.select{|r| r.eOpposite && r.eOpposite.containment}.first
parent = parentRef && element.getGeneric(parentRef.name)
if parent
if localIdent.size > 0
parentIdent = qualifiedElementName(parent)
result = parentIdent + @separator + localIdent
else
result = qualifiedElementName(parent)
end
else
result = (@leadingSeparator ? @separator : "") + localIdent
end
@elementIdentifiers[element] = result
end
def serialize(elements)
if elements.is_a?(Array)
write("[ ")
elements.each_with_index do |e, i|
serializeElement(e)
write(",\n") unless i == elements.size-1
end
write("]")
else
serializeElement(elements)
end
end
def serializeElement(element, indent="")
write(indent + "{ \"_class\": \""+element.class.ecore.name+"\"")
element.class.ecore.eAllStructuralFeatures.each do |f|
next if f.derived
value = element.getGeneric(f.name)
unless value == [] || value.nil? ||
(f.is_a?(RGen::ECore::EReference) && f.eOpposite && f.eOpposite.containment) ||
(@featureFilter && !@featureFilter.call(f))
write(", ")
writeFeature(f, value, indent)
end
end
write(" }")
end
def writeFeature(feat, value, indent)
write("\""+feat.name+"\": ")
if feat.is_a?(RGen::ECore::EAttribute)
if value.is_a?(Array)
write("[ "+value.collect{|v| attributeValue(v, feat)}.join(", ")+" ]")
else
write(attributeValue(value, feat))
end
elsif !feat.containment
if value.is_a?(Array)
write("[ "+value.collect{|v| "\""+elementIdentifier(v)+"\""}.join(", ")+" ]")
else
write("\""+elementIdentifier(value)+"\"")
end
else
if value.is_a?(Array)
write("[ \n")
value.each_with_index do |v, i|
serializeElement(v, indent+" ")
write(",\n") unless i == value.size-1
end
write("]")
else
write("\n")
serializeElement(value, indent+" ")
end
end
end
def attributeValue(value, a)
if a.eType == RGen::ECore::EString || a.eType.is_a?(RGen::ECore::EEnum)
"\""+value.to_s.gsub('\\','\\\\\\\\').gsub('"','\\"').gsub("\n","\\n").gsub("\r","\\r").
gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")+"\""
else
value.to_s
end
end
private
def write(s)
@writer.write(s)
end
end
end
end
rgen-0.10.2/lib/rgen/serializer/opposite_reference_filter.rb 0000664 0000000 0000000 00000001123 14560401624 0024150 0 ustar 00root root 0000000 0000000 module RGen
module Serializer
# Filters refereences with an eOpposite:
# 1. containment references are always preferred
# 2. at a 1-to-n reference the 1-reference is always preferred
# 3. otherwise the reference with the name in string sort order before the opposite's name is prefereed
#
OppositeReferenceFilter = proc do |features|
features.reject{|f| f.is_a?(RGen::ECore::EReference) && !f.containment && f.eOpposite &&
(f.eOpposite.containment || (f.many && !f.eOpposite.many) || (!(!f.many && f.eOpposite.many) && (f.name < f.eOpposite.name)))}
end
end
end
rgen-0.10.2/lib/rgen/serializer/qualified_name_provider.rb 0000664 0000000 0000000 00000002656 14560401624 0023614 0 ustar 00root root 0000000 0000000 module RGen
module Serializer
# simple identifier calculation based on qualified names.
# as a prerequisit, elements must have a local name stored in single attribute +attribute_name+.
# there may be classes without the name attribute though and there may be elements without a
# local name. in both cases the element will have the same qualified name as its container.
#
class QualifiedNameProvider
def initialize(options={})
@qualified_name_cache = {}
@attribute_name = options[:attribute_name] || "name"
@separator = options[:separator] || "/"
@leading_separator = options.has_key?(:leading_separator) ? options[:leading_separator] : true
end
def identifier(element)
if element.is_a?(RGen::MetamodelBuilder::MMProxy)
element.targetIdentifier
else
qualified_name(element)
end
end
def qualified_name(element)
return @qualified_name_cache[element] if @qualified_name_cache[element]
local_ident = ((element.respond_to?(@attribute_name) && element.getGeneric(@attribute_name)) || "").strip
parent = element.eContainer
if parent
if local_ident.size > 0
result = qualified_name(parent) + @separator + local_ident
else
result = qualified_name(parent)
end
else
result = (@leading_separator ? @separator : "") + local_ident
end
@qualified_name_cache[element] = result
end
end
end
end
rgen-0.10.2/lib/rgen/serializer/xmi11_serializer.rb 0000664 0000000 0000000 00000005743 14560401624 0022127 0 ustar 00root root 0000000 0000000 require 'rgen/serializer/xml_serializer'
module RGen
module Serializer
class XMI11Serializer < XMLSerializer
def initialize(file)
super
@namespacePrefix = ""
@contentLevelElements = []
end
def setNamespace(shortcut, url)
@namespaceShortcut = shortcut
@namespaceUrl = url
@namespacePrefix = shortcut+":"
end
def serialize(rootElement, headerInfo=nil)
attrs = []
attrs << ['xmi.version', "1.1"]
attrs << ['xmlns:'+@namespaceShortcut, @namespaceUrl] if @namespaceUrl
attrs << ['timestamp', Time.now.to_s]
startTag("XMI", attrs)
if headerInfo
startTag("XMI.header")
writeHeaderInfo(headerInfo)
endTag("XMI.header")
end
startTag("XMI.content")
@contentLevelElements = []
writeElement(rootElement)
# write remaining toplevel elements, each of which could have
# more toplevel elements as childs
while @contentLevelElements.size > 0
writeElement(@contentLevelElements.shift)
end
endTag("XMI.content")
endTag("XMI")
end
def writeHeaderInfo(hash)
hash.each_pair do |k,v|
tag = "XMI." + k.to_s
startTag(tag)
if v.is_a?(Hash)
writeHeaderInfo(v)
else
writeText(v.to_s)
end
endTag(tag)
end
end
def writeElement(element)
tag = @namespacePrefix + element.class.ecore.name
attrs = attributeValues(element)
startTag(tag, attrs)
containmentReferences(element).each do |r|
roletag = @namespacePrefix + r.eContainingClass.name + "." + r.name
targets = element.getGeneric(r.name)
targets = [ targets ] unless targets.is_a?(Array)
targets.compact!
next if targets.empty?
startTag(roletag)
targets.each do |t|
if xmiLevel(t) == :content
@contentLevelElements << t
else
writeElement(t)
end
end
endTag(roletag)
end
endTag(tag)
end
def attributeValues(element)
result = [["xmi.id", xmiId(element)]]
eAllAttributes(element).select{|a| !a.derived}.each do |a|
val = element.getGeneric(a.name)
result << [a.name, val] unless val.nil? || val == ""
end
eAllReferences(element).each do |r|
next if r.derived
next if r.containment
next if r.eOpposite && r.eOpposite.containment && xmiLevel(element).nil?
next if r.eOpposite && r.many && !r.eOpposite.many
targetElements = element.getGenericAsArray(r.name)
targetElements.compact!
val = targetElements.collect{|te| xmiId(te)}.compact.join(' ')
result << [r.name, val] unless val == ""
end
result
end
def xmiId(element)
if element.respond_to?(:_xmi_id) && element._xmi_id
element._xmi_id.to_s
else
element.object_id.to_s
end
end
def xmiLevel(element)
return nil unless element.respond_to?(:_xmi_level)
element._xmi_level
end
end
end
end
rgen-0.10.2/lib/rgen/serializer/xmi20_serializer.rb 0000664 0000000 0000000 00000004423 14560401624 0022121 0 ustar 00root root 0000000 0000000 require 'rgen/serializer/xml_serializer'
module RGen
module Serializer
class XMI20Serializer < XMLSerializer
def serialize(rootElement)
@referenceStrings = {}
buildReferenceStrings(rootElement, "#/")
addBuiltinReferenceStrings
attrs = attributeValues(rootElement)
attrs << ['xmi:version', "2.0"]
attrs << ['xmlns:xmi', "http://www.omg.org/XMI"]
attrs << ['xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance"]
attrs << ['xmlns:ecore', "http://www.eclipse.org/emf/2002/Ecore" ]
tag = "ecore:"+rootElement.class.ecore.name
startTag(tag, attrs)
writeComposites(rootElement)
endTag(tag)
end
def writeComposites(element)
eachReferencedElement(element, containmentReferences(element)) do |r,te|
attrs = attributeValues(te)
attrs << ['xsi:type', "ecore:"+te.class.ecore.name]
tag = r.name
startTag(tag, attrs)
writeComposites(te)
endTag(tag)
end
end
def attributeValues(element)
result = []
eAllAttributes(element).select{|a| !a.derived}.each do |a|
val = element.getGeneric(a.name)
result << [a.name, val] unless val.nil? || val == ""
end
eAllReferences(element).select{|r| !r.containment && !(r.eOpposite && r.eOpposite.containment) && !r.derived}.each do |r|
targetElements = element.getGenericAsArray(r.name)
val = targetElements.collect{|te| @referenceStrings[te]}.compact.join(' ')
result << [r.name, val] unless val.nil? || val == ""
end
result
end
def buildReferenceStrings(element, string)
@referenceStrings[element] = string
eachReferencedElement(element, containmentReferences(element)) do |r,te|
buildReferenceStrings(te, string+"/"+te.name) if te.respond_to?(:name)
end
end
def addBuiltinReferenceStrings
pre = "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore"
@referenceStrings[RGen::ECore::EString] = pre+"#//EString"
@referenceStrings[RGen::ECore::EInt] = pre+"#//EInt"
@referenceStrings[RGen::ECore::ELong] = pre+"#//ELong"
@referenceStrings[RGen::ECore::EFloat] = pre+"#//EFloat"
@referenceStrings[RGen::ECore::EBoolean] = pre+"#//EBoolean"
@referenceStrings[RGen::ECore::EJavaObject] = pre+"#//EJavaObject"
@referenceStrings[RGen::ECore::EJavaClass] = pre+"#//EJavaClass"
end
end
end
end
rgen-0.10.2/lib/rgen/serializer/xml_serializer.rb 0000664 0000000 0000000 00000004345 14560401624 0021765 0 ustar 00root root 0000000 0000000 module RGen
module Serializer
class XMLSerializer
INDENT_SPACE = 2
def initialize(file)
@indent = 0
@lastStartTag = nil
@textContent = false
@file = file
end
def serialize(rootElement)
raise "Abstract class, overwrite method in subclass!"
end
def startTag(tag, attributes={})
@textContent = false
handleLastStartTag(false, true)
if attributes.is_a?(Hash)
attrString = attributes.keys.collect{|k| "#{k}=\"#{attributes[k]}\""}.join(" ")
else
attrString = attributes.collect{|pair| "#{pair[0]}=\"#{pair[1]}\""}.join(" ")
end
@lastStartTag = " "*@indent*INDENT_SPACE + "<#{tag} "+attrString
@indent += 1
end
def endTag(tag)
@indent -= 1
unless handleLastStartTag(true, true)
output " "*@indent*INDENT_SPACE unless @textContent
output "#{tag}>\n"
end
@textContent = false
end
def writeText(text)
handleLastStartTag(false, false)
output "#{text}"
@textContent = true
end
protected
def eAllReferences(element)
@eAllReferences ||= {}
@eAllReferences[element.class] ||= element.class.ecore.eAllReferences
end
def eAllAttributes(element)
@eAllAttributes ||= {}
@eAllAttributes[element.class] ||= element.class.ecore.eAllAttributes
end
def eAllStructuralFeatures(element)
@eAllStructuralFeatures ||= {}
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
end
def eachReferencedElement(element, refs, &block)
refs.each do |r|
targetElements = element.getGeneric(r.name)
targetElements = [targetElements] unless targetElements.is_a?(Array)
targetElements.each do |te|
yield(r,te)
end
end
end
def containmentReferences(element)
@containmentReferences ||= {}
@containmentReferences[element.class] ||= eAllReferences(element).select{|r| r.containment}
end
private
def handleLastStartTag(close, newline)
return false unless @lastStartTag
output @lastStartTag
output close ? "/>" : ">"
output "\n" if newline
@lastStartTag = nil
true
end
def output(text)
@file.write(text)
end
end
end
end
rgen-0.10.2/lib/rgen/template_language.rb 0000664 0000000 0000000 00000030034 14560401624 0020233 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/template_language/directory_template_container'
require 'rgen/template_language/template_container'
module RGen
# The RGen template language has been designed to build complex generators.
# It is very similar to the EXPAND language of the Java based
# OpenArchitectureWare framework.
#
# =Templates
#
# The basic idea is to allow "templates" not only being template files
# but smaller parts. Those parts can be expanded from other parts very
# much like Ruby methods are called from other methods.
# Thus the term "template" refers to such a part within a "template file".
#
# Template files used by the RGen template language should have a
# filename with the postfix ".tpl". Those files can reside within (nested)
# template file directories.
#
# As an example a template directory could look like the following:
#
# templates/root.tpl
# templates/dbaccess/dbaccess.tpl
# templates/dbaccess/schema.tpl
# templates/headers/generic_headers.tpl
# templates/headers/specific/component.tpl
#
# A template is always called for a context object. The context object
# serves as the receiver of methods called within the template. Details are given
# below.
#
#
# =Defining Templates
#
# One or more templates can be defined in a template file using the +define+
# keyword as in the following example:
#
# <% define 'GenerateDBAdapter', :for => DBDescription do |dbtype| %>
# Content to be generated; use ERB syntax here
# <% end %>
#
# The template definition takes three kinds of parameters:
# 1. The name of the template within the template file as a String or Symbol
# 2. An optional class object describing the class of context objects for which
# this template is valid.
# 3. An arbitrary number of template parameters
# See RGen::TemplateLanguage::TemplateContainer for details about the syntax of +define+.
#
# Within a template, regular ERB syntax can be used. This is
# * <%
and %>
are used to embed Ruby code
# * <%=
and %>
are used to embed Ruby expressions with
# the expression result being written to the template output
# * <%#
and %>
are used for comments
# All content not within these tags is written to the template output verbatim.
# See below for details about output files and output formatting.
#
# All methods which are called from within the template are sent to the context
# object.
#
# Experience shows that one easily forgets the +do+ at the end of the first
# line of a template definition. This will result in an ERB parse error.
#
#
# =Expanding Templates
#
# Templates are normally expanded from within other templates. The only
# exception is the root template, which is expanded from the surrounding code.
#
# Template names can be specified in the following ways:
# * Non qualified name: use the template with the given name in the current template file
# * Relative qualified name: use the template within the template file specified by the relative path
# * Absolute qualified name: use the template within the template file specified by the absolute path
#
# The +expand+ keyword is used to expand templates.
#
# Here are some examples:
#
# <% expand 'GenerateDBAdapter', dbtype, :for => dbDesc %>
#
# Non qualified. Must be called within the file where 'GenerateDBAdapter' is defined.
# There is one template parameter passed in via variable +dbtype+.
# The context object is provided in variable +dbDesc+.
#
# <% expand 'dbaccess::ExampleSQL' %>
#
# Qualified with filename. Must be called from a file in the same directory as 'dbaccess.tpl'
# There are no parameters. The current context object will be used as the context
# object for this template expansion.
#
# <% expand '../headers/generic_headers::CHeader', :foreach => modules %>
#
# Relatively qualified. Must be called from a location from which the file
# 'generic_headers.tpl' is accessible via the relative path '../headers'.
# The template is expanded for each module in +modules+ (which has to be an Array).
# Each element of +modules+ will be the context object in turn.
#
# <% expand '/headers/generic_headers::CHeader', :foreach => modules %>
#
# Absolutely qualified: The same behaviour as before but with an absolute path from
# the template directory root (which in this example is 'templates', see above)
#
# Sometimes it is neccessary to generate some text (e.g. a ',') in between the single
# template expansion results from a :foreach
expansion. This can be achieved by
# using the :separator
keyword:
#
# <% expand 'ColumnName', :foreach => column, :separator => ', ' %>
#
# Note that the separator may also contain newline characters (\n). See below for
# details about formatting.
#
#
# =Formatting
#
# For many generator tools a formatting postprocess (e.g. using a pretty printer) is
# required in order to make the output readable. However, depending on the kind of
# generated output, such a tool might not be available.
#
# The RGen template language has been design for generators which do not need a
# postprocessing step. The basic idea is to eliminate all whitespace at the beginning
# of template lines (the indentation that makes the _template_ readable) and output
# newlines only after at least on character has been generated in the corresponding
# line. This way there are no empty lines in the output and each line will start with
# a non-whitspace character.
#
# Starting from this point one can add indentation and newlines as required by using
# explicit formatting commands:
# * <%nl%>
(newline) starts a new line
# * <%ws%>
(whitespace) adds an explicit space
# * <%iinc%>
(indentation increment) increases the current indentation
# * <%idec%>
(indentation decrement) decreases the current indentation
# * <%nonl%>
(no newline) ignore next newline
# * <%nows%>
(no whitespace) ignore next whitespace
#
# Indentation takes place for every new line in the output unless it is 0.
# The initial indentation can be specified with a root +expand+ command by using
# the :indent
keyword.
#
# Here is an example:
#
# expand 'GenerateDBAdapter', dbtype, :for => dbDesc, :indent => 1
#
# Initial indentation defaults to 0. Normally <%iinc%>
and
# <%idec%>
are used to change the indentation.
# The current indentation is kept for expansion of subtemplates.
#
# The string which is used to realize one indentation step can be set using
# DirectoryTemplateContainer#indentString or with the template language +file+ command.
# The default is " " (3 spaces), the indentation string given at a +file+ command
# overwrites the container's default which in turn overwrites the overall default.
#
# Note that commands to ignore whitespace and newlines are still useful if output
# generated from multiple template lines should show up in one single output line.
#
# Here is an example of a template generating a C program:
#
# #include
# <%nl%>
# int main() {<%iinc%>
# printf("Hello World\n");
# return 0;<%idec>
# }
#
# The result is:
#
# #include
#
# int main() {
# printf("Hello World\n");
# return 0;
# }
#
# Note that without the explicit formatting commands, the output generated from the
# example above would not have any empty lines or whitespace in the beginning of lines.
# This may seem like unneccessary extra work for the example above which could also
# have been generated by passing the template to the output verbatimly.
# However in most cases templates will contain more template specific indentation and
# newlines which should be eliminated than formatting that should be visible in the
# output.
#
# Here is a more realistic example for generating C function prototypes:
#
# <% define 'Prototype', :for => CFunction do %>
# <%= getType.name %> <%= name %>(<%nows%>
# <% expand 'Signature', :foreach => argument, :separator => ', ' %>);
# <% end %>
#
# <% define 'Signature', :for => CFunctionArgument do %>
# <%= getType.name %> <%= name%><%nows%>
# <% end %>
#
# The result could look something like:
#
# void somefunc(int a, float b, int c);
# int otherfunc(short x);
#
# In this example a separator is used to join the single arguments of the C functions.
# Note that the template generating the argument type and name needs to contain
# a <%nows%>
if the result should consist of a single line.
#
# Here is one more example for generating C array initializations:
#
# <% define 'Array', :for => CArray do %>
# <%= getType.name %> <%= name %>[<%= size %>] = {<%iinc%>
# <% expand 'InitValue', :foreach => initvalue, :separator => ",\n" %><%nl%><%idec%>
# };
# <% end %>
#
# <% define 'InitValue', :for => PrimitiveInitValue do %>
# <%= value %><%nows%>
# <% end %>
#
# The result could look something like:
#
# int myArray[3] = {
# 1,
# 2,
# 3
# };
#
# Note that in this example, the separator contains a newline. The current increment
# will be applied to each single expansion result since it starts in a new line.
#
#
# =Output Files
#
# Normally the generated content is to be written into one or more output files.
# The RGen template language facilitates this by means of the +file+ keyword.
#
# When the +file+ keyword is used to define a block, all output generated
# from template code within this block will be written to the specified file.
# This includes output generated from template expansions.
# Thus all output from templates expanded within this block is written to
# the same file as long as those templates do not use the +file+ keyword to
# define a new file context.
#
# Here is an example:
#
# <% file 'dbadapter/'+adapter.name+'.c' do %>
# all content within this block will be written to the specified file
# <% end %>
#
# Note that the filename itself can be calculated dynamically by an arbitrary
# Ruby expression.
#
# The absolute position where the output file is created depends on the output
# root directory passed to DirectoryTemplateContainer as described below.
#
# As a second argument, the +file+ command can take the indentation string which is
# used to indent output lines (see Formatting).
#
# =Setting up the Generator
#
# Setting up the generator consists of 3 steps:
# * Instantiate DirectoryTemplateContainer passing one or more metamodel(s) and the output
# directory to the constructor.
# * Load the templates into the template container
# * Expand the root template to start generation
#
# Here is an example:
#
# module MyMM
# # metaclasses are defined here, e.g. using RGen::MetamodelBuilder
# end
#
# OUTPUT_DIR = File.dirname(__FILE__)+"/output"
# TEMPLATES_DIR = File.dirname(__FILE__)+"/templates"
#
# tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new(MyMM, OUTPUT_DIR)
# tc.load(TEMPLATES_DIR)
# # testModel should hold an instance of the metamodel class expected by the root template
# # the following line starts generation
# tc.expand('root::Root', :for => testModel, :indent => 1)
#
# The metamodel is the Ruby module which contains the metaclasses.
# This information is required for the template container in order to resolve the
# metamodel classes used within the template file.
# If several metamodels shall be used, an array of modules can be passed instead
# of a single module.
#
# The output path is prepended to the relative paths provided to the +file+
# definitions in the template files.
#
# The template directory should contain template files as described above.
#
# Finally the generation process is started by calling +expand+ in the same way as it
# is used from within templates.
#
# Also see the unit tests for more examples.
#
module TemplateLanguage
end
end
rgen-0.10.2/lib/rgen/template_language/ 0000775 0000000 0000000 00000000000 14560401624 0017706 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/template_language/directory_template_container.rb 0000664 0000000 0000000 00000004161 14560401624 0026176 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/template_language/template_container'
require 'rgen/template_language/template_helper'
module RGen
module TemplateLanguage
class DirectoryTemplateContainer
include TemplateHelper
def initialize(metamodel=nil, output_path=nil, parent=nil)
@containers = {}
@directoryContainers = {}
@parent = parent
@metamodel = metamodel
@output_path = output_path
end
def load(dir)
Dir.foreach(dir) { |f|
qf = dir+"/"+f
if !File.directory?(qf) && f =~ /^(.*)\.tpl$/
(@containers[$1] = TemplateContainer.new(@metamodel, @output_path, self,qf)).load
elsif File.directory?(qf) && f != "." && f != ".."
(@directoryContainers[f] = DirectoryTemplateContainer.new(@metamodel, @output_path, self)).load(qf)
end
}
end
def expand(template, *all_args)
if template =~ /^\//
if @parent
# pass to parent
@parent.expand(template, *all_args)
else
# this is root
_expand(template, *all_args)
end
elsif template =~ /^\.\.\/(.*)/
if @parent
# pass to parent
@parent.expand($1, *all_args)
else
raise "No parent directory for root"
end
else
_expand(template, *all_args)
end
end
# Set indentation string.
# Every generated line will be prependend with n times this string at an indentation level of n.
# Defaults to " " (3 spaces)
def indentString=(str)
@indentString = str
end
def indentString
@indentString || (@parent && @parent.indentString) || " "
end
private
def _expand(template, *all_args)
if template =~ /^\/?(\w+)::(\w.*)/
raise "Template not found: #{$1}" unless @containers[$1]
@containers[$1].expand($2, *all_args)
elsif template =~ /^\/?(\w+)\/(\w.*)/
raise "Template not found: #{$1}" unless @directoryContainers[$1]
@directoryContainers[$1].expand($2, *all_args)
else
raise "Invalid template name: #{template}"
end
end
end
end
end rgen-0.10.2/lib/rgen/template_language/output_handler.rb 0000664 0000000 0000000 00000005606 14560401624 0023277 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
module RGen
module TemplateLanguage
class OutputHandler
attr_accessor :noIndentNextLine
def initialize(indent=0, indentString=" ", mode=:explicit)
self.mode = mode
@indentString = indentString
@state = :wait_for_nonws
@output = ""
@indent_string = @indentString*indent
end
def indent=(i)
@indent_string = @indentString*i
end
NL = "\n"
LFNL = "\r\n"
if RUBY_VERSION.start_with?("1.8")
NL_CHAR = 10
LF_CHAR = 13
else
NL_CHAR = "\n"
LF_CHAR = "\r"
end
# ERB will call this method for every string s which is part of the
# template file in between %> and <%. If s contains a newline, it will
# call this method for every part of s which is terminated by a \n
#
def concat(s)
if @ignoreNextNL
idx = s.index(NL)
if idx && s[0..idx].strip.empty?
s = s[idx+1..-1]
end
@ignoreNextNL = false unless s.strip.empty?
end
if @ignoreNextWS
s = s.lstrip
@ignoreNextWS = false unless s.empty?
end
if @mode == :direct
@output.concat(s)
elsif @mode == :explicit
while s.size > 0
if @state == :wait_for_nl
idx = s.index(NL)
if idx
if s[idx-1] == LF_CHAR
@output.concat(s[0..idx].rstrip)
@output.concat(LFNL)
else
@output.concat(s[0..idx].rstrip)
@output.concat(NL)
end
s = s[idx+1..-1]
@state = :wait_for_nonws
else
@output.concat(s)
break
end
elsif @state == :wait_for_nonws
s = s.lstrip
if !s.empty?
unless @noIndentNextLine || (@output[-1] && @output[-1] != NL_CHAR)
@output.concat(@indent_string)
else
@noIndentNextLine = false
end
@state = :wait_for_nl
end
end
end
end
end
alias << concat
def to_str
@output
end
alias to_s to_str
def direct_concat(s)
@output.concat(s)
end
def direct_concat_allow_indent(s)
unless @noIndentNextLine || (@output[-1] && @output[-1] != NL_CHAR)
@output.concat(@indent_string)
else
@noIndentNextLine = false
end
@state = :wait_for_nl
@output.concat(s)
end
def ignoreNextNL
@ignoreNextNL = true
end
def ignoreNextWS
@ignoreNextWS = true
end
def mode=(m)
raise StandardError.new("Unknown mode: #{m}") unless [:direct, :explicit].include?(m)
@mode = m
end
end
end
end
rgen-0.10.2/lib/rgen/template_language/template_container.rb 0000664 0000000 0000000 00000017644 14560401624 0024124 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'erb'
require 'fileutils'
require 'rgen/template_language/output_handler'
require 'rgen/template_language/template_helper'
module RGen
module TemplateLanguage
class TemplateContainer
include TemplateHelper
def initialize(metamodels, output_path, parent, filename)
@templates = {}
@parent = parent
@filename = filename
@indent = 0
@output_path = output_path
@metamodels = metamodels
@metamodels = [ @metamodels ] unless @metamodels.is_a?(Array)
end
def load
File.open(@filename,"rb") do |f|
begin
@@metamodels = @metamodels
fileContent = f.read
_detectNewLinePattern(fileContent)
ERB.new(fileContent, eoutvar: '@output').result(binding)
rescue Exception => e
processAndRaise(e)
end
end
end
def expand(template, *all_args)
args, params = _splitArgsAndOptions(all_args)
if params.has_key?(:foreach)
raise StandardError.new("expand :foreach argument is not enumerable") \
unless params[:foreach].is_a?(Enumerable)
_expand_foreach(template, args, params)
else
_expand(template, args, params)
end
end
def evaluate(template, *all_args)
args, params = _splitArgsAndOptions(all_args)
raise StandardError.new(":foreach can not be used with evaluate") if params[:foreach]
_expand(template, args, params.merge({:_evalOnly => true}))
end
def this
@context
end
def method_missing(name, *args)
@context.send(name, *args)
end
def self.const_missing(name)
super unless @@metamodels
@@metamodels.each do |mm|
return mm.const_get(name) rescue NameError
end
super
end
private
def nonl
@output.ignoreNextNL
end
def nows
@output.ignoreNextWS
end
def nl
_direct_concat(@newLinePattern)
end
def ws
_direct_concat(" ", true)
end
def iinc
@indent += 1
@output.indent = @indent
end
def idec
@indent -= 1 if @indent > 0
@output.indent = @indent
end
TemplateDesc = Struct.new(:block, :local)
def define(template, params={}, &block)
_define(template, params, &block)
end
def define_local(template, params={}, &block)
_define(template, params.merge({:local => true}), &block)
end
def file(name, indentString=nil)
old_output, @output = @output, OutputHandler.new(@indent, indentString || @parent.indentString)
begin
yield
rescue Exception => e
processAndRaise(e)
end
path = ""
path += @output_path+"/" if @output_path
dirname = File.dirname(path+name)
FileUtils.makedirs(dirname) unless File.exist?(dirname)
File.open(path+name,"wb") { |f| f.write(@output) }
@output = old_output
end
# private private
def _define(template, params={}, &block)
@templates[template] ||= {}
cls = params[:for] || Object
@templates[template][cls] = TemplateDesc.new(block, params[:local])
end
def _expand_foreach(template, args, params)
sep = params[:separator]
params[:foreach].each_with_index {|e,i|
_direct_concat(sep.to_s) if sep && i > 0
_expand(template, args, params.merge({:for => e}))
}
end
LOCAL_TEMPLATE_REGEX = /^:*(\w+)$/
def _expand(template, args, params)
raise StandardError.new("expand :for argument evaluates to nil") if params.has_key?(:for) && params[:for].nil?
context = params[:for]
old_indent = @indent
@indent = params[:indent] || @indent
noIndentNextLine = params[:_noIndentNextLine] ||
(@output.is_a?(OutputHandler) && @output.noIndentNextLine) ||
# the following line actually defines the noIndentNextLine state:
# we don't indent the next line if the previous line was not finished,
# i.e. if output has been generated but is not terminated by a newline
# BEWARE: the initial evaluation of the ERB template during template loading
# also writes to @output (it creates a String); we must ignore this
(@output.is_a?(OutputHandler) && @output.to_s.size > 0 && @output.to_s[-1] != "\n"[0])
caller = params[:_caller] || self
old_context, @context = @context, context if context
local_output = nil
if template =~ LOCAL_TEMPLATE_REGEX
tplname = $1
raise StandardError.new("Template not found: #{$1}") unless @templates[tplname]
old_output, @output = @output, OutputHandler.new(@indent, @parent.indentString)
@output.noIndentNextLine = noIndentNextLine
_call_template(tplname, @context, args, caller == self)
old_output.noIndentNextLine = false if old_output.is_a?(OutputHandler) && !@output.noIndentNextLine
local_output, @output = @output, old_output
else
local_output = @parent.expand(template, *(args.dup << {:for => @context, :indent => @indent, :_noIndentNextLine => noIndentNextLine, :_evalOnly => true, :_caller => caller}))
end
_direct_concat(local_output) unless params[:_evalOnly]
@context = old_context if old_context
@indent = old_indent
local_output.to_s
end
def processAndRaise(e, tpl=nil)
bt = e.backtrace.dup
e.backtrace.each_with_index do |t,i|
if t =~ /\(erb\):(\d+):/
bt[i] = "#{@filename}:#{$1}"
bt[i] += ":in '#{tpl}'" if tpl
break
end
end
raise e, e.to_s, bt
end
def _call_template(tpl, context, args, localCall)
found = false
@templates[tpl].each_pair do |key, value|
if context.is_a?(key)
templateDesc = @templates[tpl][key]
proc = templateDesc.block
arity = proc.arity
arity = 0 if arity == -1 # if no args are given
raise StandardError.new("Wrong number of arguments calling template #{tpl}: #{args.size} for #{arity} "+
"(Beware: Hashes as last arguments are taken as options and are ignored)") \
if arity != args.size
raise StandardError.new("Template can only be called locally: #{tpl}") \
if templateDesc.local && !localCall
begin
@@metamodels = @metamodels
proc.call(*args)
rescue Exception => e
processAndRaise(e, tpl)
end
found = true
end
end
raise StandardError.new("Template class not matching: #{tpl} for #{context.class.name}") unless found
end
def _direct_concat(s, allow_indent=false)
if @output.is_a? OutputHandler
if allow_indent
@output.direct_concat_allow_indent(s)
else
@output.direct_concat(s)
end
else
@output << s
end
end
def _detectNewLinePattern(text)
tests = 0
rnOccurances = 0
text.scan(/(\r?)\n/) do |groups|
tests += 1
rnOccurances += 1 if groups[0] == "\r"
break if tests >= 10
end
if rnOccurances > (tests / 2)
@newLinePattern = "\r\n"
else
@newLinePattern = "\n"
end
end
end
end
end
rgen-0.10.2/lib/rgen/template_language/template_helper.rb 0000664 0000000 0000000 00000000523 14560401624 0023405 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
module RGen
module TemplateLanguage
module TemplateHelper
private
def _splitArgsAndOptions(all)
if all[-1] and all[-1].is_a? Hash
args = all[0..-2] || []
options = all[-1]
else
args = all
options = {}
end
return args, options
end
end
end
end rgen-0.10.2/lib/rgen/transformer.rb 0000664 0000000 0000000 00000050666 14560401624 0017134 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore'
require 'rgen/ecore/ecore_ext'
module RGen
# The Transformer class can be used to specify model transformations.
#
# Model transformations take place between a source model (located in the source
# environment being an instance of the source metamodel) and a target model (located
# in the target environment being an instance of the target metamodel).
# Normally a "model" consists of several model elements associated with each other.
#
# =Transformation Rules
#
# The transformation is specified within a subclass of Transformer.
# Within the subclass, the Transformer.transform class method can be used to specify transformation
# blocks for specific metamodel classes of the source metamodel.
#
# If there is no transformation rule for the current object's class, a rule for the superclass
# is used instead. If there's no rule for the superclass, the class hierarchy is searched
# this way until Object.
#
# Here is an example:
#
# class MyTransformer < RGen::Transformer
#
# transform InputClass, :to => OutputClass do
# { :name => name, :otherClass => trans(otherClass) }
# end
#
# transform OtherInputClass, :to => OtherOutputClass do
# { :name => name }
# end
# end
#
# In this example a transformation rule is specified for model elements of class InputClass
# as well as for elements of class OtherInputClass. The former is to be transformed into
# an instance of OutputClass, the latter into an instance of OtherOutputClass.
# Note that the Ruby class objects are used to specifiy the classes.
#
# =Transforming Attributes
#
# Besides the target class of a transformation, the attributes of the result object are
# specified in the above example. This is done by providing a Ruby block with the call of
# +transform+. Within this block arbitrary Ruby code may be placed, however the block
# must return a hash. This hash object specifies the attribute assignment of the
# result object using key/value pairs: The key must be a Symbol specifying the attribute
# which is to be assigned by name, the value is the value that will be assigned.
#
# For convenience, the transformation block will be evaluated in the context of the
# source model element which is currently being converted. This way it is possible to just
# write :name => name
in the example in order to assign the name of the source
# object to the name attribute of the target object.
#
# =Transforming References
#
# When attributes of elements are references to other elements, those referenced
# elements have to be transformed as well. As shown above, this can be done by calling
# the Transformer#trans method. This method initiates a transformation of the element
# or array of elements passed as parameter according to transformation rules specified
# using +transform+. In fact the +trans+ method is the only way to start the transformation
# at all.
#
# For convenience and performance reasons, the result of +trans+ is cached with respect
# to the parameter object. I.e. calling trans on the same source object a second time will
# return the same result object _without_ a second evaluation of the corresponding
# transformation rules.
#
# This way the +trans+ method can be used to lookup the target element for some source
# element without the need to locally store a reference to the target element. In addition
# this can be useful if it is not clear if certain element has already been transformed
# when it is required within some other transformation block. See example below.
#
# Special care has been taken to allow the transformation of elements which reference
# each other cyclically. The key issue here is that the target element of some transformation
# is created _before_ the transformation's block is evaluated, i.e before the elements
# attributes are set. Otherwise a call to +trans+ within the transformation's block
# could lead to a +trans+ of the element itself.
#
# Here is an example:
#
# transform ModelAIn, :to => ModelAOut do
# { :name => name, :modelB => trans(modelB) }
# end
#
# transform ModelBIn, :to => ModelBOut do
# { :name => name, :modelA => trans(modelA) }
# end
#
# Note that in this case it does not matter if the transformation is initiated by calling
# +trans+ with a ModelAIn element or ModelBIn element due to the caching feature described
# above.
#
# =Transformer Methods
#
# When code in transformer blocks becomes more complex it might be useful to refactor
# it into smaller methods. If regular Ruby methods within the Transformer subclass are
# used for this purpose, it is necessary to know the source element being transformed.
# This could be achieved by explicitly passing the +@current_object+ as parameter of the
# method (see Transformer#trans).
#
# A more convenient way however is to define a special kind of method using the
# Transformer.method class method. Those methods are evaluated within the context of the
# current source element being transformed just the same as transformer blocks are.
#
# Here is an example:
#
# transform ModelIn, :to => ModelOut do
# { :number => doubleNumber }
# end
#
# method :doubleNumber do
# number * 2;
# end
#
# In this example the transformation assigns the 'number' attribute of the source element
# multiplied by 2 to the target element. The multiplication is done in a dedicated method
# called 'doubleNumber'. Note that the 'number' attribute of the source element is
# accessed without an explicit reference to the source element as the method's body
# evaluates in the source element's context.
#
# =Conditional Transformations
#
# Using the transformations as described above, all elements of the same class are
# transformed the same way. Conditional transformations allow to transform elements of
# the same class into elements of different target classes as well as applying different
# transformations on the attributes.
#
# Conditional transformations are defined by specifying multiple transformer blocks for
# the same source class and providing a condition with each block. Since it is important
# to create the target object before evaluation of the transformation block (see above),
# the conditions must also be evaluated separately _before_ the transformer block.
#
# Conditions are specified using transformer methods as described above. If the return
# value is true, the corresponding block is used for transformation. If more than one
# conditions are true, only the first transformer block will be evaluated.
#
# If there is no rule with a condition evaluating to true, rules for superclasses will
# be checked as described above.
#
# Here is an example:
#
# transform ModelIn, :to => ModelOut, :if => :largeNumber do
# { :number => number * 2}
# end
#
# transform ModelIn, :to => ModelOut, :if => :smallNumber do
# { :number => number / 2 }
# end
#
# method :largeNumber do
# number > 1000
# end
#
# method :smallNumber do
# number < 500
# end
#
# In this case the transformation of an element of class ModelIn depends on the value
# of the element's 'number' attribute. If the value is greater than 1000, the first rule
# as taken and the number is doubled. If the value is smaller than 500, the second rule
# is taken and the number is divided by two.
#
# Note that it is up to the user to avoid cycles within the conditions. A cycle could
# occure if the condition are based on transformation target elements, i.e. if +trans+
# is used within the condition to lookup or transform other elements.
#
# = Copy Transformations
#
# In some cases, transformations should just copy a model, either in the same metamodel
# or in another metamodel with the same package/class structure. Sometimes, a transformation
# is not exactly a copy, but a copy with slight modifications. Also in this case most
# classes need to be copied verbatim.
#
# The class method Transformer.copy can be used to specify a copy rule for a single
# metamodel class. If no target class is specified using the :to named parameter, the
# target class will be the same as the source class (i.e. in the same metamodel).
#
# copy MM1::ClassA # copy within the same metamodel
# copy MM1::ClassA, :to => MM2::ClassA
#
# The class method Transfomer.copy_all can be used to specify copy rules for all classes
# of a particular metamodel package. Again with :to, the target metamodel package may
# be specified which must have the same package/class structure. If :to is omitted, the
# target metamodel is the same as the source metamodel. In case that for some classes
# specific transformation rules should be used instead of copy rules, exceptions may be
# specified using the :except named parameter. +copy_all+ also provides an easy way to
# copy (clone) a model within the same metamodel.
#
# copy_all MM1 # copy rules for the whole metamodel MM1,
# # used to clone models of MM1
#
# copy_all MM1, :to => MM2, :except => %w( # copy rules for all classes of MM1 to
# ClassA # equally named classes in MM2, except
# Sub1::ClassB # "ClassA" and "Sub1::ClassB"
# )
#
# If a specific class transformation is not an exact copy, the Transformer.transform method
# should be used to actually specify the transformation. If this transformation is also
# mostly a copy, the helper method Transformer#copy_features can be used to create the
# transformation Hash required by the transform method. Any changes to this hash may be done
# in a hash returned by a block given to +copy_features+. This hash will extend or overwrite
# the default copy hash. In case a particular feature should not be part of the copy hash
# (e.g. because it does not exist in the target metamodel), exceptions can be specified using
# the :except named parameter. Here is an example:
#
# transform ClassA, :to => ClassAx do
# copy_features :except => [:featA] do
# { :featB => featA }
# end
# end
#
# In this example, ClassAx is a copy of ClassA except, that feature "featA" in ClassA is renamed
# into "featB" in ClassAx. Using +copy_features+ all features are copied except "featA". Then
# "featB" of the target class is assigned the value of "featA" of the source class.
#
class Transformer
TransformationDescription = Struct.new(:block, :target) # :nodoc:
@@methods = {}
@@transformer_blocks = {}
def self._transformer_blocks # :nodoc:
@@transformer_blocks[self] ||= {}
end
def self._methods # :nodoc:
@@methods[self] ||= {}
end
# This class method is used to specify a transformation rule.
#
# The first argument specifies the class of elements for which this rule applies.
# The second argument must be a hash including the target class
# (as value of key ':to') and an optional condition (as value of key ':if').
#
# The target class is specified by passing the actual Ruby class object.
# The condition is either the name of a transformer method (see Transfomer.method) as
# a symbol or a proc object. In either case the block is evaluated at transformation
# time and its result value determines if the rule applies.
#
def self.transform(from, desc=nil, &block)
to = (desc && desc.is_a?(Hash) && desc[:to])
condition = (desc && desc.is_a?(Hash) && desc[:if])
raise StandardError.new("No transformation target specified.") unless to
block_desc = TransformationDescription.new(block, to)
if condition
_transformer_blocks[from] ||= {}
raise StandardError.new("Multiple (non-conditional) transformations for class #{from.name}.") unless _transformer_blocks[from].is_a?(Hash)
_transformer_blocks[from][condition] = block_desc
else
raise StandardError.new("Multiple (non-conditional) transformations for class #{from.name}.") unless _transformer_blocks[from].nil?
_transformer_blocks[from] = block_desc
end
end
# This class method specifies that all objects of class +from+ are to be copied
# into an object of class +to+. If +to+ is omitted, +from+ is used as target class.
# The target class may also be specified using the :to => hash notation.
# During copy, all attributes and references of the target object
# are set to their transformed counterparts of the source object.
#
def self.copy(from, to=nil)
raise StandardError.new("Specify target class either directly as second parameter or using :to => ") \
unless to.nil? || to.is_a?(Class) || (to.is_a?(Hash) && to[:to].is_a?(Class))
to = to[:to] if to.is_a?(Hash)
transform(from, :to => to || from) do
copy_features
end
end
# Create copy rules for all classes of metamodel package (module) +from+ and its subpackages.
# The target classes are the classes with the same name in the metamodel package
# specified using named parameter :to. If no target metamodel is specified, source
# and target classes will be the same.
# The named parameter :except can be used to specify classes by qualified name for which
# no copy rules should be created. Qualified names are relative to the metamodel package
# specified.
#
def self.copy_all(from, hash={})
to = hash[:to] || from
except = hash[:except]
fromDepth = from.ecore.qualifiedName.split("::").size
from.ecore.eAllClasses.each do |c|
path = c.qualifiedName.split("::")[fromDepth..-1]
next if except && except.include?(path.join("::"))
copy c.instanceClass, :to => path.inject(to){|m,c| m.const_get(c)}
end
end
# Define a transformer method for the current transformer class.
# In contrast to regular Ruby methods, a method defined this way executes in the
# context of the object currently being transformed.
#
def self.method(name, &block)
_methods[name.to_s] = block
end
# Creates a new transformer
# Optionally an input and output Environment can be specified.
# If an elementMap is provided (normally a Hash) this map will be used to lookup
# and store transformation results. This way results can be predefined
# and it is possible to have several transformers work on the same result map.
#
def initialize(env_in=nil, env_out=nil, elementMap=nil)
@env_in = env_in
@env_out = env_out
@transformer_results = elementMap || {}
@transformer_jobs = []
end
# Transforms a given model element according to the rules specified by means of
# the Transformer.transform class method.
#
# The transformation result element is created in the output environment and returned.
# In addition, the result is cached, i.e. a second invocation with the same parameter
# object will return the same result object without any further evaluation of the
# transformation rules. Nil will be transformed into nil. Ruby "singleton" objects
# +true+, +false+, numerics and symbols will be returned without any change. Ruby strings
# will be duplicated with the result being cached.
#
# The transformation input can be given as:
# * a single object
# * an array each element of which is transformed in turn
# * a hash used as input to Environment#find with the result being transformed
#
def trans(obj)
if obj.is_a?(Hash)
raise StandardError.new("No input environment available to find model element.") unless @env_in
obj = @env_in.find(obj)
end
return nil if obj.nil?
return obj if obj.is_a?(TrueClass) or obj.is_a?(FalseClass) or obj.is_a?(Numeric) or obj.is_a?(Symbol)
return @transformer_results[obj] if @transformer_results[obj]
return @transformer_results[obj] = obj.dup if obj.is_a?(String)
return obj.collect{|o| trans(o)}.compact if obj.is_a? Array
raise StandardError.new("No transformer for class #{obj.class.name}") unless _transformerBlock(obj.class)
block_desc = _evaluateCondition(obj)
return nil unless block_desc
@transformer_results[obj] = _instantiateTargetClass(obj, block_desc.target)
# we will transform the properties later
@transformer_jobs << TransformerJob.new(self, obj, block_desc)
# if there have been jobs in the queue before, don't process them in this call
# this way calls to trans are not nested; a recursive implementation
# may cause a "Stack level too deep" exception for large models
return @transformer_results[obj] if @transformer_jobs.size > 1
# otherwise this is the first call of trans, process all jobs here
# more jobs will be added during job execution
while @transformer_jobs.size > 0
@transformer_jobs.first.execute
@transformer_jobs.shift
end
@transformer_results[obj]
end
# Create the hash required as return value of the block given to the Transformer.transform method.
# The hash will assign feature values of the source class to the features of the target class,
# assuming the features of both classes are the same. If the :except named parameter specifies
# an Array of symbols, the listed features are not copied by the hash. In order to easily manipulate
# the resulting hash, a block may be given which should also return a feature assignmet hash. This
# hash should be created manually and will extend/overwrite the automatically created hash.
#
def copy_features(options={})
hash = {}
@current_object.class.ecore.eAllStructuralFeatures.each do |f|
next if f.derived
next if options[:except] && options[:except].include?(f.name.to_sym)
hash[f.name.to_sym] = trans(@current_object.send(f.name))
end
hash.merge!(yield) if block_given?
hash
end
def _transformProperties(obj, block_desc) #:nodoc:
old_object, @current_object = @current_object, obj
block_result = instance_eval(&block_desc.block)
raise StandardError.new("Transformer must return a hash") unless block_result.is_a? Hash
@current_object = old_object
_attributesFromHash(@transformer_results[obj], block_result)
end
class TransformerJob #:nodoc:
def initialize(transformer, obj, block_desc)
@transformer, @obj, @block_desc = transformer, obj, block_desc
end
def execute
@transformer._transformProperties(@obj, @block_desc)
end
end
# Each call which is not handled by the transformer object is passed to the object
# currently being transformed.
# If that object also does not respond to the call, it is treated as a transformer
# method call (see Transformer.method).
#
def method_missing(m, *args) #:nodoc:
if @current_object.respond_to?(m)
@current_object.send(m, *args)
else
_invokeMethod(m, *args)
end
end
private
# returns _transformer_blocks content for clazz or one of its superclasses
def _transformerBlock(clazz) # :nodoc:
block = self.class._transformer_blocks[clazz]
block = _transformerBlock(clazz.superclass) if block.nil? && clazz != Object
block
end
# returns the first TransformationDescription for clazz or one of its superclasses
# for which condition is true
def _evaluateCondition(obj, clazz=obj.class) # :nodoc:
tb = self.class._transformer_blocks[clazz]
block_description = nil
if tb.is_a?(TransformationDescription)
# non-conditional
block_description = tb
elsif tb
old_object, @current_object = @current_object, obj
tb.each_pair {|condition, block|
if condition.is_a?(Proc)
result = instance_eval(&condition)
elsif condition.is_a?(Symbol)
result = _invokeMethod(condition)
else
result = condition
end
if result
block_description = block
break
end
}
@current_object = old_object
end
block_description = _evaluateCondition(obj, clazz.superclass) if block_description.nil? && clazz != Object
block_description
end
def _instantiateTargetClass(obj, target_desc) # :nodoc:
old_object, @current_object = @current_object, obj
if target_desc.is_a?(Proc)
target_class = instance_eval(&target_desc)
elsif target_desc.is_a?(Symbol)
target_class = _invokeMethod(target_desc)
else
target_class = target_desc
end
@current_object = old_object
result = target_class.new
@env_out << result if @env_out
result
end
def _invokeMethod(m) # :nodoc:
raise StandardError.new("Method not found: #{m}") unless self.class._methods[m.to_s]
instance_eval(&self.class._methods[m.to_s])
end
def _attributesFromHash(obj, hash) # :nodoc:
hash.delete(:class)
hash.each_pair{|k,v|
obj.send("#{k}=", v)
}
obj
end
end
end rgen-0.10.2/lib/rgen/util/ 0000775 0000000 0000000 00000000000 14560401624 0015205 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/rgen/util/auto_class_creator.rb 0000664 0000000 0000000 00000001762 14560401624 0021414 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/metamodel_builder'
module RGen
module Util
class Base
extend MetamodelBuilder
def initialize(env=nil)
env << self if env
end
end
class AutoCreatedClass < Base
def method_missing(m,*args)
return super unless self.class.parent.accEnabled
if m.to_s =~ /(.*)=$/
self.class.has_one($1)
send(m,args[0])
elsif args.size == 0
self.class.has_many(m)
send(m)
end
end
end
# will be "extended" to the auto created class
module ParentAccess
def parent=(p)
@parent = p
end
def parent
@parent
end
end
module AutoClassCreator
attr_reader :accEnabled
def const_missing(className)
return super unless @accEnabled
module_eval("class "+className.to_s+" < RGen::AutoCreatedClass; end")
c = const_get(className)
c.extend(ParentAccess)
c.parent = self
c
end
def enableACC
@accEnabled = true
end
def disableACC
@accEnabled = false
end
end
end
end
rgen-0.10.2/lib/rgen/util/cached_glob.rb 0000664 0000000 0000000 00000003443 14560401624 0017750 0 ustar 00root root 0000000 0000000 module RGen
module Util
# WARNING: the mechanism of taking timestamps of directories in order to find out if the
# content has changed doesn't work reliably across all kinds of filesystems
#
class CachedGlob
def initialize(dir_glob, file_glob)
@dir_glob = dir_glob
@file_glob = file_glob
@root_dirs = []
@dirs = {}
@files = {}
@timestamps = {}
end
# returns all files contained in directories matched by +dir_glob+ which match +file_glob+.
# +file_glob+ must be relative to +dir_glob+.
# dir_glob "*/a" with file_glob "**/*.txt" is basically equivalent with Dir.glob("*/a/**/*.txt")
# the idea is that the file glob will only be re-eavluated when the content of one of the
# directories matched by dir_glob has changed.
# this will only be faster than a normal Dir.glob if the number of dirs matched by dir_glob is
# relatively large and changes in files affect only a few of them at a time.
def glob
root_dirs = Dir.glob(@dir_glob)
(@root_dirs - root_dirs).each do |d|
remove_root_dir(d)
end
(@root_dirs & root_dirs).each do |d|
update_root_dir(d) if dir_changed?(d)
end
(root_dirs - @root_dirs).each do |d|
update_root_dir(d)
end
@root_dirs = root_dirs
@root_dirs.sort.collect{|d| @files[d]}.flatten
end
private
def dir_changed?(dir)
@dirs[dir].any?{|d| File.mtime(d) != @timestamps[dir][d]}
end
def update_root_dir(dir)
@dirs[dir] = Dir.glob(dir+"/**/")
@files[dir] = Dir.glob(dir+"/"+@file_glob)
@timestamps[dir] = {}
@dirs[dir].each do |d|
@timestamps[dir][d] = File.mtime(d)
end
end
def remove_root_dir(dir)
@dirs.delete(dir)
@files.delete(dir)
@timestamps.delete(dir)
end
end
end
end
rgen-0.10.2/lib/rgen/util/file_cache_map.rb 0000664 0000000 0000000 00000007056 14560401624 0020441 0 ustar 00root root 0000000 0000000 require 'digest'
require 'fileutils'
module RGen
module Util
# Implements a cache for storing and loading data associated with arbitrary files.
# The data is stored in cache files within a subfolder of the folder where
# the associated files exists.
# The cache files are protected with a checksum and loaded data will be
# invalid in case either the associated file are the cache file has changed.
#
class FileCacheMap
# optional program version info to be associated with the cache files
# if the program version changes, cached data will also be invalid
attr_accessor :version_info
# +cache_dir+ is the name of the subfolder where cache files are created
# +postfix+ is an extension appended to the original file name for
# creating the name of the cache file
def initialize(cache_dir, postfix)
@postfix = postfix
@cache_dir = cache_dir
end
# load data associated with file +key_path+
# returns :invalid in case either the associated file or the cache file has changed
#
# options:
# :invalidation_reasons:
# an array which will receive symbols indicating why the cache is invalid:
# :no_cachefile, :cachefile_corrupted, :keyfile_changed
#
def load_data(key_path, options={})
reasons = options[:invalidation_reasons] || []
cf = cache_file(key_path)
result = nil
begin
File.open(cf, "rb") do |f|
header = f.read(41)
if !header
reasons << :cachefile_corrupted
return :invalid
end
checksum = header[0..39]
data = f.read
if calc_sha1(data) == checksum
if calc_sha1_keydata(key_path) == data[0..39]
result = data[41..-1]
else
reasons << :keyfile_changed
result = :invalid
end
else
reasons << :cachefile_corrupted
result = :invalid
end
end
rescue Errno::ENOENT
reasons << :no_cachefile
result = :invalid
end
result
end
# store data +value_data+ associated with file +key_path+
def store_data(key_path, value_data)
data = calc_sha1_keydata(key_path) + "\n" + value_data
data = calc_sha1(data) + "\n" + data
cf = cache_file(key_path)
FileUtils.mkdir(File.dirname(cf)) rescue Errno::EEXIST
File.open(cf, "wb") do |f|
f.write(data)
end
end
# remove cache files which are not associated with any file in +key_paths+
# will only remove files within +root_path+
def clean_unused(root_path, key_paths)
raise "key paths must be within root path" unless key_paths.all?{|p| p.index(root_path) == 0}
used_files = key_paths.collect{|p| cache_file(p)}
files = Dir[root_path+"/**/"+@cache_dir+"/*"+@postfix]
files.each do |f|
FileUtils.rm(f) unless used_files.include?(f)
end
end
private
def cache_file(path)
File.dirname(path) + "/"+@cache_dir+"/" + File.basename(path) + @postfix
end
def calc_sha1(data)
sha1 = Digest::SHA1.new
sha1.update(data)
sha1.hexdigest
end
def keyData(path)
File.read(path)+@version_info.to_s
end
# this method is much faster than calling +keyData+ and putting the result in +calc_sha1+
# reason is probably that there are not so many big strings being created
def calc_sha1_keydata(path)
begin
sha1 = Digest::SHA1.new
sha1.file(path)
sha1.update(@version_info.to_s)
sha1.hexdigest
rescue Errno::ENOENT
""
end
end
end
end
end
rgen-0.10.2/lib/rgen/util/file_change_detector.rb 0000664 0000000 0000000 00000004555 14560401624 0021660 0 ustar 00root root 0000000 0000000 require 'digest'
module RGen
module Util
# The FileChangeDetector detects changes in a set of files.
# Changes are detected between successive calls to check_files with a give set of files.
# Changes include files being added, removed or having changed their content.
#
class FileChangeDetector
FileInfo = Struct.new(:timestamp, :digest)
# Create a FileChangeDetector, options include:
#
# :file_added
# a proc which is called when a file is added, receives the filename
#
# :file_removed
# a proc which is called when a file is removed, receives the filename
#
# :file_changed
# a proc which is called when a file is changed, receives the filename
#
def initialize(options={})
@file_added = options[:file_added]
@file_removed = options[:file_removed]
@file_changed = options[:file_changed]
@file_info = {}
end
# Checks if any of the files has changed compared to the last call of check_files.
# When called for the first time on a new object, all files will be reported as being added.
#
def check_files(files)
files_before = @file_info.keys
used_files = {}
files.each do |file|
begin
if @file_info[file]
if @file_info[file].timestamp != File.mtime(file)
@file_info[file].timestamp = File.mtime(file)
digest = calc_digest(file)
if @file_info[file].digest != digest
@file_info[file].digest = digest
@file_changed && @file_changed.call(file)
end
end
else
@file_info[file] = FileInfo.new
@file_info[file].timestamp = File.mtime(file)
@file_info[file].digest = calc_digest(file)
@file_added && @file_added.call(file)
end
used_files[file] = true
# protect against missing files
rescue Errno::ENOENT
# used_files is not set and @file_info will be removed below
# notification hook hasn't been called yet since it comes after file accesses
end
end
files_before.each do |file|
if !used_files[file]
@file_info.delete(file)
@file_removed && @file_removed.call(file)
end
end
end
private
def calc_digest(file)
sha1 = Digest::SHA1.new
sha1.file(file)
sha1.hexdigest
end
end
end
end
rgen-0.10.2/lib/rgen/util/method_delegation.rb 0000664 0000000 0000000 00000005376 14560401624 0021220 0 ustar 00root root 0000000 0000000 module RGen
module Util
module MethodDelegation
class << self
def registerDelegate(delegate, object, method)
method = method.to_sym
createDelegateStore(object)
if object._methodDelegates[method]
object._methodDelegates[method] << delegate
else
object._methodDelegates[method] = [delegate]
createDelegatingMethod(object, method)
end
end
def unregisterDelegate(delegate, object, method)
method = method.to_sym
return unless object.respond_to?(:_methodDelegates)
return unless object._methodDelegates[method]
object._methodDelegates[method].delete(delegate)
if object._methodDelegates[method].empty?
object._methodDelegates[method] = nil
removeDelegatingMethod(object, method)
removeDelegateStore(object)
end
end
private
def createDelegateStore(object)
return if object.respond_to?(:_methodDelegates)
class << object
def _methodDelegates
@_methodDelegates ||= {}
end
end
end
def removeDelegateStore(object)
return unless object.respond_to?(:_methodDelegates)
class << object
remove_method(:_methodDelegates)
end
end
def createDelegatingMethod(object, method)
if hasMethod(object, method)
object.instance_eval <<-END
class << self
alias #{aliasMethodName(method)} #{method}
end
END
end
# define the delegating method
object.instance_eval <<-END
class << self
def #{method}(*args, &block)
@_methodDelegates[:#{method}].each do |d|
catch(:continue) do
return d.#{method}_delegated(self, *args, &block)
end
end
# if aliased method does not exist, we want an exception
#{aliasMethodName(method)}(*args, &block)
end
end
END
end
def removeDelegatingMethod(object, method)
if hasMethod(object, aliasMethodName(method))
# there is an aliased original, restore it
object.instance_eval <<-END
class << self
alias #{method} #{aliasMethodName(method)}
remove_method(:#{aliasMethodName(method)})
end
END
else
# just delete the delegating method
object.instance_eval <<-END
class << self
remove_method(:#{method})
end
END
end
end
def hasMethod(object, method)
# in Ruby 1.9, #methods returns symbols
if object.methods.first.is_a?(Symbol)
method = method.to_sym
else
method = method.to_s
end
object.methods.include?(method)
end
def aliasMethodName(method)
"#{method}_delegate_original"
end
end
end
end
end
rgen-0.10.2/lib/rgen/util/model_comparator.rb 0000664 0000000 0000000 00000004001 14560401624 0021054 0 ustar 00root root 0000000 0000000 require 'rgen/ecore/ecore'
module RGen
module Util
module ModelComparator
# This method compares to models regarding equality
# For this the identity of a model element is defined based on identity
# of all attributes and referenced elements.
# Arrays are sorted before comparison if possible (if <=> is provided).
#
def modelEqual?(a, b, featureIgnoreList=[])
@modelEqual_visited = {}
_modelEqual_internal(a, b, featureIgnoreList, [])
end
def _modelEqual_internal(a, b, featureIgnoreList, path)
return true if @modelEqual_visited[[a,b]]
@modelEqual_visited[[a,b]] = true
return true if a.nil? && b.nil?
unless a.class == b.class
puts "#{path.inspect}\n Classes differ: #{a} vs. #{b}"
return false
end
if a.is_a?(Array)
unless a.size == b.size
puts "#{path.inspect}\n Array size differs: #{a.size} vs. #{b.size}"
return false
end
begin
# in Ruby 1.9 every object has the <=> operator but the default one returns
# nil and thus sorting won't work (ArgumentError)
as = a.sort
rescue ArgumentError, NoMethodError
as = a
end
begin
bs = b.sort
rescue ArgumentError, NoMethodError
bs = b
end
a.each_index do |i|
return false unless _modelEqual_internal(as[i], bs[i], featureIgnoreList, path+[i])
end
else
a.class.ecore.eAllStructuralFeatures.reject{|f| f.derived}.each do |feat|
next if featureIgnoreList.include?(feat.name)
if feat.eType.is_a?(RGen::ECore::EDataType)
unless a.getGeneric(feat.name) == b.getGeneric(feat.name)
puts "#{path.inspect}\n Value '#{feat.name}' differs: #{a.getGeneric(feat.name)} vs. #{b.getGeneric(feat.name)}"
return false
end
else
return false unless _modelEqual_internal(a.getGeneric(feat.name), b.getGeneric(feat.name), featureIgnoreList, path+["#{a.respond_to?(:name) && a.name}:#{feat.name}"])
end
end
end
return true
end
end
end
end
rgen-0.10.2/lib/rgen/util/model_comparator_base.rb 0000664 0000000 0000000 00000006632 14560401624 0022062 0 ustar 00root root 0000000 0000000 require 'andand'
module RGen
module Util
class ModelComparatorBase
CompareSpec = Struct.new(:identifier, :recurse, :filter, :ignore_features, :display_name, :sort)
INDENT = " "
class << self
attr_reader :compareSpecs
def compare_spec(clazz, hash)
@compareSpecs ||= {}
raise "Compare spec already defined for #{clazz}" if @compareSpecs[clazz]
spec = CompareSpec.new
hash.each_pair do |k,v|
spec.send("#{k}=",v)
end
@compareSpecs[clazz] = spec
end
end
# compares two sets of elements
def compare(as, bs, recursive=true)
result = []
aById = as.select{|e| useElement?(e)}.inject({}){|r, e| r[elementIdentifier(e)] = e; r}
bById = bs.select{|e| useElement?(e)}.inject({}){|r, e| r[elementIdentifier(e)] = e; r}
onlyA = sortElements((aById.keys - bById.keys).collect{|id| aById[id]})
onlyB = sortElements((bById.keys - aById.keys).collect{|id| bById[id]})
aAndB = sortElementPairs((aById.keys & bById.keys).collect{|id| [aById[id], bById[id]]})
onlyA.each do |e|
result << "- #{elementDisplayName(e)}"
end
onlyB.each do |e|
result << "+ #{elementDisplayName(e)}"
end
if recursive
aAndB.each do |ab|
a, b = *ab
r = compareElements(a, b)
if r.size > 0
result << "#{elementDisplayName(a)}"
result += r.collect{|l| INDENT+l}
end
end
end
result
end
def sortElementPairs(pairs)
pairs.sort do |x,y|
a, b = x[0], y[0]
r = a.class.name <=> b.class.name
r = compareSpec(a).sort.call(a,b) if r == 0 && compareSpec(a) && compareSpec(a).sort
r
end
end
def sortElements(elements)
elements.sort do |a,b|
r = a.class.name <=> b.class.name
r = compareSpec(a).sort.call(a,b) if r == 0 && compareSpec(a) && compareSpec(a).sort
r
end
end
def elementDisplayName(e)
if compareSpec(e) && compareSpec(e).display_name
compareSpec(e).display_name.call(e)
else
elementIdentifier(e)
end
end
def compareElements(a, b)
result = []
if a.class != b.class
result << "Class: #{a.class} -> #{b.class}"
else
a.class.ecore.eAllStructuralFeatures.reject{|f| f.derived || compareSpec(a).andand.ignore_features.andand.include?(f.name.to_sym)}.each do |f|
va, vb = a.getGeneric(f.name), b.getGeneric(f.name)
if f.is_a?(RGen::ECore::EAttribute)
r = compareValues(f.name, va, vb)
result << r if r
else
va, vb = [va].compact, [vb].compact unless f.many
r = compare(va, vb, f.containment || compareSpec(a).andand.recurse.andand.include?(f.name.to_sym))
if r.size > 0
result << "[#{f.name}]"
result += r.collect{|l| INDENT+l}
end
end
end
end
result
end
def compareValues(name, val1, val2)
result = nil
result = "[#{name}] #{val1} -> #{val2}" if val1 != val2
result
end
def elementIdentifier(element)
cs = compareSpec(element)
if cs && cs.identifier
if cs.identifier.is_a?(Proc)
cs.identifier.call(element)
else
cs.identifier
end
else
if element.respond_to?(:name)
element.name
else
element.object_id
end
end
end
def useElement?(element)
cs = compareSpec(element)
!(cs && cs.filter) || cs.filter.call(element)
end
def compareSpec(element)
@compareSpec ||= {}
return @compareSpec[element.class] if @compareSpec[element.class]
return nil unless self.class.compareSpecs
key = self.class.compareSpecs.keys.find{|k| element.is_a?(k)}
@compareSpec[element.class] = self.class.compareSpecs[key]
end
end
end
end
rgen-0.10.2/lib/rgen/util/model_dumper.rb 0000664 0000000 0000000 00000001047 14560401624 0020210 0 ustar 00root root 0000000 0000000 module RGen
module Util
module ModelDumper
def dump(obj=nil)
obj ||= self
if obj.is_a?(Array)
obj.collect {|o| dump(o)}.join("\n\n")
elsif obj.class.respond_to?(:ecore)
([obj.to_s] +
obj.class.ecore.eAllStructuralFeatures.select{|f| !f.many}.collect { |a|
" #{a} => #{obj.getGeneric(a.name)}"
} +
obj.class.ecore.eAllStructuralFeatures.select{|f| f.many}.collect { |a|
" #{a} => [ #{obj.getGeneric(a.name).join(', ')} ]"
}).join("\n")
else
obj.to_s
end
end
end
end
end
rgen-0.10.2/lib/rgen/util/name_helper.rb 0000664 0000000 0000000 00000001244 14560401624 0020012 0 ustar 00root root 0000000 0000000 # RGen Framework
# (c) Martin Thiede, 2006
module RGen
module Util
module NameHelper
def normalize(name)
name.gsub(/\W/,'_')
end
def className(object)
object.class.name =~ /::(\w+)$/; $1
end
def firstToUpper(str)
str[0..0].upcase + ( str[1..-1] || "" )
end
def firstToLower(str)
str[0..0].downcase + ( str[1..-1] || "" )
end
def saneClassName(str)
firstToUpper(normalize(str)).sub(/^Class$/, 'Clazz')
end
def saneMethodName(str)
firstToLower(normalize(str)).sub(/^class$/, 'clazz')
end
def camelize(str)
str.split(/[\W_]/).collect{|s| firstToUpper(s.downcase)}.join
end
end
end
end
rgen-0.10.2/lib/rgen/util/pattern_matcher.rb 0000664 0000000 0000000 00000023410 14560401624 0020712 0 ustar 00root root 0000000 0000000 module RGen
module Util
# A PatternMatcher can be used to find, insert and remove patterns on a given model.
#
# A pattern is specified by means of a block passed to the add_pattern method.
# The block must take an Environment as first parameter and at least one model element
# as connection point as further parameter. The pattern matches if it can be found
# in a given environment and connected to the given connection point elements.
#
class PatternMatcher
Match = Struct.new(:root, :elements, :bound_values)
attr_accessor :debug
def initialize
@patterns = {}
@insert_mode = false
@debug = false
end
def add_pattern(name, &block)
raise "a pattern needs at least 2 block parameters: " +
"an RGen environment and a model element as connection point" \
unless block.arity >= 2
@patterns[name] = block
end
def find_pattern(env, name, *connection_points)
match = find_pattern_internal(env, name, *connection_points)
end
def insert_pattern(env, name, *connection_points)
@insert_mode = true
root = evaluate_pattern(name, env, connection_points)
@insert_mode = false
root
end
def remove_pattern(env, name, *connection_points)
match = find_pattern_internal(env, name, *connection_points)
if match
match.elements.each do |e|
disconnect_element(e)
env.delete(e)
end
match
else
nil
end
end
def lazy(&block)
if @insert_mode
block.call
else
Lazy.new(&block)
end
end
class Lazy < RGen::MetamodelBuilder::MMGeneric
def initialize(&block)
@block = block
end
def _eval
@block.call
end
end
private
class Proxy < RGen::MetamodelBuilder::MMProxy
attr_reader :_target
def initialize(target)
@_target = target
end
def method_missing(m, *args)
result = @_target.send(m, *args)
if result.is_a?(Array)
result.collect do |e|
if e.is_a?(RGen::MetamodelBuilder::MMBase)
Proxy.new(e)
else
e
end
end
else
if result.is_a?(RGen::MetamodelBuilder::MMBase)
Proxy.new(result)
else
result
end
end
end
end
class Bindable < RGen::MetamodelBuilder::MMGeneric
# by being an Enumerable, Bindables can be used for many-features as well
include Enumerable
def initialize
@bound = false
@value = nil
@many = false
end
def _bound?
@bound
end
def _many?
@many
end
def _bind(value)
@value = value
@bound = true
end
def _value
@value
end
def to_s
@value.to_s
end
# pretend this is an enumerable which contains itself, so the bindable can be
# inserted into many-features, when this is done the bindable is marked as a many-bindable
def each
@many = true
yield(self)
end
def method_missing(m, *args)
raise "bindable not bound" unless _bound?
@value.send(m, *args)
end
end
TempEnv = RGen::Environment.new
class << TempEnv
def <<(el); end
end
def find_pattern_internal(env, name, *connection_points)
proxied_args = connection_points.collect{|a|
a.is_a?(RGen::MetamodelBuilder::MMBase) ? Proxy.new(a) : a }
bindables = create_bindables(name, connection_points)
pattern_root = evaluate_pattern(name, TempEnv, proxied_args+bindables)
candidates = candidates_via_connection_points(pattern_root, connection_points)
candidates ||= env.find(:class => pattern_root.class)
candidates.each do |e|
# create new bindables for every try, otherwise they can could be bound to old values
bindables = create_bindables(name, connection_points)
pattern_root = evaluate_pattern(name, TempEnv, proxied_args+bindables)
matched = match(pattern_root, e)
return Match.new(e, matched, bindables.collect{|b| b._value}) if matched
end
nil
end
def create_bindables(pattern_name, connection_points)
(1..(num_pattern_variables(pattern_name) - connection_points.size)).collect{|i| Bindable.new}
end
def candidates_via_connection_points(pattern_root, connection_points)
@candidates_via_connection_points_refs ||= {}
refs = (@candidates_via_connection_points_refs[pattern_root.class] ||=
pattern_root.class.ecore.eAllReferences.reject{|r| r.derived || r.many || !r.eOpposite})
candidates = nil
refs.each do |r|
t = pattern_root.getGeneric(r.name)
cp = t.is_a?(Proxy) && connection_points.find{|cp| cp.object_id == t._target.object_id}
if cp
elements = cp.getGenericAsArray(r.eOpposite.name)
candidates = elements if candidates.nil? || elements.size < candidates.size
end
end
candidates
end
def match(pat_element, test_element)
visited = {}
check_later = []
return false unless match_internal(pat_element, test_element, visited, check_later)
while cl = check_later.shift
pv, tv = cl.lazy._eval, cl.value
if cl.feature.is_a?(RGen::ECore::EAttribute)
unless pv == tv
match_failed(cl.feature, "wrong attribute value (lazy): #{pv} vs. #{tv}")
return false
end
else
if pv.is_a?(Proxy)
unless pv._target.object_id == tv.object_id
match_failed(f, "wrong target object")
return false
end
else
unless (pv.nil? && tv.nil?) || (!pv.nil? && !tv.nil? && match_internal(pv, tv, visited, check_later))
return false
end
end
end
end
visited.keys
end
CheckLater = Struct.new(:feature, :lazy, :value)
def match_internal(pat_element, test_element, visited, check_later)
return true if visited[test_element]
visited[test_element] = true
unless pat_element.class == test_element.class
match_failed(nil, "wrong class: #{pat_element.class} vs #{test_element.class}")
return false
end
all_structural_features(pat_element).each do |f|
pat_values = pat_element.getGeneric(f.name)
# nil values must be kept to support size check with Bindables
pat_values = [ pat_values ] unless pat_values.is_a?(Array)
test_values = test_element.getGeneric(f.name)
test_values = [ test_values] unless test_values.is_a?(Array)
if pat_values.size == 1 && pat_values.first.is_a?(Bindable) && pat_values.first._many?
unless match_many_bindable(pat_values.first, test_values)
return false
end
else
unless pat_values.size == test_values.size
match_failed(f, "wrong size #{pat_values.size} vs. #{test_values.size}")
return false
end
pat_values.each_with_index do |pv,i|
tv = test_values[i]
if pv.is_a?(Lazy)
check_later << CheckLater.new(f, pv, tv)
elsif pv.is_a?(Bindable)
if pv._bound?
unless pv._value == tv
match_failed(f, "value does not match bound value #{pv._value.class}:#{pv._value.object_id} vs. #{tv.class}:#{tv.object_id}")
return false
end
else
pv._bind(tv)
end
else
if f.is_a?(RGen::ECore::EAttribute)
unless pv == tv
match_failed(f, "wrong attribute value")
return false
end
else
if pv.is_a?(Proxy)
unless pv._target.object_id == tv.object_id
match_failed(f, "wrong target object")
return false
end
else
unless both_nil_or_match(pv, tv, visited, check_later)
return false
end
end
end
end
end
end
end
true
end
def match_many_bindable(bindable, test_values)
if bindable._bound?
bindable._value.each_with_index do |pv,i|
tv = test_values[i]
if f.is_a?(RGen::ECore::EAttribute)
unless pv == tv
match_failed(f, "wrong attribute value")
return false
end
else
unless both_nil_or_match(pv, tv, visited, check_later)
return false
end
end
end
else
bindable._bind(test_values.dup)
end
true
end
def both_nil_or_match(pv, tv, visited, check_later)
(pv.nil? && tv.nil?) || (!pv.nil? && !tv.nil? && match_internal(pv, tv, visited, check_later))
end
def match_failed(f, msg)
puts "match failed #{f&&f.eContainingClass.name}##{f&&f.name}: #{msg}" if @debug
end
def num_pattern_variables(name)
prok = @patterns[name]
prok.arity - 1
end
def evaluate_pattern(name, env, connection_points)
prok = @patterns[name]
raise "unknown pattern #{name}" unless prok
raise "wrong number of arguments, expected #{prok.arity-1} connection points)" \
unless connection_points.size == prok.arity-1
prok.call(env, *connection_points)
end
def disconnect_element(element)
return if element.nil?
all_structural_features(element).each do |f|
if f.many
element.setGeneric(f.name, [])
else
element.setGeneric(f.name, nil)
end
end
end
def all_structural_features(element)
@all_structural_features ||= {}
return @all_structural_features[element.class] if @all_structural_features[element.class]
@all_structural_features[element.class] =
element.class.ecore.eAllStructuralFeatures.reject{|f| f.derived}
end
end
end
end
rgen-0.10.2/lib/transformers/ 0000775 0000000 0000000 00000000000 14560401624 0016022 5 ustar 00root root 0000000 0000000 rgen-0.10.2/lib/transformers/ecore_to_uml13.rb 0000664 0000000 0000000 00000005216 14560401624 0021173 0 ustar 00root root 0000000 0000000 require 'rgen/transformer'
require 'rgen/ecore/ecore'
require 'metamodels/uml13_metamodel'
class ECoreToUML13 < RGen::Transformer
include RGen::ECore
def transform
trans(:class => EPackage)
trans(:class => EClass)
trans(:class => EEnum)
end
transform EPackage, :to => UML13::Package do
{:name => name,
:namespace => trans(eSuperPackage) || model,
:ownedElement => trans(eClassifiers.select{|c| c.is_a?(EClass)} + eSubpackages)
}
end
transform EClass, :to => UML13::Class do
{:name => name,
:namespace => trans(ePackage),
:feature => trans(eStructuralFeatures.select{|f| f.is_a?(EAttribute)} + eOperations),
:associationEnd => trans(eStructuralFeatures.select{|f| f.is_a?(EReference)}),
:generalization => eSuperTypes.collect { |st|
@env_out.new(UML13::Generalization, :parent => trans(st), :namespace => trans(ePackage) || model)
}
}
end
transform EEnum, :to => UML13::Class do
{:name => name,
:namespace => trans(ePackage),
:feature => trans(eLiterals)
}
end
transform EEnumLiteral, :to => UML13::Attribute do
{:name => name }
end
transform EAttribute, :to => UML13::Attribute do
_typemap = {"String" => "string", "Boolean" => "boolean", "Integer" => "int", "Float" => "float"}
{:name => name,
:taggedValue => [@env_out.new(UML13::TaggedValue, :tag => "type",
:value => _typemap[eType.instanceClassName] || eType.name)]
}
end
transform EReference, :to => UML13::AssociationEnd do
_otherAssocEnd = eOpposite ? trans(eOpposite) :
@env_out.new(UML13::AssociationEnd,
:type => trans(eType), :name => name, :multiplicity => createMultiplicity(@current_object),
:aggregation => :none, :isNavigable => true)
{ :association => trans(@current_object).association || @env_out.new(UML13::Association,
:connection => [_otherAssocEnd], :namespace => trans(eContainingClass.ePackage) || model),
:name => eOpposite && eOpposite.name,
:multiplicity => eOpposite && createMultiplicity(eOpposite),
:aggregation => containment ? :composite : :none,
:isNavigable => !eOpposite.nil?
}
end
transform EOperation, :to => UML13::Operation do
{:name => name}
end
def createMultiplicity(ref)
@env_out.new(UML13::Multiplicity, :range => [
@env_out.new(UML13::MultiplicityRange,
:lower => ref.lowerBound.to_s.sub("-1","*"), :upper => ref.upperBound.to_s.sub("-1","*"))])
end
def model
@model ||= @env_out.new(UML13::Model, :name => "Model")
end
end
rgen-0.10.2/lib/transformers/uml13_to_ecore.rb 0000664 0000000 0000000 00000010705 14560401624 0021172 0 ustar 00root root 0000000 0000000 require 'metamodels/uml13_metamodel'
require 'rgen/transformer'
require 'rgen/ecore/ecore'
require 'rgen/array_extensions'
class UML13ToECore < RGen::Transformer
include RGen::ECore
# Options:
#
# :reference_filter:
# a proc which receives an AssociationEnd or a Dependency and should return
# true or false, depending on if a referece should be created for it or not
#
def initialize(*args)
options = {}
if args.last.is_a?(Hash)
options = args.pop
end
@reference_filter = options[:reference_filter] || proc do |e|
if e.is_a?(UML13::AssociationEnd)
otherEnd = e.association.connection.find{|ae| ae != e}
otherEnd.name && otherEnd.name.size > 0
else
false
end
end
super(*args)
end
def transform
trans(:class => UML13::Class)
end
transform UML13::Model, :to => EPackage do
trans(ownedClassOrPackage)
{ :name => name && name.strip }
end
transform UML13::Package, :to => EPackage do
trans(ownedClassOrPackage)
{ :name => name && name.strip,
:eSuperPackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil) }
end
method :ownedClassOrPackage do
ownedElement.select{|e| e.is_a?(UML13::Package) || e.is_a?(UML13::Class)}
end
transform UML13::Class, :to => EClass do
{ :name => name && name.strip,
:abstract => isAbstract,
:ePackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil),
:eStructuralFeatures => trans(feature.select{|f| f.is_a?(UML13::Attribute)} +
associationEnd + clientDependency),
:eOperations => trans(feature.select{|f| f.is_a?(UML13::Operation)}),
:eSuperTypes => trans(generalization.parent + clientDependency.select{|d| d.stereotype && d.stereotype.name == "implements"}.supplier),
:eAnnotations => createAnnotations(taggedValue) }
end
transform UML13::Interface, :to => EClass do
{ :name => name && name.strip,
:abstract => isAbstract,
:ePackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil),
:eStructuralFeatures => trans(feature.select{|f| f.is_a?(UML13::Attribute)} + associationEnd),
:eOperations => trans(feature.select{|f| f.is_a?(UML13::Operation)}),
:eSuperTypes => trans(generalization.parent)}
end
transform UML13::Attribute, :to => EAttribute do
{ :name => name && name.strip, :eType => trans(getType),
:lowerBound => (multiplicity && multiplicity.range.first.lower &&
multiplicity.range.first.lower.to_i) || 0,
:upperBound => (multiplicity && multiplicity.range.first.upper &&
multiplicity.range.first.upper.gsub('*','-1').to_i) || 1,
:eAnnotations => createAnnotations(taggedValue) }
end
transform UML13::DataType, :to => EDataType do
{ :name => name && name.strip,
:ePackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil),
:eAnnotations => createAnnotations(taggedValue) }
end
transform UML13::Operation, :to => EOperation do
{ :name => name && name.strip }
end
transform UML13::AssociationEnd, :to => EReference, :if => :isReference do
otherEnd = association.connection.find{|ae| ae != @current_object}
{ :eType => trans(otherEnd.type),
:name => otherEnd.name && otherEnd.name.strip,
:eOpposite => trans(otherEnd),
:lowerBound => (otherEnd.multiplicity && otherEnd.multiplicity.range.first.lower &&
otherEnd.multiplicity.range.first.lower.to_i) || 0,
:upperBound => (otherEnd.multiplicity && otherEnd.multiplicity.range.first.upper &&
otherEnd.multiplicity.range.first.upper.gsub('*','-1').to_i) || 1,
:containment => (aggregation == :composite),
:eAnnotations => createAnnotations(association.taggedValue) }
end
transform UML13::Dependency, :to => EReference, :if => :isReference do
{ :eType => trans(supplier.first),
:name => name,
:lowerBound => 0,
:upperBound => 1,
:containment => false,
:eAnnotations => createAnnotations(taggedValue)
}
end
method :isReference do
@reference_filter.call(@current_object)
end
def createAnnotations(taggedValues)
if taggedValues.size > 0
[ EAnnotation.new(:details => trans(taggedValues)) ]
else
[]
end
end
transform UML13::TaggedValue, :to => EStringToStringMapEntry do
{ :key => tag, :value => value}
end
end
rgen-0.10.2/rgen.gemspec 0000664 0000000 0000000 00000011421 14560401624 0015026 0 ustar 00root root 0000000 0000000 # THIS FILE WAS AUTOGENERATED, DO NOT EDIT
abort 'Use the rake task or esr-gem-builder to build the gem' if $0 =~ /gem$/ && $*.first == 'build'
require 'yaml'
Class.new do
def initialize
@project_path = File.dirname(__FILE__)
@project_config_path = File.join(File.dirname(__FILE__), 'Project.yaml')
@project_config = YAML.load(File.read(@project_config_path))
end
def rubies
(@project_config['rubies'] || ['2.3']).map(&:to_s)
end
def platforms
@project_config['platforms'] || %w(x86_64-linux x64-mingw32)
end
def exclude_files
@project_config['exclude_files'] || []
end
def encrypt_sources
@project_config['encrypt_sources'].nil? ? true : @project_config['encrypt_sources']
end
def extensions
@project_config['extensions'] ||
Hash[Dir.glob(File.join(@project_path, 'ext', '*')).select { |f| File.directory?(f) }.map { |f| [File.basename(f).to_sym, f] }]
end
def common_native_binary_paths
@project_config['common_native_binary_paths']
end
def native_library_artifacts
@project_config['native_library_artifacts']
end
def common_native_binary_artifacts
@project_config['common_native_binary_artifacts'] || {}
end
def extensions_target_path
@project_config['extensions_target_path'] || File.join('lib', 'native')
end
# All extension artifact path list
def native_library_artifact_paths(only_platforms = nil)
platforms_list = only_platforms.nil? ? platforms : only_platforms
extensions.keys.flat_map do |name|
rubies.flat_map do |ruby|
platforms_list.flat_map do |platform|
if native_library_artifacts && native_library_artifacts[name.to_s]
native_library_artifacts[name.to_s].map do |artifact_platform, artifacts|
next unless platform == artifact_platform.to_s
artifacts.map { |a| File.join(extensions_target_path, name.to_s, ruby, platform, a) }
end
elsif @project_config['native_artifacts'] && @project_config['native_artifacts'][name.to_s] # backward compatibility (e.g. esr-licensing <0.3)
@project_config['native_artifacts'][name.to_s].map do |artifact|
next unless platform == platform.to_s
File.join(extensions_target_path, name.to_s, ruby.to_s, platform, artifact)
end
else
[File.join(extensions_target_path, name.to_s, ruby.to_s, platform, name.to_s + '.so')]
end
end
end
end
end
def common_native_binary_path(name, platform = nil)
path = common_native_binary_paths[name] unless common_native_binary_paths.nil?
path = path[platform || RUBY_PLATFORM] if path.is_a?(Hash)
path || File.join('native', name)
end
def common_native_binary_artifact_paths(only_platforms = nil)
common_native_binary_artifacts.flat_map do |name, info|
info.flat_map do |platform, binaries|
next if only_platforms && !only_platforms.include?(platform.to_s)
binaries.map { |binary| File.join(common_native_binary_path(name, platform), binary) }
end
end.compact
end
def native_artifact_paths(only_platforms = nil)
native_library_artifact_paths(only_platforms) + common_native_binary_artifact_paths(only_platforms)
end
def gemspec
Gem::Specification.new do |s|
s.name = @project_config['name']
s.version = @project_config['version']
s.authors = @project_config['authors']
s.homepage = @project_config['homepage']
s.summary = @project_config['summary']
s.description = @project_config['description']
s.licenses = @project_config['licenses']
s.bindir = @project_config['bindir'] || 'bin'
s.executables = @project_config['executables']
s.extra_rdoc_files = @project_config['extra_rdoc_files']
s.rdoc_options.push(*@project_config['rdoc_options']) if @project_config['rdoc_options']
include_patterns = (@project_config['include_files'] || []) + ["lib/**/*.rb", "Project.yaml"]
include = include_patterns.reject { |i| i.include?('*') } +
include_patterns.select { |i| i.include?('*') }.flat_map { |i| Dir.glob(File.join(@project_path, i)) }
include.push(*native_artifact_paths)
include << "lib/rubyencoder.lic" if encrypt_sources
exclude_files_expanded = exclude_files.reject { |i| i.include?('*') }
exclude = [exclude_files_expanded] +
exclude_files.select { |i| i.include?('*') }.flat_map { |i| Dir.glob(File.join(@project_path, i)) }
s.files = include - exclude
(@project_config['dependencies'] || {}).flat_map do |_, deps|
deps.map do |d|
if d.is_a?(String)
s.add_dependency(d)
else
dep_args = [d['name']]
dep_args.concat(if d['version'].nil?; []; else d['version'].is_a?(String) ? [d['version']] : d['version'] end)
if d['development']
s.add_development_dependency(*dep_args)
else
s.add_dependency(*dep_args)
end
end
end
end
end
end
end.new.gemspec
rgen-0.10.2/test/ 0000775 0000000 0000000 00000000000 14560401624 0013506 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/array_extensions_test.rb 0000664 0000000 0000000 00000003106 14560401624 0020467 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/array_extensions'
class ArrayExtensionsTest < Minitest::Test
def test_element_methods
c = Struct.new("SomeClass",:name,:age)
a = []
a << c.new('MyName',33)
a << c.new('YourName',22)
assert_equal ["MyName", "YourName"], a >> :name
assert_raises NoMethodError do
a.name
end
assert_equal [33, 22], a>>:age
assert_raises NoMethodError do
a.age
end
# unfortunately, any method can be called on an empty array
assert_equal [], [].age
end
class MMBaseClass < RGen::MetamodelBuilder::MMBase
has_attr 'name'
has_attr 'age', Integer
end
def test_with_mmbase
e1 = MMBaseClass.new
e1.name = "MyName"
e1.age = 33
e2 = MMBaseClass.new
e2.name = "YourName"
e2.age = 22
a = [e1, e2]
assert_equal ["MyName", "YourName"], a >> :name
assert_equal ["MyName", "YourName"], a.name
assert_equal [33, 22], a>>:age
assert_equal [33, 22], a.age
# put something into the array that is not an MMBase
a << "not a MMBase"
# the dot operator will tell that there is something not a MMBase
assert_raises StandardError do
a.age
end
# the >> operator will try to call the method anyway
assert_raises NoMethodError do
a >> :age
end
end
def test_hash_square
assert_equal({}, Hash[[]])
end
def test_to_str_on_empty_array
assert_raises NoMethodError do
[].to_str
end
end
end
rgen-0.10.2/test/ea_instantiator_test.rb 0000664 0000000 0000000 00000002120 14560401624 0020251 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/environment'
require 'metamodels/uml13_metamodel'
require 'ea_support/ea_support'
require 'transformers/uml13_to_ecore'
require 'testmodel/class_model_checker'
require 'testmodel/object_model_checker'
require 'testmodel/ecore_model_checker'
class EAInstantiatorTest < Minitest::Test
include Testmodel::ClassModelChecker
include Testmodel::ObjectModelChecker
include Testmodel::ECoreModelChecker
MODEL_DIR = File.join(File.dirname(__FILE__),"testmodel")
def test_instantiator
envUML = RGen::Environment.new
EASupport.instantiateUML13FromXMI11(envUML, MODEL_DIR+"/ea_testmodel.xml")
checkClassModel(envUML)
checkObjectModel(envUML)
envECore = RGen::Environment.new
UML13ToECore.new(envUML, envECore).transform
checkECoreModel(envECore)
end
def test_partial
envUML = RGen::Environment.new
EASupport.instantiateUML13FromXMI11(envUML, MODEL_DIR+"/ea_testmodel_partial.xml")
checkClassModelPartial(envUML)
end
end rgen-0.10.2/test/ea_serializer_test.rb 0000664 0000000 0000000 00000001336 14560401624 0017713 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/environment'
require 'metamodels/uml13_metamodel'
require 'ea_support/ea_support'
require 'rgen/serializer/xmi11_serializer'
class EASerializerTest < Minitest::Test
MODEL_DIR = File.join(File.dirname(__FILE__),"testmodel")
TEST_DIR = File.join(File.dirname(__FILE__),"ea_serializer_test")
def test_serializer
envUML = RGen::Environment.new
EASupport.instantiateUML13FromXMI11(envUML, MODEL_DIR+"/ea_testmodel.xml")
models = envUML.find(:class => UML13::Model)
assert_equal 1, models.size
EASupport.serializeUML13ToXMI11(envUML, MODEL_DIR+"/ea_testmodel_regenerated.xml")
end
end rgen-0.10.2/test/ecore_self_test.rb 0000664 0000000 0000000 00000003745 14560401624 0017211 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/ecore/ecore'
require 'rgen/array_extensions'
class ECoreSelfTest < Minitest::Test
include RGen::ECore
def test_simple
assert_equal \
%w(lowerBound ordered unique upperBound many required eType).sort,
ETypedElement.ecore.eStructuralFeatures.name.sort
assert_equal \
EClassifier.ecore,
ETypedElement.ecore.eStructuralFeatures.find{|f| f.name=="eType"}.eType
assert_equal %w(ENamedElement), ETypedElement.ecore.eSuperTypes.name
assert_equal \
EModelElement.ecore,
EModelElement.ecore.eStructuralFeatures.find{|f| f.name=="eAnnotations"}.eOpposite.eType
assert_equal \
%w(eType),
ETypedElement.ecore.eReferences.name
assert_equal \
%w(lowerBound ordered unique upperBound many required).sort,
ETypedElement.ecore.eAttributes.name.sort
assert RGen::ECore.ecore.is_a?(EPackage)
assert_equal "ECore", RGen::ECore.ecore.name
assert_equal "RGen", RGen::ECore.ecore.eSuperPackage.name
assert_equal %w(ECore), RGen.ecore.eSubpackages.name
assert_equal\
%w(EObject EModelElement EAnnotation ENamedElement ETypedElement
EStructuralFeature EAttribute EClassifier EDataType EEnum EEnumLiteral EFactory
EOperation EPackage EParameter EReference EStringToStringMapEntry EClass
ETypeArgument EGenericType).sort,
RGen::ECore.ecore.eClassifiers.name.sort
assert_equal "false", EAttribute.ecore.eAllAttributes.
find{|a|a.name == "derived"}.defaultValueLiteral
assert_equal false, EAttribute.ecore.eAllAttributes.
find{|a|a.name == "derived"}.defaultValue
assert_nil EAttribute.ecore.eAllAttributes.
find{|a|a.name == "defaultValueLiteral"}.defaultValueLiteral
assert_nil EAttribute.ecore.eAllAttributes.
find{|a|a.name == "defaultValueLiteral"}.defaultValue
end
end
rgen-0.10.2/test/ecore_to_ruby_test.rb 0000664 0000000 0000000 00000003746 14560401624 0017744 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/environment'
require 'rgen/ecore/ecore'
require 'rgen/ecore/ecore_ext'
require 'rgen/ecore/ecore_to_ruby'
class ECoreToRubyTest < Minitest::Test
module ContainerSimple
end
module ContainerUnder
end
def test_simple
p1 = create_ecore
mod = RGen::ECore::ECoreToRuby.new.create_module(p1)
assert mod.const_defined?(:P11)
assert mod::P11.const_defined?(:C1)
# temporary path
assert mod::P11::C1.to_s.start_with?("#")
ContainerSimple.const_set("P1", mod)
assert_equal "ECoreToRubyTest::ContainerSimple::P1::P11::C1", ContainerSimple::P1::P11::C1.name
end
def test_under
p1 = create_ecore
RGen::ECore::ECoreToRuby.new.create_module(p1, ContainerUnder)
assert ContainerUnder.const_defined?(:P1)
assert ContainerUnder::P1.const_defined?(:P11)
assert ContainerUnder::P1::P11.const_defined?(:C1)
assert_equal "ECoreToRubyTest::ContainerUnder::P1::P11::C1", ContainerUnder::P1::P11::C1.name
end
def test_under_temp_path
p1 = create_ecore
container = Module.new
RGen::ECore::ECoreToRuby.new.create_module(p1, container)
assert container.const_defined?(:P1)
assert container::P1.const_defined?(:P11)
assert container::P1::P11.const_defined?(:C1)
# temporary path
assert container::P1::P11::C1.to_s.start_with?("#")
self.class.const_set("Container2", container)
assert_equal "ECoreToRubyTest::Container2::P1::P11::C1", container::P1::P11::C1.to_s
end
def create_ecore
p1 = RGen::ECore::EPackage.new(:name => "P1")
p11 = RGen::ECore::EPackage.new(:name => "P11", :eSuperPackage => p1)
p12 = RGen::ECore::EPackage.new(:name => "P12", :eSuperPackage => p1)
c1 = RGen::ECore::EClass.new(:name => "C1", :ePackage => p11)
c2 = RGen::ECore::EClass.new(:name => "C2", :ePackage => p12)
c3 = RGen::ECore::EClass.new(:name => "C3", :eSuperTypes => [c1, c2], :ePackage => p11)
p1
end
end
rgen-0.10.2/test/environment_test.rb 0000664 0000000 0000000 00000004420 14560401624 0017436 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/environment'
require 'rgen/metamodel_builder'
class EnvironmentTest < Minitest::Test
class Model
attr_accessor :name
end
class ModelSub < Model
end
class ClassSuperA < RGen::MetamodelBuilder::MMBase
end
class ClassSuperB < RGen::MetamodelBuilder::MMBase
end
class ClassC < RGen::MetamodelBuilder::MMMultiple(ClassSuperA, ClassSuperB)
has_attr 'name', String
end
class ClassSubD < ClassC
end
class ClassSubE < ClassC
end
def test_find_mmbase
env = RGen::Environment.new
mA1 = env.new(ClassSuperA)
mB1 = env.new(ClassSuperB)
mD1 = env.new(ClassSubD, :name => "mD1")
mD2 = env.new(ClassSubD, :name => "mD2")
mE = env.new(ClassSubE, :name => "mE")
resultA = env.find(:class => ClassSuperA)
assert_equal sortById([mA1,mD1,mD2,mE]), sortById(resultA)
resultNamedA = env.find(:class => ClassSuperA, :name => "mD1")
assert_equal sortById([mD1]), sortById(resultNamedA)
resultB = env.find(:class => ClassSuperB)
assert_equal sortById([mB1,mD1,mD2,mE]), sortById(resultB)
resultNamedB = env.find(:class => ClassSuperB, :name => "mD1")
assert_equal sortById([mD1]), sortById(resultNamedB)
resultC = env.find(:class => ClassC)
assert_equal sortById([mD1,mD2,mE]), sortById(resultC)
resultD = env.find(:class => ClassSubD)
assert_equal sortById([mD1,mD2]), sortById(resultD)
end
def test_find
m1 = Model.new
m1.name = "M1"
m2 = ModelSub.new
m2.name = "M2"
m3 = "justAString"
env = RGen::Environment.new << m1 << m2 << m3
result = env.find(:class => Model, :name => "M1")
assert result.is_a?(Array)
assert_equal 1, result.size
assert_equal m1, result.first
result = env.find(:class => Model)
assert result.is_a?(Array)
assert_equal sortById([m1,m2]), sortById(result)
result = env.find(:name => "M2")
assert result.is_a?(Array)
assert_equal 1, result.size
assert_equal m2, result.first
result = env.find(:class => [Model, String])
assert result.is_a?(Array)
assert_equal sortById([m1,m2,m3]), sortById(result)
end
private
def sortById(array)
array.sort{|a,b| a.object_id <=> b.object_id}
end
end
rgen-0.10.2/test/json_test.rb 0000664 0000000 0000000 00000014523 14560401624 0016050 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/environment'
require 'rgen/metamodel_builder'
require 'rgen/serializer/json_serializer'
require 'rgen/instantiator/json_instantiator'
class JsonTest < Minitest::Test
module TestMM
extend RGen::MetamodelBuilder::ModuleExtension
class TestNode < RGen::MetamodelBuilder::MMBase
has_attr 'text', String
has_attr 'integer', Integer
has_attr 'float', Float
has_one 'other', TestNode
contains_many 'childs', TestNode, 'parent'
end
end
module TestMMData
extend RGen::MetamodelBuilder::ModuleExtension
# class "Data" exists in the standard Ruby namespace
class Data < RGen::MetamodelBuilder::MMBase
has_attr 'notTheBuiltin', String
end
end
module TestMMSubpackage
extend RGen::MetamodelBuilder::ModuleExtension
module SubPackage
extend RGen::MetamodelBuilder::ModuleExtension
class Data < RGen::MetamodelBuilder::MMBase
has_attr 'notTheBuiltin', String
end
class Data2 < RGen::MetamodelBuilder::MMBase
has_attr 'data2', String
end
end
end
class StringWriter < String
alias write concat
end
def test_json_serializer
testModel = TestMM::TestNode.new(:text => "some text", :childs => [
TestMM::TestNode.new(:text => "child")])
output = StringWriter.new
ser = RGen::Serializer::JsonSerializer.new(output)
assert_equal %q({ "_class": "TestNode", "text": "some text", "childs": [
{ "_class": "TestNode", "text": "child" }] }), ser.serialize(testModel)
end
def test_json_instantiator
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMM)
inst.instantiate(%q({ "_class": "TestNode", "text": "some text", "childs": [
{ "_class": "TestNode", "text": "child" }] }))
root = env.find(:class => TestMM::TestNode, :text => "some text").first
assert root != nil
assert_equal 1, root.childs.size
assert_equal TestMM::TestNode, root.childs.first.class
assert_equal "child", root.childs.first.text
end
def test_json_serializer_escapes
testModel = TestMM::TestNode.new(:text => %Q(some " \\ \\" text \r xx \n xx \r\n xx \t xx \b xx \f))
output = StringWriter.new
ser = RGen::Serializer::JsonSerializer.new(output)
assert_equal %q({ "_class": "TestNode", "text": "some \" \\\\ \\\\\" text \r xx \n xx \r\n xx \t xx \b xx \f" }),
ser.serialize(testModel)
end
def test_json_instantiator_escapes
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMM)
inst.instantiate(%q({ "_class": "TestNode", "text": "some \" \\\\ \\\\\" text \r xx \n xx \r\n xx \t xx \b xx \f" }))
assert_equal %Q(some " \\ \\" text \r xx \n xx \r\n xx \t xx \b xx \f), env.elements.first.text
end
def test_json_instantiator_escape_single_backslash
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMM)
inst.instantiate(%q({ "_class": "TestNode", "text": "a single \\ will be just itself" }))
assert_equal %q(a single \\ will be just itself), env.elements.first.text
end
def test_json_serializer_integer
testModel = TestMM::TestNode.new(:integer => 7)
output = StringWriter.new
ser = RGen::Serializer::JsonSerializer.new(output)
assert_equal %q({ "_class": "TestNode", "integer": 7 }), ser.serialize(testModel)
end
def test_json_instantiator_integer
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMM)
inst.instantiate(%q({ "_class": "TestNode", "integer": 7 }))
assert_equal 7, env.elements.first.integer
end
def test_json_serializer_float
testModel = TestMM::TestNode.new(:float => 1.23)
output = StringWriter.new
ser = RGen::Serializer::JsonSerializer.new(output)
assert_equal %q({ "_class": "TestNode", "float": 1.23 }), ser.serialize(testModel)
end
def test_json_instantiator_float
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMM)
inst.instantiate(%q({ "_class": "TestNode", "float": 1.23 }))
assert_equal 1.23, env.elements.first.float
end
def test_json_instantiator_conflict_builtin
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMMData)
inst.instantiate(%q({ "_class": "Data", "notTheBuiltin": "for sure" }))
assert_equal "for sure", env.elements.first.notTheBuiltin
end
def test_json_serializer_subpacakge
testModel = TestMMSubpackage::SubPackage::Data2.new(:data2 => "xxx")
output = StringWriter.new
ser = RGen::Serializer::JsonSerializer.new(output)
assert_equal %q({ "_class": "Data2", "data2": "xxx" }), ser.serialize(testModel)
end
def test_json_instantiator_builtin_in_subpackage
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMMSubpackage)
inst.instantiate(%q({ "_class": "Data", "notTheBuiltin": "for sure" }))
assert_equal "for sure", env.elements.first.notTheBuiltin
end
def test_json_instantiator_subpackage
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMMSubpackage)
inst.instantiate(%q({ "_class": "Data2", "data2": "something" }))
assert_equal "something", env.elements.first.data2
end
def test_json_instantiator_subpackage_no_shortname_opt
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMMSubpackage, :short_class_names => false)
assert_raises RuntimeError do
inst.instantiate(%q({ "_class": "Data2", "data2": "something" }))
end
end
def test_json_instantiator_references
env = RGen::Environment.new
inst = RGen::Instantiator::JsonInstantiator.new(env, TestMM, :nameAttribute => "text")
inst.instantiate(%q([
{ "_class": "TestNode", "text": "A", "childs": [
{ "_class": "TestNode", "text": "B" } ]},
{ "_class": "TestNode", "text": "C", "other": "/A/B"}]
))
nodeA = env.find(:class => TestMM::TestNode, :text => "A").first
nodeC = env.find(:class => TestMM::TestNode, :text => "C").first
assert_equal 1, nodeA.childs.size
assert_equal nodeA.childs[0], nodeC.other
end
end
rgen-0.10.2/test/metamodel_builder_test.rb 0000664 0000000 0000000 00000140277 14560401624 0020562 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/metamodel_builder'
require 'rgen/array_extensions'
require 'bigdecimal'
class MetamodelBuilderTest < Minitest::Test
module TestMetamodel
extend RGen::MetamodelBuilder::ModuleExtension
class SimpleClass < RGen::MetamodelBuilder::MMBase
KindType = RGen::MetamodelBuilder::DataTypes::Enum.new([:simple, :extended])
has_attr 'name' # default is String
has_attr 'stringWithDefault', String, :defaultValueLiteral => "xtest"
has_attr 'integerWithDefault', Integer, :defaultValueLiteral => "123"
has_attr 'longWithDefault', Long, :defaultValueLiteral => "1234567890"
has_attr 'floatWithDefault', Float, :defaultValueLiteral => "0.123"
has_attr 'boolWithDefault', Boolean, :defaultValueLiteral => "true"
has_attr 'anything', Object
has_attr 'allowed', RGen::MetamodelBuilder::DataTypes::Boolean
has_attr 'kind', KindType
has_attr 'kindWithDefault', KindType, :defaultValueLiteral => "extended"
end
class ManyAttrClass < RGen::MetamodelBuilder::MMBase
has_many_attr 'literals', String
has_many_attr 'bools', Boolean
has_many_attr 'integers', Integer
has_many_attr 'enums', RGen::MetamodelBuilder::DataTypes::Enum.new([:a, :b, :c])
has_many_attr 'limitTest', Integer, :upperBound => 2
end
class ClassA < RGen::MetamodelBuilder::MMBase
# metamodel accessors must work independent of the ==() method
module ClassModule
def ==(o)
o.is_a?(ClassA)
end
end
end
class ClassB < RGen::MetamodelBuilder::MMBase
end
class ClassC < RGen::MetamodelBuilder::MMBase
end
class HasOneTestClass < RGen::MetamodelBuilder::MMBase
has_one 'classA', ClassA
has_one 'classB', ClassB
end
class HasManyTestClass < RGen::MetamodelBuilder::MMBase
has_many 'classA', ClassA
end
class OneClass < RGen::MetamodelBuilder::MMBase
end
class ManyClass < RGen::MetamodelBuilder::MMBase
end
OneClass.one_to_many 'manyClasses', ManyClass, 'oneClass', :upperBound => 5
class AClassMM < RGen::MetamodelBuilder::MMBase
end
class BClassMM < RGen::MetamodelBuilder::MMBase
end
AClassMM.many_to_many 'bClasses', BClassMM, 'aClasses'
module SomePackage
extend RGen::MetamodelBuilder::ModuleExtension
class ClassA < RGen::MetamodelBuilder::MMBase
end
module SubPackage
extend RGen::MetamodelBuilder::ModuleExtension
class ClassB < RGen::MetamodelBuilder::MMBase
end
end
end
class OneClass2 < RGen::MetamodelBuilder::MMBase
end
class ManyClass2 < RGen::MetamodelBuilder::MMBase
end
ManyClass2.many_to_one 'oneClass', OneClass2, 'manyClasses'
class AClassOO < RGen::MetamodelBuilder::MMBase
end
class BClassOO < RGen::MetamodelBuilder::MMBase
end
AClassOO.one_to_one 'bClass', BClassOO, 'aClass'
class SomeSuperClass < RGen::MetamodelBuilder::MMBase
has_attr "name"
has_many "classAs", ClassA
end
class SomeSubClass < SomeSuperClass
has_attr "subname"
has_many "classBs", ClassB
end
class OtherSubClass < SomeSuperClass
has_attr "othersubname"
has_many "classCs", ClassC
end
class SubSubClass < RGen::MetamodelBuilder::MMMultiple(SomeSubClass, OtherSubClass)
has_attr "subsubname"
end
module AnnotatedModule
extend RGen::MetamodelBuilder::ModuleExtension
annotation "moduletag" => "modulevalue"
class AnnotatedClass < RGen::MetamodelBuilder::MMBase
annotation "sometag" => "somevalue", "othertag" => "othervalue"
annotation :source => "rgen/test", :details => {"thirdtag" => "thirdvalue"}
has_attr "boolAttr", Boolean do
annotation "attrtag" => "attrval"
annotation :source => "rgen/test2", :details => {"attrtag2" => "attrvalue2", "attrtag3" => "attrvalue3"}
end
has_many "others", AnnotatedClass do
annotation "reftag" => "refval"
annotation :source => "rgen/test3", :details => {"reftag2" => "refvalue2", "reftag3" => "refvalue3"}
end
many_to_many "m2m", AnnotatedClass, "m2mback" do
annotation "m2mtag" => "m2mval"
opposite_annotation "opposite_m2mtag" => "opposite_m2mval"
end
end
end
class AbstractClass < RGen::MetamodelBuilder::MMBase
abstract
end
class ContainedClass < RGen::MetamodelBuilder::MMBase
end
class ContainerClass < RGen::MetamodelBuilder::MMBase
contains_one_uni 'oneChildUni', ContainedClass
contains_one_uni 'oneChildUni2', ContainedClass
contains_one 'oneChild', ContainedClass, 'parentOne'
contains_one 'oneChild2', ContainedClass, 'parentOne2'
contains_many_uni 'manyChildUni', ContainedClass
contains_many_uni 'manyChildUni2', ContainedClass
contains_many 'manyChild', ContainedClass, 'parentMany'
contains_many 'manyChild2', ContainedClass, 'parentMany2'
end
class NestedContainerClass < ContainedClass
contains_one_uni 'oneChildUni', ContainedClass
end
class OppositeRefAssocA < RGen::MetamodelBuilder::MMBase
end
class OppositeRefAssocB < RGen::MetamodelBuilder::MMBase
end
OppositeRefAssocA.one_to_one 'bClass', OppositeRefAssocB, 'aClass'
class ReservedNameClass < RGen::MetamodelBuilder::MMBase
has_attr 'class', String
has_attr 'method', String
end
class ManyReservedNameClass < RGen::MetamodelBuilder::MMBase
has_many_attr 'class', String
has_many_attr 'method', String
end
end
def mm
TestMetamodel
end
def test_has_attr
sc = mm::SimpleClass.new
assert_respond_to sc, :name
assert_respond_to sc, :name=
sc.name = "TestName"
assert_equal "TestName", sc.name
sc.name = nil
assert_nil sc.name
err = assert_raises StandardError do
sc.name = 5
end
assert_match /In (\w+::)+SimpleClass : Can not use a (Integer|Fixnum) where a String is expected/, err.message
assert_equal "EString", mm::SimpleClass.ecore.eAttributes.find{|a| a.name=="name"}.eType.name
assert_equal "xtest", sc.stringWithDefault
assert_equal :extended, sc.kindWithDefault
assert_equal 123, sc.integerWithDefault
assert_equal 1234567890, sc.longWithDefault
assert_equal 0.123, sc.floatWithDefault
assert_equal true, sc.boolWithDefault
# setting nil should not make the default value appear on next read
sc.stringWithDefault = nil
assert_nil sc.stringWithDefault
sc.anything = :asymbol
assert_equal :asymbol, sc.anything
sc.anything = self # a class
assert_equal self, sc.anything
assert_respond_to sc, :allowed
assert_respond_to sc, :allowed=
sc.allowed = true
assert_equal true, sc.allowed
sc.allowed = false
assert_equal false, sc.allowed
sc.allowed = nil
assert_nil sc.allowed
err = assert_raises StandardError do
sc.allowed = :someSymbol
end
assert_match /In (\w+::)+SimpleClass : Can not use a Symbol(\(someSymbol\))?\(:someSymbol\) where a \[true,false\] is expected/, err.message
err = assert_raises StandardError do
sc.allowed = "a string"
end
assert_match /In (\w+::)+SimpleClass : Can not use a String where a \[true,false\] is expected/, err.message
assert_equal "EBoolean", mm::SimpleClass.ecore.eAttributes.find{|a| a.name=="allowed"}.eType.name
assert_respond_to sc, :kind
assert_respond_to sc, :kind=
sc.kind = :simple
assert_equal :simple, sc.kind
sc.kind = :extended
assert_equal :extended, sc.kind
sc.kind = nil
assert_nil sc.kind
err = assert_raises StandardError do
sc.kind = :false
end
assert_match /In (\w+::)+SimpleClass : Can not use a Symbol(\(false\))?\(:false\) where a \[:simple,:extended\] is expected/, err.message
err = assert_raises StandardError do
sc.kind = "a string"
end
assert_match /In (\w+::)+SimpleClass : Can not use a String where a \[:simple,:extended\] is expected/, err.message
enum = mm::SimpleClass.ecore.eAttributes.find{|a| a.name=="kind"}.eType
assert_equal ["extended", "simple"], enum.eLiterals.name.sort
end
def test_float
sc = mm::SimpleClass.new
sc.floatWithDefault = 7.89
assert_equal 7.89, sc.floatWithDefault
if BigDecimal.double_fig == 16
sc.floatWithDefault = 123456789012345678.0
# loss of precision
assert_equal "123456789012345680.0", sprintf("%.1f", sc.floatWithDefault)
end
sc.floatWithDefault = nil
sc.floatWithDefault = BigDecimal("123456789012345678.0")
assert sc.floatWithDefault.is_a?(BigDecimal)
assert_equal "123456789012345678.0", sc.floatWithDefault.to_s("F")
dump = Marshal.dump(sc)
sc2 = Marshal.load(dump)
assert sc2.floatWithDefault.is_a?(BigDecimal)
assert_equal "123456789012345678.0", sc2.floatWithDefault.to_s("F")
end
def test_long
sc = mm::SimpleClass.new
sc.longWithDefault = 5
assert_equal 5, sc.longWithDefault
sc.longWithDefault = (2**(0.size * 8 -2) -1) + 1
assert_equal (2**(0.size * 8 -2) -1) + 1, sc.longWithDefault
assert sc.longWithDefault.is_a?(Integer)
assert sc.longWithDefault.is_a?(Integer)
err = assert_raises StandardError do
sc.longWithDefault = "a string"
end
assert_match /In (\w+::)+SimpleClass : Can not use a String where a Integer is expected/, err.message
end
def test_many_attr
o = mm::ManyAttrClass.new
assert_respond_to o, :literals
assert_respond_to o, :addLiterals
assert_respond_to o, :removeLiterals
err = assert_raises(StandardError) do
o.addLiterals(1)
end
assert_match /In (\w+::)+ManyAttrClass : Can not use a (Integer|Fixnum) where a String is expected/, err.message
assert_equal [], o.literals
o.addLiterals("a")
assert_equal ["a"], o.literals
o.addLiterals("b")
assert_equal ["a", "b"], o.literals
o.addLiterals("b")
assert_equal ["a", "b", "b"], o.literals
# attributes allow the same object several times
o.addLiterals(o.literals.first)
assert_equal ["a", "b", "b", "a"], o.literals
assert o.literals[0].object_id == o.literals[3].object_id
# removing works by object identity, so providing a new string won't delete an existing one
o.removeLiterals("a")
assert_equal ["a", "b", "b", "a"], o.literals
theA = o.literals.first
# each remove command removes only one element: remove first "a"
o.removeLiterals(theA)
assert_equal ["b", "b", "a"], o.literals
# remove second "a" (same object)
o.removeLiterals(theA)
assert_equal ["b", "b"], o.literals
o.removeLiterals(o.literals.first)
assert_equal ["b"], o.literals
o.removeLiterals(o.literals.first)
assert_equal [], o.literals
# setting multiple elements at a time
o.literals = ["a", "b", "c"]
assert_equal ["a", "b", "c"], o.literals
# can only take enumerables
err = assert_raises(StandardError) do
o.literals = 1
end
assert_match /In (\w+::)+ManyAttrClass : Can not use a (Integer|Fixnum) where a Enumerable is expected/, err.message
o.bools = [true, false, true, false]
assert_equal [true, false, true, false], o.bools
o.integers = [1, 2, 2, 3, 3]
assert_equal [1, 2, 2, 3, 3], o.integers
o.enums = [:a, :a, :b, :c, :c]
assert_equal [:a, :a, :b, :c, :c], o.enums
lit = mm::ManyAttrClass.ecore.eAttributes.find{|a| a.name == "literals"}
assert lit.is_a?(RGen::ECore::EAttribute)
assert lit.many
lim = mm::ManyAttrClass.ecore.eAttributes.find{|a| a.name == "limitTest"}
assert lit.many
assert_equal 2, lim.upperBound
end
def test_many_attr_insert
o = mm::ManyAttrClass.new
o.addLiterals("a")
o.addLiterals("b", 0)
o.addLiterals("c", 1)
assert_equal ["b", "c", "a"], o.literals
end
def test_has_one
sc = mm::HasOneTestClass.new
assert_respond_to sc, :classA
assert_respond_to sc, :classA=
ca = mm::ClassA.new
sc.classA = ca
assert_equal ca, sc.classA
sc.classA = nil
assert_nil sc.classA
assert_respond_to sc, :classB
assert_respond_to sc, :classB=
cb = mm::ClassB.new
sc.classB = cb
assert_equal cb, sc.classB
err = assert_raises StandardError do
sc.classB = ca
end
assert_match /In (\w+::)+HasOneTestClass : Can not use a (\w+::)+ClassA where a (\w+::)+ClassB is expected/, err.message
assert_equal [], mm::ClassA.ecore.eReferences
assert_equal [], mm::ClassB.ecore.eReferences
assert_equal ["classA", "classB"].sort, mm::HasOneTestClass.ecore.eReferences.name.sort
assert_equal [], mm::HasOneTestClass.ecore.eReferences.select { |a| a.many == true }
assert_equal [], mm::HasOneTestClass.ecore.eAttributes
end
def test_has_many
o = mm::HasManyTestClass.new
ca1 = mm::ClassA.new
ca2 = mm::ClassA.new
ca3 = mm::ClassA.new
o.addClassA(ca1)
o.addClassA(ca2)
assert_equal [ca1, ca2], o.classA
# make sure we get a copy
o.classA.clear
assert_equal [ca1, ca2], o.classA
o.removeClassA(ca3)
assert_equal [ca1, ca2], o.classA
o.removeClassA(ca2)
assert_equal [ca1], o.classA
err = assert_raises StandardError do
o.addClassA(mm::ClassB.new)
end
assert_match /In (\w+::)+HasManyTestClass : Can not use a (\w+::)+ClassB where a (\w+::)+ClassA is expected/, err.message
assert_equal [], mm::HasManyTestClass.ecore.eReferences.select{|r| r.many == false}
assert_equal ["classA"], mm::HasManyTestClass.ecore.eReferences.select{|r| r.many == true}.name
end
def test_has_many_insert
o = mm::HasManyTestClass.new
ca1 = mm::ClassA.new
ca2 = mm::ClassA.new
ca3 = mm::ClassA.new
ca4 = mm::ClassA.new
ca5 = mm::ClassA.new
o.addClassA(ca1)
o.addClassA(ca2)
o.addClassA(ca3,0)
o.addClassA(ca4,1)
o.addGeneric("classA",ca5,2)
assert_equal [ca3, ca4, ca5, ca1, ca2], o.classA
end
def test_one_to_many
oc = mm::OneClass.new
assert_respond_to oc, :manyClasses
assert oc.manyClasses.empty?
mc = mm::ManyClass.new
assert_respond_to mc, :oneClass
assert_respond_to mc, :oneClass=
assert_nil mc.oneClass
# put the OneClass into the ManyClass
mc.oneClass = oc
assert_equal oc, mc.oneClass
assert oc.manyClasses.include?(mc)
# remove the OneClass from the ManyClass
mc.oneClass = nil
assert_nil mc.oneClass
assert !oc.manyClasses.include?(mc)
# put the ManyClass into the OneClass
oc.addManyClasses mc
assert oc.manyClasses.include?(mc)
assert_equal oc, mc.oneClass
# remove the ManyClass from the OneClass
oc.removeManyClasses mc
assert !oc.manyClasses.include?(mc)
assert_nil mc.oneClass
assert_equal [], mm::OneClass.ecore.eReferences.select{|r| r.many == false}
assert_equal ["manyClasses"], mm::OneClass.ecore.eReferences.select{|r| r.many == true}.name
assert_equal 5, mm::OneClass.ecore.eReferences.find{|r| r.many == true}.upperBound
assert_equal ["oneClass"], mm::ManyClass.ecore.eReferences.select{|r| r.many == false}.name
assert_equal [], mm::ManyClass.ecore.eReferences.select{|r| r.many == true}
end
def test_one_to_many_replace1
oc1 = mm::OneClass.new
oc2 = mm::OneClass.new
mc = mm::ManyClass.new
oc1.manyClasses = [mc]
assert_equal [mc], oc1.manyClasses
assert_equal [], oc2.manyClasses
assert_equal oc1, mc.oneClass
oc2.manyClasses = [mc]
assert_equal [mc], oc2.manyClasses
assert_equal [], oc1.manyClasses
assert_equal oc2, mc.oneClass
end
def test_one_to_many_replace2
oc = mm::OneClass.new
mc1 = mm::ManyClass.new
mc2 = mm::ManyClass.new
mc1.oneClass = oc
assert_equal [mc1], oc.manyClasses
assert_equal oc, mc1.oneClass
assert_nil mc2.oneClass
mc2.oneClass = oc
assert_equal [mc1, mc2], oc.manyClasses
assert_equal oc, mc1.oneClass
assert_equal oc, mc2.oneClass
end
def test_one_to_many_insert
oc = mm::OneClass.new
mc1 = mm::ManyClass.new
mc2 = mm::ManyClass.new
oc.addManyClasses(mc1, 0)
oc.addManyClasses(mc2, 0)
assert_equal [mc2, mc1], oc.manyClasses
assert_equal oc, mc1.oneClass
assert_equal oc, mc2.oneClass
end
def test_one_to_many2
oc = mm::OneClass2.new
assert_respond_to oc, :manyClasses
assert oc.manyClasses.empty?
mc = mm::ManyClass2.new
assert_respond_to mc, :oneClass
assert_respond_to mc, :oneClass=
assert_nil mc.oneClass
# put the OneClass into the ManyClass
mc.oneClass = oc
assert_equal oc, mc.oneClass
assert oc.manyClasses.include?(mc)
# remove the OneClass from the ManyClass
mc.oneClass = nil
assert_nil mc.oneClass
assert !oc.manyClasses.include?(mc)
# put the ManyClass into the OneClass
oc.addManyClasses mc
assert oc.manyClasses.include?(mc)
assert_equal oc, mc.oneClass
# remove the ManyClass from the OneClass
oc.removeManyClasses mc
assert !oc.manyClasses.include?(mc)
assert_nil mc.oneClass
assert_equal [], mm::OneClass2.ecore.eReferences.select{|r| r.many == false}
assert_equal ["manyClasses"], mm::OneClass2.ecore.eReferences.select{|r| r.many == true}.name
assert_equal ["oneClass"], mm::ManyClass2.ecore.eReferences.select{|r| r.many == false}.name
assert_equal [], mm::ManyClass2.ecore.eReferences.select{|r| r.many == true}
end
def test_one_to_one
ac = mm::AClassOO.new
assert_respond_to ac, :bClass
assert_respond_to ac, :bClass=
assert_nil ac.bClass
bc = mm::BClassOO.new
assert_respond_to bc, :aClass
assert_respond_to bc, :aClass=
assert_nil bc.aClass
# put the AClass into the BClass
bc.aClass = ac
assert_equal ac, bc.aClass
assert_equal bc, ac.bClass
# remove the AClass from the BClass
bc.aClass = nil
assert_nil bc.aClass
assert_nil ac.bClass
# put the BClass into the AClass
ac.bClass = bc
assert_equal bc, ac.bClass
assert_equal ac, bc.aClass
# remove the BClass from the AClass
ac.bClass = nil
assert_nil ac.bClass
assert_nil bc.aClass
assert_equal ["bClass"], mm::AClassOO.ecore.eReferences.select{|r| r.many == false}.name
assert_equal [], mm::AClassOO.ecore.eReferences.select{|r| r.many == true}
assert_equal ["aClass"], mm::BClassOO.ecore.eReferences.select{|r| r.many == false}.name
assert_equal [], mm::BClassOO.ecore.eReferences.select{|r| r.many == true}
end
def test_one_to_one_replace
a = mm::AClassOO.new
b1 = mm::BClassOO.new
b2 = mm::BClassOO.new
a.bClass = b1
assert_equal b1, a.bClass
assert_equal a, b1.aClass
assert_nil b2.aClass
a.bClass = b2
assert_equal b2, a.bClass
assert_nil b1.aClass
assert_equal a, b2.aClass
end
def test_many_to_many
ac = mm::AClassMM.new
assert_respond_to ac, :bClasses
assert ac.bClasses.empty?
bc = mm::BClassMM.new
assert_respond_to bc, :aClasses
assert bc.aClasses.empty?
# put the AClass into the BClass
bc.addAClasses ac
assert bc.aClasses.include?(ac)
assert ac.bClasses.include?(bc)
# put something else into the BClass
err = assert_raises StandardError do
bc.addAClasses :notaaclass
end
assert_match /In (\w+::)+BClassMM : Can not use a Symbol(\(notaaclass\))?\(:notaaclass\) where a (\w+::)+AClassMM is expected/, err.message
# remove the AClass from the BClass
bc.removeAClasses ac
assert !bc.aClasses.include?(ac)
assert !ac.bClasses.include?(bc)
# put the BClass into the AClass
ac.addBClasses bc
assert ac.bClasses.include?(bc)
assert bc.aClasses.include?(ac)
# put something else into the AClass
err = assert_raises StandardError do
ac.addBClasses :notabclass
end
assert_match /In (\w+::)+AClassMM : Can not use a Symbol(\(notabclass\))?\(:notabclass\) where a (\w+::)+BClassMM is expected/, err.message
# remove the BClass from the AClass
ac.removeBClasses bc
assert !ac.bClasses.include?(bc)
assert !bc.aClasses.include?(ac)
assert_equal [], mm::AClassMM.ecore.eReferences.select{|r| r.many == false}
assert_equal ["bClasses"], mm::AClassMM.ecore.eReferences.select{|r| r.many == true}.name
assert_equal [], mm::BClassMM.ecore.eReferences.select{|r| r.many == false}
assert_equal ["aClasses"], mm::BClassMM.ecore.eReferences.select{|r| r.many == true}.name
end
def test_many_to_many_insert
ac1 = mm::AClassMM.new
ac2 = mm::AClassMM.new
bc1= mm::BClassMM.new
bc2= mm::BClassMM.new
ac1.addBClasses(bc1)
ac1.addBClasses(bc2, 0)
ac2.addBClasses(bc1)
ac2.addBClasses(bc2, 0)
assert_equal [bc2, bc1], ac1.bClasses
assert_equal [bc2, bc1], ac2.bClasses
assert_equal [ac1, ac2], bc1.aClasses
assert_equal [ac1, ac2], bc2.aClasses
end
def test_inheritance
assert_equal ["name"], mm::SomeSuperClass.ecore.eAllAttributes.name
assert_equal ["classAs"], mm::SomeSuperClass.ecore.eAllReferences.name
assert_equal ["name", "subname"], mm::SomeSubClass.ecore.eAllAttributes.name.sort
assert_equal ["classAs", "classBs"], mm::SomeSubClass.ecore.eAllReferences.name.sort
assert_equal ["name", "othersubname"], mm::OtherSubClass.ecore.eAllAttributes.name.sort
assert_equal ["classAs", "classCs"], mm::OtherSubClass.ecore.eAllReferences.name.sort
assert mm::SomeSubClass.new.is_a?(mm::SomeSuperClass)
assert_equal ["name", "othersubname", "subname", "subsubname"], mm::SubSubClass.ecore.eAllAttributes.name.sort
assert_equal ["classAs", "classBs", "classCs"], mm::SubSubClass.ecore.eAllReferences.name.sort
assert mm::SubSubClass.new.is_a?(mm::SomeSuperClass)
assert mm::SubSubClass.new.is_a?(mm::SomeSubClass)
assert mm::SubSubClass.new.is_a?(mm::OtherSubClass)
end
def test_annotations
assert_equal 1, mm::AnnotatedModule.ecore.eAnnotations.size
anno = mm::AnnotatedModule.ecore.eAnnotations.first
checkAnnotation(anno, nil, {"moduletag" => "modulevalue"})
eClass = mm::AnnotatedModule::AnnotatedClass.ecore
assert_equal 2, eClass.eAnnotations.size
anno = eClass.eAnnotations.find{|a| a.source == "rgen/test"}
checkAnnotation(anno, "rgen/test", {"thirdtag" => "thirdvalue"})
anno = eClass.eAnnotations.find{|a| a.source == nil}
checkAnnotation(anno, nil, {"sometag" => "somevalue", "othertag" => "othervalue"})
eAttr = eClass.eAttributes.first
assert_equal 2, eAttr.eAnnotations.size
anno = eAttr.eAnnotations.find{|a| a.source == "rgen/test2"}
checkAnnotation(anno, "rgen/test2", {"attrtag2" => "attrvalue2", "attrtag3" => "attrvalue3"})
anno = eAttr.eAnnotations.find{|a| a.source == nil}
checkAnnotation(anno, nil, {"attrtag" => "attrval"})
eRef = eClass.eReferences.find{|r| !r.eOpposite}
assert_equal 2, eRef.eAnnotations.size
anno = eRef.eAnnotations.find{|a| a.source == "rgen/test3"}
checkAnnotation(anno, "rgen/test3", {"reftag2" => "refvalue2", "reftag3" => "refvalue3"})
anno = eRef.eAnnotations.find{|a| a.source == nil}
checkAnnotation(anno, nil, {"reftag" => "refval"})
eRef = eClass.eReferences.find{|r| r.eOpposite}
assert_equal 1, eRef.eAnnotations.size
anno = eRef.eAnnotations.first
checkAnnotation(anno, nil, {"m2mtag" => "m2mval"})
eRef = eRef.eOpposite
assert_equal 1, eRef.eAnnotations.size
anno = eRef.eAnnotations.first
checkAnnotation(anno, nil, {"opposite_m2mtag" => "opposite_m2mval"})
end
def checkAnnotation(anno, source, hash)
assert anno.is_a?(RGen::ECore::EAnnotation)
assert_equal source, anno.source unless source.nil?
assert_nil anno.source if source.nil?
assert_equal hash.size, anno.details.size
hash.each_pair do |k, v|
detail = anno.details.find{|d| d.key == k}
assert detail.is_a?(RGen::ECore::EStringToStringMapEntry)
assert_equal v, detail.value
end
end
def test_ecore_identity
subPackage = mm::SomePackage::SubPackage.ecore
assert_equal subPackage.eClassifiers.first.object_id, mm::SomePackage::SubPackage::ClassB.ecore.object_id
somePackage = mm::SomePackage.ecore
assert_equal somePackage.eSubpackages.first.object_id, subPackage.object_id
end
def test_proxy
p = RGen::MetamodelBuilder::MMProxy.new("test")
assert_equal "test", p.targetIdentifier
p.targetIdentifier = 123
assert_equal 123, p.targetIdentifier
p.data = "additional info"
assert_equal "additional info", p.data
q = RGen::MetamodelBuilder::MMProxy.new("ident", "data")
assert_equal "data", q.data
end
def test_proxies_has_one
e = mm::HasOneTestClass.new
proxy = RGen::MetamodelBuilder::MMProxy.new
e.classA = proxy
assert_equal proxy, e.classA
a = mm::ClassA.new
# displace proxy
e.classA = a
assert_equal a, e.classA
# displace by proxy
e.classA = proxy
assert_equal proxy, e.classA
end
def test_proxies_has_many
e = mm::HasManyTestClass.new
proxy = RGen::MetamodelBuilder::MMProxy.new
e.addClassA(proxy)
assert_equal [proxy], e.classA
# again
e.addClassA(proxy)
assert_equal [proxy], e.classA
proxy2 = RGen::MetamodelBuilder::MMProxy.new
e.addClassA(proxy2)
assert_equal [proxy, proxy2], e.classA
e.removeClassA(proxy)
assert_equal [proxy2], e.classA
# again
e.removeClassA(proxy)
assert_equal [proxy2], e.classA
e.removeClassA(proxy2)
assert_equal [], e.classA
end
def test_proxies_one_to_one
ea = mm::AClassOO.new
eb = mm::BClassOO.new
proxy1 = RGen::MetamodelBuilder::MMProxy.new
proxy2 = RGen::MetamodelBuilder::MMProxy.new
ea.bClass = proxy1
eb.aClass = proxy2
assert_equal proxy1, ea.bClass
assert_equal proxy2, eb.aClass
# displace proxies
ea.bClass = eb
assert_equal eb, ea.bClass
assert_equal ea, eb.aClass
# displace by proxy
ea.bClass = proxy1
assert_equal proxy1, ea.bClass
assert_nil eb.aClass
end
def test_proxies_one_to_many
eo = mm::OneClass.new
em = mm::ManyClass.new
proxy1 = RGen::MetamodelBuilder::MMProxy.new
proxy2 = RGen::MetamodelBuilder::MMProxy.new
eo.addManyClasses(proxy1)
assert_equal [proxy1], eo.manyClasses
em.oneClass = proxy2
assert_equal proxy2, em.oneClass
# displace proxies at many side
# adding em will set em.oneClass to eo and displace the proxy from em.oneClass
eo.addManyClasses(em)
assert_equal [proxy1, em], eo.manyClasses
assert_equal eo, em.oneClass
eo.removeManyClasses(proxy1)
assert_equal [em], eo.manyClasses
assert_equal eo, em.oneClass
# displace by proxy
em.oneClass = proxy2
assert_equal [], eo.manyClasses
assert_equal proxy2, em.oneClass
# displace proxies at one side
em.oneClass = eo
assert_equal [em], eo.manyClasses
assert_equal eo, em.oneClass
end
def test_proxies_many_to_many
e1 = mm::AClassMM.new
e2 = mm::BClassMM.new
proxy1 = RGen::MetamodelBuilder::MMProxy.new
proxy2 = RGen::MetamodelBuilder::MMProxy.new
e1.addBClasses(proxy1)
e2.addAClasses(proxy2)
assert_equal [proxy1], e1.bClasses
assert_equal [proxy2], e2.aClasses
e1.addBClasses(e2)
assert_equal [proxy1, e2], e1.bClasses
assert_equal [proxy2, e1], e2.aClasses
e1.removeBClasses(proxy1)
e2.removeAClasses(proxy2)
assert_equal [e2], e1.bClasses
assert_equal [e1], e2.aClasses
end
# Multiplicity agnostic convenience methods
def test_genericAccess
e1 = mm::OneClass.new
e2 = mm::ManyClass.new
e3 = mm::OneClass.new
e4 = mm::ManyClass.new
# use on "many" feature
e1.setOrAddGeneric("manyClasses", e2)
assert_equal [e2], e1.manyClasses
assert_equal [e2], e1.getGeneric("manyClasses")
assert_equal [e2], e1.getGenericAsArray("manyClasses")
# use on "one" feature
e2.setOrAddGeneric("oneClass", e3)
assert_equal e3, e2.oneClass
assert_equal e3, e2.getGeneric("oneClass")
assert_equal [e3], e2.getGenericAsArray("oneClass")
assert_nil e4.getGeneric("oneClass")
assert_equal [], e4.getGenericAsArray("oneClass")
end
def test_setNilOrRemoveGeneric
e1 = mm::OneClass.new
e2 = mm::ManyClass.new
e3 = mm::OneClass.new
# use on "many" feature
e1.addManyClasses(e2)
assert_equal [e2], e1.manyClasses
e1.setNilOrRemoveGeneric("manyClasses", e2)
assert_equal [], e1.manyClasses
# use on "one" feature
e2.oneClass = e3
assert_equal e3, e2.oneClass
e2.setNilOrRemoveGeneric("oneClass", e3)
assert_nil e2.oneClass
end
def test_setNilOrRemoveAllGeneric
e1 = mm::OneClass.new
e2 = mm::ManyClass.new
e3 = mm::OneClass.new
e4 = mm::ManyClass.new
# use on "many" feature
e1.addManyClasses(e2)
e1.addManyClasses(e4)
assert_equal [e2, e4], e1.manyClasses
e1.setNilOrRemoveAllGeneric("manyClasses")
assert_equal [], e1.manyClasses
# use on "one" feature
e2.oneClass = e3
assert_equal e3, e2.oneClass
e2.setNilOrRemoveAllGeneric("oneClass")
assert_nil e2.oneClass
end
def test_abstract
err = assert_raises StandardError do
mm::AbstractClass.new
end
assert_match /Class (\w+::)+AbstractClass is abstract/, err.message
end
module BadDefaultValueLiteralContainer
Test1 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
has_attr 'integerWithDefault', Integer, :defaultValueLiteral => "1.1"
end
end
Test2 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
has_attr 'integerWithDefault', Integer, :defaultValueLiteral => "x"
end
end
Test3 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
has_attr 'boolWithDefault', Boolean, :defaultValueLiteral => "1"
end
end
Test4 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
has_attr 'floatWithDefault', Float, :defaultValueLiteral => "1"
end
end
Test5 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
has_attr 'floatWithDefault', Float, :defaultValueLiteral => "true"
end
end
Test6 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
kindType = RGen::MetamodelBuilder::DataTypes::Enum.new([:simple, :extended])
has_attr 'enumWithDefault', kindType, :defaultValueLiteral => "xxx"
end
end
Test7 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
kindType = RGen::MetamodelBuilder::DataTypes::Enum.new([:simple, :extended])
has_attr 'enumWithDefault', kindType, :defaultValueLiteral => "7"
end
end
Test8 = proc do
class BadClass < RGen::MetamodelBuilder::MMBase
has_attr 'longWithDefault', Integer, :defaultValueLiteral => "1.1"
end
end
end
def test_bad_default_value_literal
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test1.call
end
assert_equal "Property integerWithDefault can not take value 1.1, expected an Integer", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test2.call
end
assert_equal "Property integerWithDefault can not take value x, expected an Integer", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test3.call
end
assert_equal "Property boolWithDefault can not take value 1, expected true or false", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test4.call
end
assert_equal "Property floatWithDefault can not take value 1, expected a Float", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test5.call
end
assert_equal "Property floatWithDefault can not take value true, expected a Float", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test6.call
end
assert_equal "Property enumWithDefault can not take value xxx, expected one of :simple, :extended", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test7.call
end
assert_equal "Property enumWithDefault can not take value 7, expected one of :simple, :extended", err.message
err = assert_raises StandardError do
BadDefaultValueLiteralContainer::Test8.call
end
assert_equal "Property longWithDefault can not take value 1.1, expected an Integer", err.message
end
def test_isset_set_to_nil
e = mm::SimpleClass.new
assert_respond_to e, :name
assert !e.eIsSet(:name)
assert !e.eIsSet("name")
e.name = nil
assert e.eIsSet(:name)
end
def test_isset_set_to_default
e = mm::SimpleClass.new
assert !e.eIsSet(:stringWithDefault)
# set the default value
e.name = "xtest"
assert e.eIsSet(:name)
end
def test_isset_many_add
e = mm::ManyAttrClass.new
assert_equal [], e.literals
assert !e.eIsSet(:literals)
e.addLiterals("x")
assert e.eIsSet(:literals)
end
def test_isset_many_remove
e = mm::ManyAttrClass.new
assert_equal [], e.literals
assert !e.eIsSet(:literals)
# removing a value which is not there
e.removeLiterals("x")
assert e.eIsSet(:literals)
end
def test_isset_ref
ac = mm::AClassOO.new
bc = mm::BClassOO.new
assert !bc.eIsSet(:aClass)
assert !ac.eIsSet(:bClass)
bc.aClass = ac
assert bc.eIsSet(:aClass)
assert ac.eIsSet(:bClass)
end
def test_isset_ref_many
ac = mm::AClassMM.new
bc = mm::BClassMM.new
assert !bc.eIsSet(:aClasses)
assert !ac.eIsSet(:bClasses)
bc.aClasses = [ac]
assert bc.eIsSet(:aClasses)
assert ac.eIsSet(:bClasses)
end
def test_unset_nil
e = mm::SimpleClass.new
e.name = nil
assert e.eIsSet(:name)
e.eUnset(:name)
assert !e.eIsSet(:name)
end
def test_unset_string
e = mm::SimpleClass.new
e.name = "someone"
assert e.eIsSet(:name)
e.eUnset(:name)
assert !e.eIsSet(:name)
end
def test_unset_ref
ac = mm::AClassOO.new
bc = mm::BClassOO.new
bc.aClass = ac
assert bc.eIsSet(:aClass)
assert ac.eIsSet(:bClass)
assert_equal bc, ac.bClass
bc.eUnset(:aClass)
assert_nil bc.aClass
assert_nil ac.bClass
assert !bc.eIsSet(:aClass)
# opposite ref is nil but still "set"
assert ac.eIsSet(:bClass)
end
def test_unset_ref_many
ac = mm::AClassMM.new
bc = mm::BClassMM.new
bc.aClasses = [ac]
assert bc.eIsSet(:aClasses)
assert ac.eIsSet(:bClasses)
assert_equal [bc], ac.bClasses
bc.eUnset(:aClasses)
assert_equal [], bc.aClasses
assert_equal [], ac.bClasses
assert !bc.eIsSet(:aClasses)
# opposite ref is empty but still "set"
assert ac.eIsSet(:bClasses)
end
def test_unset_marshal
e = mm::SimpleClass.new
e.name = "someone"
e.eUnset(:name)
e2 = Marshal.load(Marshal.dump(e))
assert e.object_id != e2.object_id
assert !e2.eIsSet(:name)
end
def test_conainer_one_uni
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
assert_equal [], a.eContents
assert_equal [], a.eAllContents
assert_nil b.eContainer
assert_nil b.eContainingFeature
a.oneChildUni = b
assert_equal a, b.eContainer
assert_equal :oneChildUni, b.eContainingFeature
assert_equal [b], a.eContents
assert_equal [b], a.eAllContents
a.oneChildUni = c
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal a, c.eContainer
assert_equal :oneChildUni, c.eContainingFeature
assert_equal [c], a.eContents
assert_equal [c], a.eAllContents
a.oneChildUni = nil
assert_nil c.eContainer
assert_nil c.eContainingFeature
assert_equal [], a.eContents
assert_equal [], a.eAllContents
end
def test_container_many_uni
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
assert_equal [], a.eContents
assert_equal [], a.eAllContents
a.addManyChildUni(b)
assert_equal a, b.eContainer
assert_equal :manyChildUni, b.eContainingFeature
assert_equal [b], a.eContents
assert_equal [b], a.eAllContents
a.addManyChildUni(c)
assert_equal a, c.eContainer
assert_equal :manyChildUni, c.eContainingFeature
assert_equal [b, c], a.eContents
assert_equal [b, c], a.eAllContents
a.removeManyChildUni(b)
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal a, c.eContainer
assert_equal :manyChildUni, c.eContainingFeature
assert_equal [c], a.eContents
assert_equal [c], a.eAllContents
a.removeManyChildUni(c)
assert_nil c.eContainer
assert_nil c.eContainingFeature
assert_equal [], a.eContents
assert_equal [], a.eAllContents
end
def test_conainer_one_bi
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainerClass.new
d = mm::ContainedClass.new
a.oneChild = b
assert_equal a, b.eContainer
assert_equal :oneChild, b.eContainingFeature
assert_equal [b], a.eContents
assert_equal [b], a.eAllContents
c.oneChild = d
assert_equal c, d.eContainer
assert_equal :oneChild, d.eContainingFeature
assert_equal [d], c.eContents
assert_equal [d], c.eAllContents
a.oneChild = d
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal a, d.eContainer
assert_equal :oneChild, d.eContainingFeature
assert_equal [d], a.eContents
assert_equal [d], a.eAllContents
assert_equal [], c.eContents
assert_equal [], c.eAllContents
end
def test_conainer_one_bi_rev
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainerClass.new
d = mm::ContainedClass.new
a.oneChild = b
assert_equal a, b.eContainer
assert_equal :oneChild, b.eContainingFeature
assert_equal [b], a.eContents
assert_equal [b], a.eAllContents
c.oneChild = d
assert_equal c, d.eContainer
assert_equal :oneChild, d.eContainingFeature
assert_equal [d], c.eContents
assert_equal [d], c.eAllContents
d.parentOne = a
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal a, d.eContainer
assert_equal :oneChild, d.eContainingFeature
assert_equal [d], a.eContents
assert_equal [d], a.eAllContents
assert_equal [], c.eContents
assert_equal [], c.eAllContents
end
def test_conainer_one_bi_nil
a = mm::ContainerClass.new
b = mm::ContainedClass.new
a.oneChild = b
assert_equal a, b.eContainer
assert_equal :oneChild, b.eContainingFeature
assert_equal [b], a.eContents
assert_equal [b], a.eAllContents
a.oneChild = nil
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal [], a.eContents
assert_equal [], a.eAllContents
end
def test_conainer_one_bi_nil_rev
a = mm::ContainerClass.new
b = mm::ContainedClass.new
a.oneChild = b
assert_equal a, b.eContainer
assert_equal :oneChild, b.eContainingFeature
assert_equal [b], a.eContents
assert_equal [b], a.eAllContents
b.parentOne = nil
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal [], a.eContents
assert_equal [], a.eAllContents
end
def test_container_many_bi
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
a.addManyChild(b)
a.addManyChild(c)
assert_equal a, b.eContainer
assert_equal :manyChild, b.eContainingFeature
assert_equal a, c.eContainer
assert_equal :manyChild, c.eContainingFeature
assert_equal [b, c], a.eContents
assert_equal [b, c], a.eAllContents
a.removeManyChild(b)
assert_nil b.eContainer
assert_nil b.eContainingFeature
assert_equal [c], a.eContents
assert_equal [c], a.eAllContents
end
def test_conainer_many_bi_steal
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
d = mm::ContainerClass.new
a.addManyChild(b)
a.addManyChild(c)
assert_equal a, b.eContainer
assert_equal :manyChild, b.eContainingFeature
assert_equal a, c.eContainer
assert_equal :manyChild, c.eContainingFeature
assert_equal [b, c], a.eContents
assert_equal [b, c], a.eAllContents
d.addManyChild(b)
assert_equal d, b.eContainer
assert_equal :manyChild, b.eContainingFeature
assert_equal [c], a.eContents
assert_equal [c], a.eAllContents
assert_equal [b], d.eContents
assert_equal [b], d.eAllContents
end
def test_conainer_many_bi_steal_rev
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
d = mm::ContainerClass.new
a.addManyChild(b)
a.addManyChild(c)
assert_equal a, b.eContainer
assert_equal :manyChild, b.eContainingFeature
assert_equal a, c.eContainer
assert_equal :manyChild, c.eContainingFeature
assert_equal [b, c], a.eContents
assert_equal [b, c], a.eAllContents
b.parentMany = d
assert_equal d, b.eContainer
assert_equal :manyChild, b.eContainingFeature
assert_equal [c], a.eContents
assert_equal [c], a.eAllContents
assert_equal [b], d.eContents
assert_equal [b], d.eAllContents
end
def test_all_contents
a = mm::ContainerClass.new
b = mm::NestedContainerClass.new
c = mm::ContainedClass.new
a.oneChildUni = b
b.oneChildUni = c
assert_equal [b, c], a.eAllContents
end
def test_all_contents_with_block
a = mm::ContainerClass.new
b = mm::NestedContainerClass.new
c = mm::ContainedClass.new
a.oneChildUni = b
b.oneChildUni = c
yielded = []
a.eAllContents do |e|
yielded << e
end
assert_equal [b, c], yielded
end
def test_all_contents_prune
a = mm::ContainerClass.new
b = mm::NestedContainerClass.new
c = mm::ContainedClass.new
a.oneChildUni = b
b.oneChildUni = c
yielded = []
a.eAllContents do |e|
yielded << e
:prune
end
assert_equal [b], yielded
end
def test_container_generic
a = mm::ContainerClass.new
a.oneChild = RGen::MetamodelBuilder::MMGeneric.new
end
def test_opposite_assoc_on_first_write
ac = mm::OppositeRefAssocA.new
bc = mm::OppositeRefAssocB.new
# no access to 'aClass' or 'bClass' methods before
# test if on-demand metamodel building creates opposite ref association on first write
bc.aClass = ac
assert_equal ac, bc.aClass
assert_equal bc, ac.bClass
end
def test_clear_by_array_assignment
oc1 = mm::OneClass.new
mc1 = mm::ManyClass.new
mc2 = mm::ManyClass.new
mc3 = mm::ManyClass.new
oc1.manyClasses = [mc1, mc2]
assert_equal [mc1, mc2], oc1.manyClasses
oc1.manyClasses = []
assert_equal [], oc1.manyClasses
end
def test_clear_by_array_assignment_uni
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
a.manyChildUni = [b, c]
assert_equal [b, c], a.manyChildUni
a.manyChildUni = []
assert_equal [], a.manyChildUni
end
def test_disconnectContainer_one_uni
a = mm::ContainerClass.new
b = mm::ContainedClass.new
a.oneChildUni = b
b.disconnectContainer
assert_nil a.oneChildUni
end
def test_disconnectContainer_one
a = mm::ContainerClass.new
b = mm::ContainedClass.new
a.oneChild = b
b.disconnectContainer
assert_nil a.oneChild
assert_nil b.parentOne
end
def test_disconnectContainer_many_uni
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
a.addManyChildUni(b)
a.addManyChildUni(c)
b.disconnectContainer
assert_equal [c], a.manyChildUni
end
def test_disconnectContainer_many
a = mm::ContainerClass.new
b = mm::ContainedClass.new
c = mm::ContainedClass.new
a.addManyChild(b)
a.addManyChild(c)
b.disconnectContainer
assert_nil b.parentMany
assert_equal [c], a.manyChild
end
# Duplicate Containment Tests
#
# Testing that no element is contained in two different containers at a time.
# This must also work for uni-directional containments as well as
# for containments via different roles.
# here the bi-dir reference disconnects from the previous container
def test_duplicate_containment_bidir_samerole_one
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.oneChild = b
a2.oneChild = b
assert_nil a1.oneChild
end
# here the bi-dir reference disconnects from the previous container
def test_duplicate_containment_bidir_samerole_many
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.addManyChild(b)
a2.addManyChild(b)
assert_equal [], a1.manyChild
end
def test_duplicate_containment_unidir_samerole_one
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.oneChildUni = b
a2.oneChildUni = b
assert_nil a1.oneChildUni
end
def test_duplicate_containment_unidir_samerole_many
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.addManyChildUni(b)
a2.addManyChildUni(b)
assert_equal [], a1.manyChildUni
end
def test_duplicate_containment_bidir_otherrole_one
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.oneChild = b
a2.oneChild2 = b
assert_nil a1.oneChild
end
def test_duplicate_containment_bidir_otherrole_many
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.addManyChild(b)
a2.addManyChild2(b)
assert_equal [], a1.manyChild
end
def test_duplicate_containment_unidir_otherrole_one
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.oneChildUni = b
a2.oneChildUni2 = b
assert_nil a1.oneChildUni
end
def test_duplicate_containment_unidir_otherrole_many
a1 = mm::ContainerClass.new
a2 = mm::ContainerClass.new
b = mm::ContainedClass.new
a1.addManyChildUni(b)
a2.addManyChildUni2(b)
assert_equal [], a1.manyChildUni
end
def test_reserved_names
e = mm::ReservedNameClass.new
%w(class method).each do |reserved|
e.setGeneric(reserved, "X")
assert_equal "X", e.getGeneric(reserved.to_sym)
assert_equal "X", e.getGeneric(reserved)
assert_equal ["X"], e.getGenericAsArray(reserved.to_sym)
assert_equal ["X"], e.getGenericAsArray(reserved)
end
assert_equal "X", e.getClass
assert_equal "X", e.getMethod
end
def test_reserved_names_many
e = mm::ManyReservedNameClass.new
%w(class method).each do |reserved|
e.addGeneric(reserved, "X")
e.addGeneric(reserved, "Y")
assert_equal ["X", "Y"], e.getGeneric(reserved.to_sym)
assert_equal ["X", "Y"], e.getGeneric(reserved)
assert_equal ["X", "Y"], e.getGenericAsArray(reserved.to_sym)
assert_equal ["X", "Y"], e.getGenericAsArray(reserved)
end
assert_equal ["X", "Y"], e.getClass
assert_equal ["X", "Y"], e.getMethod
end
end
rgen-0.10.2/test/metamodel_from_ecore_test.rb 0000664 0000000 0000000 00000004235 14560401624 0021245 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","test")
require 'metamodel_builder_test'
require 'rgen/ecore/ecore_to_ruby'
# this test suite runs all the tests of MetamodelBuilderTest with the TestMetamodel
# replaced by the result of feeding its ecore model through ECoreToRuby
#
class MetamodelFromEcoreTest < MetamodelBuilderTest
# clone the ecore model, because it will be modified below
test_ecore = Marshal.load(Marshal.dump(TestMetamodel.ecore))
# some EEnum types are not hooked into the EPackage because they do not
# appear with a constant assignment in TestMetamodel
# fix this by explicitly assigning the ePackage
# also fix the name of anonymous enums
test_ecore.eClassifiers.find{|c| c.name == "SimpleClass"}.
eAttributes.select{|a| a.name == "kind" || a.name == "kindWithDefault"}.each{|a|
a.eType.name = "KindType"
a.eType.ePackage = test_ecore}
test_ecore.eClassifiers.find{|c| c.name == "ManyAttrClass"}.
eAttributes.select{|a| a.name == "enums"}.each{|a|
a.eType.name = "ABCEnum"
a.eType.ePackage = test_ecore}
MetamodelFromEcore = RGen::ECore::ECoreToRuby.new.create_module(test_ecore)
def mm
MetamodelFromEcore
end
# alternative implementation for dynamic variant
def test_bad_default_value_literal
package = RGen::ECore::EPackage.new(:name => "Package1", :eClassifiers => [
RGen::ECore::EClass.new(:name => "Class1", :eStructuralFeatures => [
RGen::ECore::EAttribute.new(:name => "value", :eType => RGen::ECore::EInt, :defaultValueLiteral => "x")])])
mod = RGen::ECore::ECoreToRuby.new.create_module(package)
obj = mod::Class1.new
# the error is raised only when the feature is lazily constructed
assert_raises StandardError do
obj.value
end
end
# define all the test methods explicitly in the subclass
# otherwise minitest is smart enough to run the tests only in the superclass context
MetamodelBuilderTest.instance_methods.select{|m| m.to_s =~ /^test_/}.each do |m|
next if instance_methods(false).include?(m)
module_eval <<-END
def #{m}
super
end
END
end
end
rgen-0.10.2/test/metamodel_order_test.rb 0000664 0000000 0000000 00000013035 14560401624 0020236 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/ecore/ecore'
require 'rgen/array_extensions'
class MetamodelOrderTest < Minitest::Test
include RGen::ECore
module TestMM1
extend RGen::MetamodelBuilder::ModuleExtension
class Class11 < RGen::MetamodelBuilder::MMBase
end
module Module11
extend RGen::MetamodelBuilder::ModuleExtension
DataType111 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType111" ,:literals => {:b => 1})
DataType112 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType112", :literals => {:b => 1})
class Class111 < RGen::MetamodelBuilder::MMBase
end
# anonymous classes won't be handled by the order helper, but will be in eClassifiers
Class112 = Class.new(RGen::MetamodelBuilder::MMBase)
# classes that are not MMBase won't be handled
class Class113
end
# modules that are not extended by the ModuleExtension are not handled
module Module111
end
# however it can be extendend later on
module Module112
# this one is not handled by the order helper since Module112 doesn't have the ModuleExtension yet
# however, it will be in eClassifiers
class Class1121 < RGen::MetamodelBuilder::MMBase
end
end
# this datatype must be in Module11 not Module112
DataType113 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType113", :literals => {:b => 1})
Module112.extend(RGen::MetamodelBuilder::ModuleExtension)
# this datatype must be in Module11 not Module112
DataType114 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType114", :literals => {:b => 1})
module Module112
# this one is handled because now Module112 is extended
class Class1122 < RGen::MetamodelBuilder::MMBase
end
end
DataType115 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType115", :literals => {:b => 1})
DataType116 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType116", :literals => {:b => 1})
end
DataType11 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType11", :literals => {:a => 1})
class Class12 < RGen::MetamodelBuilder::MMBase
end
class Class13 < RGen::MetamodelBuilder::MMBase
end
end
# datatypes outside of a module won't be handled
DataType1 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType1", :literals => {:b => 1})
# classes outside of a module won't be handled
class Class1 < RGen::MetamodelBuilder::MMBase
end
module TestMM2
extend RGen::MetamodelBuilder::ModuleExtension
TestMM1::Module11.extend(RGen::MetamodelBuilder::ModuleExtension)
# this is a critical case: because of the previous extension of Module11 which is in a different
# hierarchy, DataType21 is looked for in Module11 and its parents; finally it is not
# found and the definition is ignored for order calculation
DataType21 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType21", :literals => {:b => 1})
module Module21
extend RGen::MetamodelBuilder::ModuleExtension
end
module Module22
extend RGen::MetamodelBuilder::ModuleExtension
end
module Module23
extend RGen::MetamodelBuilder::ModuleExtension
end
# if there is no other class or module after the last datatype, it won't show up in _constantOrder
# however, the order of eClassifiers can still be reconstructed
# note that this can not be tested if the test is executed as part of the whole testsuite
# since there will be classes and modules created within other test files
DataType22 = RGen::MetamodelBuilder::DataTypes::Enum.new(:name => "DataType22", :literals => {:b => 1})
end
def test_constant_order
assert_equal ["Class11", "Module11", "DataType11", "Class12", "Class13"], TestMM1._constantOrder
assert_equal ["DataType111", "DataType112", "Class111", "DataType113", "Module112", "DataType114", "DataType115", "DataType116"], TestMM1::Module11._constantOrder
assert_equal ["Class1122"], TestMM1::Module11::Module112._constantOrder
if File.basename($0) == "metamodel_order_test.rb"
# this won't work if run in the whole test suite (see comment at DataType22)
assert_equal ["Module21", "Module22", "Module23"], TestMM2._constantOrder
end
end
def test_classifier_order
# eClassifiers also contains the ones which where ignored in order calculation, these are expected at the end
# (in an arbitrary order)
assert_equal ["Class11", "DataType11", "Class12", "Class13"], TestMM1.ecore.eClassifiers.name
assert_equal ["DataType111", "DataType112", "Class111", "DataType113", "DataType114", "DataType115", "DataType116", "Class112"], TestMM1::Module11.ecore.eClassifiers.name
assert_equal ["Class1122", "Class1121"], TestMM1::Module11::Module112.ecore.eClassifiers.name
# no classifiers in TestMM2._constantOrder, so the datatypes can appear in arbitrary order
assert_equal ["DataType21","DataType22"], TestMM2.ecore.eClassifiers.name.sort
end
def test_subpackage_order
assert_equal ["Module11"], TestMM1.ecore.eSubpackages.name
assert_equal ["Module112"], TestMM1::Module11.ecore.eSubpackages.name
assert_equal [], TestMM1::Module11::Module112.ecore.eSubpackages.name
assert_equal ["Module21", "Module22", "Module23"], TestMM2.ecore.eSubpackages.name
end
end
rgen-0.10.2/test/metamodel_roundtrip_test.rb 0000664 0000000 0000000 00000006707 14560401624 0021161 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/array_extensions'
require 'rgen/util/model_comparator'
require 'mmgen/metamodel_generator'
require 'rgen/instantiator/ecore_xml_instantiator'
require 'rgen/serializer/xmi20_serializer'
class MetamodelRoundtripTest < Minitest::Test
TEST_DIR = File.dirname(__FILE__)+"/metamodel_roundtrip_test"
include MMGen::MetamodelGenerator
include RGen::Util::ModelComparator
module Regenerated
Inside = binding
end
def test_generator
require TEST_DIR+"/TestModel.rb"
outfile = TEST_DIR+"/TestModel_Regenerated.rb"
generateMetamodel(HouseMetamodel.ecore, outfile)
File.open(outfile) do |f|
eval(f.read, Regenerated::Inside)
end
assert modelEqual?(HouseMetamodel.ecore, Regenerated::HouseMetamodel.ecore, ["instanceClassName"])
end
module UMLRegenerated
Inside = binding
end
def test_generate_from_ecore
outfile = TEST_DIR+"/houseMetamodel_from_ecore.rb"
env = RGen::Environment.new
File.open(TEST_DIR+"/houseMetamodel.ecore") { |f|
ECoreXMLInstantiator.new(env).instantiate(f.read)
}
rootpackage = env.find(:class => RGen::ECore::EPackage).first
rootpackage.name = "HouseMetamodel"
generateMetamodel(rootpackage, outfile)
File.open(outfile) do |f|
eval(f.read, UMLRegenerated::Inside, "test_eval", 0)
end
end
def test_ecore_serializer
require TEST_DIR+"/TestModel.rb"
File.open(TEST_DIR+"/houseMetamodel_Regenerated.ecore","w") do |f|
ser = RGen::Serializer::XMI20Serializer.new(f)
ser.serialize(HouseMetamodel.ecore)
end
end
BuiltinTypesTestEcore = TEST_DIR+"/using_builtin_types.ecore"
def test_ecore_serializer_builtin_types
mm = RGen::ECore::EPackage.new(:name => "P1", :eClassifiers => [
RGen::ECore::EClass.new(:name => "C1", :eStructuralFeatures => [
RGen::ECore::EAttribute.new(:name => "a1", :eType => RGen::ECore::EString),
RGen::ECore::EAttribute.new(:name => "a2", :eType => RGen::ECore::EInt),
RGen::ECore::EAttribute.new(:name => "a3", :eType => RGen::ECore::ELong),
RGen::ECore::EAttribute.new(:name => "a4", :eType => RGen::ECore::EFloat),
RGen::ECore::EAttribute.new(:name => "a5", :eType => RGen::ECore::EBoolean)
])
])
outfile = TEST_DIR+"/using_builtin_types_serialized.ecore"
File.open(outfile, "w") do |f|
ser = RGen::Serializer::XMI20Serializer.new(f)
ser.serialize(mm)
end
assert_equal(File.read(BuiltinTypesTestEcore), File.read(outfile))
end
def test_ecore_instantiator_builtin_types
env = RGen::Environment.new
File.open(BuiltinTypesTestEcore) { |f|
ECoreXMLInstantiator.new(env).instantiate(f.read)
}
a1 = env.find(:class => RGen::ECore::EAttribute, :name => "a1").first
assert_equal(RGen::ECore::EString, a1.eType)
a2 = env.find(:class => RGen::ECore::EAttribute, :name => "a2").first
assert_equal(RGen::ECore::EInt, a2.eType)
a3 = env.find(:class => RGen::ECore::EAttribute, :name => "a3").first
assert_equal(RGen::ECore::ELong, a3.eType)
a4 = env.find(:class => RGen::ECore::EAttribute, :name => "a4").first
assert_equal(RGen::ECore::EFloat, a4.eType)
a5 = env.find(:class => RGen::ECore::EAttribute, :name => "a5").first
assert_equal(RGen::ECore::EBoolean, a5.eType)
end
end
rgen-0.10.2/test/metamodel_roundtrip_test/ 0000775 0000000 0000000 00000000000 14560401624 0020622 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/metamodel_roundtrip_test/.gitignore 0000664 0000000 0000000 00000000050 14560401624 0022605 0 ustar 00root root 0000000 0000000 using_builtin_types_serialized.ecore
rgen-0.10.2/test/metamodel_roundtrip_test/TestModel.rb 0000664 0000000 0000000 00000012157 14560401624 0023055 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder'
module HouseMetamodel
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
SexEnum = Enum.new(:name => "SexEnum", :literals => [ :male, :female ])
# TODO: Datatypes
# AggregationKind = Enum.new([ :none, :aggregate, :composite ])
class MeetingPlace < RGen::MetamodelBuilder::MMBase
annotation :source => "testmodel", :details => { 'complexity' => '1', 'date_created' => '2006-07-12 08:40:46', 'date_modified' => '2006-07-12 08:44:02', 'ea_ntype' => '0', 'ea_stype' => 'Class', 'gentype' => 'Java', 'isSpecification' => 'false', 'package' => 'EAPK_A1B83D59_CAE1_422c_BA5F_D3624D7156AD', 'package_name' => 'HouseMetamodel', 'phase' => '1.0', 'status' => 'Proposed', 'style' => 'BackColor=-1;BorderColor=-1;BorderWidth=-1;FontColor=-1;VSwimLanes=0;HSwimLanes=0;BorderStyle=0;', 'tagged' => '0', 'version' => '1.0' }
end
class Person < RGen::MetamodelBuilder::MMBase
annotation 'complexity' => '1', 'date_created' => '2006-06-27 08:34:23', 'date_modified' => '2006-06-27 08:34:26', 'ea_ntype' => '0', 'ea_stype' => 'Class', 'gentype' => 'Java', 'isSpecification' => 'false', 'package' => 'EAPK_A1B83D59_CAE1_422c_BA5F_D3624D7156AD', 'package_name' => 'HouseMetamodel', 'phase' => '1.0', 'status' => 'Proposed', 'style' => 'BackColor=-1;BorderColor=-1;BorderWidth=-1;FontColor=-1;VSwimLanes=0;HSwimLanes=0;BorderStyle=0;', 'tagged' => '0', 'version' => '1.0'
has_attr 'sex', SexEnum
has_attr 'id', Long
has_many_attr 'nicknames', String
end
class House < RGen::MetamodelBuilder::MMBase
annotation 'complexity' => '1', 'date_created' => '2005-09-16 19:52:18', 'date_modified' => '2006-02-28 08:29:19', 'ea_ntype' => '0', 'ea_stype' => 'Class', 'gentype' => 'Java', 'isSpecification' => 'false', 'package' => 'EAPK_A1B83D59_CAE1_422c_BA5F_D3624D7156AD', 'package_name' => 'HouseMetamodel', 'phase' => '1.0', 'status' => 'Proposed', 'stereotype' => 'dummy', 'style' => 'BackColor=-1;BorderColor=-1;BorderWidth=-1;FontColor=-1;VSwimLanes=0;HSwimLanes=0;BorderStyle=0;', 'tagged' => '0', 'version' => '1.0'
has_attr 'size', Integer
has_attr 'module'
has_attr 'address', String, :changeable => false do
annotation 'collection' => 'false', 'containment' => 'Not Specified', 'derived' => '0', 'duplicates' => '0', 'ea_guid' => '{A8DF581B-9AC6-4f75-AB48-8FAEDFC6E068}', 'lowerBound' => '1', 'ordered' => '0', 'position' => '0', 'styleex' => 'volatile=0;', 'type' => 'String', 'upperBound' => '1'
end
end
module Rooms
extend RGen::MetamodelBuilder::ModuleExtension
class Room < RGen::MetamodelBuilder::MMBase
abstract
annotation 'complexity' => '1', 'date_created' => '2005-09-16 19:52:28', 'date_modified' => '2006-06-22 21:15:25', 'ea_ntype' => '0', 'ea_stype' => 'Class', 'gentype' => 'Java', 'isSpecification' => 'false', 'package' => 'EAPK_F9D8C6E3_4DAD_4aa2_AD47_D0ABA4E93E08', 'package_name' => 'Rooms', 'phase' => '1.0', 'status' => 'Proposed', 'style' => 'BackColor=-1;BorderColor=-1;BorderWidth=-1;FontColor=-1;VSwimLanes=0;HSwimLanes=0;BorderStyle=0;', 'tagged' => '0', 'version' => '1.0'
end
class Bathroom < Room
annotation 'complexity' => '1', 'date_created' => '2006-06-27 08:32:25', 'date_modified' => '2006-06-27 08:34:23', 'ea_ntype' => '0', 'ea_stype' => 'Class', 'gentype' => 'Java', 'isSpecification' => 'false', 'package' => 'EAPK_F9D8C6E3_4DAD_4aa2_AD47_D0ABA4E93E08', 'package_name' => 'Rooms', 'phase' => '1.0', 'status' => 'Proposed', 'style' => 'BackColor=-1;BorderColor=-1;BorderWidth=-1;FontColor=-1;VSwimLanes=0;HSwimLanes=0;BorderStyle=0;', 'tagged' => '0', 'version' => '1.0'
end
class Kitchen < RGen::MetamodelBuilder::MMMultiple(HouseMetamodel::MeetingPlace, Room)
annotation 'complexity' => '1', 'date_created' => '2005-11-30 19:26:13', 'date_modified' => '2006-06-22 21:15:34', 'ea_ntype' => '0', 'ea_stype' => 'Class', 'gentype' => 'Java', 'isSpecification' => 'false', 'package' => 'EAPK_F9D8C6E3_4DAD_4aa2_AD47_D0ABA4E93E08', 'package_name' => 'Rooms', 'phase' => '1.0', 'status' => 'Proposed', 'style' => 'BackColor=-1;BorderColor=-1;BorderWidth=-1;FontColor=-1;VSwimLanes=0;HSwimLanes=0;BorderStyle=0;', 'tagged' => '0', 'version' => '1.0'
end
end
module DependingOnRooms
extend RGen::MetamodelBuilder::ModuleExtension
class RoomSub < Rooms::Room
end
end
end
HouseMetamodel::Person.has_many 'home', HouseMetamodel::House do
annotation 'containment' => 'Unspecified'
end
HouseMetamodel::House.has_one 'bathroom', HouseMetamodel::Rooms::Bathroom, :lowerBound => 1, :transient => true
HouseMetamodel::House.one_to_one 'kitchen', HouseMetamodel::Rooms::Kitchen, 'house', :lowerBound => 1, :opposite_lowerBound => 1 do
annotation 'containment' => 'Unspecified'
opposite_annotation 'containment' => 'Unspecified'
end
HouseMetamodel::House.contains_many 'room', HouseMetamodel::Rooms::Room, 'house', :lowerBound => 1 do
# only an opposite annotation
opposite_annotation 'containment' => 'Unspecified'
end
rgen-0.10.2/test/metamodel_roundtrip_test/houseMetamodel.ecore 0000664 0000000 0000000 00000004564 14560401624 0024625 0 ustar 00root root 0000000 0000000
rgen-0.10.2/test/metamodel_roundtrip_test/houseMetamodel_from_ecore.rb 0000664 0000000 0000000 00000002514 14560401624 0026324 0 ustar 00root root 0000000 0000000 require 'rgen/metamodel_builder'
module HouseMetamodel
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
SexEnum = Enum.new(:name => 'SexEnum', :literals =>[ :male, :female ])
class House < RGen::MetamodelBuilder::MMBase
annotation :source => "bla", :details => {'a' => 'b'}
has_attr 'address', String, :changeable => false
end
class MeetingPlace < RGen::MetamodelBuilder::MMBase
end
class Person < RGen::MetamodelBuilder::MMBase
has_attr 'sex', HouseMetamodel::SexEnum
has_attr 'id', Long
has_many_attr 'nicknames', String
end
module Rooms
extend RGen::MetamodelBuilder::ModuleExtension
include RGen::MetamodelBuilder::DataTypes
class Room < RGen::MetamodelBuilder::MMBase
end
class Bathroom < Room
end
class Kitchen < RGen::MetamodelBuilder::MMMultiple(Room, HouseMetamodel::MeetingPlace)
end
end
end
HouseMetamodel::House.has_one 'bathroom', HouseMetamodel::Rooms::Bathroom, :lowerBound => 1
HouseMetamodel::House.one_to_one 'kitchen', HouseMetamodel::Rooms::Kitchen, 'house', :lowerBound => 1
HouseMetamodel::House.contains_many 'room', HouseMetamodel::Rooms::Room, 'house'
HouseMetamodel::Person.has_many 'house', HouseMetamodel::House
rgen-0.10.2/test/metamodel_roundtrip_test/using_builtin_types.ecore 0000664 0000000 0000000 00000003362 14560401624 0025744 0 ustar 00root root 0000000 0000000
rgen-0.10.2/test/method_delegation_test.rb 0000664 0000000 0000000 00000013164 14560401624 0020552 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__) + "/../lib"
require 'minitest/autorun'
require 'rgen/util/method_delegation'
class MethodDelegationTest < Minitest::Test
include RGen
class TestDelegate
attr_accessor :mode, :callcount
def common_delegated(delegator)
@callcount ||= 0
@callcount += 1
case @mode
when :continue
throw :continue
when :delegatorId
delegator.object_id
when :return7
7
end
end
alias to_s_delegated common_delegated
alias methodInSingleton_delegated common_delegated
alias class_delegated common_delegated
alias artificialMethod_delegated common_delegated
end
class ConstPathElement < Module
def self.const_missing_delegated(delegator, const)
ConstPathElement.new(const)
end
def initialize(name, parent=nil)
@name = name.to_s
@parent = parent
end
def const_missing(const)
ConstPathElement.new(const, self)
end
def to_s
if @parent
@parent.to_s+"::"+@name
else
@name
end
end
end
# missing: check with multiple params and block param
def test_method_defined_in_singleton
# delegator is an Array
delegator = []
# delegating method is a method defined in the singleton class
class << delegator
def methodInSingleton
"result from method in singleton"
end
end
checkDelegation(delegator, "methodInSingleton", "result from method in singleton")
end
def test_method_defined_in_class
# delegator is a String
delegator = "Delegator1"
checkDelegation(delegator, "to_s", "Delegator1")
end
def test_method_defined_in_superclass
# delegator is an instance of a new anonymous class
delegator = Class.new.new
# delegating method is +object_id+ which is defined in the superclass
checkDelegation(delegator, "class", delegator.class)
end
def test_new_method
# delegator is an String
delegator = "Delegator2"
# delegating method is a new method which does not exist on String
checkDelegation(delegator, "artificialMethod", delegator.object_id, true)
end
def test_const_missing
surroundingModule = Module.nesting.first
Util::MethodDelegation.registerDelegate(ConstPathElement, surroundingModule, "const_missing")
assert_equal "SomeArbitraryConst", SomeArbitraryConst.to_s
assert_equal "AnotherConst::A::B::C", AnotherConst::A::B::C.to_s
Util::MethodDelegation.unregisterDelegate(ConstPathElement, surroundingModule, "const_missing")
assert_raises NameError do
SomeArbitraryConst
end
end
def checkDelegation(delegator, method, originalResult, newMethod=false)
delegate1 = TestDelegate.new
delegate2 = TestDelegate.new
Util::MethodDelegation.registerDelegate(delegate1, delegator, method)
Util::MethodDelegation.registerDelegate(delegate2, delegator, method)
assert delegator.respond_to?(:_methodDelegates)
if newMethod
assert !delegator.respond_to?("#{method}_delegate_original".to_sym)
else
assert delegator.respond_to?("#{method}_delegate_original".to_sym)
end
# check delegator parameter
delegate1.mode = :delegatorId
assert_equal delegator.object_id, delegator.send(method)
delegate1.callcount = 0
delegate2.callcount = 0
delegate1.mode = :return7
# delegate1 returns a value
assert_equal 7, delegator.send(method)
assert_equal 1, delegate1.callcount
# delegate2 is not called
assert_equal 0, delegate2.callcount
delegate1.mode = :nothing
# delegate1 just exits and thus returns nil
assert_nil delegator.send(method)
assert_equal 2, delegate1.callcount
# delegate2 is not called
assert_equal 0, delegate2.callcount
delegate1.mode = :continue
delegate2.mode = :return7
# delegate1 is called but continues
# delegate2 returns a value
assert_equal 7, delegator.send(method)
assert_equal 3, delegate1.callcount
assert_equal 1, delegate2.callcount
delegate1.mode = :continue
delegate2.mode = :continue
# both delegates continue, the original method returns its value
checkCallOriginal(delegator, method, originalResult, newMethod)
# both delegates are called though
assert_equal 4, delegate1.callcount
assert_equal 2, delegate2.callcount
# calling unregister with a non existing method has no effect
Util::MethodDelegation.unregisterDelegate(delegate1, delegator, "xxx")
Util::MethodDelegation.unregisterDelegate(delegate1, delegator, method)
checkCallOriginal(delegator, method, originalResult, newMethod)
# delegate1 not called any more
assert_equal 4, delegate1.callcount
# delegate2 is still called
assert_equal 3, delegate2.callcount
Util::MethodDelegation.unregisterDelegate(delegate2, delegator, method)
checkCallOriginal(delegator, method, originalResult, newMethod)
# both delegates not called any more
assert_equal 4, delegate1.callcount
assert_equal 3, delegate2.callcount
# after all delegates were unregistered, singleton class should be clean
assert !delegator.respond_to?(:_methodDelegates)
end
def checkCallOriginal(delegator, method, originalResult, newMethod)
if newMethod
assert_raises NoMethodError do
result = delegator.send(method)
end
else
result = delegator.send(method)
assert_equal originalResult, result
end
end
end
rgen-0.10.2/test/model_builder/ 0000775 0000000 0000000 00000000000 14560401624 0016314 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/model_builder/builder_context_test.rb 0000664 0000000 0000000 00000003630 14560401624 0023074 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__)+"/../lib"
require 'minitest/autorun'
require 'rgen/ecore/ecore'
require 'rgen/model_builder/builder_context'
class BuilderContextTest < Minitest::Test
module BuilderExtension1
module PackageA
def inPackAExt
3
end
module PackageB
def inPackBExt
5
end
end
end
end
class BuilderContext
def inBuilderContext
7
end
end
def test_extensionContainerFactory
aboveRoot = RGen::ECore::EPackage.new(:name => "AboveRoot")
root = RGen::ECore::EPackage.new(:name => "Root", :eSuperPackage => aboveRoot)
packageA = RGen::ECore::EPackage.new(:name => "PackageA", :eSuperPackage => root)
packageB = RGen::ECore::EPackage.new(:name => "PackageB", :eSuperPackage => packageA)
packageC = RGen::ECore::EPackage.new(:name => "PackageBC", :eSuperPackage => packageA)
factory = RGen::ModelBuilder::BuilderContext::ExtensionContainerFactory.new(root, BuilderExtension1, BuilderContext.new)
assert_equal BuilderExtension1::PackageA, factory.moduleForPackage(packageA)
packAExt = factory.extensionContainer(packageA)
assert packAExt.respond_to?(:inPackAExt)
assert !packAExt.respond_to?(:inPackBExt)
assert_equal 3, packAExt.inPackAExt
assert_equal 7, packAExt.inBuilderContext
assert_equal BuilderExtension1::PackageA::PackageB, factory.moduleForPackage(packageB)
packBExt = factory.extensionContainer(packageB)
assert !packBExt.respond_to?(:inPackAExt)
assert packBExt.respond_to?(:inPackBExt)
assert_equal 5, packBExt.inPackBExt
assert_equal 7, packBExt.inBuilderContext
assert_raises RuntimeError do
# aboveRoot is not contained within root
assert_nil factory.moduleForPackage(aboveRoot)
end
assert_nil factory.moduleForPackage(packageC)
end
end rgen-0.10.2/test/model_builder/builder_test.rb 0000664 0000000 0000000 00000020472 14560401624 0021333 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__) + "/../lib"
require 'minitest/autorun'
require 'rgen/ecore/ecore'
require 'rgen/ecore/ecore_builder_methods'
require 'rgen/environment'
require 'rgen/model_builder'
require 'model_builder/statemachine_metamodel'
class ModelBuilderTest < Minitest::Test
def test_statemachine
result = RGen::ModelBuilder.build(StatemachineMetamodel) do
statemachine "Airconditioner" do
state "Off", :kind => :START
compositeState "On" do
state "Heating" do
transition :as => :outgoingTransition, :targetState => "Cooling",
:statemachine => "Airconditioner"
end
state "Cooling" do
end
end
transition :sourceState => "On.Cooling", :targetState => "On.Heating" do
_using Condition::TimeCondition do
timeCondition :as => :condition, :timeout => 100
end
Condition::TimeCondition.timeCondition :as => :condition, :timeout => 10
end
end
_using Condition do
statemachine "AirconExtension" do
s = state "StartState"
transition :sourceState => s, :targetState => "Airconditioner.Off"
end
end
end
assert result.is_a?(Array)
assert_equal 2, result.size
sm1 = result[0]
assert sm1.is_a?(StatemachineMetamodel::Statemachine)
assert_equal "Airconditioner", sm1.name
assert_equal 2, sm1.state.size
offState = sm1.state[0]
assert offState.is_a?(StatemachineMetamodel::State)
assert_equal "Off", offState.name
assert_equal :START, offState.kind
onState = sm1.state[1]
assert onState.is_a?(StatemachineMetamodel::CompositeState)
assert_equal "On", onState.name
assert_equal 2, onState.state.size
hState = onState.state[0]
assert hState.is_a?(StatemachineMetamodel::State)
assert_equal "Heating", hState.name
cState = onState.state[1]
assert cState.is_a?(StatemachineMetamodel::State)
assert_equal "Cooling", cState.name
assert_equal 1, hState.outgoingTransition.size
hOutTrans = hState.outgoingTransition[0]
assert hOutTrans.is_a?(StatemachineMetamodel::Transition)
assert_equal cState, hOutTrans.targetState
assert_equal sm1, hOutTrans.statemachine
assert_equal 1, hState.incomingTransition.size
hInTrans = hState.incomingTransition[0]
assert hInTrans.is_a?(StatemachineMetamodel::Transition)
assert_equal cState, hInTrans.sourceState
assert_equal sm1, hInTrans.statemachine
assert_equal 2, hInTrans.condition.size
assert hInTrans.condition[0].is_a?(StatemachineMetamodel::Condition::TimeCondition::TimeCondition)
assert_equal 100, hInTrans.condition[0].timeout
assert hInTrans.condition[1].is_a?(StatemachineMetamodel::Condition::TimeCondition::TimeCondition)
assert_equal 10, hInTrans.condition[1].timeout
sm2 = result[1]
assert sm2.is_a?(StatemachineMetamodel::Statemachine)
assert_equal "AirconExtension", sm2.name
assert_equal 1, sm2.state.size
sState = sm2.state[0]
assert sState.is_a?(StatemachineMetamodel::State)
assert_equal "StartState", sState.name
assert_equal 1, sState.outgoingTransition.size
assert sState.outgoingTransition[0].is_a?(StatemachineMetamodel::Transition)
assert_equal offState, sState.outgoingTransition[0].targetState
assert_equal sm2, sState.outgoingTransition[0].statemachine
end
def test_dynamic
numStates = 5
env = RGen::Environment.new
result = RGen::ModelBuilder.build(StatemachineMetamodel, env) do
sm = statemachine "SM#{numStates}" do
(1..numStates).each do |i|
state "State#{i}" do
transition :as => :outgoingTransition, :targetState => "State#{i < numStates ? i+1 : 1}",
:statemachine => sm
end
end
end
end
assert_equal 11, env.elements.size
assert_equal "SM5", result[0].name
state = result[0].state.first
assert_equal "State1", state.name
state = state.outgoingTransition.first.targetState
assert_equal "State2", state.name
state = state.outgoingTransition.first.targetState
assert_equal "State3", state.name
state = state.outgoingTransition.first.targetState
assert_equal "State4", state.name
state = state.outgoingTransition.first.targetState
assert_equal "State5", state.name
assert_equal result[0].state[0], state.outgoingTransition.first.targetState
end
def test_multiref
result = RGen::ModelBuilder.build(StatemachineMetamodel) do
a = transition
transition "b"
transition "c"
state :outgoingTransition => [a, "b", "c"]
end
assert result[0].is_a?(StatemachineMetamodel::Transition)
assert result[1].is_a?(StatemachineMetamodel::Transition)
assert !result[1].respond_to?(:name)
assert result[2].is_a?(StatemachineMetamodel::Transition)
assert !result[2].respond_to?(:name)
state = result[3]
assert state.is_a?(StatemachineMetamodel::State)
assert_equal result[0], state.outgoingTransition[0]
assert_equal result[1], state.outgoingTransition[1]
assert_equal result[2], state.outgoingTransition[2]
end
module TestMetamodel
extend RGen::MetamodelBuilder::ModuleExtension
# these classes have no name
class TestA < RGen::MetamodelBuilder::MMBase
end
class TestB < RGen::MetamodelBuilder::MMBase
end
class TestC < RGen::MetamodelBuilder::MMBase
end
TestA.contains_many 'testB', TestB, 'testA'
TestC.has_one 'testB', TestB
end
def test_helper_names
result = RGen::ModelBuilder.build(TestMetamodel) do
testA "_a" do
testB "_b"
end
testC :testB => "_a._b"
end
assert result[0].is_a?(TestMetamodel::TestA)
assert result[1].is_a?(TestMetamodel::TestC)
assert_equal result[0].testB[0], result[1].testB
end
def test_ecore
result = RGen::ModelBuilder.build(RGen::ECore, nil, RGen::ECore::ECoreBuilderMethods) do
ePackage "TestPackage1" do
eClass "TestClass1" do
eAttribute "attr1", :eType => RGen::ECore::EString
eAttr "attr2", RGen::ECore::EInt
eBiRef "biRef1", "TestClass2", "testClass1"
contains_1toN 'testClass2', "TestClass2", "tc1Parent"
end
eClass "TestClass2" do
eRef "ref1", "TestClass1"
end
end
end
assert result.is_a?(Array)
assert_equal 1, result.size
p1 = result.first
assert p1.is_a?(RGen::ECore::EPackage)
assert_equal "TestPackage1", p1.name
# TestClass1
class1 = p1.eClassifiers.find{|c| c.name == "TestClass1"}
assert class1 != nil
assert class1.is_a?(RGen::ECore::EClass)
# TestClass1.attr1
attr1 = class1.eAllAttributes.find{|a| a.name == "attr1"}
assert attr1 != nil
assert_equal RGen::ECore::EString, attr1.eType
# TestClass1.attr2
attr2 = class1.eAllAttributes.find{|a| a.name == "attr2"}
assert attr2 != nil
assert_equal RGen::ECore::EInt, attr2.eType
# TestClass2
class2 = p1.eClassifiers.find{|c| c.name == "TestClass2"}
assert class2 != nil
assert class2.is_a?(RGen::ECore::EClass)
# TestClass2.ref1
ref1 = class2.eAllReferences.find{|a| a.name == "ref1"}
assert ref1 != nil
assert_equal class1, ref1.eType
# TestClass1.biRef1
biRef1 = class1.eAllReferences.find{|r| r.name == "biRef1"}
assert biRef1 != nil
assert_equal class2, biRef1.eType
biRef1Opp = class2.eAllReferences.find {|r| r.name == "testClass1"}
assert biRef1Opp != nil
assert_equal class1, biRef1Opp.eType
assert_equal biRef1Opp, biRef1.eOpposite
assert_equal biRef1, biRef1Opp.eOpposite
# TestClass1.testClass2
tc2Ref = class1.eAllReferences.find{|r| r.name == "testClass2"}
assert tc2Ref != nil
assert_equal class2, tc2Ref.eType
assert tc2Ref.containment
assert_equal -1, tc2Ref.upperBound
tc2RefOpp = class2.eAllReferences.find{|r| r.name == "tc1Parent"}
assert tc2RefOpp != nil
assert_equal class1, tc2RefOpp.eType
assert !tc2RefOpp.containment
assert_equal 1, tc2RefOpp.upperBound
end
end rgen-0.10.2/test/model_builder/ecore_original.rb 0000664 0000000 0000000 00000030643 14560401624 0021630 0 ustar 00root root 0000000 0000000 ePackage "ecore", :nsPrefix => "ecore", :nsURI => "http://www.eclipse.org/emf/2002/Ecore" do
eClass "EAttribute", :eSuperTypes => ["EStructuralFeature"] do
eAttribute "iD"
eReference "eAttributeType", :changeable => false, :derived => true, :transient => true, :volatile => true, :lowerBound => 1, :eType => "EDataType"
end
eClass "EAnnotation", :eSuperTypes => ["EModelElement"] do
eAttribute "source"
eReference "details", :containment => true, :resolveProxies => false, :upperBound => -1, :eType => "EStringToStringMapEntry"
eReference "eModelElement", :resolveProxies => false, :eOpposite => "EModelElement.eAnnotations", :transient => true, :eType => "EModelElement"
eReference "contents", :containment => true, :resolveProxies => false, :upperBound => -1, :eType => "EObject"
eReference "references", :upperBound => -1, :eType => "EObject"
end
eClass "EClass", :eSuperTypes => ["EClassifier"] do
eAttribute "abstract"
eAttribute "interface"
eReference "eSuperTypes", :unsettable => true, :upperBound => -1, :eType => "EClass"
eReference "eOperations", :containment => true, :resolveProxies => false, :eOpposite => "EOperation.eContainingClass", :upperBound => -1, :eType => "EOperation"
eReference "eAllAttributes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EAttribute"
eReference "eAllReferences", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EReference"
eReference "eReferences", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EReference"
eReference "eAttributes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EAttribute"
eReference "eAllContainments", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EReference"
eReference "eAllOperations", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EOperation"
eReference "eAllStructuralFeatures", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EStructuralFeature"
eReference "eAllSuperTypes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EClass"
eReference "eIDAttribute", :resolveProxies => false, :changeable => false, :derived => true, :transient => true, :volatile => true, :eType => "EAttribute"
eReference "eStructuralFeatures", :containment => true, :resolveProxies => false, :eOpposite => "EStructuralFeature.eContainingClass", :upperBound => -1, :eType => "EStructuralFeature"
eReference "eGenericSuperTypes", :containment => true, :resolveProxies => false, :unsettable => true, :upperBound => -1, :eType => "EGenericType"
eReference "eAllGenericSuperTypes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1, :eType => "EGenericType"
end
eClass "EClassifier", :abstract => true, :eSuperTypes => ["ENamedElement"], :eSubTypes => ["EClass", "EDataType"] do
eAttribute "instanceClassName", :unsettable => true, :volatile => true
eAttribute "instanceClass", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "defaultValue", :changeable => false, :derived => true, :transient => true, :volatile => true, :eType => "EJavaObject"
eAttribute "instanceTypeName", :unsettable => true, :volatile => true
eReference "ePackage", :eOpposite => "EPackage.eClassifiers", :changeable => false, :transient => true, :eType => "EPackage"
eReference "eTypeParameters", :containment => true, :upperBound => -1, :eType => "ETypeParameter"
end
eClass "EDataType", :eSuperTypes => ["EClassifier"], :eSubTypes => ["EEnum"] do
eAttribute "serializable", :defaultValueLiteral => "true"
end
eClass "EEnum", :eSuperTypes => ["EDataType"] do
eReference "eLiterals", :containment => true, :resolveProxies => false, :eOpposite => "EEnumLiteral.eEnum", :upperBound => -1, :eType => "EEnumLiteral"
end
eClass "EEnumLiteral", :eSuperTypes => ["ENamedElement"] do
eAttribute "value"
eAttribute "instance", :transient => true, :eType => "EEnumerator"
eAttribute "literal"
eReference "eEnum", :resolveProxies => false, :eOpposite => "EEnum.eLiterals", :changeable => false, :transient => true, :eType => "EEnum"
end
eClass "EFactory", :eSuperTypes => ["EModelElement"] do
eReference "ePackage", :resolveProxies => false, :eOpposite => "EPackage.eFactoryInstance", :transient => true, :lowerBound => 1, :eType => "EPackage"
end
eClass "EModelElement", :abstract => true, :eSuperTypes => ["EObject"], :eSubTypes => ["EAnnotation", "EFactory", "ENamedElement"] do
eReference "eAnnotations", :containment => true, :resolveProxies => false, :eOpposite => "EAnnotation.eModelElement", :upperBound => -1, :eType => "EAnnotation"
end
eClass "ENamedElement", :abstract => true, :eSuperTypes => ["EModelElement"], :eSubTypes => ["EClassifier", "EEnumLiteral", "EPackage", "ETypedElement", "ETypeParameter"] do
eAttribute "name"
end
eClass "EObject", :eSubTypes => ["EModelElement", "EGenericType"]
eClass "EOperation", :eSuperTypes => ["ETypedElement"] do
eReference "eContainingClass", :resolveProxies => false, :eOpposite => "EClass.eOperations", :changeable => false, :transient => true, :eType => "EClass"
eReference "eTypeParameters", :containment => true, :upperBound => -1, :eType => "ETypeParameter"
eReference "eParameters", :containment => true, :resolveProxies => false, :eOpposite => "EParameter.eOperation", :upperBound => -1, :eType => "EParameter"
eReference "eExceptions", :unsettable => true, :upperBound => -1, :eType => "EClassifier"
eReference "eGenericExceptions", :containment => true, :resolveProxies => false, :unsettable => true, :upperBound => -1, :eType => "EGenericType"
end
eClass "EPackage", :eSuperTypes => ["ENamedElement"] do
eAttribute "nsURI"
eAttribute "nsPrefix"
eReference "eFactoryInstance", :resolveProxies => false, :eOpposite => "EFactory.ePackage", :transient => true, :lowerBound => 1, :eType => "EFactory"
eReference "eClassifiers", :containment => true, :eOpposite => "EClassifier.ePackage", :upperBound => -1, :eType => "EClassifier"
eReference "eSubpackages", :containment => true, :eOpposite => "eSuperPackage", :upperBound => -1, :eType => "EPackage"
eReference "eSuperPackage", :eOpposite => "eSubpackages", :changeable => false, :transient => true, :eType => "EPackage"
end
eClass "EParameter", :eSuperTypes => ["ETypedElement"] do
eReference "eOperation", :resolveProxies => false, :eOpposite => "EOperation.eParameters", :changeable => false, :transient => true, :eType => "EOperation"
end
eClass "EReference", :eSuperTypes => ["EStructuralFeature"] do
eAttribute "containment"
eAttribute "container", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "resolveProxies", :defaultValueLiteral => "true"
eReference "eOpposite", :eType => "EReference"
eReference "eReferenceType", :changeable => false, :derived => true, :transient => true, :volatile => true, :lowerBound => 1, :eType => "EClass"
eReference "eKeys", :upperBound => -1, :eType => "EAttribute"
end
eClass "EStructuralFeature", :abstract => true, :eSuperTypes => ["ETypedElement"], :eSubTypes => ["EAttribute", "EReference"] do
eAttribute "changeable", :defaultValueLiteral => "true"
eAttribute "volatile"
eAttribute "transient"
eAttribute "defaultValueLiteral"
eAttribute "defaultValue", :changeable => false, :derived => true, :transient => true, :volatile => true, :eType => "EJavaObject"
eAttribute "unsettable"
eAttribute "derived"
eReference "eContainingClass", :resolveProxies => false, :eOpposite => "EClass.eStructuralFeatures", :changeable => false, :transient => true, :eType => "EClass"
end
eClass "ETypedElement", :abstract => true, :eSuperTypes => ["ENamedElement"], :eSubTypes => ["EOperation", "EParameter", "EStructuralFeature"] do
eAttribute "ordered", :defaultValueLiteral => "true"
eAttribute "unique", :defaultValueLiteral => "true"
eAttribute "lowerBound"
eAttribute "upperBound", :defaultValueLiteral => "1"
eAttribute "many", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "required", :changeable => false, :derived => true, :transient => true, :volatile => true
eReference "eType", :unsettable => true, :volatile => true, :eType => "EClassifier"
eReference "eGenericType", :containment => true, :resolveProxies => false, :unsettable => true, :volatile => true, :eType => "EGenericType"
end
eDataType "EBigDecimal", :instanceClassName => "java.math.BigDecimal"
eDataType "EBigInteger", :instanceClassName => "java.math.BigInteger"
eDataType "EBoolean", :instanceClassName => "boolean"
eDataType "EBooleanObject", :instanceClassName => "java.lang.Boolean"
eDataType "EByte", :instanceClassName => "byte"
eDataType "EByteArray", :instanceClassName => "byte[]"
eDataType "EByteObject", :instanceClassName => "java.lang.Byte"
eDataType "EChar", :instanceClassName => "char"
eDataType "ECharacterObject", :instanceClassName => "java.lang.Character"
eDataType "EDate", :instanceClassName => "java.util.Date"
eDataType "EDiagnosticChain", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.DiagnosticChain"
eDataType "EDouble", :instanceClassName => "double"
eDataType "EDoubleObject", :instanceClassName => "java.lang.Double"
eDataType "EEList", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.EList" do
eTypeParameter "E"
end
eDataType "EEnumerator", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.Enumerator"
eDataType "EFeatureMap", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.util.FeatureMap"
eDataType "EFeatureMapEntry", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.util.FeatureMap$Entry"
eDataType "EFloat", :instanceClassName => "float"
eDataType "EFloatObject", :instanceClassName => "java.lang.Float"
eDataType "EInt", :instanceClassName => "int"
eDataType "EIntegerObject", :instanceClassName => "java.lang.Integer"
eDataType "EJavaClass", :instanceClassName => "java.lang.Class" do
eTypeParameter "T"
end
eDataType "EJavaObject", :instanceClassName => "java.lang.Object"
eDataType "ELong", :instanceClassName => "long"
eDataType "ELongObject", :instanceClassName => "java.lang.Long"
eDataType "EMap", :serializable => false, :instanceClassName => "java.util.Map" do
eTypeParameter "K"
eTypeParameter "V"
end
eDataType "EResource", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.resource.Resource"
eDataType "EResourceSet", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.resource.ResourceSet"
eDataType "EShort", :instanceClassName => "short"
eDataType "EShortObject", :instanceClassName => "java.lang.Short"
eDataType "EString", :instanceClassName => "java.lang.String"
eClass "EStringToStringMapEntry", :instanceClassName => "java.util.Map$Entry" do
eAttribute "key"
eAttribute "value"
end
eDataType "ETreeIterator", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.TreeIterator" do
eTypeParameter "E"
end
eClass "EGenericType", :eSuperTypes => ["EObject"] do
eReference "eUpperBound", :containment => true, :resolveProxies => false, :eType => "EGenericType"
eReference "eTypeArguments", :containment => true, :resolveProxies => false, :upperBound => -1, :eType => "EGenericType"
eReference "eRawType", :changeable => false, :derived => true, :transient => true, :lowerBound => 1, :eType => "EClassifier"
eReference "eLowerBound", :containment => true, :resolveProxies => false, :eType => "EGenericType"
eReference "eTypeParameter", :resolveProxies => false, :eType => "ETypeParameter"
eReference "eClassifier", :eType => "EClassifier"
end
eClass "ETypeParameter", :eSuperTypes => ["ENamedElement"] do
eReference "eBounds", :containment => true, :resolveProxies => false, :upperBound => -1, :eType => "EGenericType"
end
end
rgen-0.10.2/test/model_builder/ecore_original_regenerated.rb 0000664 0000000 0000000 00000023144 14560401624 0024173 0 ustar 00root root 0000000 0000000 ePackage "ecore", :nsPrefix => "ecore", :nsURI => "http://www.eclipse.org/emf/2002/Ecore" do
eClass "EAttribute" do
eAttribute "iD"
eReference "eAttributeType", :changeable => false, :derived => true, :transient => true, :volatile => true, :lowerBound => 1
end
eClass "EAnnotation" do
eAttribute "source"
eReference "details", :containment => true, :resolveProxies => false, :upperBound => -1
eReference "eModelElement", :resolveProxies => false, :transient => true
eReference "contents", :containment => true, :resolveProxies => false, :upperBound => -1
eReference "references", :upperBound => -1
end
eClass "EClass" do
eAttribute "abstract"
eAttribute "interface"
eReference "eSuperTypes", :unsettable => true, :upperBound => -1
eReference "eOperations", :containment => true, :resolveProxies => false, :upperBound => -1
eReference "eAllAttributes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eAllReferences", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eReferences", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eAttributes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eAllContainments", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eAllOperations", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eAllStructuralFeatures", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eAllSuperTypes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
eReference "eIDAttribute", :resolveProxies => false, :changeable => false, :derived => true, :transient => true, :volatile => true
eReference "eStructuralFeatures", :containment => true, :resolveProxies => false, :upperBound => -1
eReference "eGenericSuperTypes", :containment => true, :resolveProxies => false, :unsettable => true, :upperBound => -1
eReference "eAllGenericSuperTypes", :changeable => false, :derived => true, :transient => true, :volatile => true, :upperBound => -1
end
eClass "EClassifier", :abstract => true do
eAttribute "instanceClassName", :unsettable => true, :volatile => true
eAttribute "instanceClass", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "defaultValue", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "instanceTypeName", :unsettable => true, :volatile => true
eReference "ePackage", :changeable => false, :transient => true
eReference "eTypeParameters", :containment => true, :upperBound => -1
end
eClass "EDataType" do
eAttribute "serializable", :defaultValueLiteral => "true"
end
eClass "EEnum" do
eReference "eLiterals", :containment => true, :resolveProxies => false, :upperBound => -1
end
eClass "EEnumLiteral" do
eAttribute "value"
eAttribute "instance", :transient => true
eAttribute "literal"
eReference "eEnum", :resolveProxies => false, :changeable => false, :transient => true
end
eClass "EFactory" do
eReference "ePackage", :resolveProxies => false, :transient => true, :lowerBound => 1
end
eClass "EModelElement", :abstract => true do
eReference "eAnnotations", :containment => true, :resolveProxies => false, :upperBound => -1
end
eClass "ENamedElement", :abstract => true do
eAttribute "name"
end
eClass "EObject"
eClass "EOperation" do
eReference "eContainingClass", :resolveProxies => false, :changeable => false, :transient => true
eReference "eTypeParameters", :containment => true, :upperBound => -1
eReference "eParameters", :containment => true, :resolveProxies => false, :upperBound => -1
eReference "eExceptions", :unsettable => true, :upperBound => -1
eReference "eGenericExceptions", :containment => true, :resolveProxies => false, :unsettable => true, :upperBound => -1
end
eClass "EPackage" do
eAttribute "nsURI"
eAttribute "nsPrefix"
eReference "eFactoryInstance", :resolveProxies => false, :transient => true, :lowerBound => 1
eReference "eClassifiers", :containment => true, :upperBound => -1
eReference "eSubpackages", :containment => true, :upperBound => -1
eReference "eSuperPackage", :changeable => false, :transient => true
end
eClass "EParameter" do
eReference "eOperation", :resolveProxies => false, :changeable => false, :transient => true
end
eClass "EReference" do
eAttribute "containment"
eAttribute "container", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "resolveProxies", :defaultValueLiteral => "true"
eReference "eOpposite"
eReference "eReferenceType", :changeable => false, :derived => true, :transient => true, :volatile => true, :lowerBound => 1
eReference "eKeys", :upperBound => -1
end
eClass "EStructuralFeature", :abstract => true do
eAttribute "changeable", :defaultValueLiteral => "true"
eAttribute "volatile"
eAttribute "transient"
eAttribute "defaultValueLiteral"
eAttribute "defaultValue", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "unsettable"
eAttribute "derived"
eReference "eContainingClass", :resolveProxies => false, :changeable => false, :transient => true
end
eClass "ETypedElement", :abstract => true do
eAttribute "ordered", :defaultValueLiteral => "true"
eAttribute "unique", :defaultValueLiteral => "true"
eAttribute "lowerBound"
eAttribute "upperBound", :defaultValueLiteral => "1"
eAttribute "many", :changeable => false, :derived => true, :transient => true, :volatile => true
eAttribute "required", :changeable => false, :derived => true, :transient => true, :volatile => true
eReference "eType", :unsettable => true, :volatile => true
eReference "eGenericType", :containment => true, :resolveProxies => false, :unsettable => true, :volatile => true
end
eDataType "EBigDecimal", :instanceClassName => "java.math.BigDecimal"
eDataType "EBigInteger", :instanceClassName => "java.math.BigInteger"
eDataType "EBoolean", :instanceClassName => "boolean"
eDataType "EBooleanObject", :instanceClassName => "java.lang.Boolean"
eDataType "EByte", :instanceClassName => "byte"
eDataType "EByteArray", :instanceClassName => "byte[]"
eDataType "EByteObject", :instanceClassName => "java.lang.Byte"
eDataType "EChar", :instanceClassName => "char"
eDataType "ECharacterObject", :instanceClassName => "java.lang.Character"
eDataType "EDate", :instanceClassName => "java.util.Date"
eDataType "EDiagnosticChain", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.DiagnosticChain"
eDataType "EDouble", :instanceClassName => "double"
eDataType "EDoubleObject", :instanceClassName => "java.lang.Double"
eDataType "EEList", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.EList" do
eTypeParameter "E"
end
eDataType "EEnumerator", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.Enumerator"
eDataType "EFeatureMap", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.util.FeatureMap"
eDataType "EFeatureMapEntry", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.util.FeatureMap$Entry"
eDataType "EFloat", :instanceClassName => "float"
eDataType "EFloatObject", :instanceClassName => "java.lang.Float"
eDataType "EInt", :instanceClassName => "int"
eDataType "EIntegerObject", :instanceClassName => "java.lang.Integer"
eDataType "EJavaClass", :instanceClassName => "java.lang.Class" do
eTypeParameter "T"
end
eDataType "EJavaObject", :instanceClassName => "java.lang.Object"
eDataType "ELong", :instanceClassName => "long"
eDataType "ELongObject", :instanceClassName => "java.lang.Long"
eDataType "EMap", :serializable => false, :instanceClassName => "java.util.Map" do
eTypeParameter "K"
eTypeParameter "V"
end
eDataType "EResource", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.resource.Resource"
eDataType "EResourceSet", :serializable => false, :instanceClassName => "org.eclipse.emf.ecore.resource.ResourceSet"
eDataType "EShort", :instanceClassName => "short"
eDataType "EShortObject", :instanceClassName => "java.lang.Short"
eDataType "EString", :instanceClassName => "java.lang.String"
eClass "EStringToStringMapEntry", :instanceClassName => "java.util.Map$Entry" do
eAttribute "key"
eAttribute "value"
end
eDataType "ETreeIterator", :serializable => false, :instanceClassName => "org.eclipse.emf.common.util.TreeIterator" do
eTypeParameter "E"
end
eClass "EGenericType" do
eReference "eUpperBound", :containment => true, :resolveProxies => false
eReference "eTypeArguments", :containment => true, :resolveProxies => false, :upperBound => -1
eReference "eRawType", :changeable => false, :derived => true, :transient => true, :lowerBound => 1
eReference "eLowerBound", :containment => true, :resolveProxies => false
eReference "eTypeParameter", :resolveProxies => false
eReference "eClassifier"
end
eClass "ETypeParameter" do
eReference "eBounds", :containment => true, :resolveProxies => false, :upperBound => -1
end
end
rgen-0.10.2/test/model_builder/reference_resolver_test.rb 0000664 0000000 0000000 00000012013 14560401624 0023554 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__)+"/../lib"
require 'minitest/autorun'
require 'rgen/metamodel_builder'
require 'rgen/model_builder/reference_resolver'
class ReferenceResolverTest < Minitest::Test
class ClassA < RGen::MetamodelBuilder::MMBase
has_attr "name"
end
class ClassB < RGen::MetamodelBuilder::MMBase
has_attr "name"
end
class ClassC < RGen::MetamodelBuilder::MMBase
has_attr "name"
end
ClassA.contains_many 'childB', ClassB, 'parentA'
ClassB.contains_many 'childC', ClassC, 'parentB'
ClassA.has_one 'refC', ClassC
ClassB.has_one 'refC', ClassC
ClassC.has_many 'refCs', ClassC
ClassC.has_one 'refA', ClassA
ClassC.has_one 'refB', ClassB
def testModel
a1 = ClassA.new(:name => "a1")
a2 = ClassA.new(:name => "a2")
b1 = ClassB.new(:name => "b1", :parentA => a1)
b2 = ClassB.new(:name => "b2", :parentA => a1)
c1 = ClassC.new(:name => "c1", :parentB => b1)
c2 = ClassC.new(:name => "c2", :parentB => b1)
c3 = ClassC.new(:name => "c3", :parentB => b1)
[a1, a2, b1, b2, c1, c2, c3]
end
def setElementNames(resolver, elements)
elements.each do |e|
resolver.setElementName(e, e.name)
end
end
def createJob(hash)
raise "Invalid arguments" unless \
hash.is_a?(Hash) && (hash.keys & [:receiver, :reference, :namespace, :string]).size == 4
RGen::ModelBuilder::ReferenceResolver::ResolverJob.new(
hash[:receiver], hash[:reference], hash[:namespace], hash[:string])
end
def test_resolve_same_namespace
a1, a2, b1, b2, c1, c2, c3 = testModel
toplevelNamespace = [a1, a2]
resolver = RGen::ModelBuilder::ReferenceResolver.new
setElementNames(resolver, [a1, a2, b1, b2, c1, c2, c3])
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refCs"},
:namespace => b1,
:string => "c1"))
resolver.addJob(createJob(
:receiver => b2,
:reference => ClassB.ecore.eReferences.find{|r| r.name == "refC"},
:namespace => a1,
:string => "b1.c1"))
resolver.addJob(createJob(
:receiver => a2,
:reference => ClassA.ecore.eReferences.find{|r| r.name == "refC"},
:namespace => nil,
:string => "a1.b1.c1"))
resolver.resolve(toplevelNamespace)
assert_equal [c1], c2.refCs
assert_equal c1, b2.refC
assert_equal c1, a2.refC
end
def test_resolve_parent_namespace
a1, a2, b1, b2, c1, c2, c3 = testModel
toplevelNamespace = [a1, a2]
resolver = RGen::ModelBuilder::ReferenceResolver.new
setElementNames(resolver, [a1, a2, b1, b2, c1, c2, c3])
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refA"},
:namespace => b1,
:string => "a1"))
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refB"},
:namespace => b1,
:string => "b1"))
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refCs"},
:namespace => b1,
:string => "b1.c1"))
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refCs"},
:namespace => b1,
:string => "a1.b1.c3"))
resolver.resolve(toplevelNamespace)
assert_equal a1, c2.refA
assert_equal b1, c2.refB
assert_equal [c1, c3], c2.refCs
end
def test_resolve_faulty
a1, a2, b1, b2, c1, c2, c3 = testModel
toplevelNamespace = [a1, a2]
resolver = RGen::ModelBuilder::ReferenceResolver.new
setElementNames(resolver, [a1, a2, b1, b2, c1, c2, c3])
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refCs"},
:namespace => b1,
:string => "b1.c5"))
assert_raises RGen::ModelBuilder::ReferenceResolver::ResolverException do
resolver.resolve(toplevelNamespace)
end
end
def test_ambiguous_prefix
a = ClassA.new(:name => "name1")
b1 = ClassB.new(:name => "name1", :parentA => a)
b2 = ClassB.new(:name => "target", :parentA => a)
c1 = ClassC.new(:name => "name21", :parentB => b1)
c2 = ClassC.new(:name => "name22", :parentB => b1)
toplevelNamespace = [a]
resolver = RGen::ModelBuilder::ReferenceResolver.new
setElementNames(resolver, [a, b1, b2, c1, c2])
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refCs"},
:namespace => b1,
:string => "name1.name1.name21"))
resolver.addJob(createJob(
:receiver => c2,
:reference => ClassC.ecore.eReferences.find{|r| r.name == "refB"},
:namespace => b1,
:string => "name1.target"))
resolver.resolve(toplevelNamespace)
assert_equal [c1], c2.refCs
assert_equal b2, c2.refB
end
end rgen-0.10.2/test/model_builder/serializer_test.rb 0000664 0000000 0000000 00000004555 14560401624 0022062 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__) + "/../lib"
require 'minitest/autorun'
require 'rgen/ecore/ecore'
# The following would also influence other tests...
#
#module RGen::ECore
# class EGenericType < EObject
# contains_many_uni 'eTypeArguments', EGenericType
# end
# class ETypeParameter < ENamedElement
# end
# class EClassifier
# contains_many_uni 'eTypeParameters', ETypeParameter
# end
# class ETypedElement
# has_one 'eGenericType', EGenericType
# end
#end
#
#RGen::ECore::ECoreInterface.clear_ecore_cache
#RGen::ECore::EString.ePackage = RGen::ECore.ecore
require 'rgen/environment'
require 'rgen/model_builder/model_serializer'
require 'rgen/instantiator/ecore_xml_instantiator'
require 'rgen/model_builder'
require 'model_builder/statemachine_metamodel'
class ModelSerializerTest < Minitest::Test
def test_ecore_internal
File.open(File.dirname(__FILE__)+"/ecore_internal.rb","w") do |f|
serializer = RGen::ModelBuilder::ModelSerializer.new(f, RGen::ECore.ecore)
serializer.serialize(RGen::ECore.ecore)
end
end
def test_roundtrip
model = %{\
statemachine "Airconditioner" do
state "Off", :kind => :START
compositeState "On" do
state "Heating"
state "Cooling"
state "Dumm"
end
transition "_Transition1", :sourceState => "On.Cooling", :targetState => "On.Heating"
transition "_Transition2", :sourceState => "On.Heating", :targetState => "On.Cooling"
end
}
check_roundtrip(StatemachineMetamodel, model)
end
module AmbiguousRoleMM
extend RGen::MetamodelBuilder::ModuleExtension
class A < RGen::MetamodelBuilder::MMBase
end
class B < RGen::MetamodelBuilder::MMBase
end
class C < B
end
A.contains_many 'role1', B, 'back1'
A.contains_many 'role2', B, 'back2'
end
def test_roundtrip_ambiguous_role
model = %{\
a "_A1" do
b "_B1", :as => :role1
b "_B2", :as => :role2
c "_C1", :as => :role2
end
}
check_roundtrip(AmbiguousRoleMM, model)
end
private
def build_model(mm, model)
RGen::ModelBuilder.build(mm) do
eval(model)
end
end
def check_roundtrip(mm, model)
sm = build_model(mm, model)
f = StringIO.new
serializer = RGen::ModelBuilder::ModelSerializer.new(f, mm.ecore)
serializer.serialize(sm)
assert_equal model, f.string
end
end
rgen-0.10.2/test/model_builder/statemachine_metamodel.rb 0000664 0000000 0000000 00000002252 14560401624 0023336 0 ustar 00root root 0000000 0000000 # a test metamodel used by the following tests
module StatemachineMetamodel
extend RGen::MetamodelBuilder::ModuleExtension
module Condition
extend RGen::MetamodelBuilder::ModuleExtension
class Condition < RGen::MetamodelBuilder::MMBase
end
module TimeCondition
extend RGen::MetamodelBuilder::ModuleExtension
class TimeCondition < Condition
has_attr 'timeout', Integer
end
end
end
class Statemachine < RGen::MetamodelBuilder::MMBase
has_attr 'name'
end
class State < RGen::MetamodelBuilder::MMBase
has_attr 'name'
has_attr 'kind', RGen::MetamodelBuilder::DataTypes::Enum.new([:START])
end
class CompositeState < State
has_attr 'name'
contains_many 'state', State, 'compositeState'
end
class Transition < RGen::MetamodelBuilder::MMBase
many_to_one 'sourceState', State, 'outgoingTransition'
many_to_one 'targetState', State, 'incomingTransition'
has_many 'condition', Condition::Condition
end
Statemachine.contains_many 'state', State, 'statemachine'
Statemachine.contains_many 'transition', Transition, 'statemachine'
end
rgen-0.10.2/test/model_builder/test_model/ 0000775 0000000 0000000 00000000000 14560401624 0020453 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/model_builder/test_model/statemachine1.rb 0000664 0000000 0000000 00000001322 14560401624 0023524 0 ustar 00root root 0000000 0000000 statemachine "Airconditioner" do
state "Off", :kind => :START
compositeState "On" do
state "Heating" do
transition :as => :outgoingTransition, :targetState => "Cooling",
:statemachine => "Airconditioner"
end
state "Cooling" do
end
end
transition :sourceState => "On.Cooling", :targetState => "On.Heating" do
_using Condition::TimeCondition do
timeCondition :as => :condition, :timeout => 100
end
Condition::TimeCondition.timeCondition :as => :condition, :timeout => 10
end
end
_using Condition do
statemachine "AirconExtension" do
s = state "StartState"
transition :sourceState => s, :targetState => "Airconditioner.Off"
end
end
rgen-0.10.2/test/model_builder_test.rb 0000664 0000000 0000000 00000000337 14560401624 0017703 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__) + "/../lib"
require 'model_builder/builder_test'
require 'model_builder/serializer_test'
require 'model_builder/builder_context_test'
require 'model_builder/reference_resolver_test'
rgen-0.10.2/test/model_fragment_test.rb 0000664 0000000 0000000 00000001332 14560401624 0020054 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/metamodel_builder'
require 'rgen/fragment/model_fragment'
class ModelFragmentTest < Minitest::Test
module TestMetamodel
extend RGen::MetamodelBuilder::ModuleExtension
class SimpleClass < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
contains_many 'subclass', SimpleClass, 'parent'
end
end
def test_elements
root = TestMetamodel::SimpleClass.new(:name => "parent",
:subclass => [TestMetamodel::SimpleClass.new(:name => "child")])
frag = RGen::Fragment::ModelFragment.new("location")
frag.set_root_elements([root])
assert_equal 2, frag.elements.size
end
end
rgen-0.10.2/test/output_handler_test.rb 0000664 0000000 0000000 00000003003 14560401624 0020123 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/template_language/output_handler'
class MetamodelBuilderTest < Minitest::Test
def test_direct_nl
h = RGen::TemplateLanguage::OutputHandler.new
h.mode = :direct
h << "Test"
h.ignoreNextNL
h << "\nContent"
assert_equal "TestContent", h.to_s
end
def test_direct_ws
h = RGen::TemplateLanguage::OutputHandler.new
h.mode = :direct
h << "Test"
h.ignoreNextWS
h << " \n Content"
assert_equal "TestContent", h.to_s
end
def test_explicit_indent
h = RGen::TemplateLanguage::OutputHandler.new
h.mode = :explicit
h.indent = 1
h << "Start"
h << " \n "
h << "Test"
h << " \n \n Content"
assert_equal " Start\n Test\n Content", h.to_s
end
def test_explicit_endswithws
h = RGen::TemplateLanguage::OutputHandler.new
h.mode = :explicit
h.indent = 1
h << "Start \n\n"
assert_equal " Start\n", h.to_s
end
def test_performance
h = RGen::TemplateLanguage::OutputHandler.new
h.mode = :explicit
h.indent = 1
line = (1..50).collect{|w| "someword"}.join(" ")+"\n"
# repeat more often to make performance differences visible
20.times do
h << line
end
end
def test_indent_string
h = RGen::TemplateLanguage::OutputHandler.new(1, "\t", :explicit)
h << "Start"
h << " \n "
h << "Test"
h << " \n \n Content"
assert_equal "\tStart\n\tTest\n\tContent", h.to_s
end
end rgen-0.10.2/test/qualified_name_provider_test.rb 0000664 0000000 0000000 00000002476 14560401624 0021760 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/metamodel_builder'
require 'rgen/serializer/qualified_name_provider'
class QualifiedNameProviderTest < Minitest::Test
class AbstractTestNode < RGen::MetamodelBuilder::MMBase
contains_many 'children', AbstractTestNode, "parent"
end
class NamedNode < AbstractTestNode
has_attr 'n', String
end
class UnnamedNode < AbstractTestNode
end
def test_simple
root = NamedNode.new(:n => "root", :children => [
NamedNode.new(:n => "a", :children => [
NamedNode.new(:n => "a1")
]),
UnnamedNode.new(:children => [
NamedNode.new(:n => "b1")
])
])
qnp = RGen::Serializer::QualifiedNameProvider.new(:attribute_name => "n")
assert_equal "/root", qnp.identifier(root)
assert_equal "/root/a", qnp.identifier(root.children[0])
assert_equal "/root/a/a1", qnp.identifier(root.children[0].children[0])
assert_equal "/root", qnp.identifier(root.children[1])
assert_equal "/root/b1", qnp.identifier(root.children[1].children[0])
end
def test_unnamed_root
root = UnnamedNode.new
qnp = RGen::Serializer::QualifiedNameProvider.new(:attribute_name => "n")
assert_equal "/", qnp.identifier(root)
end
end
rgen-0.10.2/test/qualified_name_resolver_test.rb 0000664 0000000 0000000 00000007355 14560401624 0021770 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/metamodel_builder'
require 'rgen/instantiator/qualified_name_resolver'
class QualifiedNameResolverTest < Minitest::Test
class TestNode < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
has_one 'nextSibling', TestNode
contains_many 'children', TestNode, "parent"
end
class TestNode2 < RGen::MetamodelBuilder::MMBase
has_attr 'cname', String
has_one 'nextSibling', TestNode2
contains_many 'children', TestNode2, "parent"
end
class TestNode3 < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
contains_one 'child', TestNode3, "parent"
end
def testModel
[TestNode.new(:name => "Root1", :children => [
TestNode.new(:name => "Sub11"),
TestNode.new(:name => "Sub12", :children => [
TestNode.new(:name => "Sub121")])]),
TestNode.new(:name => "Root2", :children => [
TestNode.new(:name => "Sub21", :children => [
TestNode.new(:name => "Sub211")])]),
TestNode.new(:name => "Root3"),
TestNode.new(:name => "Root3")
]
end
def testModel2
[TestNode2.new(:cname => "Root1", :children => [
TestNode2.new(:cname => "Sub11")])]
end
def testModel3
[TestNode3.new(:name => "Root1", :child =>
TestNode3.new(:name => "Sub11", :child =>
TestNode3.new(:name => "Sub111")))]
end
def test_customNameAttribute
model = testModel2
res = RGen::Instantiator::QualifiedNameResolver.new(model, :nameAttribute => "cname")
assert_equal model[0], res.resolveIdentifier("/Root1")
assert_equal model[0].children[0], res.resolveIdentifier("/Root1/Sub11")
end
def test_customSeparator
model = testModel
res = RGen::Instantiator::QualifiedNameResolver.new(model, :separator => "|")
assert_equal model[0], res.resolveIdentifier("|Root1")
assert_nil res.resolveIdentifier("/Root1")
assert_equal model[0].children[0], res.resolveIdentifier("|Root1|Sub11")
end
def test_noLeadingSeparator
model = testModel
res = RGen::Instantiator::QualifiedNameResolver.new(model, :leadingSeparator => false)
assert_equal model[0], res.resolveIdentifier("Root1")
assert_nil res.resolveIdentifier("/Root1")
assert_equal model[0].children[0], res.resolveIdentifier("Root1/Sub11")
end
def test_resolve
model = testModel
res = RGen::Instantiator::QualifiedNameResolver.new(model)
assert_equal model[0], res.resolveIdentifier("/Root1")
# again
assert_equal model[0], res.resolveIdentifier("/Root1")
assert_equal model[0].children[0], res.resolveIdentifier("/Root1/Sub11")
# again
assert_equal model[0].children[0], res.resolveIdentifier("/Root1/Sub11")
assert_equal model[0].children[1], res.resolveIdentifier("/Root1/Sub12")
assert_equal model[0].children[1].children[0], res.resolveIdentifier("/Root1/Sub12/Sub121")
assert_equal model[1], res.resolveIdentifier("/Root2")
assert_equal model[1].children[0], res.resolveIdentifier("/Root2/Sub21")
assert_equal model[1].children[0].children[0], res.resolveIdentifier("/Root2/Sub21/Sub211")
# duplicate name yields two result elements
assert_equal [model[2], model[3]], res.resolveIdentifier("/Root3")
assert_nil res.resolveIdentifier("/RootX")
assert_nil res.resolveIdentifier("/Root1/SubX")
end
def test_oneChild
model = testModel3
res = RGen::Instantiator::QualifiedNameResolver.new(model)
assert_equal model[0], res.resolveIdentifier("/Root1")
assert_equal model[0].child, res.resolveIdentifier("/Root1/Sub11")
assert_equal model[0].child.child, res.resolveIdentifier("/Root1/Sub11/Sub111")
end
end
rgen-0.10.2/test/reference_resolver_test.rb 0000664 0000000 0000000 00000010207 14560401624 0020751 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/metamodel_builder'
require 'rgen/instantiator/reference_resolver'
class ReferenceResolverTest < Minitest::Test
class TestNode < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
has_one 'other', TestNode
has_many 'others', TestNode
end
class TestNode2 < RGen::MetamodelBuilder::MMBase
has_attr 'name', String
end
def test_identifier_resolver
nodeA, nodeB, nodeC, unresolved_refs = create_model
resolver = RGen::Instantiator::ReferenceResolver.new(
:identifier_resolver => proc do |ident|
{:a => nodeA, :b => nodeB, :c => nodeC}[ident]
end)
urefs = resolver.resolve(unresolved_refs)
check_model(nodeA, nodeB, nodeC)
assert urefs.empty?
end
def test_add_identifier
nodeA, nodeB, nodeC, unresolved_refs = create_model
resolver = RGen::Instantiator::ReferenceResolver.new
resolver.add_identifier(:a, nodeA)
resolver.add_identifier(:b, nodeB)
resolver.add_identifier(:c, nodeC)
urefs = resolver.resolve(unresolved_refs)
check_model(nodeA, nodeB, nodeC)
assert urefs.empty?
end
def test_problems
nodeA, nodeB, nodeC, unresolved_refs = create_model
resolver = RGen::Instantiator::ReferenceResolver.new(
:identifier_resolver => proc do |ident|
{:a => [nodeA, nodeB], :c => nodeC}[ident]
end)
problems = []
urefs = resolver.resolve(unresolved_refs, :problems => problems)
assert_equal ["identifier b not found", "identifier a not uniq"], problems
assert_equal 2, urefs.size
assert urefs.all?{|ur| !ur.target_type_error}
end
def test_on_resolve_proc
nodeA, nodeB, nodeC, unresolved_refs = create_model
resolver = RGen::Instantiator::ReferenceResolver.new
resolver.add_identifier(:a, nodeA)
resolver.add_identifier(:b, nodeB)
resolver.add_identifier(:c, nodeC)
data = []
resolver.resolve(unresolved_refs,
:on_resolve => proc {|ur, e| data << [ ur, e ]})
assert data[0][0].is_a?(RGen::Instantiator::ReferenceResolver::UnresolvedReference)
assert_equal nodeA, data[0][0].element
assert_equal "other", data[0][0].feature_name
assert_equal :b, data[0][0].proxy.targetIdentifier
assert_equal nodeB, data[0][1]
end
def test_target_type_error
nodeA, nodeB, nodeC, unresolved_refs = create_model
resolver = RGen::Instantiator::ReferenceResolver.new(
:identifier_resolver => proc do |ident|
{:a => TestNode2.new, :b => TestNode2.new, :c => nodeC}[ident]
end)
problems = []
urefs = resolver.resolve(unresolved_refs, :problems => problems)
assert_equal 2, problems.size
assert problems[0] =~ /invalid target type .*TestNode2/
assert problems[1] =~ /invalid target type .*TestNode2/
assert_equal 2, urefs.uniq.size
assert urefs[0].target_type_error
assert urefs[1].target_type_error
assert urefs.any?{|ur| ur.proxy.object_id == nodeA.other.object_id}
assert urefs.any?{|ur| ur.proxy.object_id == nodeB.others[0].object_id}
end
private
def create_model
nodeA = TestNode.new(:name => "NodeA")
nodeB = TestNode.new(:name => "NodeB")
nodeC = TestNode.new(:name => "NodeC")
bProxy = RGen::MetamodelBuilder::MMProxy.new(:b)
nodeA.other = bProxy
aProxy = RGen::MetamodelBuilder::MMProxy.new(:a)
cProxy = RGen::MetamodelBuilder::MMProxy.new(:c)
nodeB.others = [aProxy, cProxy]
unresolved_refs = [
RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(nodeA, "other", bProxy),
RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(nodeB, "others", aProxy),
RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(nodeB, "others", cProxy)
]
return nodeA, nodeB, nodeC, unresolved_refs
end
def check_model(nodeA, nodeB, nodeC)
assert_equal nodeB, nodeA.other
assert_equal [], nodeA.others
assert_nil nodeB.other
assert_equal [nodeA, nodeC], nodeB.others
assert_nil nodeC.other
assert_equal [], nodeC.others
end
end
rgen-0.10.2/test/rgen_test.rb 0000664 0000000 0000000 00000001376 14560401624 0016034 0 ustar 00root root 0000000 0000000 $:.unshift File.dirname(__FILE__)
require 'minitest/autorun'
require 'array_extensions_test'
require 'ea_instantiator_test'
require 'ecore_self_test'
require 'environment_test'
require 'metamodel_builder_test'
require 'metamodel_roundtrip_test'
require 'output_handler_test'
require 'template_language_test'
require 'transformer_test'
require 'xml_instantiator_test'
require 'ea_serializer_test'
require 'model_builder_test'
require 'method_delegation_test'
require 'json_test'
require 'reference_resolver_test'
require 'qualified_name_resolver_test'
require 'metamodel_order_test'
require 'metamodel_from_ecore_test'
require 'util_test'
require 'model_fragment_test'
require 'qualified_name_provider_test'
require 'ecore_to_ruby_test'
rgen-0.10.2/test/template_language_test.rb 0000664 0000000 0000000 00000017751 14560401624 0020563 0 ustar 00root root 0000000 0000000 $:.unshift File.join(File.dirname(__FILE__),"..","lib")
require 'minitest/autorun'
require 'rgen/template_language'
require 'rgen/metamodel_builder'
class TemplateContainerTest < Minitest::Test
TEMPLATES_DIR = File.dirname(__FILE__)+"/template_language_test/templates"
OUTPUT_DIR = File.dirname(__FILE__)+"/template_language_test"
module MyMM
class Chapter
attr_reader :title
def initialize(title)
@title = title
end
end
class Document
attr_reader :title, :authors, :chapters
attr_accessor :sampleArray
def initialize(title)
@title = title
@chapters = []
@authors = []
end
end
class Author
attr_reader :name, :email
def initialize(name, email)
@name, @email = name, email
end
end
end
module CCodeMM
class CArray < RGen::MetamodelBuilder::MMBase
has_attr 'name'
has_attr 'size', Integer
has_attr 'type'
end
class PrimitiveInitValue < RGen::MetamodelBuilder::MMBase
has_attr 'value', Integer
end
CArray.has_many 'initvalue', PrimitiveInitValue
end
TEST_MODEL = MyMM::Document.new("SomeDocument")
TEST_MODEL.authors << MyMM::Author.new("Martin", "martin@somewhe.re")
TEST_MODEL.authors << MyMM::Author.new("Otherguy", "other@somewhereel.se")
TEST_MODEL.chapters << MyMM::Chapter.new("Intro")
TEST_MODEL.chapters << MyMM::Chapter.new("MainPart")
TEST_MODEL.chapters << MyMM::Chapter.new("Summary")
TEST_MODEL.sampleArray = CCodeMM::CArray.new(:name => "myArray", :type => "int", :size => 5,
:initvalue => (1..5).collect { |v| CCodeMM::PrimitiveInitValue.new(:value => v) })
def test_with_model
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
File.delete(OUTPUT_DIR+"/testout.txt") if File.exist? OUTPUT_DIR+"/testout.txt"
tc.expand('root::Root', :for => TEST_MODEL, :indent => 1)
result = expected = ""
File.open(OUTPUT_DIR+"/testout.txt") {|f| result = f.read}
File.open(OUTPUT_DIR+"/expected_result1.txt") {|f| expected = f.read}
assert_equal expected, result
end
def test_immediate_result
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
expected = ""
File.open(OUTPUT_DIR+"/expected_result2.txt","rb") {|f| expected = f.read}
assert_equal expected, tc.expand('code/array::ArrayDefinition', :for => TEST_MODEL.sampleArray).to_s
end
def test_indent_string
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
tc.indentString = " " # 2 spaces instead of 3 (default)
tc.expand('indent_string_test::IndentStringTest', :for => :dummy)
File.open(OUTPUT_DIR+"/indentStringTestDefaultIndent.out","rb") do |f|
assert_equal " <- your default here\r\n", f.read
end
File.open(OUTPUT_DIR+"/indentStringTestTabIndent.out","rb") do |f|
assert_equal "\t<- tab\r\n", f.read
end
end
def test_null_context
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_raises StandardError do
# the template must raise an exception because it calls expand :for => nil
tc.expand('null_context_test::NullContextTestBad', :for => :dummy)
end
assert_raises StandardError do
# the template must raise an exception because it calls expand :foreach => nil
tc.expand('null_context_test::NullContextTestBad2', :for => :dummy)
end
tc.expand('null_context_test::NullContextTestOk', :for => :dummy)
end
def test_no_indent
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal " xxx<---\r\n xxx<---\r\n xxx<---\r\n xxx<---\r\n", tc.expand('no_indent_test/test::Test', :for => :dummy)
end
def test_no_indent2
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal " return xxxx;\r\n", tc.expand("no_indent_test/test2::Test", :for => :dummy)
end
def test_no_indent3
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal " l1<---\r\n l2\r\n\r\n", tc.expand("no_indent_test/test3::Test", :for => :dummy)
end
def test_template_resolution
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal "Sub1\r\nSub1 in sub1\r\n", tc.expand('template_resolution_test/test::Test', :for => :dummy)
assert_equal "Sub1\r\nSub1\r\nSub1 in sub1\r\n", tc.expand('template_resolution_test/sub1::Test', :for => :dummy)
end
def test_evaluate
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal "xx1xxxx2xxxx3xxxx4xx\r\n", tc.expand('evaluate_test/test::Test', :for => :dummy)
end
def test_define_local
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal "Local1\r\n", tc.expand('define_local_test/test::Test', :for => :dummy)
assert_raises StandardError do
tc.expand('define_local_test/test::TestForbidden', :for => :dummy)
end
end
def test_no_backslash_r
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
expected = ""
File.open(OUTPUT_DIR+"/expected_result3.txt") {|f| expected = f.read}
assert_equal expected, tc.expand('no_backslash_r_test::Test', :for => :dummy).to_s
end
def test_callback_indent
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal("|before callback\r\n |in callback\r\n|after callback\r\n |after iinc\r\n",
tc.expand('callback_indent_test/a::caller', :for => :dummy))
end
def test_indent_nonl_at_eof
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal(" Sub\n",
tc.expand('indent_nonl_at_eof_test/test::Test', :for => :dummy))
end
def test_indent_same_line_sub
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal(" Start Sub2\r\n Sub\r\n",
tc.expand('indent_same_line_sub/test::Test', :for => :dummy))
end
def test_line_endings
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
tc.expand('line_endings/unix::Unix', :for => :dummy)
tc.expand('line_endings/windows::Windows', :for => :dummy)
tc.expand('line_endings/mixed::Mixed', :for => :dummy)
unix = binread(OUTPUT_DIR+'/line_endings_unix.txt')
assert unix.include?("|\n") && !unix.include?("|\r\n"), unix
windows = binread(OUTPUT_DIR+'/line_endings_windows.txt')
assert windows.include?("|\r\n") && !windows.include?("|\n"), windows
mixed = binread(OUTPUT_DIR+'/line_endings_mixed.txt')
assert mixed.include?("|\r\n") && mixed.include?("|\n"), mixed
end
def test_ws
tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR)
tc.load(TEMPLATES_DIR)
assert_equal("/*\n *\n */\n",
tc.expand('ws_test::WSTest', :for => :dummy))
assert_equal("somevar = 1;\n",
tc.expand('ws_test::WSTest2', :for => :dummy))
assert_equal(" /*\n *\n */\n",
tc.expand('ws_test::WSTest3', :for => :dummy))
end
private
def binread(file)
result = nil
File.open(file, "rb") do |f|
result = f.read
end
result
end
end
rgen-0.10.2/test/template_language_test/ 0000775 0000000 0000000 00000000000 14560401624 0020223 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/.gitignore 0000664 0000000 0000000 00000000111 14560401624 0022204 0 ustar 00root root 0000000 0000000 line_endings_mixed.txt
line_endings_unix.txt
line_endings_windows.txt
rgen-0.10.2/test/template_language_test/expected_result1.txt 0000664 0000000 0000000 00000001105 14560401624 0024241 0 ustar 00root root 0000000 0000000 Document: SomeDocument
by Martin, EMail: martin(at)somewhe.re and Otherguy, EMail: other(at)somewhereel.se
Index:
1 Intro in SomeDocument
2 MainPart in SomeDocument
3 Summary in SomeDocument
----------------
Chapters in one line:
*** Intro ***, *** MainPart ***, *** Summary ***
Chapters each in one line:
*** Intro ***,
*** MainPart ***,
*** Summary ***
Here are some code examples:
int myArray[5] = {
1,
2,
3,
4,
5
};
Text from Root
Text from Root
rgen-0.10.2/test/template_language_test/expected_result2.txt 0000664 0000000 0000000 00000000132 14560401624 0024241 0 ustar 00root root 0000000 0000000 int myArray[5] = {
1,
2,
3,
4,
5
};
Text from Root
Text from Root
rgen-0.10.2/test/template_language_test/expected_result3.txt 0000664 0000000 0000000 00000000230 14560401624 0024241 0 ustar 00root root 0000000 0000000 This file was created on Linux and does not contain \r before \n
The next blank line is done by the "nl" command which shall only add a \n, no \r:
END
rgen-0.10.2/test/template_language_test/indentStringTestDefaultIndent.out 0000664 0000000 0000000 00000000030 14560401624 0026724 0 ustar 00root root 0000000 0000000 <- your default here
rgen-0.10.2/test/template_language_test/indentStringTestTabIndent.out 0000664 0000000 0000000 00000000011 14560401624 0026045 0 ustar 00root root 0000000 0000000 <- tab
rgen-0.10.2/test/template_language_test/templates/ 0000775 0000000 0000000 00000000000 14560401624 0022221 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/callback_indent_test/ 0000775 0000000 0000000 00000000000 14560401624 0026355 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/callback_indent_test/a.tpl 0000664 0000000 0000000 00000000343 14560401624 0027316 0 ustar 00root root 0000000 0000000 <% define 'caller', :for => Object do %>
|before callback
<% expand 'b::do_callback' %>
|after callback
<%iinc%>
|after iinc
<% end %>
<% define 'callback', :for => Object do %>
|in callback
<% end %>
rgen-0.10.2/test/template_language_test/templates/callback_indent_test/b.tpl 0000664 0000000 0000000 00000000161 14560401624 0027315 0 ustar 00root root 0000000 0000000 <% define 'do_callback', :for => Object do %>
<%iinc%>
<% expand 'a::callback' %>
<%idec%>
<% end %>
rgen-0.10.2/test/template_language_test/templates/code/ 0000775 0000000 0000000 00000000000 14560401624 0023133 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/code/array.tpl 0000664 0000000 0000000 00000000601 14560401624 0024767 0 ustar 00root root 0000000 0000000 <% define 'ArrayDefinition', :for => CArray do %>
<%= getType %> <%= name %>[<%= size %>] = {<%iinc%>
<% expand 'InitValue', :foreach => initvalue, :separator => ",\r\n" %><%nl%><%idec%>
};
<% expand '../root::TextFromRoot' %>
<% expand '/root::TextFromRoot' %>
<% end %>
<% define 'InitValue', :for => PrimitiveInitValue do %>
<%= value %><%nows%>
<% end %>
rgen-0.10.2/test/template_language_test/templates/content/ 0000775 0000000 0000000 00000000000 14560401624 0023673 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/content/author.tpl 0000664 0000000 0000000 00000000305 14560401624 0025714 0 ustar 00root root 0000000 0000000 <% define 'Author', :for => Author do %>
<% expand 'SubAuthor' %>
<% end %>
<% define 'SubAuthor', :for => Author do %>
<%= name %>, EMail: <%= email.sub('@','(at)') %><%nows%>
<% end %>
rgen-0.10.2/test/template_language_test/templates/content/chapter.tpl 0000664 0000000 0000000 00000000126 14560401624 0026041 0 ustar 00root root 0000000 0000000 <% define 'Root', :for => Chapter do %>
*** <%= title %> ***<%nows%>
<% end %> rgen-0.10.2/test/template_language_test/templates/define_local_test/ 0000775 0000000 0000000 00000000000 14560401624 0025664 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/define_local_test/local.tpl 0000664 0000000 0000000 00000000231 14560401624 0027473 0 ustar 00root root 0000000 0000000 <% define 'CallLocal1', :for => Object do %>
<% expand 'Local1' %>
<% end %>
<% define_local 'Local1', :for => Object do %>
Local1
<% end %>
rgen-0.10.2/test/template_language_test/templates/define_local_test/test.tpl 0000664 0000000 0000000 00000000264 14560401624 0027366 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<% expand 'local::CallLocal1' %>
<% end %>
<% define 'TestForbidden', :for => Object do %>
<% expand 'local::Local1' %>
<% end %>
rgen-0.10.2/test/template_language_test/templates/evaluate_test/ 0000775 0000000 0000000 00000000000 14560401624 0025066 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/evaluate_test/test.tpl 0000664 0000000 0000000 00000000302 14560401624 0026561 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<%= [1,2,3,4].collect{|n| evaluate 'Eval', :for => n}.join %>
<% end %>
<% define 'Eval', :for => Object do %>
xx<%= this %>xx<%nows%>
<% end %>
rgen-0.10.2/test/template_language_test/templates/indent_nonl_at_eof_test/ 0000775 0000000 0000000 00000000000 14560401624 0027104 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/indent_nonl_at_eof_test/test.tpl 0000664 0000000 0000000 00000000662 14560401624 0030610 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<%iinc%>
<% expand 'Sub' %>
<%idec%>
<% end %>
<% define 'Sub', :for => Object do %>
Sub
<%# white space after the following end tag but no NL %>
<%# the bug was triggered when the @output of template loading %>
<%# was non empty and not terminated by a newline; %>
<%# without the space after end, the newline above this %>
<%# define block would be the last character %>
<% end %> rgen-0.10.2/test/template_language_test/templates/indent_same_line_sub/ 0000775 0000000 0000000 00000000000 14560401624 0026367 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/indent_same_line_sub/test.tpl 0000664 0000000 0000000 00000000544 14560401624 0030072 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<%iinc%>
Start <% expand 'Sub' %><%nows%>
<%idec%>
<% end %>
<% define 'Sub', :for => Object do %>
<%# here we have the noIndentNextLine flag set %>
<% expand 'Sub2' %>
Sub
<% end %>
<% define 'Sub2', :for => Object do %>
<%# here we reset the noIndentNextLine flag %>
Sub2
<% end %>
rgen-0.10.2/test/template_language_test/templates/indent_string_test.tpl 0000664 0000000 0000000 00000000421 14560401624 0026645 0 ustar 00root root 0000000 0000000 <% define 'IndentStringTest', :for => Object do %>
<% file 'indentStringTestDefaultIndent.out' do %>
<%iinc%>
<- your default here
<%idec%>
<% end %>
<% file 'indentStringTestTabIndent.out', "\t" do %>
<%iinc%>
<- tab
<%idec%>
<% end %>
<% end %> rgen-0.10.2/test/template_language_test/templates/index/ 0000775 0000000 0000000 00000000000 14560401624 0023330 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/index/c/ 0000775 0000000 0000000 00000000000 14560401624 0023552 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/index/c/cmod.tpl 0000664 0000000 0000000 00000000064 14560401624 0025215 0 ustar 00root root 0000000 0000000 <% define 'cmod' do %>Module C is special !<% end %> rgen-0.10.2/test/template_language_test/templates/index/chapter.tpl 0000664 0000000 0000000 00000000131 14560401624 0025472 0 ustar 00root root 0000000 0000000 <% define 'Root' do |idx, doc| %>
<%= idx%> <%= title %> in <%= doc.title %>
<% end %> rgen-0.10.2/test/template_language_test/templates/line_endings/ 0000775 0000000 0000000 00000000000 14560401624 0024657 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/line_endings/mixed.tpl 0000664 0000000 0000000 00000000234 14560401624 0026505 0 ustar 00root root 0000000 0000000 <% define 'Mixed', :for => Object do %>
<% file "line_endings_mixed.txt" do %>
first line (unix) |
second line (windows) |
<% end %>
<% end %>
rgen-0.10.2/test/template_language_test/templates/line_endings/unix.tpl 0000664 0000000 0000000 00000000210 14560401624 0026354 0 ustar 00root root 0000000 0000000 <% define 'Unix', :for => Object do %>
<% file "line_endings_unix.txt" do %>
first line |
second line |
<% end %>
<% end %>
rgen-0.10.2/test/template_language_test/templates/line_endings/windows.tpl 0000664 0000000 0000000 00000000224 14560401624 0027070 0 ustar 00root root 0000000 0000000 <% define 'Windows', :for => Object do %>
<% file "line_endings_windows.txt" do %>
first line |
second line |
<% end %>
<% end %>
rgen-0.10.2/test/template_language_test/templates/no_backslash_r_test.tpl 0000664 0000000 0000000 00000000315 14560401624 0026750 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
This file was created on Linux and does not contain \r before \n
The next blank line is done by the "nl" command which shall only add a \n, no \r:
<%nl%>END
<% end %> rgen-0.10.2/test/template_language_test/templates/no_indent_test/ 0000775 0000000 0000000 00000000000 14560401624 0025235 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/no_indent_test/no_indent.tpl 0000664 0000000 0000000 00000000106 14560401624 0027730 0 ustar 00root root 0000000 0000000 <% define 'NoIndent', :for => Object do %>
<---<%nows%>
<% end %>
rgen-0.10.2/test/template_language_test/templates/no_indent_test/sub1/ 0000775 0000000 0000000 00000000000 14560401624 0026107 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl 0000664 0000000 0000000 00000000106 14560401624 0030602 0 ustar 00root root 0000000 0000000 <% define 'NoIndent', :for => Object do %>
<---<%nows%>
<% end %>
rgen-0.10.2/test/template_language_test/templates/no_indent_test/test.tpl 0000664 0000000 0000000 00000001042 14560401624 0026732 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<%iinc%>
xxx<% expand 'NoIndent1' %>
xxx<% expand 'NoIndent2' %>
xxx<% expand 'NoIndent3' %>
xxx<% expand 'NoIndent4' %>
<%idec%>
<% end %>
<% define 'NoIndent1', :for => Object do %>
<---<%nows%>
<% end %>
<% define 'NoIndent2', :for => Object do %>
<% expand 'NoIndent1' %>
<% end %>
<% define 'NoIndent3', :for => Object do %>
<% expand 'no_indent::NoIndent' %>
<% end %>
<% define 'NoIndent4', :for => Object do %>
<% expand 'sub1/no_indent::NoIndent' %>
<% end %>
rgen-0.10.2/test/template_language_test/templates/no_indent_test/test2.tpl 0000664 0000000 0000000 00000000415 14560401624 0027017 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<%iinc%><%iinc%>
return <% expand 'Call1' %>;
<%idec%><%idec%>
<% end %>
<% define 'Call1', :for => Object do %>
x<% expand 'Call2' %><%nows%>
<% end %>
<% define 'Call2', :for => Object do %>
xxx<%nows%>
<% end %> rgen-0.10.2/test/template_language_test/templates/no_indent_test/test3.tpl 0000664 0000000 0000000 00000000242 14560401624 0027016 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<%iinc%>
l1<% expand 'Call1' %>
<%idec%>
<% end %>
<% define 'Call1', :for => Object do %>
<---
l2
<% end %>
rgen-0.10.2/test/template_language_test/templates/null_context_test.tpl 0000664 0000000 0000000 00000000712 14560401624 0026517 0 ustar 00root root 0000000 0000000 <% define 'NullContextTestBad', :for => Object do %>
<%# this must raise an exception %>
<% expand 'Callee', :for => nil %>
<% end %>
<% define 'NullContextTestBad2', :for => Object do %>
<%# this must raise an exception %>
<% expand 'Callee', :foreach => nil %>
<% end %>
<% define 'NullContextTestOk', :for => Object do %>
<%# however this is ok %>
<% expand 'Callee' %>
<% end %>
<% define 'Callee', :for => Object do %>
<% end %> rgen-0.10.2/test/template_language_test/templates/root.tpl 0000664 0000000 0000000 00000001524 14560401624 0023727 0 ustar 00root root 0000000 0000000 <% define 'Root' do %>
<% file 'testout.txt' do %>
Document: <%= title %>
<%nl%>
<%iinc%>
by <% expand 'content/author::Author', :foreach => authors, :separator => ' and ' %>
<%idec%>
<%nl%>
Index:<%iinc%>
<% for c in chapters %>
<% nr = (nr || 0); nr += 1 %>
<% expand 'index/chapter::Root', nr, this, :for => c %>
<% end %><%idec%>
<%nl%>
----------------
<%nl%>
Chapters in one line:
<% expand 'content/chapter::Root', :foreach => chapters, :separator => ", " %><%nl%>
<%nl%>
Chapters each in one line:
<% expand 'content/chapter::Root', :foreach => chapters, :separator => ",\r\n" %><%nl%>
<%nl%>
Here are some code examples:
<% expand 'code/array::ArrayDefinition', :for => sampleArray %>
<% end %>
<% end %>
<% define 'TextFromRoot' do %>
Text from Root
<% end %>
rgen-0.10.2/test/template_language_test/templates/template_resolution_test/ 0000775 0000000 0000000 00000000000 14560401624 0027356 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/template_resolution_test/sub1.tpl 0000664 0000000 0000000 00000000302 14560401624 0030744 0 ustar 00root root 0000000 0000000 <% define 'Sub1', :for => Object do %>
Sub1
<% end %>
<% define 'Test', :for => Object do %>
<% expand 'Sub1' %>
<% expand 'sub1::Sub1' %>
<% expand 'sub1/sub1::Sub1' %>
<% end %>
rgen-0.10.2/test/template_language_test/templates/template_resolution_test/sub1/ 0000775 0000000 0000000 00000000000 14560401624 0030230 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl 0000664 0000000 0000000 00000000100 14560401624 0031612 0 ustar 00root root 0000000 0000000 <% define 'Sub1', :for => Object do %>
Sub1 in sub1
<% end %> rgen-0.10.2/test/template_language_test/templates/template_resolution_test/test.tpl 0000664 0000000 0000000 00000000156 14560401624 0031060 0 ustar 00root root 0000000 0000000 <% define 'Test', :for => Object do %>
<% expand 'sub1::Sub1' %>
<% expand 'sub1/sub1::Sub1' %>
<% end %> rgen-0.10.2/test/template_language_test/templates/ws_test.tpl 0000664 0000000 0000000 00000000531 14560401624 0024431 0 ustar 00root root 0000000 0000000 <% define 'WSTest', :for => Object do %>
/*
<%ws%>*
<%ws%>*/
<% end %>
<% define 'WSTest2', :for => Object do %>
<% expand 'SubWithNows' %><%ws%>= 1;
<% end %>
<% define 'SubWithNows', :for => Object do %>
somevar<%nows%>
<% end %>
<% define 'WSTest3', :for => Object do %>
<%iinc%>
/*
<%ws%>*
<%ws%>*/
<%idec%>
<% end %>
rgen-0.10.2/test/template_language_test/testout.txt 0000664 0000000 0000000 00000001105 14560401624 0022470 0 ustar 00root root 0000000 0000000 Document: SomeDocument
by Martin, EMail: martin(at)somewhe.re and Otherguy, EMail: other(at)somewhereel.se
Index:
1 Intro in SomeDocument
2 MainPart in SomeDocument
3 Summary in SomeDocument
----------------
Chapters in one line:
*** Intro ***, *** MainPart ***, *** Summary ***
Chapters each in one line:
*** Intro ***,
*** MainPart ***,
*** Summary ***
Here are some code examples:
int myArray[5] = {
1,
2,
3,
4,
5
};
Text from Root
Text from Root
rgen-0.10.2/test/testmodel/ 0000775 0000000 0000000 00000000000 14560401624 0015506 5 ustar 00root root 0000000 0000000 rgen-0.10.2/test/testmodel/class_model_checker.rb 0000664 0000000 0000000 00000012142 14560401624 0022004 0 ustar 00root root 0000000 0000000 require 'metamodels/uml13_metamodel'
require 'metamodels/uml13_metamodel_ext'
module Testmodel
# Checks the UML Class model elements from the example model
#
module ClassModelChecker
def checkClassModel(envUML)
# check main package
mainPackage = envUML.find(:class => UML13::Package, :name => "HouseMetamodel").first
assert mainPackage != nil
# check Rooms package
subs = mainPackage.ownedElement.select{|e| e.is_a?(UML13::Package)}
assert_equal 1, subs.size
roomsPackage = subs.first
assert_equal "Rooms", roomsPackage.name
# check main package classes
classes = mainPackage.ownedElement.select{|e| e.is_a?(UML13::Class)}
assert_equal 3, classes.size
houseClass = classes.find{|c| c.name == "House"}
personClass = classes.find{|c| c.name == "Person"}
meetingPlaceClass = classes.find{|c| c.name == "MeetingPlace"}
cookingPlaceInterface = mainPackage.ownedElement.find{|e| e.is_a?(UML13::Interface) && e.name == "CookingPlace"}
assert houseClass != nil
assert personClass != nil
assert meetingPlaceClass != nil
assert cookingPlaceInterface != nil
# check Rooms package classes
classes = roomsPackage.ownedElement.select{|e| e.is_a?(UML13::Class)}
assert_equal 3, classes.size
roomClass = classes.find{|c| c.name == "Room"}
kitchenClass = classes.find{|c| c.name == "Kitchen"}
bathroomClass = classes.find{|c| c.name == "Bathroom"}
assert roomClass != nil
assert kitchenClass != nil
assert bathroomClass != nil
# check Room inheritance
assert_equal 2, roomClass.specialization.child.size
assert nil != roomClass.specialization.child.find{|c| c.name == "Kitchen"}
assert nil != roomClass.specialization.child.find{|c| c.name == "Bathroom"}
assert_equal 2, kitchenClass.generalization.parent.size
assert_equal roomClass.object_id, kitchenClass.generalization.parent.find{|c| c.name == "Room"}.object_id
assert_equal meetingPlaceClass.object_id, kitchenClass.generalization.parent.find{|c| c.name == "MeetingPlace"}.object_id
assert_equal 1, bathroomClass.generalization.parent.size
assert_equal roomClass.object_id, bathroomClass.generalization.parent.first.object_id
assert nil != kitchenClass.clientDependency.find{|d| d.stereotype.name == "implements"}
assert_equal cookingPlaceInterface.object_id, kitchenClass.clientDependency.supplier.find{|c| c.name == "CookingPlace"}.object_id
assert_equal kitchenClass.object_id, cookingPlaceInterface.supplierDependency.client.find{|c| c.name == "Kitchen"}.object_id
# check House-Room "part of" association
assert_equal 1, houseClass.localCompositeEnd.size
roomEnd = houseClass.localCompositeEnd.first.otherEnd
assert_equal UML13::Association, roomEnd.association.class
assert_equal roomClass.object_id, roomEnd.type.object_id
assert_equal "room", roomEnd.name
assert_equal UML13::Multiplicity, roomEnd.multiplicity.class
assert_equal "1", roomEnd.multiplicity.range.first.lower
assert_equal "*", roomEnd.multiplicity.range.first.upper
assert_equal 1, roomClass.remoteCompositeEnd.size
assert_equal houseClass.object_id, roomClass.remoteCompositeEnd.first.type.object_id
assert_equal "house", roomClass.remoteCompositeEnd.first.name
# check House OUT associations
assert_equal 2, houseClass.remoteNavigableEnd.size
bathEnd = houseClass.remoteNavigableEnd.find{|e| e.name == "bathroom"}
kitchenEnd = houseClass.remoteNavigableEnd.find{|e| e.name== "kitchen"}
assert bathEnd != nil
assert kitchenEnd != nil
assert_equal UML13::Association, bathEnd.association.class
assert_equal UML13::Association, kitchenEnd.association.class
assert_equal "1", kitchenEnd.multiplicity.range.first.lower
assert_equal "1", kitchenEnd.multiplicity.range.first.upper
# check House IN associations
assert_equal 3, houseClass.localNavigableEnd.size
homeEnd = houseClass.localNavigableEnd.find{|e| e.name == "home"}
assert homeEnd != nil
assert_equal UML13::Association, homeEnd.association.class
assert_equal "0", homeEnd.multiplicity.range.first.lower
assert_equal "*", homeEnd.multiplicity.range.first.upper
# check House all associations
assert_equal 4, houseClass.associationEnd.size
end
def checkClassModelPartial(envUML)
# HouseMetamodel package is not part of the partial export
mainPackage = envUML.find(:class => UML13::Package, :name => "HouseMetamodel").first
assert_nil mainPackage
roomsPackage = envUML.find(:class => UML13::Package, :name => "Rooms").first
assert roomsPackage != nil
roomClass = envUML.find(:class => UML13::Class, :name => "Room").first
assert roomClass != nil
# House is created from an EAStub
houseClass = roomClass.remoteCompositeEnd.first.type
assert houseClass != nil
assert_equal "House", houseClass.name
# House is not in a package since it's just a stub
assert houseClass.namespace.nil?
# in the partial model, House has only 3 (not 4) associations
# since the fourth class (Person) is not in Rooms package
assert_equal 3, houseClass.associationEnd.size
end
end
end rgen-0.10.2/test/testmodel/ea_testmodel.eap 0000664 0000000 0000000 00004614000 14560401624 0020647 0 ustar 00root root 0000000 0000000 Standard Jet DB µnb` ÂUé©gr@? œ~Ÿÿ…š1Åyºí0¼ßÌcÙíÇŸFûмN†ûì7]DœúÆ^(æ¶Š`T”{6õß±wôCϯ±34ay[’µ|*ñ|™˜ýOJ”l>`&_•øÐ‰$…gÆ'DÒîÏeíÿÇF¡xíé- ¸ è {ö % Ê
&