diff --git a/lib/puppet/vendor/load_rgen.rb b/lib/puppet/vendor/load_rgen.rb new file mode 100644 index 000000000..ba1f1c5c7 --- /dev/null +++ b/lib/puppet/vendor/load_rgen.rb @@ -0,0 +1 @@ +$: << File.join([File.dirname(__FILE__), "rgen/lib"]) diff --git a/lib/puppet/vendor/require_vendored.rb b/lib/puppet/vendor/require_vendored.rb index 141c810de..d0bcad6ee 100644 --- a/lib/puppet/vendor/require_vendored.rb +++ b/lib/puppet/vendor/require_vendored.rb @@ -1,7 +1,8 @@ # This adds upfront requirements on vendored code found under lib/vendor/x # Add one requirement per vendored package (or a comment if it is loaded on demand). require 'safe_yaml' require 'puppet/vendor/safe_yaml_patches' # The vendored library 'semantic' is loaded on demand. +# The vendored library 'rgen' is loaded on demand. diff --git a/lib/puppet/vendor/rgen/.gitignore b/lib/puppet/vendor/rgen/.gitignore new file mode 100644 index 000000000..d41f82329 --- /dev/null +++ b/lib/puppet/vendor/rgen/.gitignore @@ -0,0 +1,5 @@ +.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 diff --git a/lib/puppet/vendor/rgen/.project b/lib/puppet/vendor/rgen/.project new file mode 100644 index 000000000..ba7e10352 --- /dev/null +++ b/lib/puppet/vendor/rgen/.project @@ -0,0 +1,17 @@ + + + rgen-head + + + + + + org.rubypeople.rdt.core.rubybuilder + + + + + + org.rubypeople.rdt.core.rubynature + + diff --git a/lib/puppet/vendor/rgen/CHANGELOG b/lib/puppet/vendor/rgen/CHANGELOG new file mode 100644 index 000000000..bac8f6d8a --- /dev/null +++ b/lib/puppet/vendor/rgen/CHANGELOG @@ -0,0 +1,197 @@ +=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 + diff --git a/lib/puppet/vendor/rgen/MIT-LICENSE b/lib/puppet/vendor/rgen/MIT-LICENSE new file mode 100644 index 000000000..e124b6ce0 --- /dev/null +++ b/lib/puppet/vendor/rgen/MIT-LICENSE @@ -0,0 +1,20 @@ +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. diff --git a/lib/puppet/vendor/rgen/README.rdoc b/lib/puppet/vendor/rgen/README.rdoc new file mode 100644 index 000000000..b6dde344e --- /dev/null +++ b/lib/puppet/vendor/rgen/README.rdoc @@ -0,0 +1,78 @@ += 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.6, 1.8.7 and 1.9.x +* 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. + diff --git a/lib/puppet/vendor/rgen/Rakefile b/lib/puppet/vendor/rgen/Rakefile new file mode 100644 index 000000000..5d3e9e77b --- /dev/null +++ b/lib/puppet/vendor/rgen/Rakefile @@ -0,0 +1,41 @@ +require 'rubygems/package_task' +require 'rdoc/task' + +RGenGemSpec = Gem::Specification.new do |s| + s.name = %q{rgen} + s.version = "0.7.0" + s.date = Time.now.strftime("%Y-%m-%d") + s.summary = %q{Ruby Modelling and Generator Framework} + s.email = %q{martin dot thiede at gmx de} + s.homepage = %q{http://ruby-gen.org} + s.rubyforge_project = %q{rgen} + s.description = %q{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.} + s.authors = ["Martin Thiede"] + gemfiles = Rake::FileList.new + gemfiles.include("{lib,test}/**/*") + gemfiles.include("README.rdoc", "CHANGELOG", "MIT-LICENSE", "Rakefile") + gemfiles.exclude(/\b\.bak\b/) + s.files = gemfiles + s.rdoc_options = ["--main", "README.rdoc", "-x", "test", "-x", "metamodels", "-x", "ea_support/uml13*"] + s.extra_rdoc_files = ["README.rdoc", "CHANGELOG", "MIT-LICENSE"] +end + +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 + +task :prepare_package_rdoc => :rdoc do + RGenPackageTask.package_files.include("doc/**/*") +end + +task :release => [:prepare_package_rdoc, :package] + +task :clobber => [:clobber_rdoc, :clobber_package] diff --git a/lib/puppet/vendor/rgen/TODO b/lib/puppet/vendor/rgen/TODO new file mode 100644 index 000000000..f91158920 --- /dev/null +++ b/lib/puppet/vendor/rgen/TODO @@ -0,0 +1,41 @@ +=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 + diff --git a/lib/puppet/vendor/rgen/anounce.txt b/lib/puppet/vendor/rgen/anounce.txt new file mode 100644 index 000000000..c70de78af --- /dev/null +++ b/lib/puppet/vendor/rgen/anounce.txt @@ -0,0 +1,61 @@ +=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). + diff --git a/lib/puppet/vendor/rgen/design_rationale.txt b/lib/puppet/vendor/rgen/design_rationale.txt new file mode 100644 index 000000000..0c6213c78 --- /dev/null +++ b/lib/puppet/vendor/rgen/design_rationale.txt @@ -0,0 +1,71 @@ +=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 + \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/ea_support/ea_support.rb b/lib/puppet/vendor/rgen/lib/ea_support/ea_support.rb new file mode 100644 index 000000000..a9062b9f6 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/ea_support.rb @@ -0,0 +1,54 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/ea_support/id_store.rb b/lib/puppet/vendor/rgen/lib/ea_support/id_store.rb new file mode 100644 index 000000000..05af13ebf --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/id_store.rb @@ -0,0 +1,32 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel.rb b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel.rb new file mode 100644 index 000000000..7dc01a02c --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel.rb @@ -0,0 +1,562 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel_ext.rb b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel_ext.rb new file mode 100644 index 000000000..6c338c6aa --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel_ext.rb @@ -0,0 +1,45 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel_generator.rb b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel_generator.rb new file mode 100644 index 000000000..725d601a3 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_metamodel_generator.rb @@ -0,0 +1,43 @@ +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") diff --git a/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_to_uml13.rb b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_to_uml13.rb new file mode 100644 index 000000000..d857bc293 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/uml13_ea_to_uml13.rb @@ -0,0 +1,103 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/ea_support/uml13_to_uml13_ea.rb b/lib/puppet/vendor/rgen/lib/ea_support/uml13_to_uml13_ea.rb new file mode 100644 index 000000000..2d8dc7cf6 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/ea_support/uml13_to_uml13_ea.rb @@ -0,0 +1,89 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/metamodels/uml13_metamodel.rb b/lib/puppet/vendor/rgen/lib/metamodels/uml13_metamodel.rb new file mode 100644 index 000000000..01157df96 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/metamodels/uml13_metamodel.rb @@ -0,0 +1,559 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/metamodels/uml13_metamodel_ext.rb b/lib/puppet/vendor/rgen/lib/metamodels/uml13_metamodel_ext.rb new file mode 100644 index 000000000..bc7eb7178 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/metamodels/uml13_metamodel_ext.rb @@ -0,0 +1,26 @@ +# 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 diff --git a/lib/puppet/vendor/rgen/lib/mmgen/metamodel_generator.rb b/lib/puppet/vendor/rgen/lib/mmgen/metamodel_generator.rb new file mode 100644 index 000000000..9ea9fe608 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/mmgen/metamodel_generator.rb @@ -0,0 +1,20 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/mmgen/mm_ext/ecore_mmgen_ext.rb b/lib/puppet/vendor/rgen/lib/mmgen/mm_ext/ecore_mmgen_ext.rb new file mode 100644 index 000000000..17dd81c69 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/mmgen/mm_ext/ecore_mmgen_ext.rb @@ -0,0 +1,91 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/mmgen/mmgen.rb b/lib/puppet/vendor/rgen/lib/mmgen/mmgen.rb new file mode 100644 index 000000000..41a696b06 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/mmgen/mmgen.rb @@ -0,0 +1,28 @@ +$:.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) diff --git a/lib/puppet/vendor/rgen/lib/mmgen/templates/annotations.tpl b/lib/puppet/vendor/rgen/lib/mmgen/templates/annotations.tpl new file mode 100644 index 000000000..b9f5d1bfd --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/mmgen/templates/annotations.tpl @@ -0,0 +1,37 @@ +<% 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 %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/mmgen/templates/metamodel_generator.tpl b/lib/puppet/vendor/rgen/lib/mmgen/templates/metamodel_generator.tpl new file mode 100644 index 000000000..5e6877a74 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/mmgen/templates/metamodel_generator.tpl @@ -0,0 +1,172 @@ + +<% 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 %> diff --git a/lib/puppet/vendor/rgen/lib/rgen/array_extensions.rb b/lib/puppet/vendor/rgen/lib/rgen/array_extensions.rb new file mode 100644 index 000000000..d1d7beda0 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/array_extensions.rb @@ -0,0 +1,45 @@ +# 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore.rb b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore.rb new file mode 100644 index 000000000..251f60733 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore.rb @@ -0,0 +1,218 @@ +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 + end + + class EModelElement < RGen::MetamodelBuilder::MMBase + end + + class EAnnotation < RGen::MetamodelBuilder::MMMultiple(EModelElement, EObject) + has_attr 'source', String + end + + class ENamedElement < EModelElement + has_attr 'name', String + end + + class ETypedElement < ENamedElement + 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 + 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 + 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 + has_attr 'interface', Boolean + 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_builder_methods.rb b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_builder_methods.rb new file mode 100644 index 000000000..8d78415b0 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_builder_methods.rb @@ -0,0 +1,81 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_ext.rb b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_ext.rb new file mode 100644 index 000000000..8c3441389 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_ext.rb @@ -0,0 +1,69 @@ +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 + + end + end +end diff --git a/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_interface.rb b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_interface.rb new file mode 100644 index 000000000..89ec32547 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_interface.rb @@ -0,0 +1,47 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_to_ruby.rb b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_to_ruby.rb new file mode 100644 index 000000000..c69bd38ed --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/ecore/ecore_to_ruby.rb @@ -0,0 +1,167 @@ +require 'rgen/ecore/ecore' + +module RGen + +module ECore + +class ECoreToRuby + + def initialize + @modules = {} + @classifiers = {} + @features_added = {} + @in_create_module = false + end + + def create_module(epackage) + return @modules[epackage] if @modules[epackage] + + top = (@in_create_module == false) + @in_create_module = true + + m = Module.new do + extend RGen::MetamodelBuilder::ModuleExtension + end + @modules[epackage] = m + + epackage.eSubpackages.each{|p| create_module(p)} + m._set_ecore_internal(epackage) + + create_module(epackage.eSuperPackage).const_set(epackage.name, m) if epackage.eSuperPackage + + # create classes only after all modules have been created + # otherwise classes may be created multiple times + if top + epackage.eAllClassifiers.each do |c| + if c.is_a?(RGen::ECore::EClass) + create_class(c) + elsif c.is_a?(RGen::ECore::EEnum) + create_enum(c) + end + end + @in_create_module = false + end + m + end + + def create_class(eclass) + return @classifiers[eclass] if @classifiers[eclass] + + c = Class.new(super_class(eclass)) do + abstract if eclass.abstract + class << self + attr_accessor :_ecore_to_ruby + end + end + class << eclass + attr_accessor :instanceClass + def instanceClassName + instanceClass.to_s + end + end + eclass.instanceClass = c + c::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] = c + c._set_ecore_internal(eclass) + c._ecore_to_ruby = self + + create_module(eclass.ePackage).const_set(eclass.name, c) + c + 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 + + create_module(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 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 + + def super_class(eclass) + super_types = eclass.eSuperTypes + case super_types.size + when 0 + RGen::MetamodelBuilder::MMBase + when 1 + create_class(super_types.first) + else + RGen::MetamodelBuilder::MMMultiple(*super_types.collect{|t| create_class(t)}) + end + end + +end + +end + +end + diff --git a/lib/puppet/vendor/rgen/lib/rgen/ecore/ruby_to_ecore.rb b/lib/puppet/vendor/rgen/lib/rgen/ecore/ruby_to_ecore.rb new file mode 100644 index 000000000..841b72f89 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/ecore/ruby_to_ecore.rb @@ -0,0 +1,91 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/environment.rb b/lib/puppet/vendor/rgen/lib/rgen/environment.rb new file mode 100644 index 000000000..cf88bd4ea --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/environment.rb @@ -0,0 +1,129 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/fragment/dump_file_cache.rb b/lib/puppet/vendor/rgen/lib/rgen/fragment/dump_file_cache.rb new file mode 100644 index 000000000..3963aeb2b --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/fragment/dump_file_cache.rb @@ -0,0 +1,63 @@ +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 + + diff --git a/lib/puppet/vendor/rgen/lib/rgen/fragment/fragmented_model.rb b/lib/puppet/vendor/rgen/lib/rgen/fragment/fragmented_model.rb new file mode 100644 index 000000000..155c767ff --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/fragment/fragmented_model.rb @@ -0,0 +1,140 @@ +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 + 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/fragment/model_fragment.rb b/lib/puppet/vendor/rgen/lib/rgen/fragment/model_fragment.rb new file mode 100644 index 000000000..4e79f6126 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/fragment/model_fragment.rb @@ -0,0 +1,289 @@ +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. + # + 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 + + diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/abstract_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/abstract_instantiator.rb new file mode 100644 index 000000000..8329a3851 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/abstract_instantiator.rb @@ -0,0 +1,66 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/abstract_xml_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/abstract_xml_instantiator.rb new file mode 100644 index 000000000..441950e73 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/abstract_xml_instantiator.rb @@ -0,0 +1,66 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/default_xml_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/default_xml_instantiator.rb new file mode 100644 index 000000000..e8d2571f8 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/default_xml_instantiator.rb @@ -0,0 +1,117 @@ +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 + mod.const_get(class_name).new + 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/ecore_xml_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/ecore_xml_instantiator.rb new file mode 100644 index 000000000..bc911afd4 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/ecore_xml_instantiator.rb @@ -0,0 +1,169 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_instantiator.rb new file mode 100644 index 000000000..1aa4d951e --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_instantiator.rb @@ -0,0 +1,126 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_parser.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_parser.rb new file mode 100644 index 000000000..02c04a0e8 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_parser.rb @@ -0,0 +1,331 @@ +# +# 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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_parser.y b/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_parser.y new file mode 100644 index 000000000..7bdc46464 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/json_parser.y @@ -0,0 +1,94 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/nodebased_xml_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/nodebased_xml_instantiator.rb new file mode 100644 index 000000000..5abc05523 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/nodebased_xml_instantiator.rb @@ -0,0 +1,137 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/qualified_name_resolver.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/qualified_name_resolver.rb new file mode 100644 index 000000000..7fe1c7cc2 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/qualified_name_resolver.rb @@ -0,0 +1,97 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/reference_resolver.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/reference_resolver.rb new file mode 100644 index 000000000..87a4a834a --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/reference_resolver.rb @@ -0,0 +1,128 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/resolution_helper.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/resolution_helper.rb new file mode 100644 index 000000000..5452fec4c --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/resolution_helper.rb @@ -0,0 +1,47 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/instantiator/xmi11_instantiator.rb b/lib/puppet/vendor/rgen/lib/rgen/instantiator/xmi11_instantiator.rb new file mode 100644 index 000000000..af9657b16 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/instantiator/xmi11_instantiator.rb @@ -0,0 +1,168 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder.rb new file mode 100644 index 000000000..6fd680f7a --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder.rb @@ -0,0 +1,224 @@ +# 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/builder_extensions.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/builder_extensions.rb new file mode 100644 index 000000000..dfa4a517e --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/builder_extensions.rb @@ -0,0 +1,556 @@ +# 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) %> + <% 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) %> + ( @<%= name %> ? @<%= name %>.dup : [] ) + end + <% if name != "class" %> + alias <%= name %> get<%= firstToUpper(name) %> + <% 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 @<%= name %> + return if val.nil? || (@<%= name %>.any?{|e| e.object_id == val.object_id} && (val.is_a?(MMBase) || val.is_a?(MMGeneric))) + <%= 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 @<%= name %> + @<%= name %>.each_with_index do |e,i| + if e.object_id == val.object_id + @<%= 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) + } + val.each {|v| + add<%= firstToUpper(name) %>(v) + } + end + alias <%= name %>= set<%= firstToUpper(name) %> + + def _register<%= firstToUpper(name) %>(val) + @<%= name %> = [] unless @<%= 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?(#{props.impl_type}) || #{varname}.is_a?(MMGeneric)" + code << " || #{varname}.is_a?(BigDecimal)" if props.impl_type == Float && defined?(BigDecimal) + code << "\n" + expected = props.impl_type.to_s + elsif props.impl_type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum) + code << "unless #{varname}.nil? || [#{props.impl_type.literals_as_strings.join(',')}].include?(#{varname}) || #{varname}.is_a?(MMGeneric)\n" + expected = "["+props.impl_type.literals_as_strings.join(',')+"]" + else + raise StandardError.new("Unkown type "+props.impl_type.to_s) + end + code << "raise _assignmentTypeError(self,#{varname},\"#{expected}\")\n" + code << "end" + code + end + + def _ownProps(props) + Hash[*(props.select{|k,v| !(k.to_s =~ /^opposite_/)}.flatten)] + end + + def _oppositeProps(props) + r = {} + props.each_pair do |k,v| + if k.to_s =~ /^opposite_(.*)$/ + r[$1.to_sym] = v + end + end + r + end + + def _setManyUpperBound(props) + props[:upperBound] = -1 unless props[:upperBound].is_a?(Integer) && props[:upperBound] > 1 + props + end + +end + +end + +end diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/builder_runtime.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/builder_runtime.rb new file mode 100644 index 000000000..3ddbe3b5a --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/builder_runtime.rb @@ -0,0 +1,174 @@ +# 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 + + def getGeneric(role) + send("get#{firstToUpper(role.to_s)}") + end + + def getGenericAsArray(role) + result = getGeneric(role) + result = [result].compact unless result.is_a?(Array) + result + 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. + # + # 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/constant_order_helper.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/constant_order_helper.rb new file mode 100644 index 000000000..51c8034a0 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/constant_order_helper.rb @@ -0,0 +1,89 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/data_types.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/data_types.rb new file mode 100644 index 000000000..17268f4e3 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/data_types.rb @@ -0,0 +1,77 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/intermediate/annotation.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/intermediate/annotation.rb new file mode 100644 index 000000000..eaa1eee6e --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/intermediate/annotation.rb @@ -0,0 +1,30 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/intermediate/feature.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/intermediate/feature.rb new file mode 100644 index 000000000..ed319ea1d --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/intermediate/feature.rb @@ -0,0 +1,168 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/mm_multiple.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/mm_multiple.rb new file mode 100644 index 000000000..8c4b402bf --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/mm_multiple.rb @@ -0,0 +1,23 @@ + +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/module_extension.rb b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/module_extension.rb new file mode 100644 index 000000000..16d61b0e0 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/metamodel_builder/module_extension.rb @@ -0,0 +1,42 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/model_builder.rb b/lib/puppet/vendor/rgen/lib/rgen/model_builder.rb new file mode 100644 index 000000000..f4643c8d7 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/model_builder.rb @@ -0,0 +1,32 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/model_builder/builder_context.rb b/lib/puppet/vendor/rgen/lib/rgen/model_builder/builder_context.rb new file mode 100644 index 000000000..09de32a22 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/model_builder/builder_context.rb @@ -0,0 +1,334 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/model_builder/model_serializer.rb b/lib/puppet/vendor/rgen/lib/rgen/model_builder/model_serializer.rb new file mode 100644 index 000000000..7799c4dc0 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/model_builder/model_serializer.rb @@ -0,0 +1,225 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/model_builder/reference_resolver.rb b/lib/puppet/vendor/rgen/lib/rgen/model_builder/reference_resolver.rb new file mode 100644 index 000000000..cf870b0c2 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/model_builder/reference_resolver.rb @@ -0,0 +1,156 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/serializer/json_serializer.rb b/lib/puppet/vendor/rgen/lib/rgen/serializer/json_serializer.rb new file mode 100644 index 000000000..46e7bcf68 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/serializer/json_serializer.rb @@ -0,0 +1,121 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/serializer/opposite_reference_filter.rb b/lib/puppet/vendor/rgen/lib/rgen/serializer/opposite_reference_filter.rb new file mode 100644 index 000000000..7a6f7235c --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/serializer/opposite_reference_filter.rb @@ -0,0 +1,18 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/serializer/qualified_name_provider.rb b/lib/puppet/vendor/rgen/lib/rgen/serializer/qualified_name_provider.rb new file mode 100644 index 000000000..1fcce1bc5 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/serializer/qualified_name_provider.rb @@ -0,0 +1,47 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/serializer/xmi11_serializer.rb b/lib/puppet/vendor/rgen/lib/rgen/serializer/xmi11_serializer.rb new file mode 100644 index 000000000..f76f6c3c7 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/serializer/xmi11_serializer.rb @@ -0,0 +1,116 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/serializer/xmi20_serializer.rb b/lib/puppet/vendor/rgen/lib/rgen/serializer/xmi20_serializer.rb new file mode 100644 index 000000000..bfe7d8a3d --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/serializer/xmi20_serializer.rb @@ -0,0 +1,71 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/serializer/xml_serializer.rb b/lib/puppet/vendor/rgen/lib/rgen/serializer/xml_serializer.rb new file mode 100644 index 000000000..c89da8ec1 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/serializer/xml_serializer.rb @@ -0,0 +1,98 @@ +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 "\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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/template_language.rb b/lib/puppet/vendor/rgen/lib/rgen/template_language.rb new file mode 100644 index 000000000..05fe5cc47 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/template_language.rb @@ -0,0 +1,297 @@ +# 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 +# * <%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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/template_language/directory_template_container.rb b/lib/puppet/vendor/rgen/lib/rgen/template_language/directory_template_container.rb new file mode 100644 index 000000000..5f355b6c4 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/template_language/directory_template_container.rb @@ -0,0 +1,83 @@ +# 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/template_language/output_handler.rb b/lib/puppet/vendor/rgen/lib/rgen/template_language/output_handler.rb new file mode 100644 index 000000000..e6cd692a1 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/template_language/output_handler.rb @@ -0,0 +1,87 @@ +# RGen Framework +# (c) Martin Thiede, 2006 + +module RGen + +module TemplateLanguage + + class OutputHandler + attr_writer :indent + attr_accessor :noIndentNextLine + + def initialize(indent=0, indentString=" ", mode=:explicit) + self.mode = mode + @indent = indent + @indentString = indentString + @state = :wait_for_nonws + @output = "" + 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) + return @output.concat(s) if s.is_a? OutputHandler + #puts [object_id, noIndentNextLine, @state, @output.to_s, s].inspect + s = s.to_str.gsub(/^[\t ]*\r?\n/,'') if @ignoreNextNL + s = s.to_str.gsub(/^\s+/,'') if @ignoreNextWS + @ignoreNextNL = @ignoreNextWS = false if s =~ /\S/ + if @mode == :direct + @output.concat(s) + elsif @mode == :explicit + while s.size > 0 + if @state == :wait_for_nl + if s =~ /\A([^\r\n]*\r?\n)(.*)/m + rest = $2 + @output.concat($1.gsub(/[\t ]+(?=\r|\n)/,'')) + s = rest || "" + @state = :wait_for_nonws + else + @output.concat(s) + s = "" + end + elsif @state == :wait_for_nonws + if s =~ /\A\s*(\S+.*)/m + s = $1 || "" + if !@noIndentNextLine && !(@output.to_s.size > 0 && @output.to_s[-1] != "\n"[0]) + @output.concat(@indentString * @indent) + else + @noIndentNextLine = false + end + @state = :wait_for_nl + else + s = "" + 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 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/template_language/template_container.rb b/lib/puppet/vendor/rgen/lib/rgen/template_language/template_container.rb new file mode 100644 index 000000000..0a8331448 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/template_language/template_container.rb @@ -0,0 +1,234 @@ +# 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,nil,nil,'@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(" ") + 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 + output = _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) || + (@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) && !old_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) + if @output.is_a? OutputHandler + @output.direct_concat(s) + 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 diff --git a/lib/puppet/vendor/rgen/lib/rgen/template_language/template_helper.rb b/lib/puppet/vendor/rgen/lib/rgen/template_language/template_helper.rb new file mode 100644 index 000000000..5eb3a98a2 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/template_language/template_helper.rb @@ -0,0 +1,26 @@ +# 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/transformer.rb b/lib/puppet/vendor/rgen/lib/rgen/transformer.rb new file mode 100644 index 000000000..097c089c5 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/transformer.rb @@ -0,0 +1,475 @@ +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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/auto_class_creator.rb b/lib/puppet/vendor/rgen/lib/rgen/util/auto_class_creator.rb new file mode 100644 index 000000000..1bdaa44f0 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/auto_class_creator.rb @@ -0,0 +1,61 @@ +# 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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/cached_glob.rb b/lib/puppet/vendor/rgen/lib/rgen/util/cached_glob.rb new file mode 100644 index 000000000..c5718c8b6 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/cached_glob.rb @@ -0,0 +1,67 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/file_cache_map.rb b/lib/puppet/vendor/rgen/lib/rgen/util/file_cache_map.rb new file mode 100644 index 000000000..a8464d43b --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/file_cache_map.rb @@ -0,0 +1,124 @@ +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 + + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/file_change_detector.rb b/lib/puppet/vendor/rgen/lib/rgen/util/file_change_detector.rb new file mode 100644 index 000000000..061802efc --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/file_change_detector.rb @@ -0,0 +1,84 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/method_delegation.rb b/lib/puppet/vendor/rgen/lib/rgen/util/method_delegation.rb new file mode 100644 index 000000000..7d540e944 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/method_delegation.rb @@ -0,0 +1,114 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/model_comparator.rb b/lib/puppet/vendor/rgen/lib/rgen/util/model_comparator.rb new file mode 100644 index 000000000..f17ebc722 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/model_comparator.rb @@ -0,0 +1,68 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/model_comparator_base.rb b/lib/puppet/vendor/rgen/lib/rgen/util/model_comparator_base.rb new file mode 100644 index 000000000..bf8137f8c --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/model_comparator_base.rb @@ -0,0 +1,142 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/model_dumper.rb b/lib/puppet/vendor/rgen/lib/rgen/util/model_dumper.rb new file mode 100644 index 000000000..20fc2d886 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/model_dumper.rb @@ -0,0 +1,29 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/name_helper.rb b/lib/puppet/vendor/rgen/lib/rgen/util/name_helper.rb new file mode 100644 index 000000000..2f827de0c --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/name_helper.rb @@ -0,0 +1,42 @@ +# 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 + diff --git a/lib/puppet/vendor/rgen/lib/rgen/util/pattern_matcher.rb b/lib/puppet/vendor/rgen/lib/rgen/util/pattern_matcher.rb new file mode 100644 index 000000000..0429cd154 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/rgen/util/pattern_matcher.rb @@ -0,0 +1,329 @@ +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 + diff --git a/lib/puppet/vendor/rgen/lib/transformers/ecore_to_uml13.rb b/lib/puppet/vendor/rgen/lib/transformers/ecore_to_uml13.rb new file mode 100644 index 000000000..cbd832a35 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/transformers/ecore_to_uml13.rb @@ -0,0 +1,79 @@ +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 diff --git a/lib/puppet/vendor/rgen/lib/transformers/uml13_to_ecore.rb b/lib/puppet/vendor/rgen/lib/transformers/uml13_to_ecore.rb new file mode 100644 index 000000000..1bc57ba62 --- /dev/null +++ b/lib/puppet/vendor/rgen/lib/transformers/uml13_to_ecore.rb @@ -0,0 +1,127 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/array_extensions_test.rb b/lib/puppet/vendor/rgen/test/array_extensions_test.rb new file mode 100644 index 000000000..e6caa9037 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/array_extensions_test.rb @@ -0,0 +1,64 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/array_extensions' + +class ArrayExtensionsTest < Test::Unit::TestCase + + 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_raise NoMethodError do + a.name + end + assert_equal [33, 22], a>>:age + assert_raise 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_raise StandardError do + a.age + end + # the >> operator will try to call the method anyway + assert_raise NoMethodError do + a >> :age + end + end + + def test_hash_square + assert_equal({}, Hash[[]]) + end + + def test_to_str_on_empty_array + assert_raise NoMethodError do + [].to_str + end + end + +end diff --git a/lib/puppet/vendor/rgen/test/ea_instantiator_test.rb b/lib/puppet/vendor/rgen/test/ea_instantiator_test.rb new file mode 100644 index 000000000..bafae8af2 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/ea_instantiator_test.rb @@ -0,0 +1,35 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +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 < Test::Unit::TestCase + + 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/ea_serializer_test.rb b/lib/puppet/vendor/rgen/test/ea_serializer_test.rb new file mode 100644 index 000000000..c5ecc2755 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/ea_serializer_test.rb @@ -0,0 +1,23 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/environment' +require 'metamodels/uml13_metamodel' +require 'ea_support/ea_support' +require 'rgen/serializer/xmi11_serializer' + +class EASerializerTest < Test::Unit::TestCase + + 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/ecore_self_test.rb b/lib/puppet/vendor/rgen/test/ecore_self_test.rb new file mode 100644 index 000000000..ae523faa8 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/ecore_self_test.rb @@ -0,0 +1,54 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/ecore/ecore' +require 'rgen/array_extensions' + +class ECoreSelfTest < Test::Unit::TestCase + 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 diff --git a/lib/puppet/vendor/rgen/test/environment_test.rb b/lib/puppet/vendor/rgen/test/environment_test.rb new file mode 100644 index 000000000..aefd480e5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/environment_test.rb @@ -0,0 +1,90 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/environment' +require 'rgen/metamodel_builder' + +class EnvironmentTest < Test::Unit::TestCase + + 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 diff --git a/lib/puppet/vendor/rgen/test/json_test.rb b/lib/puppet/vendor/rgen/test/json_test.rb new file mode 100644 index 000000000..0c73b9723 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/json_test.rb @@ -0,0 +1,171 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/environment' +require 'rgen/metamodel_builder' +require 'rgen/serializer/json_serializer' +require 'rgen/instantiator/json_instantiator' + +class JsonTest < Test::Unit::TestCase + + 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_not_nil root + 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_raise 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 + diff --git a/lib/puppet/vendor/rgen/test/metamodel_builder_test.rb b/lib/puppet/vendor/rgen/test/metamodel_builder_test.rb new file mode 100644 index 000000000..4f0308b44 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_builder_test.rb @@ -0,0 +1,1482 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/metamodel_builder' +require 'rgen/array_extensions' +require 'bigdecimal' + +class MetamodelBuilderTest < Test::Unit::TestCase + + 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' + + 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_equal nil, sc.name + err = assert_raise StandardError do + sc.name = 5 + end + assert_match /In (\w+::)+SimpleClass : Can not use a 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_equal nil, sc.allowed + err = assert_raise StandardError do + sc.allowed = :someSymbol + end + assert_match /In (\w+::)+SimpleClass : Can not use a Symbol\(:someSymbol\) where a \[true,false\] is expected/, err.message + err = assert_raise 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_equal nil, sc.kind + err = assert_raise StandardError do + sc.kind = :false + end + assert_match /In (\w+::)+SimpleClass : Can not use a Symbol\(:false\) where a \[:simple,:extended\] is expected/, err.message + err = assert_raise 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.new("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 = 1234567890 + assert_equal 1234567890, sc.longWithDefault + assert sc.longWithDefault.is_a?(Bignum) + assert sc.longWithDefault.is_a?(Integer) + err = assert_raise 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_raise(StandardError) do + o.addLiterals(1) + end + assert_match /In (\w+::)+ManyAttrClass : Can not use a 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_raise(StandardError) do + o.literals = 1 + end + assert_match /In (\w+::)+ManyAttrClass : Can not use a 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_equal 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_raise 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_raise 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_equal 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_equal 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_equal 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_equal 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_equal 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_equal nil, bc.aClass + assert_equal 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_equal nil, ac.bClass + assert_equal 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_equal nil, b2.aClass + + a.bClass = b2 + assert_equal b2, a.bClass + assert_equal 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_raise StandardError do + bc.addAClasses :notaaclass + end + assert_match /In (\w+::)+BClassMM : Can not use a Symbol\(: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_raise StandardError do + ac.addBClasses :notabclass + end + assert_match /In (\w+::)+AClassMM : Can not use a Symbol\(: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 + 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_raise 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_raise StandardError do + BadDefaultValueLiteralContainer::Test1.call + end + assert_equal "Property integerWithDefault can not take value 1.1, expected an Integer", err.message + err = assert_raise StandardError do + BadDefaultValueLiteralContainer::Test2.call + end + assert_equal "Property integerWithDefault can not take value x, expected an Integer", err.message + err = assert_raise StandardError do + BadDefaultValueLiteralContainer::Test3.call + end + assert_equal "Property boolWithDefault can not take value 1, expected true or false", err.message + err = assert_raise StandardError do + BadDefaultValueLiteralContainer::Test4.call + end + assert_equal "Property floatWithDefault can not take value 1, expected a Float", err.message + err = assert_raise StandardError do + BadDefaultValueLiteralContainer::Test5.call + end + assert_equal "Property floatWithDefault can not take value true, expected a Float", err.message + err = assert_raise StandardError do + BadDefaultValueLiteralContainer::Test6.call + end + assert_equal "Property enumWithDefault can not take value xxx, expected one of :simple, :extended", err.message + err = assert_raise StandardError do + BadDefaultValueLiteralContainer::Test7.call + end + assert_equal "Property enumWithDefault can not take value 7, expected one of :simple, :extended", err.message + err = assert_raise 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 + assert_nothing_raised do + a.oneChild = RGen::MetamodelBuilder::MMGeneric.new + end + 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 + +end diff --git a/lib/puppet/vendor/rgen/test/metamodel_from_ecore_test.rb b/lib/puppet/vendor/rgen/test/metamodel_from_ecore_test.rb new file mode 100644 index 000000000..6617f652d --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_from_ecore_test.rb @@ -0,0 +1,57 @@ +$:.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_raise 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 + diff --git a/lib/puppet/vendor/rgen/test/metamodel_order_test.rb b/lib/puppet/vendor/rgen/test/metamodel_order_test.rb new file mode 100644 index 000000000..a81cad4b5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_order_test.rb @@ -0,0 +1,131 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/ecore/ecore' +require 'rgen/array_extensions' + +class MetamodelOrderTest < Test::Unit::TestCase + 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 + + diff --git a/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test.rb b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test.rb new file mode 100644 index 000000000..f0be7cf43 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test.rb @@ -0,0 +1,98 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +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 < Test::Unit::TestCase + + 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 diff --git a/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/.gitignore b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/.gitignore new file mode 100644 index 000000000..817ba4948 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/.gitignore @@ -0,0 +1,2 @@ +using_builtin_types_serialized.ecore + diff --git a/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/TestModel.rb b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/TestModel.rb new file mode 100644 index 000000000..01342286f --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/TestModel.rb @@ -0,0 +1,70 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/houseMetamodel.ecore b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/houseMetamodel.ecore new file mode 100644 index 000000000..6f5c01b03 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/houseMetamodel.ecore @@ -0,0 +1,42 @@ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/houseMetamodel_from_ecore.rb b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/houseMetamodel_from_ecore.rb new file mode 100644 index 000000000..a7a1104d6 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/houseMetamodel_from_ecore.rb @@ -0,0 +1,44 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/using_builtin_types.ecore b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/using_builtin_types.ecore new file mode 100644 index 000000000..2f93239c4 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/metamodel_roundtrip_test/using_builtin_types.ecore @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/lib/puppet/vendor/rgen/test/method_delegation_test.rb b/lib/puppet/vendor/rgen/test/method_delegation_test.rb new file mode 100644 index 000000000..9b540ea2b --- /dev/null +++ b/lib/puppet/vendor/rgen/test/method_delegation_test.rb @@ -0,0 +1,178 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" + +require 'test/unit' +require 'rgen/util/method_delegation' + +class MethodDelegationTest < Test::Unit::TestCase + 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_raise 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_equal 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_raise NoMethodError do + result = delegator.send(method) + end + else + result = delegator.send(method) + assert_equal originalResult, result + end + end +end diff --git a/lib/puppet/vendor/rgen/test/model_builder/builder_context_test.rb b/lib/puppet/vendor/rgen/test/model_builder/builder_context_test.rb new file mode 100644 index 000000000..7cb62b3d5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/builder_context_test.rb @@ -0,0 +1,59 @@ +$:.unshift File.dirname(__FILE__)+"/../lib" + +require 'test/unit' +require 'rgen/ecore/ecore' +require 'rgen/model_builder/builder_context' + +class BuilderContextTest < Test::Unit::TestCase + + 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_raise RuntimeError do + # aboveRoot is not contained within root + assert_nil factory.moduleForPackage(aboveRoot) + end + assert_nil factory.moduleForPackage(packageC) + end +end \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/model_builder/builder_test.rb b/lib/puppet/vendor/rgen/test/model_builder/builder_test.rb new file mode 100644 index 000000000..ce4225f94 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/builder_test.rb @@ -0,0 +1,242 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" + +require 'test/unit' +require 'rgen/ecore/ecore' +require 'rgen/ecore/ecore_builder_methods' +require 'rgen/environment' +require 'rgen/model_builder' +require 'model_builder/statemachine_metamodel' + +class ModelBuilderTest < Test::Unit::TestCase + + 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_not_nil class1 + assert class1.is_a?(RGen::ECore::EClass) + + # TestClass1.attr1 + attr1 = class1.eAllAttributes.find{|a| a.name == "attr1"} + assert_not_nil attr1 + assert_equal RGen::ECore::EString, attr1.eType + + # TestClass1.attr2 + attr2 = class1.eAllAttributes.find{|a| a.name == "attr2"} + assert_not_nil attr2 + assert_equal RGen::ECore::EInt, attr2.eType + + # TestClass2 + class2 = p1.eClassifiers.find{|c| c.name == "TestClass2"} + assert_not_nil class2 + assert class2.is_a?(RGen::ECore::EClass) + + # TestClass2.ref1 + ref1 = class2.eAllReferences.find{|a| a.name == "ref1"} + assert_not_nil ref1 + assert_equal class1, ref1.eType + + # TestClass1.biRef1 + biRef1 = class1.eAllReferences.find{|r| r.name == "biRef1"} + assert_not_nil biRef1 + assert_equal class2, biRef1.eType + biRef1Opp = class2.eAllReferences.find {|r| r.name == "testClass1"} + assert_not_nil biRef1Opp + 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_not_nil tc2Ref + assert_equal class2, tc2Ref.eType + assert tc2Ref.containment + assert_equal -1, tc2Ref.upperBound + tc2RefOpp = class2.eAllReferences.find{|r| r.name == "tc1Parent"} + assert_not_nil tc2RefOpp + assert_equal class1, tc2RefOpp.eType + assert !tc2RefOpp.containment + assert_equal 1, tc2RefOpp.upperBound + end + +end \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/model_builder/ecore_original.rb b/lib/puppet/vendor/rgen/test/model_builder/ecore_original.rb new file mode 100644 index 000000000..bf3a1a5a1 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/ecore_original.rb @@ -0,0 +1,163 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/model_builder/ecore_original_regenerated.rb b/lib/puppet/vendor/rgen/test/model_builder/ecore_original_regenerated.rb new file mode 100644 index 000000000..1eda80f6c --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/ecore_original_regenerated.rb @@ -0,0 +1,163 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/model_builder/reference_resolver_test.rb b/lib/puppet/vendor/rgen/test/model_builder/reference_resolver_test.rb new file mode 100644 index 000000000..099335340 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/reference_resolver_test.rb @@ -0,0 +1,156 @@ +$:.unshift File.dirname(__FILE__)+"/../lib" + +require 'test/unit' +require 'rgen/metamodel_builder' +require 'rgen/model_builder/reference_resolver' + +class ReferenceResolverTest < Test::Unit::TestCase + + 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_raise 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/model_builder/serializer_test.rb b/lib/puppet/vendor/rgen/test/model_builder/serializer_test.rb new file mode 100644 index 000000000..46eab3d05 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/serializer_test.rb @@ -0,0 +1,94 @@ +$:.unshift File.dirname(__FILE__) + "/../lib" + +require 'test/unit' +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 < Test::Unit::TestCase + 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 diff --git a/lib/puppet/vendor/rgen/test/model_builder/statemachine_metamodel.rb b/lib/puppet/vendor/rgen/test/model_builder/statemachine_metamodel.rb new file mode 100644 index 000000000..f7a42cb0e --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/statemachine_metamodel.rb @@ -0,0 +1,42 @@ +# 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 diff --git a/lib/puppet/vendor/rgen/test/model_builder/test_model/statemachine1.rb b/lib/puppet/vendor/rgen/test/model_builder/test_model/statemachine1.rb new file mode 100644 index 000000000..9f44b7018 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder/test_model/statemachine1.rb @@ -0,0 +1,23 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/model_builder_test.rb b/lib/puppet/vendor/rgen/test/model_builder_test.rb new file mode 100644 index 000000000..a8e550c88 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_builder_test.rb @@ -0,0 +1,6 @@ +$:.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' diff --git a/lib/puppet/vendor/rgen/test/model_fragment_test.rb b/lib/puppet/vendor/rgen/test/model_fragment_test.rb new file mode 100644 index 000000000..deadf587d --- /dev/null +++ b/lib/puppet/vendor/rgen/test/model_fragment_test.rb @@ -0,0 +1,30 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/metamodel_builder' +require 'rgen/fragment/model_fragment' + +class ModelFragmentTest < Test::Unit::TestCase + +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 + + diff --git a/lib/puppet/vendor/rgen/test/output_handler_test.rb b/lib/puppet/vendor/rgen/test/output_handler_test.rb new file mode 100644 index 000000000..f00a8af71 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/output_handler_test.rb @@ -0,0 +1,58 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/template_language/output_handler' + +class MetamodelBuilderTest < Test::Unit::TestCase + 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/qualified_name_provider_test.rb b/lib/puppet/vendor/rgen/test/qualified_name_provider_test.rb new file mode 100644 index 000000000..c59cfce47 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/qualified_name_provider_test.rb @@ -0,0 +1,48 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/metamodel_builder' +require 'rgen/serializer/qualified_name_provider' + +class QualifiedNameProviderTest < Test::Unit::TestCase + + 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 + diff --git a/lib/puppet/vendor/rgen/test/qualified_name_resolver_test.rb b/lib/puppet/vendor/rgen/test/qualified_name_resolver_test.rb new file mode 100644 index 000000000..f14929226 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/qualified_name_resolver_test.rb @@ -0,0 +1,102 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/metamodel_builder' +require 'rgen/instantiator/qualified_name_resolver' + +class QualifiedNameResolverTest < Test::Unit::TestCase + + 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_equal nil, res.resolveIdentifier("/RootX") + assert_equal 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 + diff --git a/lib/puppet/vendor/rgen/test/reference_resolver_test.rb b/lib/puppet/vendor/rgen/test/reference_resolver_test.rb new file mode 100644 index 000000000..b3dd58d73 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/reference_resolver_test.rb @@ -0,0 +1,117 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/metamodel_builder' +require 'rgen/instantiator/reference_resolver' + +class ReferenceResolverTest < Test::Unit::TestCase + + 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_equal nil, nodeB.other + assert_equal [nodeA, nodeC], nodeB.others + assert_equal nil, nodeC.other + assert_equal [], nodeC.others + end + +end + diff --git a/lib/puppet/vendor/rgen/test/rgen_test.rb b/lib/puppet/vendor/rgen/test/rgen_test.rb new file mode 100644 index 000000000..68f1ac565 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/rgen_test.rb @@ -0,0 +1,26 @@ +$:.unshift File.dirname(__FILE__) + +require 'test/unit' + +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' + diff --git a/lib/puppet/vendor/rgen/test/template_language_test.rb b/lib/puppet/vendor/rgen/test/template_language_test.rb new file mode 100644 index 000000000..a1f14dda8 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test.rb @@ -0,0 +1,163 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/template_language' +require 'rgen/metamodel_builder' + +class TemplateContainerTest < Test::Unit::TestCase + + 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.exists? 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_raise StandardError do + # the template must raise an exception because it calls expand :for => nil + tc.expand('null_context_test::NullContextTestBad', :for => :dummy) + end + assert_raise StandardError do + # the template must raise an exception because it calls expand :foreach => nil + tc.expand('null_context_test::NullContextTestBad2', :for => :dummy) + end + assert_nothing_raised do + tc.expand('null_context_test::NullContextTestOk', :for => :dummy) + end + 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_raise 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 +end diff --git a/lib/puppet/vendor/rgen/test/template_language_test/expected_result1.txt b/lib/puppet/vendor/rgen/test/template_language_test/expected_result1.txt new file mode 100644 index 000000000..551d273a5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/expected_result1.txt @@ -0,0 +1,29 @@ + 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 diff --git a/lib/puppet/vendor/rgen/test/template_language_test/expected_result2.txt b/lib/puppet/vendor/rgen/test/template_language_test/expected_result2.txt new file mode 100644 index 000000000..b16ec5237 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/expected_result2.txt @@ -0,0 +1,9 @@ +int myArray[5] = { + 1, + 2, + 3, + 4, + 5 +}; +Text from Root +Text from Root diff --git a/lib/puppet/vendor/rgen/test/template_language_test/expected_result3.txt b/lib/puppet/vendor/rgen/test/template_language_test/expected_result3.txt new file mode 100644 index 000000000..79e6f8d1c --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/expected_result3.txt @@ -0,0 +1,4 @@ +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 diff --git a/lib/puppet/vendor/rgen/test/template_language_test/indentStringTestDefaultIndent.out b/lib/puppet/vendor/rgen/test/template_language_test/indentStringTestDefaultIndent.out new file mode 100644 index 000000000..6cf7396e1 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/indentStringTestDefaultIndent.out @@ -0,0 +1 @@ + <- your default here diff --git a/lib/puppet/vendor/rgen/test/template_language_test/indentStringTestTabIndent.out b/lib/puppet/vendor/rgen/test/template_language_test/indentStringTestTabIndent.out new file mode 100644 index 000000000..f70482c3b --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/indentStringTestTabIndent.out @@ -0,0 +1 @@ + <- tab diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/callback_indent_test/a.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/callback_indent_test/a.tpl new file mode 100644 index 000000000..30fd9d6df --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/callback_indent_test/a.tpl @@ -0,0 +1,12 @@ +<% 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 %> + diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/callback_indent_test/b.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/callback_indent_test/b.tpl new file mode 100644 index 000000000..293edd87e --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/callback_indent_test/b.tpl @@ -0,0 +1,5 @@ +<% define 'do_callback', :for => Object do %> + <%iinc%> + <% expand 'a::callback' %> + <%idec%> +<% end %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/code/array.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/code/array.tpl new file mode 100644 index 000000000..78d3a8b0f --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/code/array.tpl @@ -0,0 +1,11 @@ +<% 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 %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/content/author.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/content/author.tpl new file mode 100644 index 000000000..8c03422b1 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/content/author.tpl @@ -0,0 +1,7 @@ +<% define 'Author', :for => Author do %> + <% expand 'SubAuthor' %> +<% end %> + +<% define 'SubAuthor', :for => Author do %> + <%= name %>, EMail: <%= email.sub('@','(at)') %><%nows%> +<% end %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/content/chapter.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/content/chapter.tpl new file mode 100644 index 000000000..53ada2239 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/content/chapter.tpl @@ -0,0 +1,5 @@ +<% define 'Root', :for => Chapter do %> + *** <%= title %> ***<%nows%> + + +<% end %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/define_local_test/local.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/define_local_test/local.tpl new file mode 100644 index 000000000..5e4aad74b --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/define_local_test/local.tpl @@ -0,0 +1,8 @@ +<% define 'CallLocal1', :for => Object do %> + <% expand 'Local1' %> +<% end %> + +<% define_local 'Local1', :for => Object do %> + Local1 +<% end %> + diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/define_local_test/test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/define_local_test/test.tpl new file mode 100644 index 000000000..8511132e9 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/define_local_test/test.tpl @@ -0,0 +1,8 @@ +<% define 'Test', :for => Object do %> + <% expand 'local::CallLocal1' %> +<% end %> + +<% define 'TestForbidden', :for => Object do %> + <% expand 'local::Local1' %> +<% end %> + \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/evaluate_test/test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/evaluate_test/test.tpl new file mode 100644 index 000000000..7cc20b2e6 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/evaluate_test/test.tpl @@ -0,0 +1,7 @@ +<% 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 %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/indent_string_test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/indent_string_test.tpl new file mode 100644 index 000000000..a57840cc8 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/indent_string_test.tpl @@ -0,0 +1,12 @@ +<% 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 %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/index/c/cmod.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/index/c/cmod.tpl new file mode 100644 index 000000000..411d68c4b --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/index/c/cmod.tpl @@ -0,0 +1 @@ +<% define 'cmod' do %>Module C is special !<% end %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/index/chapter.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/index/chapter.tpl new file mode 100644 index 000000000..7396f611c --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/index/chapter.tpl @@ -0,0 +1,3 @@ +<% define 'Root' do |idx, doc| %> + <%= idx%> <%= title %> in <%= doc.title %> +<% end %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/no_backslash_r_test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_backslash_r_test.tpl new file mode 100644 index 000000000..3c06203d9 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_backslash_r_test.tpl @@ -0,0 +1,5 @@ +<% 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 %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/no_indent.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/no_indent.tpl new file mode 100644 index 000000000..9bdacde57 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/no_indent.tpl @@ -0,0 +1,3 @@ +<% define 'NoIndent', :for => Object do %> + <---<%nows%> +<% end %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl new file mode 100644 index 000000000..9bdacde57 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl @@ -0,0 +1,3 @@ +<% define 'NoIndent', :for => Object do %> + <---<%nows%> +<% end %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test.tpl new file mode 100644 index 000000000..0acec2774 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test.tpl @@ -0,0 +1,24 @@ +<% 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 %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test2.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test2.tpl new file mode 100644 index 000000000..63cf99263 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test2.tpl @@ -0,0 +1,13 @@ +<% 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 %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test3.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test3.tpl new file mode 100644 index 000000000..8c4c3eea5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/no_indent_test/test3.tpl @@ -0,0 +1,10 @@ +<% define 'Test', :for => Object do %> +<%iinc%> + l1<% expand 'Call1' %> +<%idec%> +<% end %> + +<% define 'Call1', :for => Object do %> + <--- + l2 +<% end %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/null_context_test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/null_context_test.tpl new file mode 100644 index 000000000..856b77109 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/null_context_test.tpl @@ -0,0 +1,17 @@ +<% 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 %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/root.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/root.tpl new file mode 100644 index 000000000..8b70e31a4 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/root.tpl @@ -0,0 +1,31 @@ +<% 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 %> + diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/sub1.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/sub1.tpl new file mode 100644 index 000000000..3c7a5cfa2 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/sub1.tpl @@ -0,0 +1,9 @@ +<% define 'Sub1', :for => Object do %> + Sub1 +<% end %> + +<% define 'Test', :for => Object do %> + <% expand 'Sub1' %> + <% expand 'sub1::Sub1' %> + <% expand 'sub1/sub1::Sub1' %> +<% end %> diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl new file mode 100644 index 000000000..1b6dc9700 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl @@ -0,0 +1,3 @@ +<% define 'Sub1', :for => Object do %> + Sub1 in sub1 +<% end %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/test.tpl b/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/test.tpl new file mode 100644 index 000000000..b1a970a01 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/templates/template_resolution_test/test.tpl @@ -0,0 +1,4 @@ +<% define 'Test', :for => Object do %> + <% expand 'sub1::Sub1' %> + <% expand 'sub1/sub1::Sub1' %> +<% end %> \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/template_language_test/testout.txt b/lib/puppet/vendor/rgen/test/template_language_test/testout.txt new file mode 100644 index 000000000..551d273a5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/template_language_test/testout.txt @@ -0,0 +1,29 @@ + 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 diff --git a/lib/puppet/vendor/rgen/test/testmodel/class_model_checker.rb b/lib/puppet/vendor/rgen/test/testmodel/class_model_checker.rb new file mode 100644 index 000000000..0bebbdc6b --- /dev/null +++ b/lib/puppet/vendor/rgen/test/testmodel/class_model_checker.rb @@ -0,0 +1,119 @@ +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_not_nil mainPackage + + # 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_not_nil houseClass + assert_not_nil personClass + assert_not_nil meetingPlaceClass + assert_not_nil cookingPlaceInterface + + # 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_not_nil roomClass + assert_not_nil kitchenClass + assert_not_nil bathroomClass + + # check Room inheritance + assert_equal 2, roomClass.specialization.child.size + assert_not_nil roomClass.specialization.child.find{|c| c.name == "Kitchen"} + assert_not_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_not_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_not_nil bathEnd + assert_not_nil kitchenEnd + 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_not_nil homeEnd + 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_not_nil roomsPackage + + roomClass = envUML.find(:class => UML13::Class, :name => "Room").first + assert_not_nil roomClass + + # House is created from an EAStub + houseClass = roomClass.remoteCompositeEnd.first.type + assert_not_nil houseClass + 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 \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel.eap b/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel.eap new file mode 100644 index 000000000..737b49cdf Binary files /dev/null and b/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel.eap differ diff --git a/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel.xml b/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel.xml new file mode 100644 index 000000000..b2bee1593 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel.xml @@ -0,0 +1,1029 @@ + + + + + Enterprise Architect + 2.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HouseMetamodel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rooms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HouseExampleModel + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel_partial.xml b/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel_partial.xml new file mode 100644 index 000000000..3ad796eb5 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/testmodel/ea_testmodel_partial.xml @@ -0,0 +1,317 @@ + + + + + Enterprise Architect + 2.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/puppet/vendor/rgen/test/testmodel/ecore_model_checker.rb b/lib/puppet/vendor/rgen/test/testmodel/ecore_model_checker.rb new file mode 100644 index 000000000..3fe1f1e6c --- /dev/null +++ b/lib/puppet/vendor/rgen/test/testmodel/ecore_model_checker.rb @@ -0,0 +1,101 @@ +require 'rgen/ecore/ecore' + +module Testmodel + +# Checks the ECore model elements created by transformation from the +# UML Class model elements from the example model +# +module ECoreModelChecker + include RGen::ECore + + def checkECoreModel(env) + + # check main package + mainPackage = env.elements.select {|e| e.is_a? EPackage and e.name == "HouseMetamodel"}.first + assert_not_nil mainPackage + + # check Rooms package + assert mainPackage.eSubpackages.is_a?(Array) + assert_equal 1, mainPackage.eSubpackages.size + assert mainPackage.eSubpackages[0].is_a?(EPackage) + roomsPackage = mainPackage.eSubpackages[0] + assert_equal "Rooms", roomsPackage.name + + # check main package classes + assert mainPackage.eClassifiers.is_a?(Array) + assert_equal 4, mainPackage.eClassifiers.size + assert mainPackage.eClassifiers.all?{|c| c.is_a?(EClass)} + houseClass = mainPackage.eClassifiers.select{|c| c.name == "House"}.first + personClass = mainPackage.eClassifiers.select{|c| c.name == "Person"}.first + meetingPlaceClass = mainPackage.eClassifiers.select{|c| c.name == "MeetingPlace"}.first + cookingPlaceInterface = mainPackage.eClassifiers.select{|c| c.name == "CookingPlace"}.first + assert_not_nil houseClass + assert_not_nil personClass + assert_not_nil meetingPlaceClass + assert_not_nil cookingPlaceInterface + + # check Rooms package classes + assert roomsPackage.eClassifiers.is_a?(Array) + assert_equal 3, roomsPackage.eClassifiers.size + assert roomsPackage.eClassifiers.all?{|c| c.is_a?(EClass)} + roomClass = roomsPackage.eClassifiers.select{|c| c.name == "Room"}.first + kitchenClass = roomsPackage.eClassifiers.select{|c| c.name == "Kitchen"}.first + bathroomClass = roomsPackage.eClassifiers.select{|c| c.name == "Bathroom"}.first + assert_not_nil roomClass + assert_not_nil kitchenClass + assert_not_nil bathroomClass + + # check Room inheritance + assert kitchenClass.eSuperTypes.is_a?(Array) + assert_equal 3, kitchenClass.eSuperTypes.size + assert_equal roomClass.object_id, kitchenClass.eSuperTypes.select{|c| c.name == "Room"}.first.object_id + assert_equal meetingPlaceClass.object_id, kitchenClass.eSuperTypes.select{|c| c.name == "MeetingPlace"}.first.object_id + assert_equal cookingPlaceInterface.object_id, kitchenClass.eSuperTypes.select{|c| c.name == "CookingPlace"}.first.object_id + assert bathroomClass.eSuperTypes.is_a?(Array) + assert_equal 1, bathroomClass.eSuperTypes.size + assert_equal roomClass.object_id, bathroomClass.eSuperTypes[0].object_id + + # check House-Room "part of" association + assert houseClass.eAllContainments.eType.is_a?(Array) + assert_equal 1, houseClass.eAllContainments.eType.size + roomRef = houseClass.eAllContainments.first + assert_equal roomClass.object_id, roomRef.eType.object_id + assert_equal "room", roomRef.name + assert_equal 1, roomRef.lowerBound + assert_equal(-1, roomRef.upperBound) + assert_not_nil roomRef.eOpposite + assert_equal houseClass.object_id, roomRef.eOpposite.eType.object_id + + partOfRefs = roomClass.eReferences.select{|r| r.eOpposite && r.eOpposite.containment} + assert_equal 1, partOfRefs.size + assert_equal houseClass.object_id, partOfRefs.first.eType.object_id + assert_equal "house", partOfRefs.first.name + assert_equal roomRef.object_id, partOfRefs.first.eOpposite.object_id + + # check House OUT associations + assert houseClass.eReferences.is_a?(Array) + assert_equal 3, houseClass.eReferences.size + bathRef = houseClass.eReferences.find {|e| e.name == "bathroom"} + kitchenRef = houseClass.eReferences.find {|e| e.name == "kitchen"} + roomRef = houseClass.eReferences.find {|e| e.name == "room"} + assert_not_nil bathRef + assert_nil bathRef.eOpposite + assert_not_nil kitchenRef + assert_not_nil roomRef + assert_equal 1, kitchenRef.lowerBound + assert_equal 1, kitchenRef.upperBound + assert_equal 1, roomRef.lowerBound + assert_equal(-1, roomRef.upperBound) + + # check House IN associations + houseInRefs = env.find(:class => EReference, :eType => houseClass) + assert_equal 3, houseInRefs.size + homeEnd = houseInRefs.find{|e| e.name == "home"} + assert_not_nil homeEnd + assert_equal 0, homeEnd.lowerBound + assert_equal(-1, homeEnd.upperBound) + + end +end + +end \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/testmodel/manual_testmodel.xml b/lib/puppet/vendor/rgen/test/testmodel/manual_testmodel.xml new file mode 100644 index 000000000..d45a0a611 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/testmodel/manual_testmodel.xml @@ -0,0 +1,22 @@ + + + before kitchen + + after kitchen + within toms room + after toms room + + + + + + + + + + + + + + + diff --git a/lib/puppet/vendor/rgen/test/testmodel/object_model_checker.rb b/lib/puppet/vendor/rgen/test/testmodel/object_model_checker.rb new file mode 100644 index 000000000..415a8d3b2 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/testmodel/object_model_checker.rb @@ -0,0 +1,67 @@ +require 'metamodels/uml13_metamodel' +require 'metamodels/uml13_metamodel_ext' + +module Testmodel + +# Checks the UML Object model elements from the example model +# +module ObjectModelChecker + + # convenient extension for this test only + module UML13::ClassifierRole::ClassModule + def classname + taggedValue.find{|tv| tv.tag == "classname"}.value + end + end + + def checkObjectModel(envUML) + + # check main package + mainPackage = envUML.find(:class => UML13::Package, :name => "HouseExampleModel").first + assert_not_nil mainPackage + + eaRootCollaboration = mainPackage.ownedElement.find{|e| e.is_a?(UML13::Collaboration) && e.name == "Collaborations"} + assert_not_nil eaRootCollaboration + + # check main package objects + objects = eaRootCollaboration.ownedElement.select{|e| e.is_a?(UML13::ClassifierRole)} + assert_equal 6, objects.size + + someone = objects.find{|o| o.name == "Someone"} + assert_equal "Person", someone.classname + + someonesHouse = objects.find{|o| o.name == "SomeonesHouse"} + assert_equal "House", someonesHouse.classname + + greenRoom = objects.find{|o| o.name == "GreenRoom"} + assert_equal "Room", greenRoom.classname + + yellowRoom = objects.find{|o| o.name == "YellowRoom"} + assert_equal "Room", yellowRoom.classname + + hotRoom = objects.find{|o| o.name == "HotRoom"} + assert_equal "Kitchen", hotRoom.classname + + wetRoom = objects.find{|o| o.name == "WetRoom"} + assert_equal "Bathroom", wetRoom.classname + + # Someone to SomeonesHouse + assert someone.associationEnd.otherEnd.getType.is_a?(Array) + assert_equal 1, someone.associationEnd.otherEnd.getType.size + houseEnd = someone.associationEnd.otherEnd[0] + assert_equal someonesHouse.object_id, houseEnd.getType.object_id + assert_equal "home", houseEnd.name + + # Someone to SomeonesHouse + assert someonesHouse.localCompositeEnd.otherEnd.is_a?(Array) + assert_equal 4, someonesHouse.localCompositeEnd.otherEnd.size + assert someonesHouse.localCompositeEnd.otherEnd.all?{|e| e.name == "room"} + assert_not_nil someonesHouse.localCompositeEnd.otherEnd.getType.find{|o| o == yellowRoom} + assert_not_nil someonesHouse.localCompositeEnd.otherEnd.getType.find{|o| o == greenRoom} + assert_not_nil someonesHouse.localCompositeEnd.otherEnd.getType.find{|o| o == hotRoom} + assert_not_nil someonesHouse.localCompositeEnd.otherEnd.getType.find{|o| o == wetRoom} + + end +end + +end \ No newline at end of file diff --git a/lib/puppet/vendor/rgen/test/transformer_test.rb b/lib/puppet/vendor/rgen/test/transformer_test.rb new file mode 100644 index 000000000..afbf5fba0 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/transformer_test.rb @@ -0,0 +1,254 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") +$:.unshift File.join(File.dirname(__FILE__),"..","test") + +require 'test/unit' +require 'rgen/transformer' +require 'rgen/environment' +require 'rgen/util/model_comparator' +require 'metamodels/uml13_metamodel' +require 'testmodel/class_model_checker' + +class TransformerTest < Test::Unit::TestCase + + class ModelIn + attr_accessor :name + end + + class ModelInSub < ModelIn + end + + class ModelAIn + attr_accessor :name + attr_accessor :modelB + end + + class ModelBIn + attr_accessor :name + attr_accessor :modelA + end + + class ModelCIn + attr_accessor :number + end + + class ModelOut + attr_accessor :name + end + + class ModelAOut + attr_accessor :name + attr_accessor :modelB + end + + class ModelBOut + attr_accessor :name + attr_accessor :modelA + end + + class ModelCOut + attr_accessor :number + end + + class MyTransformer < RGen::Transformer + attr_reader :modelInTrans_count + attr_reader :modelAInTrans_count + attr_reader :modelBInTrans_count + + transform ModelIn, :to => ModelOut do + # aribitrary ruby code may be placed before the hash creating the output element + @modelInTrans_count ||= 0; @modelInTrans_count += 1 + { :name => name } + end + + transform ModelAIn, :to => ModelAOut do + @modelAInTrans_count ||= 0; @modelAInTrans_count += 1 + { :name => name, :modelB => trans(modelB) } + end + + transform ModelBIn, :to => ModelBOut do + @modelBInTrans_count ||= 0; @modelBInTrans_count += 1 + { :name => name, :modelA => trans(modelA) } + end + + transform ModelCIn, :to => ModelCOut, :if => :largeNumber do + # a method can be called anywhere in a transformer block + { :number => duplicateNumber } + end + + transform ModelCIn, :to => ModelCOut, :if => :smallNumber do + { :number => number / 2 } + end + + method :largeNumber do + number > 1000 + end + + method :smallNumber do + number < 500 + end + + method :duplicateNumber do + number * 2; + end + + end + + class MyTransformer2 < RGen::Transformer + # check that subclasses are independent (i.e. do not share the rules) + transform ModelIn, :to => ModelOut do + { :name => name } + end + end + + def test_transformer + from = ModelIn.new + from.name = "TestName" + env_out = RGen::Environment.new + t = MyTransformer.new(:env_in, env_out) + assert t.trans(from).is_a?(ModelOut) + assert_equal "TestName", t.trans(from).name + assert_equal 1, env_out.elements.size + assert_equal env_out.elements.first, t.trans(from) + assert_equal 1, t.modelInTrans_count + end + + def test_transformer_chain + from = ModelIn.new + from.name = "Test1" + from2 = ModelIn.new + from2.name = "Test2" + from3 = ModelIn.new + from3.name = "Test3" + env_out = RGen::Environment.new + elementMap = {} + t1 = MyTransformer.new(:env_in, env_out, elementMap) + assert t1.trans(from).is_a?(ModelOut) + assert_equal "Test1", t1.trans(from).name + assert_equal 1, t1.modelInTrans_count + # modifying the element map means that following calls of +trans+ will be affected + assert_equal( {from => t1.trans(from)}, elementMap ) + elementMap.merge!({from2 => :dummy}) + assert_equal :dummy, t1.trans(from2) + # second transformer based on the element map of the first + t2 = MyTransformer.new(:env_in, env_out, elementMap) + # second transformer returns same objects + assert_equal t1.trans(from).object_id, t2.trans(from).object_id + assert_equal :dummy, t2.trans(from2) + # and no transformer rule is evaluated at this point + assert_equal nil, t2.modelInTrans_count + # now transform a new object in second transformer + assert t2.trans(from3).is_a?(ModelOut) + assert_equal "Test3", t2.trans(from3).name + assert_equal 1, t2.modelInTrans_count + # the first transformer returns the same object without evaluation of a transformer rule + assert_equal t1.trans(from3).object_id, t2.trans(from3).object_id + assert_equal 1, t1.modelInTrans_count + end + + def test_transformer_subclass + from = ModelInSub.new + from.name = "TestName" + t = MyTransformer.new + assert t.trans(from).is_a?(ModelOut) + assert_equal "TestName", t.trans(from).name + assert_equal 1, t.modelInTrans_count + end + + def test_transformer_array + froms = [ModelIn.new, ModelIn.new] + froms[0].name = "M1" + froms[1].name = "M2" + env_out = RGen::Environment.new + t = MyTransformer.new(:env_in, env_out) + assert t.trans(froms).is_a?(Array) + assert t.trans(froms)[0].is_a?(ModelOut) + assert_equal "M1", t.trans(froms)[0].name + assert t.trans(froms)[1].is_a?(ModelOut) + assert_equal "M2", t.trans(froms)[1].name + assert_equal 2, env_out.elements.size + assert (t.trans(froms)-env_out.elements).empty? + assert_equal 2, t.modelInTrans_count + end + + def test_transformer_cyclic + # setup a cyclic dependency between fromA and fromB + fromA = ModelAIn.new + fromB = ModelBIn.new + fromA.modelB = fromB + fromA.name = "ModelA" + fromB.modelA = fromA + fromB.name = "ModelB" + env_out = RGen::Environment.new + t = MyTransformer.new(:env_in, env_out) + # check that trans resolves the cycle correctly (no endless loop) + # both elements, fromA and fromB will be transformed with the transformation + # of the first element, either fromA or fromB + assert t.trans(fromA).is_a?(ModelAOut) + assert_equal "ModelA", t.trans(fromA).name + assert t.trans(fromA).modelB.is_a?(ModelBOut) + assert_equal "ModelB", t.trans(fromA).modelB.name + assert_equal t.trans(fromA), t.trans(fromA).modelB.modelA + assert_equal t.trans(fromB), t.trans(fromA).modelB + assert_equal 2, env_out.elements.size + assert (env_out.elements - [t.trans(fromA), t.trans(fromB)]).empty? + assert_equal 1, t.modelAInTrans_count + assert_equal 1, t.modelBInTrans_count + end + + def test_transformer_conditional + froms = [ModelCIn.new, ModelCIn.new, ModelCIn.new] + froms[0].number = 100 + froms[1].number = 1000 + froms[2].number = 2000 + + env_out = RGen::Environment.new + t = MyTransformer.new(:env_in, env_out) + + assert t.trans(froms).is_a?(Array) + assert_equal 2, t.trans(froms).size + + # this one matched the smallNumber rule + assert t.trans(froms[0]).is_a?(ModelCOut) + assert_equal 50, t.trans(froms[0]).number + + # this one did not match any rule + assert t.trans(froms[1]).nil? + + # this one matched the largeNumber rule + assert t.trans(froms[2]).is_a?(ModelCOut) + assert_equal 4000, t.trans(froms[2]).number + + # elements in environment are the same as the ones returned + assert_equal 2, env_out.elements.size + assert (t.trans(froms)-env_out.elements).empty? + end + + class CopyTransformer < RGen::Transformer + include UML13 + def transform + trans(:class => UML13::Package) + end + UML13.ecore.eClassifiers.each do |c| + copy c.instanceClass + end + end + + MODEL_DIR = File.join(File.dirname(__FILE__),"testmodel") + + include Testmodel::ClassModelChecker + include RGen::Util::ModelComparator + + def test_copyTransformer + envIn = RGen::Environment.new + envOut = RGen::Environment.new + + EASupport.instantiateUML13FromXMI11(envIn, MODEL_DIR+"/ea_testmodel.xml") + + CopyTransformer.new(envIn, envOut).transform + checkClassModel(envOut) + assert modelEqual?( + envIn.find(:class => UML13::Model).first, + envOut.find(:class => UML13::Model).first) + end + +end diff --git a/lib/puppet/vendor/rgen/test/util/file_cache_map_test.rb b/lib/puppet/vendor/rgen/test/util/file_cache_map_test.rb new file mode 100644 index 000000000..fcb4e32f2 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/util/file_cache_map_test.rb @@ -0,0 +1,99 @@ +$:.unshift(File.dirname(__FILE__)+"/../../lib") + +require 'test/unit' +require 'fileutils' +require 'rgen/util/file_cache_map' + +class FileCacheMapTest < Test::Unit::TestCase + + TestDir = File.dirname(__FILE__)+"/file_cache_map_test/testdir" + + def setup + FileUtils.rm_r(Dir[TestDir+"/*"]) + # * doesn't include dot files + FileUtils.rm_r(Dir[TestDir+"/.cache"]) + @cm = RGen::Util::FileCacheMap.new(".cache", ".test") + end + + def test_nocache + reasons = [] + assert_equal(:invalid, @cm.load_data(TestDir+"/fileA", :invalidation_reasons => reasons)) + assert_equal [:no_cachefile], reasons + end + + def test_storeload + keyFile = TestDir+"/fileA" + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.store_data(keyFile, "valuedata") + assert(File.exist?(TestDir+"/.cache/fileA.test")) + assert_equal("valuedata", @cm.load_data(keyFile)) + end + + def test_storeload_subdir + keyFile = TestDir+"/subdir/fileA" + FileUtils.mkdir(TestDir+"/subdir") + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.store_data(keyFile, "valuedata") + assert(File.exist?(TestDir+"/subdir/.cache/fileA.test")) + assert_equal("valuedata", @cm.load_data(keyFile)) + end + + def test_storeload_postfix + keyFile = TestDir+"/fileB.txt" + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.store_data(keyFile, "valuedata") + assert(File.exist?(TestDir+"/.cache/fileB.txt.test")) + assert_equal("valuedata", @cm.load_data(keyFile)) + end + + def test_storeload_empty + keyFile = TestDir+"/fileA" + File.open(keyFile, "w") {|f| f.write("")} + @cm.store_data(keyFile, "valuedata") + assert(File.exist?(TestDir+"/.cache/fileA.test")) + assert_equal("valuedata", @cm.load_data(keyFile)) + end + + def test_corruptcache + keyFile = TestDir+"/fileA" + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.store_data(keyFile, "valuedata") + File.open(TestDir+"/.cache/fileA.test","a") {|f| f.write("more data")} + reasons = [] + assert_equal(:invalid, @cm.load_data(keyFile, :invalidation_reasons => reasons)) + assert_equal [:cachefile_corrupted], reasons + end + + def test_changedcontent + keyFile = TestDir+"/fileA" + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.store_data(keyFile, "valuedata") + File.open(keyFile, "a") {|f| f.write("more data")} + reasons = [] + assert_equal(:invalid, @cm.load_data(keyFile, :invalidation_reasons => reasons)) + assert_equal [:keyfile_changed], reasons + end + + def test_versioninfo + keyFile = TestDir+"/fileA" + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.version_info = "123" + @cm.store_data(keyFile, "valuedata") + assert(File.exist?(TestDir+"/.cache/fileA.test")) + assert_equal("valuedata", @cm.load_data(keyFile)) + end + + def test_changed_version + keyFile = TestDir+"/fileA" + File.open(keyFile, "w") {|f| f.write("somedata")} + @cm.version_info = "123" + @cm.store_data(keyFile, "valuedata") + @cm.version_info = "456" + reasons = [] + assert_equal(:invalid, @cm.load_data(keyFile, :invalidation_reasons => reasons)) + assert_equal [:keyfile_changed], reasons + end + +end + + diff --git a/lib/puppet/vendor/rgen/test/util/file_cache_map_test/testdir/.gitignore b/lib/puppet/vendor/rgen/test/util/file_cache_map_test/testdir/.gitignore new file mode 100644 index 000000000..bad49a678 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/util/file_cache_map_test/testdir/.gitignore @@ -0,0 +1,3 @@ +.cache +fileA + diff --git a/lib/puppet/vendor/rgen/test/util/pattern_matcher_test.rb b/lib/puppet/vendor/rgen/test/util/pattern_matcher_test.rb new file mode 100644 index 000000000..156856492 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/util/pattern_matcher_test.rb @@ -0,0 +1,97 @@ +$:.unshift(File.dirname(__FILE__)+"/../../lib") + +require 'test/unit' +require 'rgen/environment' +require 'rgen/metamodel_builder' +require 'rgen/model_builder' +require 'rgen/util/pattern_matcher' + +class PatternMatcherTest < Test::Unit::TestCase + +module TestMM + extend RGen::MetamodelBuilder::ModuleExtension + + class Node < RGen::MetamodelBuilder::MMBase + has_attr 'name', String + contains_many 'children', Node, 'parent' + end +end + +def modelA + env = RGen::Environment.new + RGen::ModelBuilder.build(TestMM, env) do + node "A" do + node "AA" + end + node "B" do + node "B1" + node "B2" + node "B3" + end + node "C" do + node "C1" + node "C2" + end + node "D" do + node "DD" + end + end + env +end + +def test_simple + matcher = RGen::Util::PatternMatcher.new + matcher.add_pattern("simple") do |env, c| + TestMM::Node.new(:name => "A", :children => [ + TestMM::Node.new(:name => "AA")]) + end + matcher.add_pattern("bad") do |env, c| + TestMM::Node.new(:name => "X") + end + env = modelA + + match = matcher.find_pattern(env, "simple") + assert_not_nil match + assert_equal "A", match.root.name + assert_equal env.find(:class => TestMM::Node, :name => "A").first.object_id, match.root.object_id + assert_equal 2, match.elements.size + assert_equal [nil], match.bound_values + + assert_nil matcher.find_pattern(env, "bad") +end + +def test_value_binding + matcher = RGen::Util::PatternMatcher.new + matcher.add_pattern("single_child") do |env, name, child| + TestMM::Node.new(:name => name, :children => [ child ]) + end + matcher.add_pattern("double_child") do |env, name, child1, child2| + TestMM::Node.new(:name => name, :children => [ child1, child2 ]) + end + matcher.add_pattern("child_pattern") do |env, child_name| + TestMM::Node.new(:name => "A", :children => [ + TestMM::Node.new(:name => child_name)]) + end + env = modelA + + match = matcher.find_pattern(env, "single_child") + assert_not_nil match + assert_equal "A", match.root.name + assert_equal "AA", match.bound_values[1].name + + match = matcher.find_pattern(env, "single_child", "D") + assert_not_nil match + assert_equal "D", match.root.name + assert_equal "DD", match.bound_values[0].name + + match = matcher.find_pattern(env, "double_child") + assert_not_nil match + assert_equal "C", match.root.name + + match = matcher.find_pattern(env, "child_pattern") + assert_not_nil match + assert_equal ["AA"], match.bound_values +end + +end + diff --git a/lib/puppet/vendor/rgen/test/util_test.rb b/lib/puppet/vendor/rgen/test/util_test.rb new file mode 100644 index 000000000..d36e366db --- /dev/null +++ b/lib/puppet/vendor/rgen/test/util_test.rb @@ -0,0 +1,5 @@ +$:.unshift File.dirname(__FILE__) + +require 'util/file_cache_map_test' +require 'util/pattern_matcher_test' + diff --git a/lib/puppet/vendor/rgen/test/xml_instantiator_test.rb b/lib/puppet/vendor/rgen/test/xml_instantiator_test.rb new file mode 100644 index 000000000..145058250 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/xml_instantiator_test.rb @@ -0,0 +1,160 @@ +$:.unshift File.join(File.dirname(__FILE__),"..","lib") + +require 'test/unit' +require 'rgen/instantiator/default_xml_instantiator' +require 'rgen/environment' +require 'rgen/util/model_dumper' +require 'xml_instantiator_test/simple_xmi_ecore_instantiator' +require 'xml_instantiator_test/simple_ecore_model_checker' + +module EmptyMM +end + +module DefaultMM + module MNS + class Room < RGen::MetamodelBuilder::MMBase; end + end + class Person < RGen::MetamodelBuilder::MMBase; end + Person.one_to_one 'personalRoom', MNS::Room, 'inhabitant' +end + +class XMLInstantiatorTest < Test::Unit::TestCase + + XML_DIR = File.join(File.dirname(__FILE__),"testmodel") + + include RGen::Util::ModelDumper + + class MyInstantiator < RGen::Instantiator::DefaultXMLInstantiator + + map_tag_ns "testmodel.org/myNamespace", DefaultMM::MNS + + def class_name(str) + camelize(str) + end + +# resolve :type do +# @env.find(:xmi_id => getType).first +# end + + resolve_by_id :personalRoom, :id => :getId, :src => :room + + end + + class PruneTestInstantiator < RGen::Instantiator::NodebasedXMLInstantiator + attr_reader :max_depth + + set_prune_level 2 + + def initialize(env) + super(env) + @max_depth = 0 + end + + def on_descent(node) + end + + def on_ascent(node) + calc_max_depth(node, 0) + end + + def calc_max_depth(node, offset) + if node.children.nil? || node.children.size == 0 + @max_depth = offset if offset > @max_depth + else + node.children.each do |c| + calc_max_depth(c, offset+1) + end + end + end + end + + module PruneTestMM + end + + def test_pruning + env = RGen::Environment.new + + # prune level 2 is set in the class body + inst = PruneTestInstantiator.new(env) + inst.instantiate_file(File.join(XML_DIR,"manual_testmodel.xml")) + assert_equal 2, inst.max_depth + + PruneTestInstantiator.set_prune_level(0) + inst = PruneTestInstantiator.new(env) + inst.instantiate_file(File.join(XML_DIR,"manual_testmodel.xml")) + assert_equal 5, inst.max_depth + + PruneTestInstantiator.set_prune_level(1) + inst = PruneTestInstantiator.new(env) + inst.instantiate_file(File.join(XML_DIR,"manual_testmodel.xml")) + assert_equal 1, inst.max_depth + end + + def test_custom + env = RGen::Environment.new + inst = MyInstantiator.new(env, DefaultMM, true) + inst.instantiate_file(File.join(XML_DIR,"manual_testmodel.xml")) + + house = env.find(:class => DefaultMM::MNS::House).first + assert_not_nil house + assert_equal 2, house.room.size + + rooms = env.find(:class => DefaultMM::MNS::Room) + assert_equal 2, rooms.size + assert_equal 0, (house.room - rooms).size + rooms.each {|r| assert r.parent == house} + tomsRoom = rooms.select{|r| r.name == "TomsRoom"}.first + assert_not_nil tomsRoom + + persons = env.find(:class => DefaultMM::Person) + assert_equal 4, persons.size + tom = persons.select{|p| p.name == "Tom"}.first + assert_not_nil tom + + assert tom.personalRoom == tomsRoom + + mpns = env.find(:class => DefaultMM::MultiPartName) + assert mpns.first.respond_to?("insideMultiPart") + end + + def test_default + env = RGen::Environment.new + inst = RGen::Instantiator::DefaultXMLInstantiator.new(env, EmptyMM, true) + inst.instantiate_file(File.join(XML_DIR,"manual_testmodel.xml")) + + house = env.find(:class => EmptyMM::MNS_House).first + assert_not_nil house + assert_equal 2, house.mNS_Room.size + assert_equal "before kitchen", remove_whitespace_elements(house.chardata)[0].strip + assert_equal "after kitchen", remove_whitespace_elements(house.chardata)[1].strip + assert_equal "after toms room", remove_whitespace_elements(house.chardata)[2].strip + + rooms = env.find(:class => EmptyMM::MNS_Room) + assert_equal 2, rooms.size + assert_equal 0, (house.mNS_Room - rooms).size + rooms.each {|r| assert r.parent == house} + tomsRoom = rooms.select{|r| r.name == "TomsRoom"}.first + assert_not_nil tomsRoom + assert_equal "within toms room", remove_whitespace_elements(tomsRoom.chardata)[0] + + persons = env.find(:class => EmptyMM::Person) + assert_equal 4, persons.size + tom = persons.select{|p| p.name == "Tom"}.first + assert_not_nil tom + end + + def remove_whitespace_elements(elements) + elements.reject{|e| e.strip == ""} + end + + include SimpleECoreModelChecker + + def test_simle_xmi_ecore_instantiator + envECore = RGen::Environment.new + File.open(XML_DIR+"/ea_testmodel.xml") { |f| + SimpleXMIECoreInstantiator.new.instantiateECoreModel(envECore, f.read) + } + checkECoreModel(envECore) + end + +end diff --git a/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_ecore_model_checker.rb b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_ecore_model_checker.rb new file mode 100644 index 000000000..986af3309 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_ecore_model_checker.rb @@ -0,0 +1,94 @@ +require 'rgen/ecore/ecore' + +# This "light" version of the ECore model checker is used to check the +# model produced by the XMLInstantiatorTest only. +# +module SimpleECoreModelChecker + include RGen::ECore + + def checkECoreModel(env) + + # check main package + mainPackage = env.elements.select {|e| e.is_a? EPackage and e.name == "HouseMetamodel"}.first + assert_not_nil mainPackage + + # check Rooms package + assert mainPackage.eSubpackages.is_a?(Array) + assert_equal 1, mainPackage.eSubpackages.size + assert mainPackage.eSubpackages[0].is_a?(EPackage) + roomsPackage = mainPackage.eSubpackages[0] + assert_equal "Rooms", roomsPackage.name + + # check main package classes + assert mainPackage.eClassifiers.is_a?(Array) + assert_equal 3, mainPackage.eClassifiers.size + assert mainPackage.eClassifiers.all?{|c| c.is_a?(EClass)} + houseClass = mainPackage.eClassifiers.select{|c| c.name == "House"}.first + personClass = mainPackage.eClassifiers.select{|c| c.name == "Person"}.first + meetingPlaceClass = mainPackage.eClassifiers.select{|c| c.name == "MeetingPlace"}.first + assert_not_nil houseClass + assert_not_nil personClass + assert_not_nil meetingPlaceClass + + # check Rooms package classes + assert roomsPackage.eClassifiers.is_a?(Array) + assert_equal 3, roomsPackage.eClassifiers.size + assert roomsPackage.eClassifiers.all?{|c| c.is_a?(EClass)} + roomClass = roomsPackage.eClassifiers.select{|c| c.name == "Room"}.first + kitchenClass = roomsPackage.eClassifiers.select{|c| c.name == "Kitchen"}.first + bathroomClass = roomsPackage.eClassifiers.select{|c| c.name == "Bathroom"}.first + assert_not_nil roomClass + assert_not_nil kitchenClass + assert_not_nil bathroomClass + + # check Room inheritance + assert kitchenClass.eSuperTypes.is_a?(Array) + assert_equal 2, kitchenClass.eSuperTypes.size + assert_equal roomClass.object_id, kitchenClass.eSuperTypes.select{|c| c.name == "Room"}.first.object_id + assert_equal meetingPlaceClass.object_id, kitchenClass.eSuperTypes.select{|c| c.name == "MeetingPlace"}.first.object_id + assert bathroomClass.eSuperTypes.is_a?(Array) + assert_equal 1, bathroomClass.eSuperTypes.size + assert_equal roomClass.object_id, bathroomClass.eSuperTypes[0].object_id + + # check House-Room "part of" association + assert houseClass.eAllContainments.eType.is_a?(Array) + assert_equal 1, houseClass.eAllContainments.eType.size + roomRef = houseClass.eAllContainments.first + assert_equal roomClass.object_id, roomRef.eType.object_id + assert_equal "room", roomRef.name + assert_equal 1, roomRef.lowerBound + assert_equal(-1, roomRef.upperBound) + assert_not_nil roomRef.eOpposite + assert_equal houseClass.object_id, roomRef.eOpposite.eType.object_id + + partOfRefs = roomClass.eReferences.select{|r| r.eOpposite && r.eOpposite.containment} + assert_equal 1, partOfRefs.size + assert_equal houseClass.object_id, partOfRefs.first.eType.object_id + assert_equal "house", partOfRefs.first.name + assert_equal roomRef.object_id, partOfRefs.first.eOpposite.object_id + + # check House OUT associations + assert houseClass.eReferences.is_a?(Array) + assert_equal 3, houseClass.eReferences.size + bathRef = houseClass.eReferences.find {|e| e.name == "bathroom"} + kitchenRef = houseClass.eReferences.find {|e| e.name == "kitchen"} + roomRef = houseClass.eReferences.find {|e| e.name == "room"} + assert_not_nil bathRef + assert_nil bathRef.eOpposite + assert_not_nil kitchenRef + assert_not_nil roomRef + assert_equal 1, kitchenRef.lowerBound + assert_equal 1, kitchenRef.upperBound + assert_equal 1, roomRef.lowerBound + assert_equal(-1, roomRef.upperBound) + + # check House IN associations + houseInRefs = env.find(:class => EReference, :eType => houseClass) + assert_equal 3, houseInRefs.size + homeEnd = houseInRefs.find{|e| e.name == "home"} + assert_not_nil homeEnd + assert_equal 0, homeEnd.lowerBound + assert_equal(-1, homeEnd.upperBound) + + end +end diff --git a/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_ecore_instantiator.rb b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_ecore_instantiator.rb new file mode 100644 index 000000000..33cf97759 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_ecore_instantiator.rb @@ -0,0 +1,53 @@ +require 'rgen/instantiator/default_xml_instantiator' +require 'rgen/environment' +require 'rgen/ecore/ecore' +require 'xml_instantiator_test/simple_xmi_metamodel' + +# SimpleXMIECoreInstantiator demonstrates the usage of the DefaultXMLInstantiator. +# It can be used to instantiate an ECore model from an XMI description +# produced by Enterprise Architect. +# +# Note however, that this is *not* the recommended way to read an EA model. +# See EAInstantiatorTest for the clean way to do this. +# +# This example shows how arbitrary XML content can be used to instantiate +# an implicit metamodel. The resulting model is transformed into a simple +# ECore model. +# +# See XMLInstantiatorTest for an example of how to use this class. +# +class SimpleXMIECoreInstantiator < RGen::Instantiator::DefaultXMLInstantiator + + map_tag_ns "omg.org/UML1.3", SimpleXMIMetaModel::UML + + resolve_by_id :typeClass, :src => :type, :id => :xmi_id + resolve_by_id :subtypeClass, :src => :subtype, :id => :xmi_id + resolve_by_id :supertypeClass, :src => :supertype, :id => :xmi_id + + def initialize + @envXMI = RGen::Environment.new + super(@envXMI, SimpleXMIMetaModel, true) + end + + def new_object(node) + if node.tag == "EAStub" + class_name = saneClassName(node.attributes["UMLType"]) + mod = XMIMetaModel::UML + build_on_error(NameError, :build_class, class_name, mod) do + mod.const_get(class_name).new + end + else + super + end + end + + # This method does the actual work. + def instantiateECoreModel(envOut, str) + instantiate(str) + + require 'xml_instantiator_test/simple_xmi_to_ecore' + + SimpleXmiToECore.new(@envXMI,envOut).transform + end + +end diff --git a/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_metamodel.rb b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_metamodel.rb new file mode 100644 index 000000000..38bc0ceb4 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_metamodel.rb @@ -0,0 +1,49 @@ +# This is an extension of the implicit metamodel created by the +# DefaultXMLInstantiator when it reads an Enterprise Architect +# XMI file. +# +module SimpleXMIMetaModel + + module UML + include RGen::MetamodelBuilder + + class Classifier_feature < MMBase + end + + class ClassifierRole < MMBase + end + + class Clazz < ClassifierRole + end + + class Interface < ClassifierRole + end + + class Operation < MMBase + end + + class Generalization < MMBase + end + + class ModelElement_stereotype < MMBase + end + + class AssociationEnd < MMBase + module ClassModule + def otherEnd + parent.associationEnd.find{|ae| ae != self} + end + end + end + + class AssociationEndRole < MMBase + end + + ClassifierRole.one_to_many 'associationEnds', AssociationEnd, 'typeClass' + ClassifierRole.one_to_many 'associationEndRoles', AssociationEndRole, 'typeClass' + Clazz.one_to_many 'generalizationsAsSubtype', Generalization, 'subtypeClass' + Clazz.one_to_many 'generalizationsAsSupertype', Generalization, 'supertypeClass' + + end + +end diff --git a/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_to_ecore.rb b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_to_ecore.rb new file mode 100644 index 000000000..d95477562 --- /dev/null +++ b/lib/puppet/vendor/rgen/test/xml_instantiator_test/simple_xmi_to_ecore.rb @@ -0,0 +1,75 @@ +require 'rgen/transformer' +require 'rgen/ecore/ecore' +require 'rgen/array_extensions' +require 'xml_instantiator_test/simple_xmi_metamodel' + +class SimpleXmiToECore < RGen::Transformer + include RGen::ECore + + class MapHelper + def initialize(keyMethod,valueMethod,elements) + @keyMethod, @valueMethod, @elements = keyMethod, valueMethod, elements + end + def [](key) + return @elements.select{|e| e.send(@keyMethod) == key}.first.send(@valueMethod) rescue NoMethodError + nil + end + end + + class TaggedValueHelper < MapHelper + def initialize(element) + super('tag','value',element.modelElement_taggedValue.taggedValue) + end + end + + # Do the actual transformation. + # Input and output environment have to be provided to the transformer constructor. + def transform + trans(:class => SimpleXMIMetaModel::UML::Clazz) + end + + transform SimpleXMIMetaModel::UML::Package, :to => EPackage do + { :name => name, + :eSuperPackage => trans(parent.parent.is_a?(SimpleXMIMetaModel::UML::Package) ? parent.parent : nil) } + end + + transform SimpleXMIMetaModel::UML::Clazz, :to => EClass do + { :name => name, + :ePackage => trans(parent.parent.is_a?(SimpleXMIMetaModel::UML::Package) ? parent.parent : nil), + :eStructuralFeatures => trans(classifier_feature.attribute + associationEnds), + :eOperations => trans(classifier_feature.operation), + :eSuperTypes => trans(generalizationsAsSubtype.supertypeClass), + :eAnnotations => [ EAnnotation.new(:details => trans(modelElement_taggedValue.taggedValue)) ] } + end + + transform SimpleXMIMetaModel::UML::TaggedValue, :to => EStringToStringMapEntry do + { :key => tag, :value => value} + end + + transform SimpleXMIMetaModel::UML::Attribute, :to => EAttribute do + typemap = { "String" => EString, "boolean" => EBoolean, "int" => EInt, "long" => ELong, "float" => EFloat } + tv = TaggedValueHelper.new(@current_object) + { :name => name, :eType => typemap[tv['type']], + :eAnnotations => [ EAnnotation.new(:details => trans(modelElement_taggedValue.taggedValue)) ] } + end + + transform SimpleXMIMetaModel::UML::Operation, :to => EOperation do + { :name => name } + end + + transform SimpleXMIMetaModel::UML::AssociationEnd, :to => EReference, :if => :isReference do + { :eType => trans(otherEnd.typeClass), + :name => otherEnd.name, + :eOpposite => trans(otherEnd), + :lowerBound => (otherEnd.multiplicity || '0').split('..').first.to_i, + :upperBound => (otherEnd.multiplicity || '1').split('..').last.gsub('*','-1').to_i, + :containment => (aggregation == 'composite'), + :eAnnotations => [ EAnnotation.new(:details => trans(modelElement_taggedValue.taggedValue)) ] } + end + + method :isReference do + otherEnd.isNavigable == 'true' || + # composite assocations are bidirectional + aggregation == 'composite' || otherEnd.aggregation == 'composite' + end +end