rgen-0.8.0/0000755000004100000410000000000012642233644012510 5ustar www-datawww-datargen-0.8.0/Rakefile0000644000004100000410000000306112642233644014155 0ustar www-datawww-datarequire 'rubygems/package_task' require 'rdoc/task' RGenGemSpec = Gem::Specification.new do |s| s.name = %q{rgen} s.version = "0.8.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] rgen-0.8.0/CHANGELOG0000644000004100000410000002233412642233644013726 0ustar www-datawww-data=0.1.0 (August 3rd, 2006) * First public release =0.2.0 (September 3rd, 2006) * Added model transformation language (Transformer) * Now RGen is distributed as a gem * More complete documentation =0.3.0 (October 9th, 2006) * Improved XML Instantiator (Namespaces, Resolver, Customization) * Added many_to_one builder method * Added attribute reflection to MMBase (one_attributes, many_attributes) * Added +copy+ method to Transformer * Added simple model dumper module * Fixed mmgen/mmgen.rb =0.4.0 (Aug 8th, 2007) * Added ECore metamodel and use it as the core metametamodel * Revised and extended MetamodelBuilder language * There is an ECore instance describing each metamodel built using MetamodelBuilder now * Metamodel generator is now ECore based * Added Ruby implementation of Boolean and Enum types * Switched XML Instantiator to xmlscan for performance reasons * Cleaned up instantiator file structure * Renamed RGen::XMLInstantiator into RGen::Instantiator::DefaultXMLInstantiator * Included xmlscan as a redistributed module * Added support for chardata within XML tags * Added (Enterprise Architect) XMI to ECore instantiator * Some minor fixes in NameHelper * Some fixes to template language * Added UML1.3 Metamodel * Added tranformation from UML1.3 to ECore =0.4.1 (Nov 25th, 2007) * Template language performance improvement * Bugfix: use true/false instead of symbols for boolean attribute default values in metamodel classes * Minor fixes on metamodel generator and ecore primitive type handling * Made transformer implementation non-recursive to prevent "stack level too deep" exception for large models * Minor fixes on EAInstantiator * Made transformer search for matching rules for superclasses * Bugfix: Enums are now added to EPackages created using the "ecore" method on a module * Bugfix: Metamodel generator now writes enum names * Performance improvement: don't require ecore transformer every time someone calls "ecore" * Major performance improvement of template engine (no Regexps to check \n at end of line) * Major performance improvement: AbstractXMLInstantiator optionally controls the garbage collector * Major performance improvement: ERB templates are reused in metamodel_builder * Added delete method to Environment =0.4.2 (Mar 2nd, 2008) * Performance improvement: collection feature of array extension uses hashes now to speed up array union * Performance improvement: find on environment hashes elements by class * Extended Transformer to allow sharing of result maps between several Transformer instances * Bugfix: User defined upper bound values are no longer overwritten by -1 in all "many" metamodel builder methods =0.4.3 (Aug 12th, 2008) * Performance improvement: significant speed up of metamodel reverse registration * Bugfix: Use object identity for metamodel to-many add/remove methods * Bugfix: If expand's :for expression evaluates to nil an error is generated (silently used current context before) * Template language indentation string can be set on DirectoryTemplateContainer and with the "file" command =0.4.4 (Sep 10th, 2008) * Added "abstract" metamodel DSL command * Added ecore_ext.rb with convenience methods * Added XMI1.1 serializer, revised XMLSerializer super class =0.4.5 (Nov 17th, 2008) * Updated XMI1.1 serializer to support explicit placement of elements on content level of the XMI file =0.4.6 (Mar 1st, 2009) * Bugfix: expand :foreach silently assumed current context if :foreach evalutated to nil * Bugfix: fixed unit test for non-Windows plattforms (\r\n) * Bugfix: depending on the Ruby version and/or platform constants used in templates could not be resolved * Added automatic line ending detection (\n or \r\n) for template language +nl+ command =0.5.0 (Jun 8th, 2009) * Added ModelBuilder and ModelSerializer * Added template language "define_local" command * Added template language "evaluate" command * Fixed template language bug: indentation problem when expand continues a non-empty line * Fixed template language bug: template content expands several times when a template container is called recursively * Fixed template language bug: template resolution problem if a template file has the same name as a template directory * Cleaned up EA support * Added method to clear ecore metamodel reflection cache * Improved overriding of metamodel features in reopened classes =0.5.1 (Nov 10th, 2009) * Fixed metamodel builder bug: _register at one-side did not unregister from the element referenced by the old value * Added helper class for building simple model comparators =0.5.2 (Jun 13th, 2010) * Added has_many_attr to metamodel builder, support for "many" attributes * Added JSON support (json instantiator and serializer) * Added QualifiedNameResolver instantiation helper * Added reference proxy support * Added more generic access methods on metaclasses * Added ReferenceResolver resolver mixin * Fixed ecore xml instantiator and serializer to handle references to builtin datatypes correctly * Fixed bug in ecore xml serializer to not output references which are opposites of containment references =0.5.3 (Aug 13th, 2010) * Fixed string escaping in JSON instantiator and serializer * Fixed order of eClassifiers and eSubpackages within an EPackage created by reflection on a RGen module =0.5.4 * Fixed undeterministic order of child elements in ModelSerializer * Fixed undeterministic order of attributes in XMI serializers * Fixed ModelSerializer to always serialize the to-one part of bidirectional 1:N references * Fixed ModelSerializer to add :as => in case of ambiguous child roles * Made JsonInstantiator search subpackages for unqualified class names =0.6.0 * Added exception when trying to instantiate abstract class * Replaced xmlscan by dependency to nokogiri * Made RGen work with Ruby 1.9 * Cleaned up intermediate attribute and reference description, improvement of metamodel load time * Added optional data property for MMProxy * Added ECoreToRuby which can create Ruby classes and modules from ECore models in memory (without metamodel generator) * Refactored out QualifiedNameProvider and OppositeReferenceFilter * Added model fragment/fragmented models support * Extended Instantiator::ReferenceResolver and changed it into a class * Moved utilities into util folder/module * Added FileCacheMap * Fixed template language bug: indenting not correct after callback into same template container and iinc/idec * Added support for fragmented models * Added FileChangeDetector utility * Added CachedGlob utility * Added index parameter to model element add methods * Added MMGeneric * Modified has_many_attr to allow the same value in the same attribute multiple times * Made Environment#delete faster on large models * Added type check of ecore defaultValueLiteral content in MetamodelBuilder * Many-feature setters can work with an Enumerable instead of an Array * Added pattern matcher utility * Fixed problem of Ruby hanging when exceptions occur * Fixed metamodel generator to quote illegal enum literal symbols * Imporved UML to ECore transformer and EA support =0.6.1 * Fixed metamodel builder to not overwrite a model element's 'class' method * Added enum type transformation to ECoreToUML13 transformer, primitive type mapping based on instanceClassName * Fixed default value appearing on read after setting a feature value to nil * Added eIsSet and eUnset methods * Added eContainer and eContainingFeature methods * Fixed ModelFragment#elements not containing root elements * Added optional output of invalidation reason to FileCacheMap#load_data =0.6.2 * Made qualified name provider work with unidirectional containment references * Fixed array_extension breaking the Hash[] method =0.6.3 * Added BigDecimal support =0.6.4 * Made FileChangeDetector and FileCacheMap robust against missing files =0.6.5 * Fixed missing default argument of FragmentedModel#resolve * Added to_str to methods which aren't forwarded by array extension on empty arrays =0.6.6 * Added ModelFragment#mark_resolved and ResolutionHelper * Added ReferenceResolver option to output failed resolutions * Major performance improvement of FragmentedModel#resolve * Fixed a Ruby 2.0 related warning =0.7.0 * Enforce unique container rule by automatically disconnecting elements from other containers * Added support for long typed values (ELong), thanks to Thomas Hallgren; Note that this is merely an EMF compatibility thing, RGen could already handle big integers before * Added eContents and eAllContents methods * Added setNilOrRemoveGeneric and setNilOrRemoveAllGeneric methods * Added disconnectContainer method =0.8.0 * Fixed missing indentation when template file is not terminated by a newline * Fixed missing indentation when expand in same line expands sub templates * Fixed DefaultXMLInstantiator naming error with a tag named 'File' (issue #19, pull request #21 from jkugs) * Simplified ECoreToRuby and optionally let it create modules with non-temporary names * Improved performance of output handler * Improved performance of setXXX and addXXX methods (pull request #22 from thallgren) * Use a value larger than Fixnum max to test Bignum support (pull request #18 from graaff) rgen-0.8.0/MIT-LICENSE0000644000004100000410000000204112642233644014141 0ustar www-datawww-dataCopyright (c) 2013 Martin Thiede Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rgen-0.8.0/README.rdoc0000644000004100000410000000407412642233644014323 0ustar www-datawww-data= RGen - Ruby Modelling and Generator Framework RGen is a framework for Model Driven Software Development (MDSD)in Ruby. This means that it helps you build Metamodels, instantiate Models, modify and transform Models and finally generate arbitrary textual content from it. RGen features include: * Supporting Ruby 1.8.7, 1.9.x, 2.0, 2.1, 2.2 * Metamodel definition language (internal Ruby DSL) * ECore Meta-metamodel with an ECore instance available for every Metamodel * Generator creating the Ruby metamodel definition from an ECore instance * Transformer creating Ruby metamodel classes/modules from an ECore instance * Instantiation of Metamodels, i.e. creation of Models (e.g. from XML) * Model builder, internal Ruby DSL * Model fragmentation over several several files and per-fragment caching * Model Transformation language (internal Ruby DSL) * Powerful template based generator language (internal Ruby DSL inside of ERB) * UML 1.3 metamodel and XMI 1.1 instantiator included * ECore XML support (XMI 2.0) * UML-to-ECore and ECore-to-UML transformation (UML class models) * Enterprise Architect support (UML1.3/XMI1.1) == Download Get the latest release from Github: https://github.com/mthiede/rgen == Installation Install RGen as a Ruby gem: gem install rgen == Running the Tests Change to the 'test' folder and run the test suite: cd test ruby rgen_test.rb == Documentation RDoc documentation is available at Github: http://mthiede.github.com/rgen/ Find the main documentation parts for: * RGen::MetamodelBuilder * RGen::Transformer * RGen::TemplateLanguage * RGen::Fragment::FragmentedModel == Examples There are several examples of using RGen within the framework itself. Metamodel Definition: lib/rgen/ecore/ecore.rb lib/metamodels/uml13_metamodel.rb Instantiation: lib/rgen/instantiator/xmi11_instantiator.rb lib/rgen/instantiator/ecore_xml_instantiator.rb Transformations: lib/rgen/ecore/ruby_to_ecore.rb lib/transformers/uml13_to_ecore.rb Generators: lib/mmgen/metamodel_generator.rb == License RGen is released under the MIT license. rgen-0.8.0/lib/0000755000004100000410000000000012642233643013255 5ustar www-datawww-datargen-0.8.0/lib/ea_support/0000755000004100000410000000000012642233643015436 5ustar www-datawww-datargen-0.8.0/lib/ea_support/uml13_ea_metamodel_generator.rb0000644000004100000410000000362312642233643023472 0ustar www-datawww-datarequire 'metamodels/uml13_metamodel' require 'mmgen/metamodel_generator' require 'rgen/transformer' require 'rgen/environment' require 'rgen/ecore/ecore' include MMGen::MetamodelGenerator class ECoreCopyTransformer < RGen::Transformer copy_all RGen::ECore end eaMMRoot = ECoreCopyTransformer.new.trans(UML13.ecore) eaMMRoot.name = "UML13EA" eaMMRoot.eClassifiers.find{|c| c.name == "ActivityGraph"}.name = "ActivityModel" eaMMRoot.eClassifiers.find{|c| c.name == "Pseudostate"}.name = "PseudoState" compositeState = eaMMRoot.eClassifiers.find{|c| c.name == "CompositeState"} compositeState.eReferences.find{|r| r.name == "subvertex"}.name = "substate" generalization = eaMMRoot.eClassifiers.find{|c| c.name == "Generalization"} generalization.eReferences.find{|r| r.name == "parent"}.name = "supertype" generalization.eReferences.find{|r| r.name == "child"}.name = "subtype" assocEnd = eaMMRoot.eClassifiers.find{|c| c.name == "AssociationEnd"} assocEnd.eAttributes.find{|r| r.name == "ordering"}.name = "isOrdered" assocEnd.eAttributes.find{|r| r.name == "changeability"}.name = "changeable" assocEnd.eAttributes.find{|r| r.name == "isOrdered"}.eType = RGen::ECore::EBoolean assocEnd.eAttributes.find{|r| r.name == "changeable"}.eType.eLiterals.find{|l| l.name == "frozen"}.name = "none" multRef = assocEnd.eStructuralFeatures.find{|f| f.name == "multiplicity"} multRef.eType = nil assocEnd.removeEStructuralFeatures(multRef) assocEnd.addEStructuralFeatures(RGen::ECore::EAttribute.new(:name => "multiplicity", :eType => RGen::ECore::EString)) xmiIdProvider = RGen::ECore::EClass.new(:name => "XmiIdProvider", :ePackage => eaMMRoot) eaMMRoot.eClassifiers.each do |c| if %w(Package Class Generalization Association AssociationEnd StateVertex).include?(c.name) c.addESuperTypes(xmiIdProvider) end end generateMetamodel(eaMMRoot, File.dirname(__FILE__)+"/uml13_ea_metamodel.rb") rgen-0.8.0/lib/ea_support/id_store.rb0000644000004100000410000000117612642233643017600 0ustar www-datawww-datarequire '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 endrgen-0.8.0/lib/ea_support/uml13_ea_metamodel_ext.rb0000644000004100000410000000220312642233643022275 0ustar www-datawww-datamodule UML13EA class << self attr_accessor :idStore end module ModelElement::ClassModule def qualifiedName _name = (respond_to?(:_name) ? self._name : name) || "unnamed" _namespace = respond_to?(:_namespace) ? self._namespace : namespace _namespace && _namespace.qualifiedName ? _namespace.qualifiedName+"::"+_name : _name end end module XmiIdProvider::ClassModule def _xmi_id UML13EA.idStore.idHash[qualifiedName] ||= "EAID_"+object_id.to_s end end module Package::ClassModule def _xmi_id UML13EA.idStore.idHash[qualifiedName] ||= "EAPK_"+object_id.to_s end end module Generalization::ClassModule def _name "#{subtype.name}_#{supertype.name}" end end module Association::ClassModule def _name connection.collect{|c| "#{c.getType.name}_#{c.name}"}.sort.join("_") end end module AssociationEnd::ClassModule def _name "#{getType.name}_#{name}" end def _namespace association end end module StateVertex::ClassModule def _namespace container end end end rgen-0.8.0/lib/ea_support/uml13_ea_to_uml13.rb0000644000004100000410000000635512642233643021125 0ustar www-datawww-datarequire 'rgen/transformer' require 'metamodels/uml13_metamodel' require 'ea_support/uml13_ea_metamodel' class UML13EAToUML13 < RGen::Transformer include UML13EA def transform trans(:class => Package) trans(:class => Class) @env_out.find(:class => UML13::Attribute).each do |me| # remove tagged vales internally used by EA which have been converted to UML me.taggedValue = me.taggedValue.reject{|tv| ["lowerBound", "upperBound"].include?(tv.tag)} end end def cleanModel @env_out.find(:class => UML13::ModelElement).each do |me| me.taggedValue = [] end end copy_all UML13EA, :to => UML13, :except => %w( XmiIdProvider AssociationEnd AssociationEndRole StructuralFeature Attribute Generalization ActivityModel CompositeState PseudoState Dependency ) transform AssociationEndRole, :to => UML13::AssociationEndRole do copyAssociationEnd end transform AssociationEnd, :to => UML13::AssociationEnd do copyAssociationEnd end def copyAssociationEnd copy_features :except => [:isOrdered, :changeable] do {:ordering => isOrdered ? :ordered : :unordered, :changeability => {:none => :frozen}[changeable] || changeable, :aggregation => {:shared => :aggregate}[aggregation] || aggregation, :multiplicity => UML13::Multiplicity.new( :range => [UML13::MultiplicityRange.new( :lower => multiplicity && multiplicity.split("..").first, :upper => multiplicity && multiplicity.split("..").last)])} end end transform StructuralFeature, :to => UML13::StructuralFeature, :if => lambda{|c| !@current_object.is_a?(UML13EA::Attribute)} do copy_features :except => [:changeable] do {:changeability => {:none => :frozen}[changeable] } end end transform StructuralFeature, :to => UML13::Attribute, :if => lambda{|c| @current_object.is_a?(UML13EA::Attribute)} do _lowerBound = taggedValue.find{|tv| tv.tag == "lowerBound"} _upperBound = taggedValue.find{|tv| tv.tag == "upperBound"} if _lowerBound || _upperBound _multiplicity = UML13::Multiplicity.new( :range => [UML13::MultiplicityRange.new( :lower => (_lowerBound && _lowerBound.value) || "0", :upper => (_upperBound && _upperBound.value) || "1" )]) end copy_features :except => [:changeable] do {:changeability => {:none => :frozen}[changeable], :multiplicity => _multiplicity } end end transform Generalization, :to => UML13::Generalization do copy_features :except => [:subtype, :supertype] do { :child => trans(subtype), :parent => trans(supertype) } end end copy ActivityModel, :to => UML13::ActivityGraph transform CompositeState, :to => UML13::CompositeState do copy_features :except => [:substate] do { :subvertex => trans(substate) } end end copy PseudoState, :to => UML13::Pseudostate transform Dependency, :to => UML13::Dependency do _name_tag = taggedValue.find{|tv| tv.tag == "dst_name"} copy_features do { :name => (_name_tag && _name_tag.value) || "Anonymous" } end end end rgen-0.8.0/lib/ea_support/uml13_to_uml13_ea.rb0000644000004100000410000000605712642233643021124 0ustar www-datawww-datarequire 'rgen/transformer' require 'metamodels/uml13_metamodel' require 'ea_support/uml13_ea_metamodel' require 'ea_support/uml13_ea_metamodel_ext' class UML13ToUML13EA < RGen::Transformer include UML13 def transform trans(:class => Package) trans(:class => Class) end copy_all UML13, :to => UML13EA, :except => %w( ActivityGraph CompositeState SimpleState Class Association AssociationEnd AssociationEndRole Generalization Pseudostate Attribute ) copy ActivityGraph, :to => UML13EA::ActivityModel copy Pseudostate, :to => UML13EA::PseudoState transform CompositeState, :to => UML13EA::CompositeState do copy_features :except => [:subvertex] do { :substate => trans(subvertex) } end end transform SimpleState, :to => UML13EA::SimpleState do copy_features :except => [:container] do { :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_stype", :value => "State")] + (container ? [ @env_out.new(UML13EA::TaggedValue, :tag => "owner", :value => trans(container)._xmi_id)] : []) } end end transform Class, :to => UML13EA::Class do copy_features do { :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_stype", :value => "Class")]} end end transform Association, :to => UML13EA::Association do copy_features do { :connection => trans(connection[1].isNavigable ? [connection[0], connection[1]] : [connection[1], connection[0]]), :taggedValue => trans(taggedValue) + [ @env_out.new(UML13EA::TaggedValue, :tag => "ea_type", :value => "Association"), @env_out.new(UML13EA::TaggedValue, :tag => "direction", :value => connection.all?{|c| c.isNavigable} ? "Bi-Directional" : "Source -> Destination")] } end end transform AssociationEnd, :to => UML13EA::AssociationEnd do copyAssociationEnd end transform AssociationEndRole, :to => UML13EA::AssociationEndRole do copyAssociationEnd end def copyAssociationEnd _lower = multiplicity && multiplicity.range.first.lower _upper = multiplicity && multiplicity.range.first.upper copy_features :except => [:multiplicity, :ordering, :changeability] do { :multiplicity => _lower == _upper ? _lower : "#{_lower}..#{_upper}", :isOrdered => ordering == :ordered, :changeable => :none } #{:frozen => :none}[changeability] || changeability} end end transform Attribute, :to => UML13EA::Attribute do copy_features :except => [:changeability] do { :changeable => {:frozen => :none}[changeability] } end end transform Generalization, :to => UML13EA::Generalization do copy_features :except => [:child, :parent] do { :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_type", :value => "Generalization")], :subtype => trans(child), :supertype => trans(parent)} end end end rgen-0.8.0/lib/ea_support/uml13_ea_metamodel.rb0000644000004100000410000005205512642233643021427 0ustar www-datawww-datarequire 'rgen/metamodel_builder' module UML13EA extend RGen::MetamodelBuilder::ModuleExtension include RGen::MetamodelBuilder::DataTypes OperationDirectionKind = Enum.new(:name => 'OperationDirectionKind', :literals =>[ ]) MessageDirectionKind = Enum.new(:name => 'MessageDirectionKind', :literals =>[ ]) ChangeableKind = Enum.new(:name => 'ChangeableKind', :literals =>[ :changeable, :none, :addOnly ]) PseudostateKind = Enum.new(:name => 'PseudostateKind', :literals =>[ :initial, :deepHistory, :shallowHistory, :join, :fork, :branch, :junction, :final ]) ParameterDirectionKind = Enum.new(:name => 'ParameterDirectionKind', :literals =>[ :in, :inout, :out, :return ]) ScopeKind = Enum.new(:name => 'ScopeKind', :literals =>[ :instance, :classifier ]) OrderingKind = Enum.new(:name => 'OrderingKind', :literals =>[ :unordered, :ordered, :sorted ]) CallConcurrencyKind = Enum.new(:name => 'CallConcurrencyKind', :literals =>[ :sequential, :guarded, :concurrent ]) AggregationKind = Enum.new(:name => 'AggregationKind', :literals =>[ :none, :aggregate, :composite, :shared ]) VisibilityKind = Enum.new(:name => 'VisibilityKind', :literals =>[ :public, :protected, :private ]) end class UML13EA::Expression < RGen::MetamodelBuilder::MMBase has_attr 'language', String has_attr 'body', String end class UML13EA::ActionExpression < UML13EA::Expression end class UML13EA::Element < RGen::MetamodelBuilder::MMBase end class UML13EA::ModelElement < UML13EA::Element has_attr 'name', String has_attr 'visibility', UML13EA::VisibilityKind, :defaultValueLiteral => "public" has_attr 'isSpecification', Boolean end class UML13EA::Namespace < UML13EA::ModelElement end class UML13EA::GeneralizableElement < UML13EA::ModelElement has_attr 'isRoot', Boolean has_attr 'isLeaf', Boolean has_attr 'isAbstract', Boolean end class UML13EA::Classifier < RGen::MetamodelBuilder::MMMultiple(UML13EA::GeneralizableElement, UML13EA::Namespace) end class UML13EA::ClassifierRole < UML13EA::Classifier end class UML13EA::PresentationElement < UML13EA::Element end class UML13EA::DiagramElement < UML13EA::PresentationElement has_attr 'geometry', String has_attr 'style', String end class UML13EA::Feature < UML13EA::ModelElement has_attr 'ownerScope', UML13EA::ScopeKind, :defaultValueLiteral => "instance" end class UML13EA::BehavioralFeature < UML13EA::Feature has_attr 'isQuery', Boolean end class UML13EA::Method < UML13EA::BehavioralFeature end class UML13EA::Actor < UML13EA::Classifier end class UML13EA::DataType < UML13EA::Classifier end class UML13EA::Primitive < UML13EA::DataType end class UML13EA::Action < UML13EA::ModelElement has_attr 'isAsynchronous', Boolean end class UML13EA::SendAction < UML13EA::Action end class UML13EA::Interface < UML13EA::Classifier end class UML13EA::Event < UML13EA::ModelElement end class UML13EA::ChangeEvent < UML13EA::Event end class UML13EA::Partition < UML13EA::ModelElement end class UML13EA::Comment < UML13EA::ModelElement has_attr 'body', String end class UML13EA::ProgrammingLanguageType < UML13EA::DataType end class UML13EA::StateMachine < UML13EA::ModelElement end class UML13EA::Call < RGen::MetamodelBuilder::MMBase end class UML13EA::Operation < UML13EA::BehavioralFeature has_attr 'concurrency', UML13EA::CallConcurrencyKind, :defaultValueLiteral => "sequential" has_attr 'isRoot', Boolean has_attr 'isLeaf', Boolean has_attr 'isAbstract', Boolean end class UML13EA::XmiIdProvider < RGen::MetamodelBuilder::MMBase end class UML13EA::StateVertex < RGen::MetamodelBuilder::MMMultiple(UML13EA::ModelElement, UML13EA::XmiIdProvider) end class UML13EA::SynchState < UML13EA::StateVertex has_attr 'bound', Integer end class UML13EA::ClassifierInState < UML13EA::Classifier end class UML13EA::Link < UML13EA::ModelElement end class UML13EA::ProcedureExpression < UML13EA::Expression end class UML13EA::CallEvent < UML13EA::Event end class UML13EA::AssignmentAction < UML13EA::Action end class UML13EA::Relationship < UML13EA::ModelElement end class UML13EA::Association < RGen::MetamodelBuilder::MMMultiple(UML13EA::GeneralizableElement, UML13EA::Relationship, UML13EA::XmiIdProvider) end class UML13EA::AssociationRole < UML13EA::Association end class UML13EA::Diagram < UML13EA::PresentationElement has_attr 'name', String has_attr 'toolName', String has_attr 'diagramType', String has_attr 'style', String end class UML13EA::MultiplicityRange < RGen::MetamodelBuilder::MMBase has_attr 'lower', String has_attr 'upper', String end class UML13EA::ActionSequence < UML13EA::Action end class UML13EA::Constraint < UML13EA::ModelElement end class UML13EA::Instance < UML13EA::ModelElement end class UML13EA::UseCaseInstance < UML13EA::Instance end class UML13EA::State < UML13EA::StateVertex end class UML13EA::CompositeState < UML13EA::State has_attr 'isConcurrent', Boolean end class UML13EA::SubmachineState < UML13EA::CompositeState end class UML13EA::SubactivityState < UML13EA::SubmachineState has_attr 'isDynamic', Boolean end class UML13EA::StructuralFeature < UML13EA::Feature has_attr 'changeable', UML13EA::ChangeableKind, :defaultValueLiteral => "changeable" has_attr 'targetScope', UML13EA::ScopeKind, :defaultValueLiteral => "instance" end class UML13EA::Attribute < UML13EA::StructuralFeature end class UML13EA::Flow < UML13EA::Relationship end class UML13EA::Class < RGen::MetamodelBuilder::MMMultiple(UML13EA::Classifier, UML13EA::XmiIdProvider) has_attr 'isActive', Boolean end class UML13EA::Guard < UML13EA::ModelElement end class UML13EA::CreateAction < UML13EA::Action end class UML13EA::IterationExpression < UML13EA::Expression end class UML13EA::ReturnAction < UML13EA::Action end class UML13EA::Parameter < UML13EA::ModelElement has_attr 'kind', UML13EA::ParameterDirectionKind, :defaultValueLiteral => "inout" end class UML13EA::Dependency < UML13EA::Relationship end class UML13EA::Binding < UML13EA::Dependency end class UML13EA::Package < RGen::MetamodelBuilder::MMMultiple(UML13EA::Namespace, UML13EA::GeneralizableElement, UML13EA::XmiIdProvider) end class UML13EA::ObjectSetExpression < UML13EA::Expression end class UML13EA::StubState < UML13EA::StateVertex has_attr 'referenceState', String end class UML13EA::Stereotype < UML13EA::GeneralizableElement has_attr 'icon', String has_attr 'baseClass', String end class UML13EA::Object < UML13EA::Instance end class UML13EA::LinkObject < RGen::MetamodelBuilder::MMMultiple(UML13EA::Link, UML13EA::Object) end class UML13EA::ComponentInstance < UML13EA::Instance end class UML13EA::Usage < UML13EA::Dependency end class UML13EA::SignalEvent < UML13EA::Event end class UML13EA::Structure < UML13EA::DataType end class UML13EA::AssociationEnd < RGen::MetamodelBuilder::MMMultiple(UML13EA::ModelElement, UML13EA::XmiIdProvider) has_attr 'isNavigable', Boolean, :defaultValueLiteral => "false" has_attr 'isOrdered', Boolean, :defaultValueLiteral => "false" has_attr 'aggregation', UML13EA::AggregationKind, :defaultValueLiteral => "none" has_attr 'targetScope', UML13EA::ScopeKind, :defaultValueLiteral => "instance" has_attr 'changeable', UML13EA::ChangeableKind, :defaultValueLiteral => "changeable" has_attr 'multiplicity', String end class UML13EA::AssociationEndRole < UML13EA::AssociationEnd end class UML13EA::Signal < UML13EA::Classifier end class UML13EA::Exception < UML13EA::Signal end class UML13EA::Extend < UML13EA::Relationship end class UML13EA::Argument < UML13EA::ModelElement end class UML13EA::TemplateParameter < RGen::MetamodelBuilder::MMBase end class UML13EA::PseudoState < UML13EA::StateVertex has_attr 'kind', UML13EA::PseudostateKind, :defaultValueLiteral => "initial" end class UML13EA::SimpleState < UML13EA::State end class UML13EA::ActionState < UML13EA::SimpleState has_attr 'isDynamic', Boolean end class UML13EA::TypeExpression < UML13EA::Expression end class UML13EA::DestroyAction < UML13EA::Action end class UML13EA::TerminateAction < UML13EA::Action end class UML13EA::Generalization < RGen::MetamodelBuilder::MMMultiple(UML13EA::Relationship, UML13EA::XmiIdProvider) has_attr 'discriminator', String end class UML13EA::FinalState < UML13EA::State end class UML13EA::Subsystem < RGen::MetamodelBuilder::MMMultiple(UML13EA::Package, UML13EA::Classifier) has_attr 'isInstantiable', Boolean end class UML13EA::TimeExpression < UML13EA::Expression end class UML13EA::TaggedValue < UML13EA::Element has_attr 'tag', String has_attr 'value', String end class UML13EA::DataValue < UML13EA::Instance end class UML13EA::Transition < UML13EA::ModelElement end class UML13EA::NodeInstance < UML13EA::Instance end class UML13EA::Component < UML13EA::Classifier end class UML13EA::Message < UML13EA::ModelElement end class UML13EA::Enumeration < UML13EA::DataType end class UML13EA::Reception < UML13EA::BehavioralFeature has_attr 'isPolymorphic', Boolean has_attr 'specification', String end class UML13EA::Include < UML13EA::Relationship end class UML13EA::CallState < UML13EA::ActionState end class UML13EA::ElementResidence < RGen::MetamodelBuilder::MMBase has_attr 'visibility', UML13EA::VisibilityKind, :defaultValueLiteral => "public" end class UML13EA::UninterpretedAction < UML13EA::Action end class UML13EA::ArgListsExpression < UML13EA::Expression end class UML13EA::Stimulus < UML13EA::ModelElement end class UML13EA::AssociationClass < RGen::MetamodelBuilder::MMMultiple(UML13EA::Class, UML13EA::Association) end class UML13EA::Node < UML13EA::Classifier end class UML13EA::ElementImport < RGen::MetamodelBuilder::MMBase has_attr 'visibility', UML13EA::VisibilityKind, :defaultValueLiteral => "public" has_attr 'alias', String end class UML13EA::BooleanExpression < UML13EA::Expression end class UML13EA::Collaboration < RGen::MetamodelBuilder::MMMultiple(UML13EA::GeneralizableElement, UML13EA::Namespace) end class UML13EA::CallAction < UML13EA::Action end class UML13EA::UseCase < UML13EA::Classifier end class UML13EA::ActivityModel < UML13EA::StateMachine end class UML13EA::Permission < UML13EA::Dependency end class UML13EA::Interaction < UML13EA::ModelElement end class UML13EA::EnumerationLiteral < RGen::MetamodelBuilder::MMBase has_attr 'name', String end class UML13EA::Model < UML13EA::Package end class UML13EA::LinkEnd < UML13EA::ModelElement end class UML13EA::ExtensionPoint < UML13EA::ModelElement has_attr 'location', String end class UML13EA::Multiplicity < RGen::MetamodelBuilder::MMBase end class UML13EA::ObjectFlowState < UML13EA::SimpleState has_attr 'isSynch', Boolean end class UML13EA::AttributeLink < UML13EA::ModelElement end class UML13EA::MappingExpression < UML13EA::Expression end class UML13EA::TimeEvent < UML13EA::Event end class UML13EA::Abstraction < UML13EA::Dependency end class UML13EA::ActionInstance < RGen::MetamodelBuilder::MMBase end UML13EA::ClassifierRole.contains_one_uni 'multiplicity', UML13EA::Multiplicity UML13EA::ClassifierRole.has_many 'availableContents', UML13EA::ModelElement UML13EA::ClassifierRole.has_many 'availableFeature', UML13EA::Feature UML13EA::ClassifierRole.has_one 'base', UML13EA::Classifier, :lowerBound => 1 UML13EA::Diagram.contains_many 'element', UML13EA::DiagramElement, 'diagram' UML13EA::Method.many_to_one 'specification', UML13EA::Operation, 'method' UML13EA::Method.contains_one_uni 'body', UML13EA::ProcedureExpression UML13EA::SendAction.has_one 'signal', UML13EA::Signal, :lowerBound => 1 UML13EA::ChangeEvent.contains_one_uni 'changeExpression', UML13EA::BooleanExpression UML13EA::Partition.has_many 'contents', UML13EA::ModelElement UML13EA::Comment.many_to_many 'annotatedElement', UML13EA::ModelElement, 'comment' UML13EA::ProgrammingLanguageType.contains_one_uni 'type', UML13EA::TypeExpression UML13EA::Action.contains_one_uni 'recurrence', UML13EA::IterationExpression UML13EA::Action.contains_one_uni 'target', UML13EA::ObjectSetExpression UML13EA::Action.contains_one_uni 'script', UML13EA::ActionExpression UML13EA::Action.contains_many_uni 'actualArgument', UML13EA::Argument UML13EA::StateMachine.many_to_one 'context', UML13EA::ModelElement, 'behavior' UML13EA::StateMachine.contains_many_uni 'transitions', UML13EA::Transition UML13EA::StateMachine.contains_one_uni 'top', UML13EA::State, :lowerBound => 1 UML13EA::Operation.one_to_many 'occurrence', UML13EA::CallEvent, 'operation' UML13EA::ClassifierInState.has_one 'type', UML13EA::Classifier, :lowerBound => 1 UML13EA::ClassifierInState.has_many 'inState', UML13EA::State UML13EA::Link.contains_many_uni 'connection', UML13EA::LinkEnd, :lowerBound => 2 UML13EA::Link.has_one 'association', UML13EA::Association, :lowerBound => 1 UML13EA::PresentationElement.many_to_many 'subject', UML13EA::ModelElement, 'presentation' UML13EA::AssociationRole.contains_one_uni 'multiplicity', UML13EA::Multiplicity UML13EA::AssociationRole.has_one 'base', UML13EA::Association UML13EA::Diagram.has_one 'owner', UML13EA::ModelElement, :lowerBound => 1 UML13EA::ActionSequence.contains_many_uni 'action', UML13EA::Action UML13EA::Constraint.contains_one_uni 'body', UML13EA::BooleanExpression UML13EA::Constraint.many_to_many 'constrainedElement', UML13EA::ModelElement, 'constraint', :lowerBound => 1 UML13EA::SubactivityState.contains_one_uni 'dynamicArguments', UML13EA::ArgListsExpression UML13EA::AssociationEnd.contains_many 'qualifier', UML13EA::Attribute, 'associationEnd' UML13EA::Attribute.contains_one_uni 'initialValue', UML13EA::Expression UML13EA::Flow.many_to_many 'source', UML13EA::ModelElement, 'sourceFlow' UML13EA::Flow.many_to_many 'target', UML13EA::ModelElement, 'targetFlow' UML13EA::Guard.contains_one_uni 'expression', UML13EA::BooleanExpression UML13EA::CreateAction.has_one 'instantiation', UML13EA::Classifier, :lowerBound => 1 UML13EA::Namespace.contains_many 'ownedElement', UML13EA::ModelElement, 'namespace' UML13EA::Parameter.contains_one_uni 'defaultValue', UML13EA::Expression UML13EA::Parameter.many_to_many 'state', UML13EA::ObjectFlowState, 'parameter' UML13EA::Parameter.has_one 'type', UML13EA::Classifier, :lowerBound => 1 UML13EA::Binding.has_many 'argument', UML13EA::ModelElement, :lowerBound => 1 UML13EA::Event.contains_many_uni 'parameters', UML13EA::Parameter UML13EA::Dependency.many_to_many 'supplier', UML13EA::ModelElement, 'supplierDependency', :opposite_lowerBound => 1 UML13EA::Dependency.many_to_many 'client', UML13EA::ModelElement, 'clientDependency', :opposite_lowerBound => 1 UML13EA::Package.contains_many 'importedElement', UML13EA::ElementImport, 'package' UML13EA::Classifier.contains_many 'feature', UML13EA::Feature, 'owner' UML13EA::Stereotype.one_to_many 'extendedElement', UML13EA::ModelElement, 'stereotype' UML13EA::Stereotype.has_many 'requiredTag', UML13EA::TaggedValue UML13EA::ComponentInstance.has_many 'resident', UML13EA::Instance UML13EA::SignalEvent.many_to_one 'signal', UML13EA::Signal, 'occurrence', :lowerBound => 1 UML13EA::Instance.contains_many_uni 'slot', UML13EA::AttributeLink UML13EA::Instance.one_to_many 'linkEnd', UML13EA::LinkEnd, 'instance' UML13EA::Instance.has_many 'classifier', UML13EA::Classifier, :lowerBound => 1 UML13EA::AssociationEndRole.has_many 'availableQualifier', UML13EA::Attribute UML13EA::AssociationEndRole.has_one 'base', UML13EA::AssociationEnd UML13EA::Extend.many_to_one 'extension', UML13EA::UseCase, 'extend' UML13EA::Extend.contains_one_uni 'condition', UML13EA::BooleanExpression UML13EA::Extend.has_many 'extensionPoint', UML13EA::ExtensionPoint, :lowerBound => 1 UML13EA::Extend.has_one 'base', UML13EA::UseCase, :lowerBound => 1 UML13EA::Argument.contains_one_uni 'value', UML13EA::Expression UML13EA::TemplateParameter.has_one 'modelElement', UML13EA::ModelElement UML13EA::TemplateParameter.has_one 'defaultElement', UML13EA::ModelElement UML13EA::ActionState.contains_one_uni 'dynamicArguments', UML13EA::ArgListsExpression UML13EA::GeneralizableElement.one_to_many 'specialization', UML13EA::Generalization, 'supertype' UML13EA::GeneralizableElement.one_to_many 'generalization', UML13EA::Generalization, 'subtype' UML13EA::StateVertex.one_to_many 'incoming', UML13EA::Transition, 'target', :opposite_lowerBound => 1 UML13EA::StateVertex.one_to_many 'outgoing', UML13EA::Transition, 'source', :opposite_lowerBound => 1 UML13EA::CompositeState.contains_many 'substate', UML13EA::StateVertex, 'container', :lowerBound => 1 UML13EA::ModelElement.contains_many 'taggedValue', UML13EA::TaggedValue, 'modelElement' UML13EA::StructuralFeature.contains_one_uni 'multiplicity', UML13EA::Multiplicity UML13EA::StructuralFeature.has_one 'type', UML13EA::Classifier, :lowerBound => 1 UML13EA::Transition.has_one 'trigger', UML13EA::Event UML13EA::Transition.contains_one_uni 'effect', UML13EA::Action UML13EA::Transition.contains_one_uni 'guard', UML13EA::Guard UML13EA::NodeInstance.has_many 'resident', UML13EA::ComponentInstance UML13EA::Component.contains_many 'residentElement', UML13EA::ElementResidence, 'implementationLocation' UML13EA::Component.many_to_many 'deploymentLocation', UML13EA::Node, 'resident' UML13EA::Message.has_one 'action', UML13EA::Action, :lowerBound => 1 UML13EA::Message.has_one 'communicationConnection', UML13EA::AssociationRole UML13EA::Message.has_many 'predecessor', UML13EA::Message UML13EA::Message.has_one 'receiver', UML13EA::ClassifierRole, :lowerBound => 1 UML13EA::Message.has_one 'sender', UML13EA::ClassifierRole, :lowerBound => 1 UML13EA::Message.has_one 'activator', UML13EA::Message UML13EA::Interaction.contains_many 'message', UML13EA::Message, 'interaction', :lowerBound => 1 UML13EA::ModelElement.one_to_many 'elementResidence', UML13EA::ElementResidence, 'resident' UML13EA::ModelElement.contains_many_uni 'templateParameter', UML13EA::TemplateParameter UML13EA::ModelElement.one_to_many 'elementImport', UML13EA::ElementImport, 'modelElement' UML13EA::Enumeration.contains_many_uni 'literal', UML13EA::EnumerationLiteral, :lowerBound => 1 UML13EA::Reception.many_to_one 'signal', UML13EA::Signal, 'reception' UML13EA::Association.contains_many 'connection', UML13EA::AssociationEnd, 'association', :lowerBound => 2 UML13EA::Include.many_to_one 'base', UML13EA::UseCase, 'include' UML13EA::Include.has_one 'addition', UML13EA::UseCase, :lowerBound => 1 UML13EA::Classifier.many_to_many 'participant', UML13EA::AssociationEnd, 'specification' UML13EA::Classifier.one_to_many 'associationEnd', UML13EA::AssociationEnd, 'type' UML13EA::Stimulus.has_one 'dispatchAction', UML13EA::Action, :lowerBound => 1 UML13EA::Stimulus.has_one 'communicationLink', UML13EA::Link UML13EA::Stimulus.has_one 'receiver', UML13EA::Instance, :lowerBound => 1 UML13EA::Stimulus.has_one 'sender', UML13EA::Instance, :lowerBound => 1 UML13EA::Stimulus.has_many 'argument', UML13EA::Instance UML13EA::State.contains_one_uni 'doActivity', UML13EA::Action UML13EA::State.contains_many_uni 'internalTransition', UML13EA::Transition UML13EA::State.has_many 'deferrableEvent', UML13EA::Event UML13EA::State.contains_one_uni 'exit', UML13EA::Action UML13EA::State.contains_one_uni 'entry', UML13EA::Action UML13EA::Collaboration.has_one 'representedOperation', UML13EA::Operation UML13EA::Collaboration.has_one 'representedClassifier', UML13EA::Classifier UML13EA::Collaboration.has_many 'constrainingElement', UML13EA::ModelElement UML13EA::Collaboration.contains_many 'interaction', UML13EA::Interaction, 'context' UML13EA::CallAction.has_one 'operation', UML13EA::Operation, :lowerBound => 1 UML13EA::UseCase.has_many 'extensionPoint', UML13EA::ExtensionPoint UML13EA::ActivityModel.contains_many_uni 'partition', UML13EA::Partition UML13EA::Interaction.contains_many_uni 'link', UML13EA::Link UML13EA::LinkEnd.has_one 'associationEnd', UML13EA::AssociationEnd, :lowerBound => 1 UML13EA::LinkEnd.has_one 'participant', UML13EA::Instance, :lowerBound => 1 UML13EA::BehavioralFeature.many_to_many 'raisedSignal', UML13EA::Signal, 'context' UML13EA::BehavioralFeature.contains_many_uni 'parameter', UML13EA::Parameter UML13EA::SubmachineState.has_one 'submachine', UML13EA::StateMachine, :lowerBound => 1 UML13EA::Multiplicity.contains_many_uni 'range', UML13EA::MultiplicityRange, :lowerBound => 1 UML13EA::ObjectFlowState.has_one 'type', UML13EA::Classifier, :lowerBound => 1 UML13EA::ObjectFlowState.has_one 'available', UML13EA::Parameter, :lowerBound => 1 UML13EA::AttributeLink.has_one 'value', UML13EA::Instance, :lowerBound => 1 UML13EA::AttributeLink.has_one 'attribute', UML13EA::Attribute, :lowerBound => 1 UML13EA::TimeEvent.contains_one_uni 'when', UML13EA::TimeExpression UML13EA::Abstraction.contains_one_uni 'mapping', UML13EA::MappingExpression rgen-0.8.0/lib/ea_support/ea_support.rb0000644000004100000410000000332412642233643020146 0ustar www-datawww-datarequire '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 endrgen-0.8.0/lib/transformers/0000755000004100000410000000000012642233643016002 5ustar www-datawww-datargen-0.8.0/lib/transformers/ecore_to_uml13.rb0000644000004100000410000000521612642233643021153 0ustar www-datawww-datarequire 'rgen/transformer' require 'rgen/ecore/ecore' require 'metamodels/uml13_metamodel' class ECoreToUML13 < RGen::Transformer include RGen::ECore def transform trans(:class => EPackage) trans(:class => EClass) trans(:class => EEnum) end transform EPackage, :to => UML13::Package do {:name => name, :namespace => trans(eSuperPackage) || model, :ownedElement => trans(eClassifiers.select{|c| c.is_a?(EClass)} + eSubpackages) } end transform EClass, :to => UML13::Class do {:name => name, :namespace => trans(ePackage), :feature => trans(eStructuralFeatures.select{|f| f.is_a?(EAttribute)} + eOperations), :associationEnd => trans(eStructuralFeatures.select{|f| f.is_a?(EReference)}), :generalization => eSuperTypes.collect { |st| @env_out.new(UML13::Generalization, :parent => trans(st), :namespace => trans(ePackage) || model) } } end transform EEnum, :to => UML13::Class do {:name => name, :namespace => trans(ePackage), :feature => trans(eLiterals) } end transform EEnumLiteral, :to => UML13::Attribute do {:name => name } end transform EAttribute, :to => UML13::Attribute do _typemap = {"String" => "string", "Boolean" => "boolean", "Integer" => "int", "Float" => "float"} {:name => name, :taggedValue => [@env_out.new(UML13::TaggedValue, :tag => "type", :value => _typemap[eType.instanceClassName] || eType.name)] } end transform EReference, :to => UML13::AssociationEnd do _otherAssocEnd = eOpposite ? trans(eOpposite) : @env_out.new(UML13::AssociationEnd, :type => trans(eType), :name => name, :multiplicity => createMultiplicity(@current_object), :aggregation => :none, :isNavigable => true) { :association => trans(@current_object).association || @env_out.new(UML13::Association, :connection => [_otherAssocEnd], :namespace => trans(eContainingClass.ePackage) || model), :name => eOpposite && eOpposite.name, :multiplicity => eOpposite && createMultiplicity(eOpposite), :aggregation => containment ? :composite : :none, :isNavigable => !eOpposite.nil? } end transform EOperation, :to => UML13::Operation do {:name => name} end def createMultiplicity(ref) @env_out.new(UML13::Multiplicity, :range => [ @env_out.new(UML13::MultiplicityRange, :lower => ref.lowerBound.to_s.sub("-1","*"), :upper => ref.upperBound.to_s.sub("-1","*"))]) end def model @model ||= @env_out.new(UML13::Model, :name => "Model") end end rgen-0.8.0/lib/transformers/uml13_to_ecore.rb0000644000004100000410000001070512642233643021152 0ustar www-datawww-datarequire 'metamodels/uml13_metamodel' require 'rgen/transformer' require 'rgen/ecore/ecore' require 'rgen/array_extensions' class UML13ToECore < RGen::Transformer include RGen::ECore # Options: # # :reference_filter: # a proc which receives an AssociationEnd or a Dependency and should return # true or false, depending on if a referece should be created for it or not # def initialize(*args) options = {} if args.last.is_a?(Hash) options = args.pop end @reference_filter = options[:reference_filter] || proc do |e| if e.is_a?(UML13::AssociationEnd) otherEnd = e.association.connection.find{|ae| ae != e} otherEnd.name && otherEnd.name.size > 0 else false end end super(*args) end def transform trans(:class => UML13::Class) end transform UML13::Model, :to => EPackage do trans(ownedClassOrPackage) { :name => name && name.strip } end transform UML13::Package, :to => EPackage do trans(ownedClassOrPackage) { :name => name && name.strip, :eSuperPackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil) } end method :ownedClassOrPackage do ownedElement.select{|e| e.is_a?(UML13::Package) || e.is_a?(UML13::Class)} end transform UML13::Class, :to => EClass do { :name => name && name.strip, :abstract => isAbstract, :ePackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil), :eStructuralFeatures => trans(feature.select{|f| f.is_a?(UML13::Attribute)} + associationEnd + clientDependency), :eOperations => trans(feature.select{|f| f.is_a?(UML13::Operation)}), :eSuperTypes => trans(generalization.parent + clientDependency.select{|d| d.stereotype && d.stereotype.name == "implements"}.supplier), :eAnnotations => createAnnotations(taggedValue) } end transform UML13::Interface, :to => EClass do { :name => name && name.strip, :abstract => isAbstract, :ePackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil), :eStructuralFeatures => trans(feature.select{|f| f.is_a?(UML13::Attribute)} + associationEnd), :eOperations => trans(feature.select{|f| f.is_a?(UML13::Operation)}), :eSuperTypes => trans(generalization.parent)} end transform UML13::Attribute, :to => EAttribute do { :name => name && name.strip, :eType => trans(getType), :lowerBound => (multiplicity && multiplicity.range.first.lower && multiplicity.range.first.lower.to_i) || 0, :upperBound => (multiplicity && multiplicity.range.first.upper && multiplicity.range.first.upper.gsub('*','-1').to_i) || 1, :eAnnotations => createAnnotations(taggedValue) } end transform UML13::DataType, :to => EDataType do { :name => name && name.strip, :ePackage => trans(namespace.is_a?(UML13::Package) ? namespace : nil), :eAnnotations => createAnnotations(taggedValue) } end transform UML13::Operation, :to => EOperation do { :name => name && name.strip } end transform UML13::AssociationEnd, :to => EReference, :if => :isReference do otherEnd = association.connection.find{|ae| ae != @current_object} { :eType => trans(otherEnd.type), :name => otherEnd.name && otherEnd.name.strip, :eOpposite => trans(otherEnd), :lowerBound => (otherEnd.multiplicity && otherEnd.multiplicity.range.first.lower && otherEnd.multiplicity.range.first.lower.to_i) || 0, :upperBound => (otherEnd.multiplicity && otherEnd.multiplicity.range.first.upper && otherEnd.multiplicity.range.first.upper.gsub('*','-1').to_i) || 1, :containment => (aggregation == :composite), :eAnnotations => createAnnotations(association.taggedValue) } end transform UML13::Dependency, :to => EReference, :if => :isReference do { :eType => trans(supplier.first), :name => name, :lowerBound => 0, :upperBound => 1, :containment => false, :eAnnotations => createAnnotations(taggedValue) } end method :isReference do @reference_filter.call(@current_object) end def createAnnotations(taggedValues) if taggedValues.size > 0 [ EAnnotation.new(:details => trans(taggedValues)) ] else [] end end transform UML13::TaggedValue, :to => EStringToStringMapEntry do { :key => tag, :value => value} end end rgen-0.8.0/lib/rgen/0000755000004100000410000000000012642233643014210 5ustar www-datawww-datargen-0.8.0/lib/rgen/template_language.rb0000644000004100000410000002773512642233643020231 0ustar www-datawww-data# 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 endrgen-0.8.0/lib/rgen/fragment/0000755000004100000410000000000012642233643016013 5ustar www-datawww-datargen-0.8.0/lib/rgen/fragment/fragmented_model.rb0000644000004100000410000001034512642233643021637 0ustar www-datawww-datarequire '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 rgen-0.8.0/lib/rgen/fragment/model_fragment.rb0000644000004100000410000002331012642233643021322 0ustar www-datawww-datarequire '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 rgen-0.8.0/lib/rgen/fragment/dump_file_cache.rb0000644000004100000410000000355512642233643021437 0ustar www-datawww-datamodule RGen module Fragment # Caches model fragments in Ruby dump files. # # Dump files are created per each fragment file. # # The main goal is to support fast loading and joining of fragments. Therefore the cache # stores additional information which makes the joining process faster (adding to # environment, resolving references) # class DumpFileCache # +cache_map+ must be an object responding to +load_data+ and +store_data+ # for loading or storing data associated with a file; # this can be an instance of Util::FileCacheMap def initialize(cache_map) @cache_map = cache_map end # Note that the fragment must not be connected to other fragments by resolved references # unresolve the fragment if necessary def store(fragment) fref = fragment.fragment_ref # temporarily remove the reference to the fragment to avoid dumping the fragment fref.fragment = nil @cache_map.store_data(fragment.location, Marshal.dump({ :root_elements => fragment.root_elements, :elements => fragment.elements, :index => fragment.index, :unresolved_refs => fragment.unresolved_refs, :fragment_ref => fref, :data => fragment.data })) fref.fragment = fragment end def load(fragment) dump = @cache_map.load_data(fragment.location) return :invalid if dump == :invalid header = Marshal.load(dump) fragment.set_root_elements(header[:root_elements], :elements => header[:elements], :index => header[:index], :unresolved_refs => header[:unresolved_refs]) fragment.data = header[:data] if header[:fragment_ref] fragment.fragment_ref = header[:fragment_ref] fragment.fragment_ref.fragment = fragment else raise "no fragment_ref in fragment loaded from cache" end end end end end rgen-0.8.0/lib/rgen/metamodel_builder.rb0000644000004100000410000001712512642233643020220 0ustar www-datawww-data# RGen Framework # (c) Martin Thiede, 2006 require 'rgen/metamodel_builder/constant_order_helper' require 'rgen/metamodel_builder/builder_runtime' require 'rgen/metamodel_builder/builder_extensions' require 'rgen/metamodel_builder/module_extension' require 'rgen/metamodel_builder/data_types' require 'rgen/metamodel_builder/mm_multiple' require 'rgen/ecore/ecore_interface' module RGen # MetamodelBuilder can be used to create a metamodel, i.e. Ruby classes which # act as metamodel elements. # # To create a new metamodel element, create a Ruby class which inherits from # MetamodelBuilder::MMBase # # class Person < RGen::MetamodelBuilder::MMBase # end # # This way a couple of class methods are made available to the new class. # These methods can be used to: # * add attributes to the class # * add associations with other classes # # Here is an example: # # class Person < RGen::MetamodelBuilder::MMBase # has_attr 'name', String # has_attr 'age', Integer # end # # class House < RGen::MetamodelBuilder::MMBase # has_attr 'address' # String is default # end # # Person.many_to_many 'homes', House, 'inhabitants' # # See BuilderExtensions for details about the available class methods. # # =Attributes # # The example above creates two classes 'Person' and 'House'. Person has the attributes # 'name' and 'age', House has the attribute 'address'. The attributes can be # accessed on instances of the classes in the following way: # # p = Person.new # p.name = "MyName" # p.age = 22 # p.name # => "MyName" # p.age # => 22 # # Note that the class Person takes care of the type of its attributes. As # declared above, a 'name' can only be a String, an 'age' must be an Integer. # So the following would return an exception: # # p.name = :myName # => exception: can not put a Symbol where a String is expected # # If the type of an attribute should be left undefined, use Object as type. # # =Associations # # As well as attributes show up as instance methods, associations bring their own # accessor methods. For the Person-to-House association this would be: # # h1 = House.new # h1.address = "Street1" # h2 = House.new # h2.address = "Street2" # p.addHomes(h1) # p.addHomes(h2) # p.removeHomes(h1) # p.homes # => [ h2 ] # # The Person-to-House association is _bidirectional_. This means that with the # addition of a House to a Person, the Person is also added to the House. Thus: # # h1.inhabitants # => [] # h2.inhabitants # => [ p ] # # Note that the association is defined between two specific classes, instances of # different classes can not be added. Thus, the following would result in an # exception: # # p.addHomes(:justASymbol) # => exception: can not put a Symbol where a House is expected # # =ECore Metamodel description # # The class methods described above are used to create a Ruby representation of the metamodel # we have in mind in a very simple and easy way. We don't have to care about all the details # of a metamodel at this point (e.g. multiplicities, changeability, etc). # # At the same time however, an instance of the ECore metametamodel (i.e. a ECore based # description of our metamodel) is provided for all the Ruby classes and modules we create. # Since we did not provide the nitty-gritty details of the metamodel, defaults are used to # fully complete the ECore metamodel description. # # In order to access the ECore metamodel description, just call the +ecore+ method on a # Ruby class or module object belonging to your metamodel. # # Here is the example continued from above: # # Person.ecore.eAttributes.name # => ["name", "age"] # h2pRef = House.ecore.eReferences.first # h2pRef.eType # => Person # h2pRef.eOpposite.eType # => House # h2pRef.lowerBound # => 0 # h2pRef.upperBound # => -1 # h2pRef.many # => true # h2pRef.containment # => false # # Note that the use of array_extensions.rb is assumed here to make model navigation convenient. # # The following metamodel builder methods are supported, see individual method description # for details: # # Attributes: # * BuilderExtensions#has_attr # # Unidirectional references: # * BuilderExtensions#has_one # * BuilderExtensions#has_many # * BuilderExtensions#contains_one_uni # * BuilderExtensions#contains_many_uni # # Bidirectional references: # * BuilderExtensions#one_to_one # * BuilderExtensions#one_to_many # * BuilderExtensions#many_to_one # * BuilderExtensions#many_to_many # * BuilderExtensions#contains_one # * BuilderExtensions#contains_many # # Every builder command can optionally take a specification of further ECore properties. # Additional properties for Attributes and References are (with defaults in brackets): # * :ordered (true), # * :unique (true), # * :changeable (true), # * :volatile (false), # * :transient (false), # * :unsettable (false), # * :derived (false), # * :lowerBound (0), # * :resolveProxies (true) references only, # # Using these additional properties, the above example can be refined as follows: # # class Person < RGen::MetamodelBuilder::MMBase # has_attr 'name', String, :lowerBound => 1 # has_attr 'yearOfBirth', Integer, # has_attr 'age', Integer, :derived => true # def age_derived # Time.now.year - yearOfBirth # end # end # # Person.many_to_many 'homes', House, 'inhabitants', :upperBound => 5 # # Person.ecore.eReferences.find{|r| r.name == 'homes'}.upperBound # => 5 # # This way we state that there must be a name for each person, we introduce a new attribute # 'yearOfBirth' and make 'age' a derived attribute. We also say that a person can # have at most 5 houses in our metamodel. # # ==Derived attributes and references # # If the attribute 'derived' of an attribute or reference is set to true, a method +attributeName_derived+ # has to be provided. This method is called whenever the original attribute is accessed. The # original attribute can not be written if it is derived. # # module MetamodelBuilder # Use this class as a start for new metamodel elements (i.e. Ruby classes) # by inheriting for it. # # See MetamodelBuilder for an example. class MMBase include BuilderRuntime include DataTypes extend BuilderExtensions extend ModuleExtension extend RGen::ECore::ECoreInterface def initialize(arg=nil) raise StandardError.new("Class #{self.class} is abstract") if self.class._abstract_class arg.each_pair { |k,v| setGeneric(k, v) } if arg.is_a?(Hash) end # Object#inspect causes problems on most models def inspect self.class.name end def self.method_added(m) raise "Do not add methods to model classes directly, add them to the ClassModule instead" end end # Instances of MMGeneric can be used as values of any attribute are reference class MMGeneric # empty implementation so we don't have to check if a value is a MMGeneriv before setting the container def _set_container(container, containing_feature_name) end end # MMProxy objects can be used instead of real target elements in case references should be resolved later on class MMProxy < MMGeneric # The +targetIdentifer+ is an object identifying the element the proxy represents attr_accessor :targetIdentifier # +data+ is optional additional information to be associated with the proxy attr_accessor :data def initialize(ident=nil, data=nil) @targetIdentifier = ident @data = data end end end end rgen-0.8.0/lib/rgen/model_builder.rb0000644000004100000410000000174612642233643017353 0ustar www-datawww-datarequire 'rgen/model_builder/builder_context' require 'rgen/util/method_delegation' #require 'ruby-prof' module RGen module ModelBuilder def self.build(package, env=nil, builderMethodsModule=nil, &block) resolver = ReferenceResolver.new bc = BuilderContext.new(package, builderMethodsModule, resolver, env) contextModule = eval("Module.nesting", block.binding).first Util::MethodDelegation.registerDelegate(bc, contextModule, "const_missing") BuilderContext.currentBuilderContext = bc begin #RubyProf.start bc.instance_eval(&block) #prof = RubyProf.stop #File.open("profile_flat.txt","w+") do |f| # RubyProf::FlatPrinter.new(prof).print(f, 0) # end ensure BuilderContext.currentBuilderContext = nil end Util::MethodDelegation.unregisterDelegate(bc, contextModule, "const_missing") #puts "Resolving..." resolver.resolve(bc.toplevelElements) bc.toplevelElements end end end rgen-0.8.0/lib/rgen/template_language/0000755000004100000410000000000012642233643017666 5ustar www-datawww-datargen-0.8.0/lib/rgen/template_language/template_container.rb0000644000004100000410000001743112642233643024076 0ustar www-datawww-data# 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 _expand(template, args, params.merge({:for => e})) } end LOCAL_TEMPLATE_REGEX = /^:*(\w+)$/ def _expand(template, args, params) raise StandardError.new("expand :for argument evaluates to nil") if params.has_key?(:for) && params[:for].nil? context = params[:for] old_indent = @indent @indent = params[:indent] || @indent noIndentNextLine = params[:_noIndentNextLine] || (@output.is_a?(OutputHandler) && @output.noIndentNextLine) || # the following line actually defines the noIndentNextLine state: # we don't indent the next line if the previous line was not finished, # i.e. if output has been generated but is not terminated by a newline # BEWARE: the initial evaluation of the ERB template during template loading # also writes to @output (it creates a String); we must ignore this (@output.is_a?(OutputHandler) && @output.to_s.size > 0 && @output.to_s[-1] != "\n"[0]) caller = params[:_caller] || self old_context, @context = @context, context if context local_output = nil if template =~ LOCAL_TEMPLATE_REGEX tplname = $1 raise StandardError.new("Template not found: #{$1}") unless @templates[tplname] old_output, @output = @output, OutputHandler.new(@indent, @parent.indentString) @output.noIndentNextLine = noIndentNextLine _call_template(tplname, @context, args, caller == self) old_output.noIndentNextLine = false if old_output.is_a?(OutputHandler) && !@output.noIndentNextLine local_output, @output = @output, old_output else local_output = @parent.expand(template, *(args.dup << {:for => @context, :indent => @indent, :_noIndentNextLine => noIndentNextLine, :_evalOnly => true, :_caller => caller})) end _direct_concat(local_output) unless params[:_evalOnly] @context = old_context if old_context @indent = old_indent local_output.to_s end def processAndRaise(e, tpl=nil) bt = e.backtrace.dup e.backtrace.each_with_index do |t,i| if t =~ /\(erb\):(\d+):/ bt[i] = "#{@filename}:#{$1}" bt[i] += ":in '#{tpl}'" if tpl break end end raise e, e.to_s, bt end def _call_template(tpl, context, args, localCall) found = false @templates[tpl].each_pair do |key, value| if context.is_a?(key) templateDesc = @templates[tpl][key] proc = templateDesc.block arity = proc.arity arity = 0 if arity == -1 # if no args are given raise StandardError.new("Wrong number of arguments calling template #{tpl}: #{args.size} for #{arity} "+ "(Beware: Hashes as last arguments are taken as options and are ignored)") \ if arity != args.size raise StandardError.new("Template can only be called locally: #{tpl}") \ if templateDesc.local && !localCall begin @@metamodels = @metamodels proc.call(*args) rescue Exception => e processAndRaise(e, tpl) end found = true end end raise StandardError.new("Template class not matching: #{tpl} for #{context.class.name}") unless found end def _direct_concat(s) 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 rgen-0.8.0/lib/rgen/template_language/output_handler.rb0000644000004100000410000000516112642233643023253 0ustar www-datawww-data# RGen Framework # (c) Martin Thiede, 2006 module RGen module TemplateLanguage class OutputHandler attr_accessor :noIndentNextLine def initialize(indent=0, indentString=" ", mode=:explicit) self.mode = mode @indentString = indentString @state = :wait_for_nonws @output = "" @indent_string = @indentString*indent end def indent=(i) @indent_string = @indentString*i end NL = "\n" LFNL = "\r\n" if RUBY_VERSION.start_with?("1.8") NL_CHAR = 10 LF_CHAR = 13 else NL_CHAR = "\n" LF_CHAR = "\r" end # ERB will call this method for every string s which is part of the # template file in between %> and <%. If s contains a newline, it will # call this method for every part of s which is terminated by a \n # def concat(s) if @ignoreNextNL idx = s.index(NL) if idx && s[0..idx].strip.empty? s = s[idx+1..-1] end @ignoreNextNL = false unless s.strip.empty? end if @ignoreNextWS s = s.lstrip @ignoreNextWS = false unless s.empty? end if @mode == :direct @output.concat(s) elsif @mode == :explicit while s.size > 0 if @state == :wait_for_nl idx = s.index(NL) if idx if s[idx-1] == LF_CHAR @output.concat(s[0..idx].rstrip) @output.concat(LFNL) else @output.concat(s[0..idx].rstrip) @output.concat(NL) end s = s[idx+1..-1] @state = :wait_for_nonws else @output.concat(s) break end elsif @state == :wait_for_nonws s = s.lstrip if !s.empty? unless @noIndentNextLine || (@output[-1] && @output[-1] != NL_CHAR) @output.concat(@indent_string) else @noIndentNextLine = false end @state = :wait_for_nl end end end end end alias << concat def to_str @output end alias to_s to_str def direct_concat(s) @output.concat(s) end def ignoreNextNL @ignoreNextNL = true end def ignoreNextWS @ignoreNextWS = true end def mode=(m) raise StandardError.new("Unknown mode: #{m}") unless [:direct, :explicit].include?(m) @mode = m end end end end rgen-0.8.0/lib/rgen/template_language/template_helper.rb0000644000004100000410000000052312642233643023365 0ustar www-datawww-data# 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 endrgen-0.8.0/lib/rgen/template_language/directory_template_container.rb0000644000004100000410000000416112642233643026156 0ustar www-datawww-data# 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 endrgen-0.8.0/lib/rgen/ecore/0000755000004100000410000000000012642233643015305 5ustar www-datawww-datargen-0.8.0/lib/rgen/ecore/ecore_interface.rb0000644000004100000410000000240312642233643020746 0ustar www-datawww-datamodule RGen module ECore # Mixin to provide access to the ECore model describing a Ruby class or module # built using MetamodelBuilder. # The module should be used to +extend+ a class or module, i.e. to make its # methods class methods. # module ECoreInterface # This method will lazily build to ECore model element belonging to the calling # class or module using RubyToECore. # Alternatively, the ECore model element can be provided up front. This is used # when the Ruby metamodel classes and modules are created from ECore. # def ecore if defined?(@ecore) @ecore else unless defined?(@@transformer) require 'rgen/ecore/ruby_to_ecore' @@transformer = RubyToECore.new end @@transformer.trans(self) end end # This method can be used to clear the ecore cache after the metamodel classes # or modules have been changed; the ecore model will be recreated on next access # to the +ecore+ method # Beware, the ecore cache is global, i.e. for all metamodels. # def self.clear_ecore_cache require 'rgen/ecore/ruby_to_ecore' @@transformer = RubyToECore.new end def _set_ecore_internal(ecore) # :nodoc: @ecore = ecore end end end end rgen-0.8.0/lib/rgen/ecore/ecore.rb0000644000004100000410000002061012642233643016726 0ustar www-datawww-datarequire '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 rgen-0.8.0/lib/rgen/ecore/ecore_ext.rb0000644000004100000410000000266712642233643017622 0ustar www-datawww-datarequire '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 rgen-0.8.0/lib/rgen/ecore/ecore_to_ruby.rb0000644000004100000410000001430212642233643020472 0ustar www-datawww-datarequire 'rgen/ecore/ecore' module RGen module ECore # ECoreToRuby can turn ECore models into their Ruby metamodel representations class ECoreToRuby def initialize @modules = {} @classifiers = {} @features_added = {} end # Create a Ruby module representing +epackage+. # This includes all nested modules/packages, classes and enums. # # If a parent module is provided with the "under" parameter, # the new module will be nested under the parent module. # # If the parent module has a non-temporary name, # (more precisely: a non-temporary classpath) i.e. if it is reachable # via a path of constant names from the root, then the nested # modules and classes will also have non-temporary names. # In particular, this means that they will keep their names even # if they are assigned to new constants. # # If no parent module is provided or the parent module has a # temporary name by itself, then the nested modules and classes will # also have temporary names. This means that their name will stay # 'volatile' until they are assigned to constants reachable from # the root and the Module#name method is called for the first time. # # While the second approach is more flexible, it can come with a major # performance impact. The reason is that Ruby searches the space of # all known non-temporary classes/modules every time the name # of a class/module with a temporary name is queried. # def create_module(epackage, under=Module.new) temp = under.to_s.start_with?("#") mod = create_module_internal(epackage, under, temp) epackage.eAllClassifiers.each do |c| if c.is_a?(RGen::ECore::EClass) create_class(c, temp) elsif c.is_a?(RGen::ECore::EEnum) create_enum(c) end end mod end private def create_module_internal(epackage, under, temp) return @modules[epackage] if @modules[epackage] if temp mod = Module.new do extend RGen::MetamodelBuilder::ModuleExtension end under.const_set(epackage.name, mod) else under.module_eval <<-END module #{epackage.name} extend RGen::MetamodelBuilder::ModuleExtension end END mod = under.const_get(epackage.name) end @modules[epackage] = mod epackage.eSubpackages.each{|p| create_module_internal(p, mod, temp)} mod._set_ecore_internal(epackage) mod end def create_class(eclass, temp) return @classifiers[eclass] if @classifiers[eclass] mod = @modules[eclass.ePackage] if temp cls = Class.new(super_class(eclass, temp)) do abstract if eclass.abstract class << self attr_accessor :_ecore_to_ruby end end mod.const_set(eclass.name, cls) else mod.module_eval <<-END class #{eclass.name} < #{super_class(eclass, temp)} #{eclass.abstract ? 'abstract' : ''} class << self attr_accessor :_ecore_to_ruby end end END cls = mod.const_get(eclass.name) end class << eclass attr_accessor :instanceClass def instanceClassName instanceClass.to_s end end eclass.instanceClass = cls cls::ClassModule.module_eval do alias _method_missing method_missing def method_missing(m, *args) if self.class._ecore_to_ruby.add_features(self.class.ecore) send(m, *args) else _method_missing(m, *args) end end alias _respond_to respond_to? def respond_to?(m, include_all=false) self.class._ecore_to_ruby.add_features(self.class.ecore) _respond_to(m) end end @classifiers[eclass] = cls cls._set_ecore_internal(eclass) cls._ecore_to_ruby = self cls end def create_enum(eenum) return @classifiers[eenum] if @classifiers[eenum] e = RGen::MetamodelBuilder::DataTypes::Enum.new(eenum.eLiterals.collect{|l| l.name.to_sym}) @classifiers[eenum] = e @modules[eenum.ePackage].const_set(eenum.name, e) e end class FeatureWrapper def initialize(efeature, classifiers) @efeature = efeature @classifiers = classifiers end def value(prop) return false if prop == :containment && @efeature.is_a?(RGen::ECore::EAttribute) @efeature.send(prop) end def many? @efeature.many end def reference? @efeature.is_a?(RGen::ECore::EReference) end def opposite @efeature.eOpposite end def impl_type etype = @efeature.eType if etype.is_a?(RGen::ECore::EClass) || etype.is_a?(RGen::ECore::EEnum) @classifiers[etype] else ic = etype.instanceClass if ic ic else raise "unknown type: #{etype.name}" end end end end def super_class(eclass, temp) super_types = eclass.eSuperTypes if temp case super_types.size when 0 RGen::MetamodelBuilder::MMBase when 1 create_class(super_types.first, temp) else RGen::MetamodelBuilder::MMMultiple(*super_types.collect{|t| create_class(t, temp)}) end else case super_types.size when 0 "RGen::MetamodelBuilder::MMBase" when 1 create_class(super_types.first, temp).name else "RGen::MetamodelBuilder::MMMultiple(" + super_types.collect{|t| create_class(t, temp).name}.join(",") + ")" end end end public def add_features(eclass) return false if @features_added[eclass] c = @classifiers[eclass] eclass.eStructuralFeatures.each do |f| w1 = FeatureWrapper.new(f, @classifiers) w2 = FeatureWrapper.new(f.eOpposite, @classifiers) if f.is_a?(RGen::ECore::EReference) && f.eOpposite c.module_eval do if w1.many? _build_many_methods(w1, w2) else _build_one_methods(w1, w2) end end end @features_added[eclass] = true eclass.eSuperTypes.each do |t| add_features(t) end true end end end end rgen-0.8.0/lib/rgen/ecore/ecore_builder_methods.rb0000644000004100000410000000545212642233643022166 0ustar www-datawww-datamodule 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 endrgen-0.8.0/lib/rgen/ecore/ruby_to_ecore.rb0000644000004100000410000000613212642233643020474 0ustar www-datawww-datarequire 'rgen/transformer' require 'rgen/ecore/ecore' module RGen module ECore # This transformer creates an ECore model from Ruby classes built # by RGen::MetamodelBuilder. # class RubyToECore < Transformer transform Class, :to => EClass, :if => :convert? do { :name => name.gsub(/.*::(\w+)$/,'\1'), :abstract => _abstract_class, :interface => false, :eStructuralFeatures => trans(_metamodel_description), :ePackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil), :eSuperTypes => trans(superclasses), :instanceClassName => name, :eAnnotations => trans(_annotations) } end method :superclasses do if superclass.respond_to?(:multiple_superclasses) && superclass.multiple_superclasses superclass.multiple_superclasses else [ superclass ] end end transform Module, :to => EPackage, :if => :convert? do @enumParentModule ||= {} _constants = _constantOrder + (constants - _constantOrder) _constants.select {|c| const_get(c).is_a?(MetamodelBuilder::DataTypes::Enum)}. each {|c| @enumParentModule[const_get(c)] = @current_object} { :name => name.gsub(/.*::(\w+)$/,'\1'), :eClassifiers => trans(_constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Class) || (c.is_a?(MetamodelBuilder::DataTypes::Enum) && c != MetamodelBuilder::DataTypes::Boolean) }), :eSuperPackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil), :eSubpackages => trans(_constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Module) && !c.is_a?(Class)}), :eAnnotations => trans(_annotations) } end method :convert? do @current_object.respond_to?(:ecore) && @current_object != RGen::MetamodelBuilder::MMBase end transform MetamodelBuilder::Intermediate::Attribute, :to => EAttribute do Hash[*MetamodelBuilder::Intermediate::Attribute.properties.collect{|p| [p, value(p)]}.flatten].merge({ :eType => (etype == :EEnumerable ? trans(impl_type) : RGen::ECore.const_get(etype)), :eAnnotations => trans(annotations) }) end transform MetamodelBuilder::Intermediate::Reference, :to => EReference do Hash[*MetamodelBuilder::Intermediate::Reference.properties.collect{|p| [p, value(p)]}.flatten].merge({ :eType => trans(impl_type), :eOpposite => trans(opposite), :eAnnotations => trans(annotations) }) end transform MetamodelBuilder::Intermediate::Annotation, :to => EAnnotation do { :source => source, :details => details.keys.collect do |k| e = RGen::ECore::EStringToStringMapEntry.new e.key = k e.value = details[k] e end } end transform MetamodelBuilder::DataTypes::Enum, :to => EEnum do { :name => name, :instanceClassName => @enumParentModule && @enumParentModule[@current_object] && @enumParentModule[@current_object].name+"::"+name, :eLiterals => literals.collect do |l| lit = RGen::ECore::EEnumLiteral.new lit.name = l.to_s lit end } end end end end rgen-0.8.0/lib/rgen/metamodel_builder/0000755000004100000410000000000012642233643017665 5ustar www-datawww-datargen-0.8.0/lib/rgen/metamodel_builder/builder_runtime.rb0000644000004100000410000001100112642233643023374 0ustar www-datawww-data# 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 rgen-0.8.0/lib/rgen/metamodel_builder/intermediate/0000755000004100000410000000000012642233643022337 5ustar www-datawww-datargen-0.8.0/lib/rgen/metamodel_builder/intermediate/feature.rb0000644000004100000410000000671012642233643024323 0ustar www-datawww-datarequire 'rgen/metamodel_builder/data_types' module RGen module MetamodelBuilder module Intermediate class Feature attr_reader :etype, :impl_type def value(prop) @props[prop] end def annotations @annotations ||= [] end def many? value(:upperBound) > 1 || value(:upperBound) == -1 end def reference? is_a?(Reference) end protected def check(props) @props.keys.each do |p| kind = props[p] raise StandardError.new("invalid property #{p}") unless kind raise StandardError.new("property '#{p}' not set") if value(p).nil? && kind == :required end end end class Attribute < Feature Properties = { :name => :required, :ordered => :required, :unique => :required, :changeable => :required, :volatile => :required, :transient => :required, :unsettable => :required, :derived => :required, :lowerBound => :required, :upperBound => :required, :defaultValueLiteral => :optional } Defaults = { :ordered => true, :unique => true, :changeable => true, :volatile => false, :transient => false, :unsettable => false, :derived => false, :lowerBound => 0 } Types = { String => :EString, Integer => :EInt, RGen::MetamodelBuilder::DataTypes::Long => :ELong, Float => :EFloat, RGen::MetamodelBuilder::DataTypes::Boolean => :EBoolean, Object => :ERubyObject, Class => :ERubyClass } def self.default_value(prop) Defaults[prop] end def self.properties Properties.keys.sort{|a,b| a.to_s <=> b.to_s} end def initialize(type, props) @props = Defaults.merge(props) type ||= String @etype = Types[type] if @etype @impl_type = type elsif type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum) @etype = :EEnumerable @impl_type = type else raise ArgumentError.new("invalid type '#{type}'") end if @props[:derived] @props[:changeable] = false @props[:volatile] = true @props[:transient] = true end check(Properties) end end class Reference < Feature attr_accessor :opposite Properties = { :name => :required, :ordered => :required, :unique => :required, :changeable => :required, :volatile => :required, :transient => :required, :unsettable => :required, :derived => :required, :lowerBound => :required, :upperBound => :required, :resolveProxies => :required, :containment => :required } Defaults = { :ordered => true, :unique => true, :changeable => true, :volatile => false, :transient => false, :unsettable => false, :derived => false, :lowerBound => 0, :resolveProxies => true } def self.default_value(prop) Defaults[prop] end def self.properties Properties.keys.sort{|a,b| a.to_s <=> b.to_s} end def initialize(type, props) @props = Defaults.merge(props) if type.respond_to?(:_metamodel_description) @etype = nil @impl_type = type else raise ArgumentError.new("'#{type}' (#{type.class}) is not a MMBase in reference #{props[:name]}") end if @props[:derived] @props[:changeable] = false @props[:volatile] = true @props[:transient] = true end check(Properties) end end end end end rgen-0.8.0/lib/rgen/metamodel_builder/intermediate/annotation.rb0000644000004100000410000000127012642233643025036 0ustar www-datawww-datamodule RGen module MetamodelBuilder module Intermediate class Annotation attr_reader :details, :source def initialize(hash) if hash[:source] || hash[:details] restKeys = hash.keys - [:source, :details] raise "Hash key #{restKeys.first} not allowed." unless restKeys.empty? raise "Details not provided, key :details is missing" unless hash[:details] raise "Details must be provided as a hash" unless hash[:details].is_a?(Hash) @details = hash[:details] @source = hash[:source] else raise "Details must be provided as a hash" unless hash.is_a?(Hash) @details = hash end end end end end end rgen-0.8.0/lib/rgen/metamodel_builder/module_extension.rb0000644000004100000410000000146612642233643023602 0ustar www-datawww-datarequire 'rgen/ecore/ecore_interface' require 'rgen/metamodel_builder/intermediate/annotation' module RGen module MetamodelBuilder # This module is used to extend modules which should be # part of RGen metamodels module ModuleExtension include RGen::ECore::ECoreInterface def annotation(hash) _annotations << Intermediate::Annotation.new(hash) end def _annotations @_annotations ||= [] end def _constantOrder @_constantOrder ||= [] end def final_method(m) @final_methods ||= [] @final_methods << m end def method_added(m) raise "Method #{m} can not be redefined" if @final_methods && @final_methods.include?(m) end def self.extended(m) MetamodelBuilder::ConstantOrderHelper.moduleCreated(m) end end end end rgen-0.8.0/lib/rgen/metamodel_builder/data_types.rb0000644000004100000410000000444212642233643022353 0ustar www-datawww-datamodule RGen module MetamodelBuilder module DataTypes # An enum object is used to describe possible attribute values within a # MetamodelBuilder attribute definition. An attribute defined this way can only # take the values specified when creating the Enum object. # Literal values can only be symbols or true or false. # Optionally a name may be specified for the enum object. # # Examples: # # Enum.new(:name => "AnimalEnum", :literals => [:cat, :dog]) # Enum.new(:literals => [:cat, :dog]) # Enum.new([:cat, :dog]) # class Enum attr_reader :name, :literals # Creates a new named enum type object consisting of the elements passed as arguments. def initialize(params) MetamodelBuilder::ConstantOrderHelper.enumCreated(self) if params.is_a?(Array) @literals = params @name = "anonymous" elsif params.is_a?(Hash) raise StandardError.new("Hash entry :literals is missing") unless params[:literals] @literals = params[:literals] @name = params[:name] || "anonymous" else raise StandardError.new("Pass an Array or a Hash") end end # This method can be used to check if an object can be used as value for # variables having this enum object as type. def validLiteral?(l) literals.include?(l) end def literals_as_strings literals.collect do |l| if l.is_a?(Symbol) if l.to_s =~ /^\d|\W/ ":'"+l.to_s+"'" else ":"+l.to_s end elsif l.is_a?(TrueClass) || l.is_a?(FalseClass) l.to_s else raise StandardError.new("Literal values can only be symbols or true/false") end end end def to_s # :nodoc: name end end # Boolean is a predefined enum object having Ruby's true and false singletons # as possible values. Boolean = Enum.new(:name => "Boolean", :literals => [true, false]) # Long represents a 64-bit Integer # This constant is merely a marker for keeping this information in the Ruby version of the metamodel, # values of this type will always be instances of Integer or Bignum; # Setting it to a string value ensures that it responds to "to_s" which is used in the metamodel generator Long = "Long" end end end rgen-0.8.0/lib/rgen/metamodel_builder/constant_order_helper.rb0000644000004100000410000000664112642233643024604 0ustar www-datawww-datamodule RGen module MetamodelBuilder # The purpose of the ConstantOrderHelper is to capture the definition order of RGen metamodel builder # classes, modules and enums. The problem is that Ruby doesn't seem to track the order of # constants being created in a module. However the order is important because it defines the order # of eClassifiers and eSubpackages in a EPackage. # # It would be helpful here if Ruby provided a +const_added+ callback, but this is not the case up to now. # # The idea for capturing is that all events of creating a RGen class, module or enum are reported to the # ConstantOrderHelper singleton. # For classes and modules it tries to add their names to the parent's +_constantOrder+ array. # The parent module is derived from the class's or module's name. However, the new name is only added # if the respective parent module has a new constant (which is not yet in +_constantOrder+) which # points to the new class or module. # For enums it is a bit more complicated, because at the time the enum is created, the parent # module does not yet contain the constant to which the enum is assigned. Therefor, the enum is remembered # and it is tried to be stored on the next event (class, module or enum) within the module which was # created last (which was last extended with ModuleExtension). If it can not be found in that module, # all parent modules of the last module are searched. This way it should also be correctly entered in # case it was defined outside of the last created module. # Note that an enum is not stored to the constant order array unless another event occurs. That's why # it is possible that one enum is missing at the enum. This needs to be taken care of by the ECore transformer. # # This way of capturing should be sufficient for the regular use cases of the RGen metamodel builder language. # However, it is possible to write code which messes this up, see unit tests for details. # In the worst case, the new classes, modules or enums will just not be found in a parent module and thus be ignored. # ConstantOrderHelper = Class.new do def initialize @currentModule = nil @pendingEnum = nil end def classCreated(c) handlePendingEnum cont = containerModule(c) name = (c.name || "").split("::").last return unless cont.respond_to?(:_constantOrder) && !cont._constantOrder.include?(name) cont._constantOrder << name end def moduleCreated(m) handlePendingEnum cont = containerModule(m) name = (m.name || "").split("::").last return unless cont.respond_to?(:_constantOrder) && !cont._constantOrder.include?(name) cont._constantOrder << name @currentModule = m end def enumCreated(e) handlePendingEnum @pendingEnum = e end private def containerModule(m) containerName = (m.name || "").split("::")[0..-2].join("::") containerName.empty? ? nil : eval(containerName, TOPLEVEL_BINDING) end def handlePendingEnum return unless @pendingEnum m = @currentModule while m if m.respond_to?(:_constantOrder) newConstants = m.constants - m._constantOrder const = newConstants.find{|c| m.const_get(c).object_id == @pendingEnum.object_id} if const m._constantOrder << const.to_s break end end m = containerModule(m) end @pendingEnum = nil end end.new end end rgen-0.8.0/lib/rgen/metamodel_builder/mm_multiple.rb0000644000004100000410000000065012642233643022537 0ustar www-datawww-data 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 endrgen-0.8.0/lib/rgen/metamodel_builder/builder_extensions.rb0000644000004100000410000005472212642233643024131 0ustar www-datawww-data# 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? || (val.is_a?(MMBase) || val.is_a?(MMGeneric)) && @<%= name %>.any? {|e| e.equal?(val)} <%= type_check_code("val", props) %> @<%= name %>.insert(index, val) <% if other_role %> val._register<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMGeneric) <% end %> <% if props.reference? && props.value(:containment) %> val._set_container(self, :<%= name %>) <% end %> end def remove<%= firstToUpper(name) %>(val) @<%= name %> = [] unless @<%= name %> @<%= name %>.each_with_index do |e,i| if e.equal?(val) @<%= name %>.delete_at(i) <% if props.reference? && props.value(:containment) %> val._set_container(nil, nil) <% end %> <% if other_role %> val._unregister<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMGeneric) <% end %> return end end end def set<%= firstToUpper(name) %>(val) return if val.nil? raise _assignmentTypeError(self, val, Enumerable) unless val.is_a? Enumerable get<%= firstToUpper(name) %>.each {|e| remove<%= firstToUpper(name) %>(e) } @<%= name %> = [] unless @<%= name %> <% if props.reference? %> val.uniq {|elem| elem.object_id }.each {|elem| next if elem.nil? <%= type_check_code("elem", props) %> @<%= name %> << elem <% if other_role %> elem._register<%= firstToUpper(other_role) %>(self) unless elem.is_a?(MMGeneric) <% end %> <% if props.value(:containment) %> elem._set_container(self, :<%= name %>) <% end %> } <% else %> val.each {|elem| <%= type_check_code("elem", props) %> @<%= name %> << elem } <% end %> end alias <%= name %>= set<%= firstToUpper(name) %> def _register<%= firstToUpper(name) %>(val) @<%= name %> = [] unless @<%= 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 rgen-0.8.0/lib/rgen/environment.rb0000644000004100000410000000563212642233643017107 0ustar www-datawww-datamodule 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 endrgen-0.8.0/lib/rgen/util/0000755000004100000410000000000012642233643015165 5ustar www-datawww-datargen-0.8.0/lib/rgen/util/name_helper.rb0000644000004100000410000000124412642233643017772 0ustar www-datawww-data# RGen Framework # (c) Martin Thiede, 2006 module RGen module Util module NameHelper def normalize(name) name.gsub(/\W/,'_') end def className(object) object.class.name =~ /::(\w+)$/; $1 end def firstToUpper(str) str[0..0].upcase + ( str[1..-1] || "" ) end def firstToLower(str) str[0..0].downcase + ( str[1..-1] || "" ) end def saneClassName(str) firstToUpper(normalize(str)).sub(/^Class$/, 'Clazz') end def saneMethodName(str) firstToLower(normalize(str)).sub(/^class$/, 'clazz') end def camelize(str) str.split(/[\W_]/).collect{|s| firstToUpper(s.downcase)}.join end end end end rgen-0.8.0/lib/rgen/util/auto_class_creator.rb0000644000004100000410000000176212642233643021374 0ustar www-datawww-data# RGen Framework # (c) Martin Thiede, 2006 require 'rgen/metamodel_builder' module RGen module Util class Base extend MetamodelBuilder def initialize(env=nil) env << self if env end end class AutoCreatedClass < Base def method_missing(m,*args) return super unless self.class.parent.accEnabled if m.to_s =~ /(.*)=$/ self.class.has_one($1) send(m,args[0]) elsif args.size == 0 self.class.has_many(m) send(m) end end end # will be "extended" to the auto created class module ParentAccess def parent=(p) @parent = p end def parent @parent end end module AutoClassCreator attr_reader :accEnabled def const_missing(className) return super unless @accEnabled module_eval("class "+className.to_s+" < RGen::AutoCreatedClass; end") c = const_get(className) c.extend(ParentAccess) c.parent = self c end def enableACC @accEnabled = true end def disableACC @accEnabled = false end end end end rgen-0.8.0/lib/rgen/util/file_change_detector.rb0000644000004100000410000000455512642233643021640 0ustar www-datawww-datarequire 'digest' module RGen module Util # The FileChangeDetector detects changes in a set of files. # Changes are detected between successive calls to check_files with a give set of files. # Changes include files being added, removed or having changed their content. # class FileChangeDetector FileInfo = Struct.new(:timestamp, :digest) # Create a FileChangeDetector, options include: # # :file_added # a proc which is called when a file is added, receives the filename # # :file_removed # a proc which is called when a file is removed, receives the filename # # :file_changed # a proc which is called when a file is changed, receives the filename # def initialize(options={}) @file_added = options[:file_added] @file_removed = options[:file_removed] @file_changed = options[:file_changed] @file_info = {} end # Checks if any of the files has changed compared to the last call of check_files. # When called for the first time on a new object, all files will be reported as being added. # def check_files(files) files_before = @file_info.keys used_files = {} files.each do |file| begin if @file_info[file] if @file_info[file].timestamp != File.mtime(file) @file_info[file].timestamp = File.mtime(file) digest = calc_digest(file) if @file_info[file].digest != digest @file_info[file].digest = digest @file_changed && @file_changed.call(file) end end else @file_info[file] = FileInfo.new @file_info[file].timestamp = File.mtime(file) @file_info[file].digest = calc_digest(file) @file_added && @file_added.call(file) end used_files[file] = true # protect against missing files rescue Errno::ENOENT # used_files is not set and @file_info will be removed below # notification hook hasn't been called yet since it comes after file accesses end end files_before.each do |file| if !used_files[file] @file_info.delete(file) @file_removed && @file_removed.call(file) end end end private def calc_digest(file) sha1 = Digest::SHA1.new sha1.file(file) sha1.hexdigest end end end end rgen-0.8.0/lib/rgen/util/model_comparator_base.rb0000644000004100000410000000663212642233643022042 0ustar www-datawww-datarequire 'andand' module RGen module Util class ModelComparatorBase CompareSpec = Struct.new(:identifier, :recurse, :filter, :ignore_features, :display_name, :sort) INDENT = " " class << self attr_reader :compareSpecs def compare_spec(clazz, hash) @compareSpecs ||= {} raise "Compare spec already defined for #{clazz}" if @compareSpecs[clazz] spec = CompareSpec.new hash.each_pair do |k,v| spec.send("#{k}=",v) end @compareSpecs[clazz] = spec end end # compares two sets of elements def compare(as, bs, recursive=true) result = [] aById = as.select{|e| useElement?(e)}.inject({}){|r, e| r[elementIdentifier(e)] = e; r} bById = bs.select{|e| useElement?(e)}.inject({}){|r, e| r[elementIdentifier(e)] = e; r} onlyA = sortElements((aById.keys - bById.keys).collect{|id| aById[id]}) onlyB = sortElements((bById.keys - aById.keys).collect{|id| bById[id]}) aAndB = sortElementPairs((aById.keys & bById.keys).collect{|id| [aById[id], bById[id]]}) onlyA.each do |e| result << "- #{elementDisplayName(e)}" end onlyB.each do |e| result << "+ #{elementDisplayName(e)}" end if recursive aAndB.each do |ab| a, b = *ab r = compareElements(a, b) if r.size > 0 result << "#{elementDisplayName(a)}" result += r.collect{|l| INDENT+l} end end end result end def sortElementPairs(pairs) pairs.sort do |x,y| a, b = x[0], y[0] r = a.class.name <=> b.class.name r = compareSpec(a).sort.call(a,b) if r == 0 && compareSpec(a) && compareSpec(a).sort r end end def sortElements(elements) elements.sort do |a,b| r = a.class.name <=> b.class.name r = compareSpec(a).sort.call(a,b) if r == 0 && compareSpec(a) && compareSpec(a).sort r end end def elementDisplayName(e) if compareSpec(e) && compareSpec(e).display_name compareSpec(e).display_name.call(e) else elementIdentifier(e) end end def compareElements(a, b) result = [] if a.class != b.class result << "Class: #{a.class} -> #{b.class}" else a.class.ecore.eAllStructuralFeatures.reject{|f| f.derived || compareSpec(a).andand.ignore_features.andand.include?(f.name.to_sym)}.each do |f| va, vb = a.getGeneric(f.name), b.getGeneric(f.name) if f.is_a?(RGen::ECore::EAttribute) r = compareValues(f.name, va, vb) result << r if r else va, vb = [va].compact, [vb].compact unless f.many r = compare(va, vb, f.containment || compareSpec(a).andand.recurse.andand.include?(f.name.to_sym)) if r.size > 0 result << "[#{f.name}]" result += r.collect{|l| INDENT+l} end end end end result end def compareValues(name, val1, val2) result = nil result = "[#{name}] #{val1} -> #{val2}" if val1 != val2 result end def elementIdentifier(element) cs = compareSpec(element) if cs && cs.identifier if cs.identifier.is_a?(Proc) cs.identifier.call(element) else cs.identifier end else if element.respond_to?(:name) element.name else element.object_id end end end def useElement?(element) cs = compareSpec(element) !(cs && cs.filter) || cs.filter.call(element) end def compareSpec(element) @compareSpec ||= {} return @compareSpec[element.class] if @compareSpec[element.class] return nil unless self.class.compareSpecs key = self.class.compareSpecs.keys.find{|k| element.is_a?(k)} @compareSpec[element.class] = self.class.compareSpecs[key] end end end end rgen-0.8.0/lib/rgen/util/model_dumper.rb0000644000004100000410000000104712642233643020170 0ustar www-datawww-datamodule RGen module Util module ModelDumper def dump(obj=nil) obj ||= self if obj.is_a?(Array) obj.collect {|o| dump(o)}.join("\n\n") elsif obj.class.respond_to?(:ecore) ([obj.to_s] + obj.class.ecore.eAllStructuralFeatures.select{|f| !f.many}.collect { |a| " #{a} => #{obj.getGeneric(a.name)}" } + obj.class.ecore.eAllStructuralFeatures.select{|f| f.many}.collect { |a| " #{a} => [ #{obj.getGeneric(a.name).join(', ')} ]" }).join("\n") else obj.to_s end end end end end rgen-0.8.0/lib/rgen/util/file_cache_map.rb0000644000004100000410000000705612642233643020421 0ustar www-datawww-datarequire 'digest' require 'fileutils' module RGen module Util # Implements a cache for storing and loading data associated with arbitrary files. # The data is stored in cache files within a subfolder of the folder where # the associated files exists. # The cache files are protected with a checksum and loaded data will be # invalid in case either the associated file are the cache file has changed. # class FileCacheMap # optional program version info to be associated with the cache files # if the program version changes, cached data will also be invalid attr_accessor :version_info # +cache_dir+ is the name of the subfolder where cache files are created # +postfix+ is an extension appended to the original file name for # creating the name of the cache file def initialize(cache_dir, postfix) @postfix = postfix @cache_dir = cache_dir end # load data associated with file +key_path+ # returns :invalid in case either the associated file or the cache file has changed # # options: # :invalidation_reasons: # an array which will receive symbols indicating why the cache is invalid: # :no_cachefile, :cachefile_corrupted, :keyfile_changed # def load_data(key_path, options={}) reasons = options[:invalidation_reasons] || [] cf = cache_file(key_path) result = nil begin File.open(cf, "rb") do |f| header = f.read(41) if !header reasons << :cachefile_corrupted return :invalid end checksum = header[0..39] data = f.read if calc_sha1(data) == checksum if calc_sha1_keydata(key_path) == data[0..39] result = data[41..-1] else reasons << :keyfile_changed result = :invalid end else reasons << :cachefile_corrupted result = :invalid end end rescue Errno::ENOENT reasons << :no_cachefile result = :invalid end result end # store data +value_data+ associated with file +key_path+ def store_data(key_path, value_data) data = calc_sha1_keydata(key_path) + "\n" + value_data data = calc_sha1(data) + "\n" + data cf = cache_file(key_path) FileUtils.mkdir(File.dirname(cf)) rescue Errno::EEXIST File.open(cf, "wb") do |f| f.write(data) end end # remove cache files which are not associated with any file in +key_paths+ # will only remove files within +root_path+ def clean_unused(root_path, key_paths) raise "key paths must be within root path" unless key_paths.all?{|p| p.index(root_path) == 0} used_files = key_paths.collect{|p| cache_file(p)} files = Dir[root_path+"/**/"+@cache_dir+"/*"+@postfix] files.each do |f| FileUtils.rm(f) unless used_files.include?(f) end end private def cache_file(path) File.dirname(path) + "/"+@cache_dir+"/" + File.basename(path) + @postfix end def calc_sha1(data) sha1 = Digest::SHA1.new sha1.update(data) sha1.hexdigest end def keyData(path) File.read(path)+@version_info.to_s end # this method is much faster than calling +keyData+ and putting the result in +calc_sha1+ # reason is probably that there are not so many big strings being created def calc_sha1_keydata(path) begin sha1 = Digest::SHA1.new sha1.file(path) sha1.update(@version_info.to_s) sha1.hexdigest rescue Errno::ENOENT "" end end end end end rgen-0.8.0/lib/rgen/util/model_comparator.rb0000644000004100000410000000400112642233643021034 0ustar www-datawww-datarequire 'rgen/ecore/ecore' module RGen module Util module ModelComparator # This method compares to models regarding equality # For this the identity of a model element is defined based on identity # of all attributes and referenced elements. # Arrays are sorted before comparison if possible (if <=> is provided). # def modelEqual?(a, b, featureIgnoreList=[]) @modelEqual_visited = {} _modelEqual_internal(a, b, featureIgnoreList, []) end def _modelEqual_internal(a, b, featureIgnoreList, path) return true if @modelEqual_visited[[a,b]] @modelEqual_visited[[a,b]] = true return true if a.nil? && b.nil? unless a.class == b.class puts "#{path.inspect}\n Classes differ: #{a} vs. #{b}" return false end if a.is_a?(Array) unless a.size == b.size puts "#{path.inspect}\n Array size differs: #{a.size} vs. #{b.size}" return false end begin # in Ruby 1.9 every object has the <=> operator but the default one returns # nil and thus sorting won't work (ArgumentError) as = a.sort rescue ArgumentError, NoMethodError as = a end begin bs = b.sort rescue ArgumentError, NoMethodError bs = b end a.each_index do |i| return false unless _modelEqual_internal(as[i], bs[i], featureIgnoreList, path+[i]) end else a.class.ecore.eAllStructuralFeatures.reject{|f| f.derived}.each do |feat| next if featureIgnoreList.include?(feat.name) if feat.eType.is_a?(RGen::ECore::EDataType) unless a.getGeneric(feat.name) == b.getGeneric(feat.name) puts "#{path.inspect}\n Value '#{feat.name}' differs: #{a.getGeneric(feat.name)} vs. #{b.getGeneric(feat.name)}" return false end else return false unless _modelEqual_internal(a.getGeneric(feat.name), b.getGeneric(feat.name), featureIgnoreList, path+["#{a.respond_to?(:name) && a.name}:#{feat.name}"]) end end end return true end end end end rgen-0.8.0/lib/rgen/util/cached_glob.rb0000644000004100000410000000344312642233643017730 0ustar www-datawww-datamodule RGen module Util # WARNING: the mechanism of taking timestamps of directories in order to find out if the # content has changed doesn't work reliably across all kinds of filesystems # class CachedGlob def initialize(dir_glob, file_glob) @dir_glob = dir_glob @file_glob = file_glob @root_dirs = [] @dirs = {} @files = {} @timestamps = {} end # returns all files contained in directories matched by +dir_glob+ which match +file_glob+. # +file_glob+ must be relative to +dir_glob+. # dir_glob "*/a" with file_glob "**/*.txt" is basically equivalent with Dir.glob("*/a/**/*.txt") # the idea is that the file glob will only be re-eavluated when the content of one of the # directories matched by dir_glob has changed. # this will only be faster than a normal Dir.glob if the number of dirs matched by dir_glob is # relatively large and changes in files affect only a few of them at a time. def glob root_dirs = Dir.glob(@dir_glob) (@root_dirs - root_dirs).each do |d| remove_root_dir(d) end (@root_dirs & root_dirs).each do |d| update_root_dir(d) if dir_changed?(d) end (root_dirs - @root_dirs).each do |d| update_root_dir(d) end @root_dirs = root_dirs @root_dirs.sort.collect{|d| @files[d]}.flatten end private def dir_changed?(dir) @dirs[dir].any?{|d| File.mtime(d) != @timestamps[dir][d]} end def update_root_dir(dir) @dirs[dir] = Dir.glob(dir+"/**/") @files[dir] = Dir.glob(dir+"/"+@file_glob) @timestamps[dir] = {} @dirs[dir].each do |d| @timestamps[dir][d] = File.mtime(d) end end def remove_root_dir(dir) @dirs.delete(dir) @files.delete(dir) @timestamps.delete(dir) end end end end rgen-0.8.0/lib/rgen/util/method_delegation.rb0000644000004100000410000000537612642233643021200 0ustar www-datawww-datamodule RGen module Util module MethodDelegation class << self def registerDelegate(delegate, object, method) method = method.to_sym createDelegateStore(object) if object._methodDelegates[method] object._methodDelegates[method] << delegate else object._methodDelegates[method] = [delegate] createDelegatingMethod(object, method) end end def unregisterDelegate(delegate, object, method) method = method.to_sym return unless object.respond_to?(:_methodDelegates) return unless object._methodDelegates[method] object._methodDelegates[method].delete(delegate) if object._methodDelegates[method].empty? object._methodDelegates[method] = nil removeDelegatingMethod(object, method) removeDelegateStore(object) end end private def createDelegateStore(object) return if object.respond_to?(:_methodDelegates) class << object def _methodDelegates @_methodDelegates ||= {} end end end def removeDelegateStore(object) return unless object.respond_to?(:_methodDelegates) class << object remove_method(:_methodDelegates) end end def createDelegatingMethod(object, method) if hasMethod(object, method) object.instance_eval <<-END class << self alias #{aliasMethodName(method)} #{method} end END end # define the delegating method object.instance_eval <<-END class << self def #{method}(*args, &block) @_methodDelegates[:#{method}].each do |d| catch(:continue) do return d.#{method}_delegated(self, *args, &block) end end # if aliased method does not exist, we want an exception #{aliasMethodName(method)}(*args, &block) end end END end def removeDelegatingMethod(object, method) if hasMethod(object, aliasMethodName(method)) # there is an aliased original, restore it object.instance_eval <<-END class << self alias #{method} #{aliasMethodName(method)} remove_method(:#{aliasMethodName(method)}) end END else # just delete the delegating method object.instance_eval <<-END class << self remove_method(:#{method}) end END end end def hasMethod(object, method) # in Ruby 1.9, #methods returns symbols if object.methods.first.is_a?(Symbol) method = method.to_sym else method = method.to_s end object.methods.include?(method) end def aliasMethodName(method) "#{method}_delegate_original" end end end end end rgen-0.8.0/lib/rgen/util/pattern_matcher.rb0000644000004100000410000002341012642233643020672 0ustar www-datawww-datamodule RGen module Util # A PatternMatcher can be used to find, insert and remove patterns on a given model. # # A pattern is specified by means of a block passed to the add_pattern method. # The block must take an Environment as first parameter and at least one model element # as connection point as further parameter. The pattern matches if it can be found # in a given environment and connected to the given connection point elements. # class PatternMatcher Match = Struct.new(:root, :elements, :bound_values) attr_accessor :debug def initialize @patterns = {} @insert_mode = false @debug = false end def add_pattern(name, &block) raise "a pattern needs at least 2 block parameters: " + "an RGen environment and a model element as connection point" \ unless block.arity >= 2 @patterns[name] = block end def find_pattern(env, name, *connection_points) match = find_pattern_internal(env, name, *connection_points) end def insert_pattern(env, name, *connection_points) @insert_mode = true root = evaluate_pattern(name, env, connection_points) @insert_mode = false root end def remove_pattern(env, name, *connection_points) match = find_pattern_internal(env, name, *connection_points) if match match.elements.each do |e| disconnect_element(e) env.delete(e) end match else nil end end def lazy(&block) if @insert_mode block.call else Lazy.new(&block) end end class Lazy < RGen::MetamodelBuilder::MMGeneric def initialize(&block) @block = block end def _eval @block.call end end private class Proxy < RGen::MetamodelBuilder::MMProxy attr_reader :_target def initialize(target) @_target = target end def method_missing(m, *args) result = @_target.send(m, *args) if result.is_a?(Array) result.collect do |e| if e.is_a?(RGen::MetamodelBuilder::MMBase) Proxy.new(e) else e end end else if result.is_a?(RGen::MetamodelBuilder::MMBase) Proxy.new(result) else result end end end end class Bindable < RGen::MetamodelBuilder::MMGeneric # by being an Enumerable, Bindables can be used for many-features as well include Enumerable def initialize @bound = false @value = nil @many = false end def _bound? @bound end def _many? @many end def _bind(value) @value = value @bound = true end def _value @value end def to_s @value.to_s end # pretend this is an enumerable which contains itself, so the bindable can be # inserted into many-features, when this is done the bindable is marked as a many-bindable def each @many = true yield(self) end def method_missing(m, *args) raise "bindable not bound" unless _bound? @value.send(m, *args) end end TempEnv = RGen::Environment.new class << TempEnv def <<(el); end end def find_pattern_internal(env, name, *connection_points) proxied_args = connection_points.collect{|a| a.is_a?(RGen::MetamodelBuilder::MMBase) ? Proxy.new(a) : a } bindables = create_bindables(name, connection_points) pattern_root = evaluate_pattern(name, TempEnv, proxied_args+bindables) candidates = candidates_via_connection_points(pattern_root, connection_points) candidates ||= env.find(:class => pattern_root.class) candidates.each do |e| # create new bindables for every try, otherwise they can could be bound to old values bindables = create_bindables(name, connection_points) pattern_root = evaluate_pattern(name, TempEnv, proxied_args+bindables) matched = match(pattern_root, e) return Match.new(e, matched, bindables.collect{|b| b._value}) if matched end nil end def create_bindables(pattern_name, connection_points) (1..(num_pattern_variables(pattern_name) - connection_points.size)).collect{|i| Bindable.new} end def candidates_via_connection_points(pattern_root, connection_points) @candidates_via_connection_points_refs ||= {} refs = (@candidates_via_connection_points_refs[pattern_root.class] ||= pattern_root.class.ecore.eAllReferences.reject{|r| r.derived || r.many || !r.eOpposite}) candidates = nil refs.each do |r| t = pattern_root.getGeneric(r.name) cp = t.is_a?(Proxy) && connection_points.find{|cp| cp.object_id == t._target.object_id} if cp elements = cp.getGenericAsArray(r.eOpposite.name) candidates = elements if candidates.nil? || elements.size < candidates.size end end candidates end def match(pat_element, test_element) visited = {} check_later = [] return false unless match_internal(pat_element, test_element, visited, check_later) while cl = check_later.shift pv, tv = cl.lazy._eval, cl.value if cl.feature.is_a?(RGen::ECore::EAttribute) unless pv == tv match_failed(cl.feature, "wrong attribute value (lazy): #{pv} vs. #{tv}") return false end else if pv.is_a?(Proxy) unless pv._target.object_id == tv.object_id match_failed(f, "wrong target object") return false end else unless (pv.nil? && tv.nil?) || (!pv.nil? && !tv.nil? && match_internal(pv, tv, visited, check_later)) return false end end end end visited.keys end CheckLater = Struct.new(:feature, :lazy, :value) def match_internal(pat_element, test_element, visited, check_later) return true if visited[test_element] visited[test_element] = true unless pat_element.class == test_element.class match_failed(nil, "wrong class: #{pat_element.class} vs #{test_element.class}") return false end all_structural_features(pat_element).each do |f| pat_values = pat_element.getGeneric(f.name) # nil values must be kept to support size check with Bindables pat_values = [ pat_values ] unless pat_values.is_a?(Array) test_values = test_element.getGeneric(f.name) test_values = [ test_values] unless test_values.is_a?(Array) if pat_values.size == 1 && pat_values.first.is_a?(Bindable) && pat_values.first._many? unless match_many_bindable(pat_values.first, test_values) return false end else unless pat_values.size == test_values.size match_failed(f, "wrong size #{pat_values.size} vs. #{test_values.size}") return false end pat_values.each_with_index do |pv,i| tv = test_values[i] if pv.is_a?(Lazy) check_later << CheckLater.new(f, pv, tv) elsif pv.is_a?(Bindable) if pv._bound? unless pv._value == tv match_failed(f, "value does not match bound value #{pv._value.class}:#{pv._value.object_id} vs. #{tv.class}:#{tv.object_id}") return false end else pv._bind(tv) end else if f.is_a?(RGen::ECore::EAttribute) unless pv == tv match_failed(f, "wrong attribute value") return false end else if pv.is_a?(Proxy) unless pv._target.object_id == tv.object_id match_failed(f, "wrong target object") return false end else unless both_nil_or_match(pv, tv, visited, check_later) return false end end end end end end end true end def match_many_bindable(bindable, test_values) if bindable._bound? bindable._value.each_with_index do |pv,i| tv = test_values[i] if f.is_a?(RGen::ECore::EAttribute) unless pv == tv match_failed(f, "wrong attribute value") return false end else unless both_nil_or_match(pv, tv, visited, check_later) return false end end end else bindable._bind(test_values.dup) end true end def both_nil_or_match(pv, tv, visited, check_later) (pv.nil? && tv.nil?) || (!pv.nil? && !tv.nil? && match_internal(pv, tv, visited, check_later)) end def match_failed(f, msg) puts "match failed #{f&&f.eContainingClass.name}##{f&&f.name}: #{msg}" if @debug end def num_pattern_variables(name) prok = @patterns[name] prok.arity - 1 end def evaluate_pattern(name, env, connection_points) prok = @patterns[name] raise "unknown pattern #{name}" unless prok raise "wrong number of arguments, expected #{prok.arity-1} connection points)" \ unless connection_points.size == prok.arity-1 prok.call(env, *connection_points) end def disconnect_element(element) return if element.nil? all_structural_features(element).each do |f| if f.many element.setGeneric(f.name, []) else element.setGeneric(f.name, nil) end end end def all_structural_features(element) @all_structural_features ||= {} return @all_structural_features[element.class] if @all_structural_features[element.class] @all_structural_features[element.class] = element.class.ecore.eAllStructuralFeatures.reject{|f| f.derived} end end end end rgen-0.8.0/lib/rgen/serializer/0000755000004100000410000000000012642233643016361 5ustar www-datawww-datargen-0.8.0/lib/rgen/serializer/qualified_name_provider.rb0000644000004100000410000000265612642233643023574 0ustar www-datawww-datamodule RGen module Serializer # simple identifier calculation based on qualified names. # as a prerequisit, elements must have a local name stored in single attribute +attribute_name+. # there may be classes without the name attribute though and there may be elements without a # local name. in both cases the element will have the same qualified name as its container. # class QualifiedNameProvider def initialize(options={}) @qualified_name_cache = {} @attribute_name = options[:attribute_name] || "name" @separator = options[:separator] || "/" @leading_separator = options.has_key?(:leading_separator) ? options[:leading_separator] : true end def identifier(element) if element.is_a?(RGen::MetamodelBuilder::MMProxy) element.targetIdentifier else qualified_name(element) end end def qualified_name(element) return @qualified_name_cache[element] if @qualified_name_cache[element] local_ident = ((element.respond_to?(@attribute_name) && element.getGeneric(@attribute_name)) || "").strip parent = element.eContainer if parent if local_ident.size > 0 result = qualified_name(parent) + @separator + local_ident else result = qualified_name(parent) end else result = (@leading_separator ? @separator : "") + local_ident end @qualified_name_cache[element] = result end end end end rgen-0.8.0/lib/rgen/serializer/opposite_reference_filter.rb0000644000004100000410000000112312642233643024130 0ustar www-datawww-datamodule RGen module Serializer # Filters refereences with an eOpposite: # 1. containment references are always preferred # 2. at a 1-to-n reference the 1-reference is always preferred # 3. otherwise the reference with the name in string sort order before the opposite's name is prefereed # OppositeReferenceFilter = proc do |features| features.reject{|f| f.is_a?(RGen::ECore::EReference) && !f.containment && f.eOpposite && (f.eOpposite.containment || (f.many && !f.eOpposite.many) || (!(!f.many && f.eOpposite.many) && (f.name < f.eOpposite.name)))} end end end rgen-0.8.0/lib/rgen/serializer/json_serializer.rb0000644000004100000410000000720312642233643022112 0ustar www-datawww-datamodule RGen module Serializer class JsonSerializer def initialize(writer, opts={}) @writer = writer @elementIdentifiers = {} @identAttrName = opts[:identAttrName] || "name" @separator = opts[:separator] || "/" @leadingSeparator = opts.has_key?(:leadingSeparator) ? opts[:leadingSeparator] : true @featureFilter = opts[:featureFilter] @identifierProvider = opts[:identifierProvider] end def elementIdentifier(element) ident = @identifierProvider && @identifierProvider.call(element) ident || (element.is_a?(RGen::MetamodelBuilder::MMProxy) && element.targetIdentifier) || qualifiedElementName(element) end # simple identifier calculation based on qualified names # prerequisits: # * containment relations must be bidirectionsl # * local name stored in single attribute +@identAttrName+ for all classes # def qualifiedElementName(element) return @elementIdentifiers[element] if @elementIdentifiers[element] localIdent = ((element.respond_to?(@identAttrName) && element.getGeneric(@identAttrName)) || "").strip parentRef = element.class.ecore.eAllReferences.select{|r| r.eOpposite && r.eOpposite.containment}.first parent = parentRef && element.getGeneric(parentRef.name) if parent if localIdent.size > 0 parentIdent = qualifiedElementName(parent) result = parentIdent + @separator + localIdent else result = qualifiedElementName(parent) end else result = (@leadingSeparator ? @separator : "") + localIdent end @elementIdentifiers[element] = result end def serialize(elements) if elements.is_a?(Array) write("[ ") elements.each_with_index do |e, i| serializeElement(e) write(",\n") unless i == elements.size-1 end write("]") else serializeElement(elements) end end def serializeElement(element, indent="") write(indent + "{ \"_class\": \""+element.class.ecore.name+"\"") element.class.ecore.eAllStructuralFeatures.each do |f| next if f.derived value = element.getGeneric(f.name) unless value == [] || value.nil? || (f.is_a?(RGen::ECore::EReference) && f.eOpposite && f.eOpposite.containment) || (@featureFilter && !@featureFilter.call(f)) write(", ") writeFeature(f, value, indent) end end write(" }") end def writeFeature(feat, value, indent) write("\""+feat.name+"\": ") if feat.is_a?(RGen::ECore::EAttribute) if value.is_a?(Array) write("[ "+value.collect{|v| attributeValue(v, feat)}.join(", ")+" ]") else write(attributeValue(value, feat)) end elsif !feat.containment if value.is_a?(Array) write("[ "+value.collect{|v| "\""+elementIdentifier(v)+"\""}.join(", ")+" ]") else write("\""+elementIdentifier(value)+"\"") end else if value.is_a?(Array) write("[ \n") value.each_with_index do |v, i| serializeElement(v, indent+" ") write(",\n") unless i == value.size-1 end write("]") else write("\n") serializeElement(value, indent+" ") end end end def attributeValue(value, a) if a.eType == RGen::ECore::EString || a.eType.is_a?(RGen::ECore::EEnum) "\""+value.to_s.gsub('\\','\\\\\\\\').gsub('"','\\"').gsub("\n","\\n").gsub("\r","\\r"). gsub("\t","\\t").gsub("\f","\\f").gsub("\b","\\b")+"\"" else value.to_s end end private def write(s) @writer.write(s) end end end end rgen-0.8.0/lib/rgen/serializer/xmi20_serializer.rb0000644000004100000410000000442312642233643022101 0ustar www-datawww-datarequire 'rgen/serializer/xml_serializer' module RGen module Serializer class XMI20Serializer < XMLSerializer def serialize(rootElement) @referenceStrings = {} buildReferenceStrings(rootElement, "#/") addBuiltinReferenceStrings attrs = attributeValues(rootElement) attrs << ['xmi:version', "2.0"] attrs << ['xmlns:xmi', "http://www.omg.org/XMI"] attrs << ['xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance"] attrs << ['xmlns:ecore', "http://www.eclipse.org/emf/2002/Ecore" ] tag = "ecore:"+rootElement.class.ecore.name startTag(tag, attrs) writeComposites(rootElement) endTag(tag) end def writeComposites(element) eachReferencedElement(element, containmentReferences(element)) do |r,te| attrs = attributeValues(te) attrs << ['xsi:type', "ecore:"+te.class.ecore.name] tag = r.name startTag(tag, attrs) writeComposites(te) endTag(tag) end end def attributeValues(element) result = [] eAllAttributes(element).select{|a| !a.derived}.each do |a| val = element.getGeneric(a.name) result << [a.name, val] unless val.nil? || val == "" end eAllReferences(element).select{|r| !r.containment && !(r.eOpposite && r.eOpposite.containment) && !r.derived}.each do |r| targetElements = element.getGenericAsArray(r.name) val = targetElements.collect{|te| @referenceStrings[te]}.compact.join(' ') result << [r.name, val] unless val.nil? || val == "" end result end def buildReferenceStrings(element, string) @referenceStrings[element] = string eachReferencedElement(element, containmentReferences(element)) do |r,te| buildReferenceStrings(te, string+"/"+te.name) if te.respond_to?(:name) end end def addBuiltinReferenceStrings pre = "ecore:EDataType http://www.eclipse.org/emf/2002/Ecore" @referenceStrings[RGen::ECore::EString] = pre+"#//EString" @referenceStrings[RGen::ECore::EInt] = pre+"#//EInt" @referenceStrings[RGen::ECore::ELong] = pre+"#//ELong" @referenceStrings[RGen::ECore::EFloat] = pre+"#//EFloat" @referenceStrings[RGen::ECore::EBoolean] = pre+"#//EBoolean" @referenceStrings[RGen::ECore::EJavaObject] = pre+"#//EJavaObject" @referenceStrings[RGen::ECore::EJavaClass] = pre+"#//EJavaClass" end end end end rgen-0.8.0/lib/rgen/serializer/xml_serializer.rb0000644000004100000410000000434512642233643021745 0ustar www-datawww-datamodule 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 rgen-0.8.0/lib/rgen/serializer/xmi11_serializer.rb0000644000004100000410000000574312642233643022107 0ustar www-datawww-datarequire 'rgen/serializer/xml_serializer' module RGen module Serializer class XMI11Serializer < XMLSerializer def initialize(file) super @namespacePrefix = "" @contentLevelElements = [] end def setNamespace(shortcut, url) @namespaceShortcut = shortcut @namespaceUrl = url @namespacePrefix = shortcut+":" end def serialize(rootElement, headerInfo=nil) attrs = [] attrs << ['xmi.version', "1.1"] attrs << ['xmlns:'+@namespaceShortcut, @namespaceUrl] if @namespaceUrl attrs << ['timestamp', Time.now.to_s] startTag("XMI", attrs) if headerInfo startTag("XMI.header") writeHeaderInfo(headerInfo) endTag("XMI.header") end startTag("XMI.content") @contentLevelElements = [] writeElement(rootElement) # write remaining toplevel elements, each of which could have # more toplevel elements as childs while @contentLevelElements.size > 0 writeElement(@contentLevelElements.shift) end endTag("XMI.content") endTag("XMI") end def writeHeaderInfo(hash) hash.each_pair do |k,v| tag = "XMI." + k.to_s startTag(tag) if v.is_a?(Hash) writeHeaderInfo(v) else writeText(v.to_s) end endTag(tag) end end def writeElement(element) tag = @namespacePrefix + element.class.ecore.name attrs = attributeValues(element) startTag(tag, attrs) containmentReferences(element).each do |r| roletag = @namespacePrefix + r.eContainingClass.name + "." + r.name targets = element.getGeneric(r.name) targets = [ targets ] unless targets.is_a?(Array) targets.compact! next if targets.empty? startTag(roletag) targets.each do |t| if xmiLevel(t) == :content @contentLevelElements << t else writeElement(t) end end endTag(roletag) end endTag(tag) end def attributeValues(element) result = [["xmi.id", xmiId(element)]] eAllAttributes(element).select{|a| !a.derived}.each do |a| val = element.getGeneric(a.name) result << [a.name, val] unless val.nil? || val == "" end eAllReferences(element).each do |r| next if r.derived next if r.containment next if r.eOpposite && r.eOpposite.containment && xmiLevel(element).nil? next if r.eOpposite && r.many && !r.eOpposite.many targetElements = element.getGenericAsArray(r.name) targetElements.compact! val = targetElements.collect{|te| xmiId(te)}.compact.join(' ') result << [r.name, val] unless val == "" end result end def xmiId(element) if element.respond_to?(:_xmi_id) && element._xmi_id element._xmi_id.to_s else element.object_id.to_s end end def xmiLevel(element) return nil unless element.respond_to?(:_xmi_level) element._xmi_level end end end end rgen-0.8.0/lib/rgen/model_builder/0000755000004100000410000000000012642233643017016 5ustar www-datawww-datargen-0.8.0/lib/rgen/model_builder/builder_context.rb0000644000004100000410000002551712642233643022547 0ustar www-datawww-datarequire 'rgen/ecore/ecore_ext' require 'rgen/model_builder/reference_resolver' module RGen module ModelBuilder class BuilderContext attr_reader :toplevelElements def initialize(package, extensionsModule, resolver, env=nil) package = package.ecore unless package.is_a?(RGen::ECore::EPackage) raise "First argument must be a metamodel package" \ unless package.is_a?(RGen::ECore::EPackage) @rootPackage, @env = package, env @commandResolver = CommandResolver.new(package, extensionsModule, self) @package = @rootPackage @resolver = resolver @contextStack = [] @toplevelElements = [] @helperNames = {} end def const_missing_delegated(delegator, const) ConstPathElement.new(const, self) end # in Ruby 1.9.0 and 1.9.1 #instance_eval looks up constants in the calling scope # that's why const_missing needs to be prepared in BuilderContext, too class << self def currentBuilderContext=(bc) @@currentBuilderContext = bc end def const_missing(name) if @@currentBuilderContext ConstPathElement.new(name, @@currentBuilderContext) else super end end end class CommandResolver def initialize(rootPackage, extensionsModule, builderContext) @extensionFactory = ExtensionContainerFactory.new(rootPackage, extensionsModule, builderContext) @packageResolver = PackageResolver.new(rootPackage, @extensionFactory) @resolveCommand = {} end def resolveCommand(cmd, parentPackage) return @resolveCommand[[parentPackage, cmd]] if @resolveCommand.has_key?([parentPackage, cmd]) package = @packageResolver.packageByCommand(parentPackage, cmd) result = nil if package extensionContainer = @extensionFactory.extensionContainer(package) if extensionContainer.respond_to?(cmd) result = extensionContainer else className = cmd.to_s[0..0].upcase + cmd.to_s[1..-1] result = package.eClasses.find{|c| c.name == className} end end @resolveCommand[[parentPackage, cmd]] = [package, result] end end def method_missing(m, *args, &block) package, classOrContainer = @commandResolver.resolveCommand(m, @package) return super if package.nil? return classOrContainer.send(m, *args, &block) if classOrContainer.is_a?(ExtensionContainerFactory::ExtensionContainer) eClass = classOrContainer nameArg, argHash = self.class.processArguments(args) internalName = nameArg || argHash[:name] argHash[:name] ||= nameArg if nameArg && self.class.hasNameAttribute(eClass) resolverJobs, asRole, helperName = self.class.filterArgHash(argHash, eClass) element = eClass.instanceClass.new(argHash) @resolver.setElementName(element, internalName) @env << element if @env contextElement = @contextStack.last if contextElement self.class.associateWithContextElement(element, contextElement, asRole) else @toplevelElements << element end resolverJobs.each do |job| job.receiver = element job.namespace = contextElement @resolver.addJob(job) end # process block if block @contextStack.push(element) @package, oldPackage = package, @package instance_eval(&block) @package = oldPackage @contextStack.pop end element end def _using(constPathElement, &block) @package, oldPackage = self.class.resolvePackage(@package, @rootPackage, constPathElement.constPath), @package instance_eval(&block) @package = oldPackage end def _context(depth=1) @contextStack[-depth] end class ExtensionContainerFactory class ExtensionContainer def initialize(builderContext) @builderContext = builderContext end def method_missing(m, *args, &block) @builderContext.send(m, *args, &block) end end def initialize(rootPackage, extensionsModule, builderContext) @rootPackage, @extensionsModule, @builderContext = rootPackage, extensionsModule, builderContext @extensionContainer = {} end def moduleForPackage(package) qName = package.qualifiedName rqName = @rootPackage.qualifiedName raise "Package #{qName} is not contained within #{rqName}" unless qName.index(rqName) == 0 path = qName.sub(rqName,'').split('::') path.shift if path.first == "" mod = @extensionsModule path.each do |p| if mod && mod.const_defined?(p) mod = mod.const_get(p) else mod = nil break end end mod end def extensionContainer(package) return @extensionContainer[package] if @extensionContainer[package] container = ExtensionContainer.new(@builderContext) extensionModule = moduleForPackage(package) container.extend(extensionModule) if extensionModule @extensionContainer[package] = container end end class PackageResolver def initialize(rootPackage, extensionFactory) @rootPackage = rootPackage @extensionFactory = extensionFactory @packageByCommand = {} end def packageByCommand(contextPackage, name) return @packageByCommand[[contextPackage, name]] if @packageByCommand.has_key?([contextPackage, name]) if @extensionFactory.extensionContainer(contextPackage).respond_to?(name) result = contextPackage else className = name.to_s[0..0].upcase + name.to_s[1..-1] eClass = contextPackage.eClasses.find{|c| c.name == className} if eClass result = contextPackage elsif contextPackage != @rootPackage result = packageByCommand(contextPackage.eSuperPackage, name) else result = nil end end @packageByCommand[[contextPackage, name]] = result end end class ConstPathElement < Module def initialize(name, builderContext, parent=nil) @name = name.to_s @builderContext = builderContext @parent = parent end def const_missing(const) ConstPathElement.new(const, @builderContext, self) end def method_missing(m, *args, &block) @builderContext._using(self) do send(m, *args, &block) end end def constPath if @parent @parent.constPath << @name else [@name] end end end # helper methods put in the class object to be out of the way of # method evaluation in the builder context class << self class PackageNotFoundException < Exception end def resolvePackage(contextPackage, rootPackage, path) begin return resolvePackageDownwards(contextPackage, path) rescue PackageNotFoundException if contextPackage.eSuperPackage && contextPackage != rootPackage return resolvePackage(contextPackage.eSuperPackage, rootPackage, path) else raise end end end def resolvePackageDownwards(contextPackage, path) first, *rest = path package = contextPackage.eSubpackages.find{|p| p.name == first} raise PackageNotFoundException.new("Could not resolve package: #{first} is not a subpackage of #{contextPackage.name}") unless package if rest.empty? package else resolvePackageDownwards(package, rest) end end def processArguments(args) unless (args.size == 2 && args.first.is_a?(String) && args.last.is_a?(Hash)) || (args.size == 1 && (args.first.is_a?(String) || args.first.is_a?(Hash))) || args.size == 0 raise "Provide a Hash to set feature values, " + "optionally the first argument may be a String specifying " + "the value of the \"name\" attribute." end if args.last.is_a?(Hash) argHash = args.last else argHash = {} end nameArg = args.first if args.first.is_a?(String) [nameArg, argHash] end def filterArgHash(argHash, eClass) resolverJobs = [] asRole, helperName = nil, nil refByName = {} eAllReferences(eClass).each {|r| refByName[r.name] = r} argHash.each_pair do |k,v| if k == :as asRole = v argHash.delete(k) elsif k == :name && !hasNameAttribute(eClass) helperName = v argHash.delete(k) elsif v.is_a?(String) ref = refByName[k.to_s]#eAllReferences(eClass).find{|r| r.name == k.to_s} if ref argHash.delete(k) resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, v) end elsif v.is_a?(Array) ref = refByName[k.to_s] #eAllReferences(eClass).find{|r| r.name == k.to_s} ref && v.dup.each do |e| if e.is_a?(String) v.delete(e) resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, e) end end end end [ resolverJobs, asRole, helperName ] end def hasNameAttribute(eClass) @hasNameAttribute ||= {} @hasNameAttribute[eClass] ||= eClass.eAllAttributes.any?{|a| a.name == "name"} end def eAllReferences(eClass) @eAllReferences ||= {} @eAllReferences[eClass] ||= eClass.eAllReferences end def containmentRefs(contextClass, eClass) @containmentRefs ||= {} @containmentRefs[[contextClass, eClass]] ||= eAllReferences(contextClass).select do |r| r.containment && (eClass.eAllSuperTypes << eClass).include?(r.eType) end end def associateWithContextElement(element, contextElement, asRole) return unless contextElement contextClass = contextElement.class.ecore if asRole asRoleRef = eAllReferences(contextClass).find{|r| r.name == asRole.to_s} raise "Context class #{contextClass.name} has no reference named #{asRole}" unless asRoleRef ref = asRoleRef else possibleContainmentRefs = containmentRefs(contextClass, element.class.ecore) if possibleContainmentRefs.size == 1 ref = possibleContainmentRefs.first elsif possibleContainmentRefs.size == 0 raise "Context class #{contextClass.name} can not contain a #{element.class.ecore.name}" else raise "Context class #{contextClass.name} has several containment references to a #{element.class.ecore.name}." + " Clearify using \":as => \"" end end if ref.many contextElement.addGeneric(ref.name, element) else contextElement.setGeneric(ref.name, element) end end end end end end rgen-0.8.0/lib/rgen/model_builder/model_serializer.rb0000644000004100000410000001730612642233643022703 0ustar www-datawww-datarequire 'rgen/array_extensions' require 'rgen/ecore/ecore_ext' module RGen module ModelBuilder class ModelSerializer def initialize(writable, rootPackage) @writable = writable @currentPackage = rootPackage @qualifiedElementName = {} @internalElementName = {} @relativeQualifiedElementName = {} end def serialize(elements) calcQualifiedElementNames(elements) unifyQualifiedElementNames elements = [elements] unless elements.is_a?(Enumerable) elements.each do |e| serializeElement(e) end end private def serializeElement(element, viaRef=nil, namePath=[], indent=0) className = element.class.ecore.name cmd = className[0..0].downcase+className[1..-1] args = ["\"#{@internalElementName[element]}\""] namePath = namePath + [@internalElementName[element]] childs = [] eAllStructuralFeatures(element).each do |f| next if f.derived if f.is_a?(RGen::ECore::EAttribute) next if f.name == "name" && element.name == @internalElementName[element] val = element.getGeneric(f.name) #puts f.defaultValue.inspect if f.name == "isRoot" args << ":#{f.name} => #{serializeAttribute(val)}" unless val == f.defaultValue || val.nil? elsif !f.containment next if f.eOpposite && f.eOpposite == viaRef val = element.getGeneric(f.name) refString = serializeReference(element, f, val) args << ":#{f.name} => #{refString}" if refString else cs = element.getGeneric(f.name) refString = nil if cs.is_a?(Array) cs.compact! rcs = cs.select{|c| serializeChild?(c, namePath)} childs << [f, rcs] unless rcs.empty? refString = serializeReference(element, f, cs-rcs) else if cs && serializeChild?(cs, namePath) childs << [f, [cs]] else refString = serializeReference(element, f, cs) end end args << ":#{f.name} => #{refString}" if refString end end args << ":as => :#{viaRef.name}" if viaRef && containmentRefs(viaRef.eContainingClass, element.class.ecore).size > 1 cmd = elementPackage(element)+"."+cmd if elementPackage(element).size > 0 @writable.write " " * indent + cmd + " " + args.join(", ") if childs.size > 0 @writable.write " do\n" oldPackage, @currentPackage = @currentPackage, element.class.ecore.ePackage childs.each do |pair| f, cs = pair cs.each {|c| serializeElement(c, f, namePath, indent+1) } end @currentPackage = oldPackage @writable.write " " * indent + "end\n" else @writable.write "\n" end end def serializeChild?(child, namePath) @qualifiedElementName[child][0..-2] == namePath end def serializeAttribute(value) if value.is_a?(String) "\"#{value.gsub("\"","\\\"")}\"" elsif value.is_a?(Symbol) ":#{value}" elsif value.nil? "nil" else value.to_s end end def serializeReference(element, ref, value) if value.is_a?(Array) value = value.compact value = value.select{|v| compareWithOppositeReference(ref, element, v) > 0} if ref.eOpposite qualNames = value.collect do |v| relativeQualifiedElementName(v, element).join(".") end !qualNames.empty? && ("[" + qualNames.collect { |v| "\"#{v}\"" }.join(", ") + "]") elsif value && (!ref.eOpposite || compareWithOppositeReference(ref, element, value) > 0) qualName = relativeQualifiedElementName(value, element).join(".") ("\"#{qualName}\"") end end # descide which part of a bidirectional reference get serialized def compareWithOppositeReference(ref, element, target) result = 0 # first try to make the reference from the many side to the one side result = -1 if ref.many && !ref.eOpposite.many result = 1 if !ref.many && ref.eOpposite.many return result if result != 0 # for 1:1 or many:many perfer, shorter references result = relativeQualifiedElementName(element, target).size <=> relativeQualifiedElementName(target, element).size return result if result != 0 # there just needs to be a descision, use class name or object_id result = element.class.name <=> target.class.name return result if result != 0 element.object_id <=> target.object_id end def elementPackage(element) @elementPackage ||= {} return @elementPackage[element] if @elementPackage[element] eNames = element.class.ecore.ePackage.qualifiedName.split("::") rNames = @currentPackage.qualifiedName.split("::") while eNames.first == rNames.first && !eNames.first.nil? eNames.shift rNames.shift end @elementPackage[element] = eNames.join("::") end def relativeQualifiedElementName(element, context) return @relativeQualifiedElementName[[element, context]] if @relativeQualifiedElementName[[element, context]] # elements which are not in the @qualifiedElementName Hash are not in the scope # of this serialization and will be ignored return [] if element.nil? || @qualifiedElementName[element].nil? return [] if context.nil? || @qualifiedElementName[context].nil? eNames = @qualifiedElementName[element].dup cNames = @qualifiedElementName[context].dup while eNames.first == cNames.first && eNames.size > 1 eNames.shift cNames.shift end @relativeQualifiedElementName[[element, context]] = eNames end def calcQualifiedElementNames(elements, prefix=[], takenNames=[]) elements = [elements] unless elements.is_a?(Array) elements.compact! elements.each do |element| qualifiedNamePath = prefix + [calcInternalElementName(element, takenNames)] @qualifiedElementName[element] ||= [] @qualifiedElementName[element] << qualifiedNamePath takenChildNames = [] eAllStructuralFeatures(element).each do |f| if f.is_a?(RGen::ECore::EReference) && f.containment childs = element.getGeneric(f.name) calcQualifiedElementNames(childs, qualifiedNamePath, takenChildNames) end end end end def unifyQualifiedElementNames @qualifiedElementName.keys.each do |k| @qualifiedElementName[k] = @qualifiedElementName[k].sort{|a,b| a.size <=> b.size}.first end end def calcInternalElementName(element, takenNames) return @internalElementName[element] if @internalElementName[element] name = if element.respond_to?(:name) && element.name && !element.name.empty? element.name else nextElementHelperName(element) end while takenNames.include?(name) name = nextElementHelperName(element) end takenNames << name @internalElementName[element] = name end def nextElementHelperName(element) eClass = element.class.ecore @nextElementNameId ||= {} @nextElementNameId[eClass] ||= 1 result = "_#{eClass.name}#{@nextElementNameId[eClass]}" @nextElementNameId[eClass] += 1 result end def eAllStructuralFeatures(element) @eAllStructuralFeatures ||= {} @eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures end def eAllReferences(eClass) @eAllReferences ||= {} @eAllReferences[eClass] ||= eClass.eAllReferences end def containmentRefs(contextClass, eClass) @containmentRefs ||= {} @containmentRefs[[contextClass, eClass]] ||= eAllReferences(contextClass).select do |r| r.containment && (eClass.eAllSuperTypes << eClass).include?(r.eType) end end end end end rgen-0.8.0/lib/rgen/model_builder/reference_resolver.rb0000644000004100000410000001050312642233643023221 0ustar www-datawww-datarequire '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 endrgen-0.8.0/lib/rgen/instantiator/0000755000004100000410000000000012642233643016727 5ustar www-datawww-datargen-0.8.0/lib/rgen/instantiator/ecore_xml_instantiator.rb0000644000004100000410000001212512642233643024031 0ustar www-datawww-datarequire 'rgen/ecore/ecore' require 'rgen/instantiator/abstract_xml_instantiator' require 'rgen/array_extensions' class ECoreXMLInstantiator < AbstractXMLInstantiator include RGen::ECore INFO = 0 WARN = 1 ERROR = 2 def initialize(env, loglevel=ERROR) @env = env @rolestack = [] @elementstack = [] @element_by_id = {} @loglevel = loglevel end def start_tag(prefix, tag, namespaces, attributes) eRef = nil if @elementstack.last eRef = eAllReferences(@elementstack.last).find{|r|r.name == tag} if eRef if attributes["xsi:type"] && attributes["xsi:type"] =~ /ecore:(\w+)/ class_name = $1 attributes.delete("xsi:type") else class_name = eRef.eType.name end else raise "Reference not found: #{tag} on #{@elementstack.last}" end else class_name = tag end eClass = RGen::ECore.ecore.eClassifiers.find{|c| c.name == class_name} if eClass obj = RGen::ECore.const_get(class_name).new if attributes["xmi:id"] @element_by_id[attributes["xmi:id"]] = obj attributes.delete("xmi:id") end if eRef if eRef.many @elementstack.last.addGeneric(eRef.name, obj) else @elementstack.last.setGeneric(eRef.name, obj) end end @env << obj @elementstack.push obj else log WARN, "Class not found: #{class_name}" @elementstack.push nil end attributes.each_pair do |attr, value| set_attribute_internal(attr, value) end end def end_tag(prefix, tag) @elementstack.pop end ResolverDescription = Struct.new(:object, :attribute, :value) def set_attribute(attr, value) # do nothing, already handled by start_tag/set_attribute_internal end def set_attribute_internal(attr, value) return unless @elementstack.last eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr} if eFeat.is_a?(EReference) rd = ResolverDescription.new rd.object = @elementstack.last rd.attribute = attr rd.value = value @resolver_descs << rd elsif eFeat value = true if value == "true" && eFeat.eType == EBoolean value = false if value == "false" && eFeat.eType == EBoolean value = value.to_i if eFeat.eType == EInt || eFeat.eType == ELong @elementstack.last.setGeneric(attr, value) else log WARN, "Feature not found: #{attr} on #{@elementstack.last}" end end def instantiate(str) @resolver_descs = [] # puts "Instantiating ..." super(str, 1000) rootpackage = @env.find(:class => EPackage).first # puts "Resolving ..." @resolver_descs.each do |rd| refed = find_referenced(rootpackage, rd.value) feature = eAllStructuralFeatures(rd.object).find{|f| f.name == rd.attribute} raise StandardError.new("StructuralFeature not found: #{rd.attribute}") unless feature if feature.many rd.object.setGeneric(feature.name, refed) else rd.object.setGeneric(feature.name, refed.first) end end end def eAllReferences(element) @eAllReferences ||= {} @eAllReferences[element.class] ||= element.class.ecore.eAllReferences end def eAllAttributes(element) @eAllAttributes ||= {} @eAllAttributes[element.class] ||= element.class.ecore.eAllAttributes end def eAllStructuralFeatures(element) @eAllStructuralFeatures ||= {} @eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures end def find_referenced(context, desc) desc.split(/\s+/).collect do |r| if r =~ /^#([^\/]+)$/ @element_by_id[$1] elsif r =~ /^#\/\d*\/([\w\/]+)/ find_in_context(context, $1.split('/')) elsif r =~ /#\/\/(\w+)$/ case $1 when "EString"; RGen::ECore::EString when "EInt"; RGen::ECore::EInt when "ELong"; RGen::ECore::ELong when "EBoolean"; RGen::ECore::EBoolean when "EFloat"; RGen::ECore::EFloat when "EJavaObject"; RGen::ECore::EJavaObject when "EJavaClass"; RGen::ECore::EJavaClass end end end.compact end def find_in_context(context, desc_elements) if context.is_a?(EPackage) r = (context.eClassifiers + context.eSubpackages).find{|c| c.name == desc_elements.first} elsif context.is_a?(EClass) r = context.eStructuralFeatures.find{|s| s.name == desc_elements.first} else raise StandardError.new("Don't know how to find #{desc_elements.join('/')} in context #{context}") end if r if desc_elements.size > 1 find_in_context(r, desc_elements[1..-1]) else r end else log WARN, "Can not follow path, element #{desc_elements.first} not found within #{context}(#{context.name})" end end def log(level, msg) puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel end end rgen-0.8.0/lib/rgen/instantiator/abstract_xml_instantiator.rb0000644000004100000410000000376012642233643024544 0ustar www-datawww-datarequire 'nokogiri' class AbstractXMLInstantiator class Visitor < Nokogiri::XML::SAX::Document def initialize(inst, gcSuspendCount) @instantiator = inst @gcSuspendCount = gcSuspendCount @namespaces = {} end def start_element_namespace(tag, attributes, prefix, uri, ns) controlGC ns.each{|n| @namespaces[n[0]] = n[1]} attrs = attributes.collect{|a| [a.prefix ? a.prefix+":"+a.localname : a.localname, a.value]} @instantiator.start_tag(prefix, tag, @namespaces, Hash[*(attrs.flatten)]) attrs.each { |pair| @instantiator.set_attribute(pair[0], pair[1]) } end def end_element_namespace(tag, prefix, uri) @instantiator.end_tag(prefix, tag) end def characters(str) @instantiator.text(str) end def controlGC return unless @gcSuspendCount > 0 @gcCounter ||= 0 @gcCounter += 1 if @gcCounter == @gcSuspendCount @gcCounter = 0 GC.enable ObjectSpace.garbage_collect GC.disable end end end # Parses str and calls start_tag, end_tag, set_attribute and text methods of a subclass. # # If gcSuspendCount is specified, the garbage collector will be disabled for that # number of start or end tags. After that period it will clean up and then be disabled again. # A value of about 1000 can significantly improve overall performance. # The memory usage normally does not increase. # Depending on the work done for every xml tag the value might have to be adjusted. # def instantiate(str, gcSuspendCount=0) gcDisabledBefore = GC.disable gcSuspendCount = 0 if gcDisabledBefore begin visitor = Visitor.new(self, gcSuspendCount) parser = Nokogiri::XML::SAX::Parser.new(visitor) parser.parse(str) do |ctx| @parserContext = ctx end ensure GC.enable unless gcDisabledBefore end end def text(str) end end rgen-0.8.0/lib/rgen/instantiator/qualified_name_resolver.rb0000644000004100000410000000554512642233643024151 0ustar www-datawww-datarequire 'rgen/instantiator/reference_resolver' module RGen module Instantiator # This is a resolver resolving element identifiers which are qualified names. class QualifiedNameResolver attr_reader :nameAttribute attr_reader :separator attr_reader :leadingSeparator def initialize(rootElements, options={}) @rootElements = rootElements @nameAttribute = options[:nameAttribute] || "name" @separator = options[:separator] || "/" @leadingSeparator = options.has_key?(:leadingSeparator) ? options[:leadingSeparator] : true @elementByQName = {} @visitedQName = {} @childReferences = {} @resolverDelegate = ReferenceResolver.new(:identifier_resolver => method(:resolveIdentifier)) end def resolveIdentifier(qualifiedName) return @elementByQName[qualifiedName] if @elementByQName.has_key?(qualifiedName) path = qualifiedName.split(separator).reject{|s| s == ""} if path.size > 1 parentQName = (leadingSeparator ? separator : "") + path[0..-2].join(separator) parents = resolveIdentifier(parentQName) parents = [parents].compact unless parents.is_a?(Array) children = parents.collect{|p| allNamedChildren(p)}.flatten elsif path.size == 1 parentQName = "" children = allRootNamedChildren else return @elementByQName[qualifiedName] = nil end # if the parent was already visited all matching elements are the hash if !@visitedQName[parentQName] children.each do |c| name = c.send(nameAttribute) if name qname = parentQName + ((parentQName != "" || leadingSeparator) ? separator : "") + name existing = @elementByQName[qname] if existing @elementByQName[qname] = [existing] unless existing.is_a?(Array) @elementByQName[qname] << c else @elementByQName[qname] = c end end end # all named children of praent have been checked and hashed @visitedQName[parentQName] = true end @elementByQName[qualifiedName] ||= nil end def resolveReferences(unresolvedReferences, problems=[]) @resolverDelegate.resolve(unresolvedReferences, :problems => problems) end private def allNamedChildren(element) childReferences(element.class).collect do |r| element.getGenericAsArray(r.name).collect do |c| if c.respond_to?(nameAttribute) c else allNamedChildren(c) end end end.flatten end def allRootNamedChildren @rootElements.collect do |e| if e.respond_to?(nameAttribute) e else allNamedChildren(e) end end.flatten end def childReferences(clazz) @childReferences[clazz] ||= clazz.ecore.eAllReferences.select{|r| r.containment} end end end end rgen-0.8.0/lib/rgen/instantiator/default_xml_instantiator.rb0000644000004100000410000000623312642233643024363 0ustar www-datawww-datarequire 'rgen/instantiator/nodebased_xml_instantiator' module RGen module Instantiator # A default XML instantiator. # Derive your own instantiator from this class or use it as is. # class DefaultXMLInstantiator < NodebasedXMLInstantiator include Util::NameHelper NamespaceDescriptor = Struct.new(:prefix, :target) class << self def map_tag_ns(from, to, prefix="") tag_ns_map[from] = NamespaceDescriptor.new(prefix, to) end def tag_ns_map # :nodoc: @tag_ns_map ||={} @tag_ns_map end end def initialize(env, default_module, create_mm=false) super(env) @default_module = default_module @create_mm = create_mm end def on_descent(node) obj = new_object(node) @env << obj unless obj.nil? node.object = obj node.attributes.each_pair { |k,v| set_attribute(node, k, v) } end def on_ascent(node) node.children.each { |c| assoc_p2c(node, c) } node.object.class.has_attr 'chardata', Object unless node.object.respond_to?(:chardata) set_attribute(node, "chardata", node.chardata) end def class_name(str) saneClassName(str) end def new_object(node) ns_desc = self.class.tag_ns_map[node.namespace] class_name = class_name(ns_desc.nil? ? node.qtag : ns_desc.prefix+node.tag) mod = (ns_desc && ns_desc.target) || @default_module build_on_error(NameError, :build_class, class_name, mod) do begin mod.const_get(class_name, false).new rescue ArgumentError # Ruby 1.8 mod.const_get(class_name).new end end end def build_class(name, mod) mod.const_set(name, Class.new(RGen::MetamodelBuilder::MMBase)) end def method_name(str) saneMethodName(str) end def assoc_p2c(parent, child) return unless parent.object && child.object method_name = method_name(className(child.object)) build_on_error(NoMethodError, :build_p2c_assoc, parent, child, method_name) do parent.object.addGeneric(method_name, child.object) child.object.setGeneric("parent", parent.object) end end def build_p2c_assoc(parent, child, method_name) parent.object.class.has_many(method_name, child.object.class) child.object.class.has_one("parent", RGen::MetamodelBuilder::MMBase) end def set_attribute(node, attr, value) return unless node.object build_on_error(NoMethodError, :build_attribute, node, attr, value) do node.object.setGeneric(method_name(attr), value) end end def build_attribute(node, attr, value) node.object.class.has_attr(method_name(attr)) end protected # Helper method for implementing classes. # This method yields the given block. # If the metamodel should be create automatically (see constructor) # rescues +error+ and calls +builder_method+ with +args+, then # yields the block again. def build_on_error(error, builder_method, *args) begin yield rescue error if @create_mm send(builder_method, *args) yield else raise end end end end end end rgen-0.8.0/lib/rgen/instantiator/json_instantiator.rb0000644000004100000410000001015412642233643023025 0ustar www-datawww-datarequire 'rgen/instantiator/qualified_name_resolver' require 'rgen/instantiator/json_parser' module RGen module Instantiator # JsonInstantiator is used to create RGen models from JSON. # # Each JSON object needs to have an attribute "_class" which is used to find # the metamodel class to instantiate. The value of "_class" should be the # the relative qualified class name within the root package as a string. # # If the option "short_class_names" is set to true, unqualified class names can be used. # In this case, metamodel classes are searched in the metamodel root package first. # If this search is not successful, all subpackages will be searched for the class name. # class JsonInstantiator # Model elements will be created in evironment +env+, # classes are looked for in metamodel package module +mm+, # +options+ include: # short_class_names: if true subpackages will be searched for unqualifed class names (default: true) # ignore_keys: an array of json object key names which are to be ignored (default: none) # # The options are also passed to the underlying QualifiedNameResolver. # def initialize(env, mm, options={}) @env = env @mm = mm @options = options @short_class_names = !@options.has_key?(:short_class_names) || @options[:short_class_names] @ignore_keys = @options[:ignore_keys] || [] @unresolvedReferences = [] @classes = {} @classes_flat = {} mm.ecore.eAllClasses.each do |c| @classes[c.instanceClass.name.sub(mm.name+"::","")] = c @classes_flat[c.name] = c end @parser = JsonParser.new(self) end # Creates the elements described by the json string +str+. # Returns an array of ReferenceResolver::UnresolvedReference # describing the references which could not be resolved # # Options: # :root_elements: if an array is provided, it will be filled with the root elements # def instantiate(str, options={}) root = @parser.parse(str) if options[:root_elements].is_a?(Array) options[:root_elements].clear root.each{|r| options[:root_elements] << r} end resolver = QualifiedNameResolver.new(root, @options) resolver.resolveReferences(@unresolvedReferences) end def createObject(hash) className = hash["_class"] # hashes without a _class key are returned as is return hash unless className if @classes[className] clazz = @classes[className].instanceClass elsif @short_class_names && @classes_flat[className] clazz = @classes_flat[className].instanceClass else raise "class not found: #{className}" end hash.delete("_class") @ignore_keys.each do |k| hash.delete(k) end urefs = [] hash.keys.each do |k| f = eFeature(k, clazz) hash[k] = [hash[k]] if f.many && !hash[k].is_a?(Array) if f.is_a?(RGen::ECore::EReference) && !f.containment if f.many idents = hash[k] hash[k] = idents.collect do |i| proxy = RGen::MetamodelBuilder::MMProxy.new(i) urefs << ReferenceResolver::UnresolvedReference.new(nil, k, proxy) proxy end else ident = hash[k] ident = ident.first if ident.is_a?(Array) proxy = RGen::MetamodelBuilder::MMProxy.new(ident) hash[k] = proxy urefs << ReferenceResolver::UnresolvedReference.new(nil, k, proxy) end elsif f.eType.is_a?(RGen::ECore::EEnum) hash[k] = hash[k].to_sym elsif f.eType.instanceClassName == "Float" hash[k] = hash[k].to_f end end obj = @env.new(clazz, hash) urefs.each do |r| r.element = obj @unresolvedReferences << r end obj end private def eFeature(name, clazz) @eFeature ||= {} @eFeature[clazz] ||= {} unless @eFeature[clazz][name] feature = clazz.ecore.eAllStructuralFeatures.find{|f| f.name == name} raise "feature '#{name}' not found in class '#{clazz}'" unless feature end @eFeature[clazz][name] ||= feature end end end end rgen-0.8.0/lib/rgen/instantiator/nodebased_xml_instantiator.rb0000644000004100000410000000676212642233643024672 0ustar www-datawww-datarequire 'rgen/metamodel_builder' require 'rgen/instantiator/abstract_instantiator' require 'nokogiri' module RGen module Instantiator class NodebasedXMLInstantiator < AbstractInstantiator class << self # The prune level is the number of parent/children associations which # is kept when the instantiator ascents the XML tree. # If the level is 2, information for the node's children and the childrens' # children will be available as an XMLNodeDescriptor object. # If the level is 0 no pruning will take place, i.e. the whole information # is kept until the end of the instantiation process. 0 is default. def set_prune_level(level) @prune_level = level end def prune_level # :nodoc: @prune_level ||= 0 end end class XMLNodeDescriptor attr_reader :namespace, :qtag, :prefix, :tag, :parent, :attributes, :chardata attr_accessor :object, :children def initialize(ns, qtag, prefix, tag, parent, children, attributes) @namespace, @qtag, @prefix, @tag, @parent, @children, @attributes = ns, qtag, prefix, tag, parent, children, attributes @parent.children << self if @parent @chardata = [] end end class Visitor < Nokogiri::XML::SAX::Document attr_reader :namespaces def initialize(inst) @instantiator = inst @namespaces = {} end def start_element_namespace(tag, attributes, prefix, uri, ns) ns.each{|n| @namespaces[n[0]] = n[1]} attrs = {} attributes.each{|a| attrs[a.prefix ? a.prefix+":"+a.localname : a.localname] = a.value} qname = prefix ? prefix+":"+tag : tag @instantiator.start_element(uri, qname, prefix, tag, attrs) end def end_element(name) @instantiator.end_element end def characters(str) @instantiator.on_chardata(str) end end def initialize(env) super @env = env @stack = [] end def instantiate_file(file) File.open(file) { |f| parse(f.read)} resolve end def instantiate(text) parse(text) resolve end def parse(src) @visitor = Visitor.new(self) parser = Nokogiri::XML::SAX::Parser.new(@visitor) parser.parse(src) @visitor = nil end def start_element(ns, qtag, prefix, tag, attributes) node = XMLNodeDescriptor.new(ns, qtag, prefix, tag, @stack[-1], [], attributes) @stack.push node on_descent(node) end def end_element node = @stack.pop on_ascent(node) prune_children(node, self.class.prune_level - 1) if self.class.prune_level > 0 end def on_chardata(str) node = @stack.last node.chardata << str end # This method is called when the XML parser goes down the tree. # An XMLNodeDescriptor +node+ describes the current node. # Implementing classes must overwrite this method. def on_descent(node) raise "Overwrite this method !" end # This method is called when the XML parser goes up the tree. # An XMLNodeDescriptor +node+ describes the current node. # Implementing classes must overwrite this method. def on_ascent(node) raise "Overwrite this method !" end def namespaces @visitor.namespaces if @visitor end private def prune_children(node, level) if level == 0 node.children = nil else node.children.each { |c| prune_children(c, level-1) } end end end end end rgen-0.8.0/lib/rgen/instantiator/json_parser.rb0000644000004100000410000001562512642233643021612 0ustar www-datawww-data# # DO NOT MODIFY!!!! # This file is automatically generated by racc 1.4.5 # from racc grammer file "json_parser.y". # require 'racc/parser' module RGen module Instantiator class JsonParser < Racc::Parser module_eval <<'..end json_parser.y modeval..id3d5fb611e2', 'json_parser.y', 38 ParserToken = Struct.new(:line, :file, :value) def initialize(instantiator) @instantiator = instantiator end def parse(str, file=nil) @q = [] line = 1 until str.empty? case str when /\A\n/ str = $' line +=1 when /\A\s+/ str = $' when /\A([-+]?\d+\.\d+)/ str = $' @q << [:FLOAT, ParserToken.new(line, file, $1)] when /\A([-+]?\d+)/ str = $' @q << [:INTEGER, ParserToken.new(line, file, $1)] when /\A"((?:[^"\\]|\\"|\\\\|\\[^"\\])*)"/ str = $' sval = $1 sval.gsub!('\\\\','\\') sval.gsub!('\\"','"') sval.gsub!('\\n',"\n") sval.gsub!('\\r',"\r") sval.gsub!('\\t',"\t") sval.gsub!('\\f',"\f") sval.gsub!('\\b',"\b") @q << [:STRING, ParserToken.new(line, file, sval)] when /\A(\{|\}|\[|\]|,|:|true|false)/ str = $' @q << [$1, ParserToken.new(line, file, $1)] else raise "parse error in line #{line} on "+str[0..20].inspect+"..." end end @q.push [false, ParserToken.new(line, file, '$end')] do_parse end def next_token r = @q.shift r end ..end json_parser.y modeval..id3d5fb611e2 ##### racc 1.4.5 generates ### racc_reduce_table = [ 0, 0, :racc_error, 1, 14, :_reduce_1, 3, 16, :_reduce_2, 2, 16, :_reduce_3, 1, 17, :_reduce_4, 3, 17, :_reduce_5, 3, 18, :_reduce_6, 2, 18, :_reduce_7, 1, 19, :_reduce_8, 3, 19, :_reduce_9, 3, 20, :_reduce_10, 1, 15, :_reduce_11, 1, 15, :_reduce_12, 1, 15, :_reduce_13, 1, 15, :_reduce_14, 1, 15, :_reduce_15, 1, 15, :_reduce_16, 1, 15, :_reduce_17 ] racc_reduce_n = 18 racc_shift_n = 29 racc_action_table = [ 3, 16, 17, 7, 22, 8, 21, 10, 11, 1, 2, 3, 12, 23, 7, 24, 8, 25, 10, 11, 1, 2, 3, 20, 15, 7, 17, 8, nil, 10, 11, 1, 2, 3, nil, nil, 7, nil, 8, nil, 10, 11, 1, 2 ] racc_action_check = [ 0, 7, 7, 0, 15, 0, 14, 0, 0, 0, 0, 3, 3, 17, 3, 18, 3, 19, 3, 3, 3, 3, 20, 13, 4, 20, 25, 20, nil, 20, 20, 20, 20, 23, nil, nil, 23, nil, 23, nil, 23, 23, 23, 23 ] racc_action_pointer = [ -2, nil, nil, 9, 24, nil, nil, -5, nil, nil, nil, nil, nil, 19, 3, 4, nil, 5, 9, 13, 20, nil, nil, 31, nil, 19, nil, nil, nil ] racc_action_default = [ -18, -16, -17, -18, -18, -1, -11, -18, -13, -12, -14, -15, -3, -4, -18, -18, -7, -18, -18, -8, -18, -2, 29, -18, -6, -18, -5, -10, -9 ] racc_goto_table = [ 5, 18, 4, 14, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 28, 26, nil, nil, 27 ] racc_goto_check = [ 2, 6, 1, 4, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 6, 4, nil, nil, 2 ] racc_goto_pointer = [ nil, 2, 0, nil, 0, nil, -6, nil ] racc_goto_default = [ nil, nil, 13, 6, nil, 9, nil, 19 ] racc_token_table = { false => 0, Object.new => 1, "[" => 2, "]" => 3, "," => 4, "{" => 5, "}" => 6, :STRING => 7, ":" => 8, :INTEGER => 9, :FLOAT => 10, "true" => 11, "false" => 12 } racc_use_result_var = true racc_nt_base = 13 Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_token_to_s_table = [ '$end', 'error', '"["', '"]"', '","', '"{"', '"}"', 'STRING', '":"', 'INTEGER', 'FLOAT', '"true"', '"false"', '$start', 'json', 'value', 'array', 'valueList', 'object', 'memberList', 'member'] Racc_debug_parser = false ##### racc system variables end ##### # reduce 0 omitted module_eval <<'.,.,', 'json_parser.y', 4 def _reduce_1( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'json_parser.y', 6 def _reduce_2( val, _values, result ) result = val[1] result end .,., module_eval <<'.,.,', 'json_parser.y', 7 def _reduce_3( val, _values, result ) result = [] result end .,., module_eval <<'.,.,', 'json_parser.y', 9 def _reduce_4( val, _values, result ) result = [ val[0] ] result end .,., module_eval <<'.,.,', 'json_parser.y', 10 def _reduce_5( val, _values, result ) result = [ val[0] ] + val[2] result end .,., module_eval <<'.,.,', 'json_parser.y', 12 def _reduce_6( val, _values, result ) result = @instantiator.createObject(val[1]) result end .,., module_eval <<'.,.,', 'json_parser.y', 13 def _reduce_7( val, _values, result ) result = nil result end .,., module_eval <<'.,.,', 'json_parser.y', 15 def _reduce_8( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'json_parser.y', 16 def _reduce_9( val, _values, result ) result = val[0].merge(val[2]) result end .,., module_eval <<'.,.,', 'json_parser.y', 18 def _reduce_10( val, _values, result ) result = {val[0].value => val[2]} result end .,., module_eval <<'.,.,', 'json_parser.y', 20 def _reduce_11( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'json_parser.y', 21 def _reduce_12( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'json_parser.y', 22 def _reduce_13( val, _values, result ) result = val[0].value result end .,., module_eval <<'.,.,', 'json_parser.y', 23 def _reduce_14( val, _values, result ) result = val[0].value.to_i result end .,., module_eval <<'.,.,', 'json_parser.y', 24 def _reduce_15( val, _values, result ) result = val[0].value.to_f result end .,., module_eval <<'.,.,', 'json_parser.y', 25 def _reduce_16( val, _values, result ) result = true result end .,., module_eval <<'.,.,', 'json_parser.y', 26 def _reduce_17( val, _values, result ) result = false result end .,., def _reduce_none( val, _values, result ) result end end # class JsonParser end end rgen-0.8.0/lib/rgen/instantiator/json_parser.y0000644000004100000410000000411312642233643021445 0ustar www-datawww-dataclass JsonParser rule json: value { result = val[0] } array: "[" valueList "]" { result = val[1] } | "[" "]" { result = [] } valueList: value { result = [ val[0] ] } | value "," valueList { result = [ val[0] ] + val[2] } object: "{" memberList "}" { result = @instantiator.createObject(val[1]) } | "{" "}" { result = nil } memberList: member { result = val[0] } | member "," memberList { result = val[0].merge(val[2]) } member: STRING ":" value { result = {val[0].value => val[2]} } value: array { result = val[0] } | object { result = val[0] } | STRING { result = val[0].value } | INTEGER { result = val[0].value.to_i } | FLOAT { result = val[0].value.to_f } | "true" { result = true } | "false" { result = false } end ---- header module RGen module Instantiator ---- inner ParserToken = Struct.new(:line, :file, :value) def initialize(instantiator) @instantiator = instantiator end def parse(str, file=nil) @q = [] line = 1 until str.empty? case str when /\A\n/ str = $' line +=1 when /\A\s+/ str = $' when /\A([-+]?\d+\.\d+)/ str = $' @q << [:FLOAT, ParserToken.new(line, file, $1)] when /\A([-+]?\d+)/ str = $' @q << [:INTEGER, ParserToken.new(line, file, $1)] when /\A"((?:[^"\\]|\\"|\\\\|\\[^"\\])*)"/ str = $' sval = $1 sval.gsub!('\\\\','\\') sval.gsub!('\\"','"') sval.gsub!('\\n',"\n") sval.gsub!('\\r',"\r") sval.gsub!('\\t',"\t") sval.gsub!('\\f',"\f") sval.gsub!('\\b',"\b") @q << [:STRING, ParserToken.new(line, file, sval)] when /\A(\{|\}|\[|\]|,|:|true|false)/ str = $' @q << [$1, ParserToken.new(line, file, $1)] else raise "parse error in line #{line} on "+str[0..20].inspect+"..." end end @q.push [false, ParserToken.new(line, file, '$end')] do_parse end def next_token r = @q.shift r end ---- footer end end rgen-0.8.0/lib/rgen/instantiator/reference_resolver.rb0000644000004100000410000001114412642233643023134 0ustar www-datawww-datarequire 'rgen/instantiator/resolution_helper' module RGen module Instantiator # The ReferenceResolver can be used to resolve unresolved references, i.e. instances # of class UnresolvedReference # # There are two ways how this can be used: # 1. the identifiers and associated model elements are added upfront using +add_identifier+ # 2. register an :identifier_resolver with the constructor, which will be invoked # for every unresolved identifier # class ReferenceResolver # Instances of this class represent information about not yet resolved references. # This consists of the +element+ and metamodel +feature_name+ which hold/is to hold the # reference and the +proxy+ object which is the placeholder for the reference. # If the reference could not be resolved because the target type does not match the # feature type, the flag +target_type_error+ will be set. # class UnresolvedReference attr_reader :feature_name, :proxy attr_accessor :element, :target_type_error def initialize(element, feature_name, proxy) @element = element @feature_name = feature_name @proxy = proxy end end # Create a reference resolver, options: # # :identifier_resolver: # a proc which is called with an identifier and which should return the associated element # in case the identifier is not uniq, the proc may return multiple values # default: lookup element in internal map # def initialize(options={}) @identifier_resolver = options[:identifier_resolver] @identifier_map = {} end # Add an +identifer+ / +element+ pair which will be used for looking up unresolved identifers def add_identifier(ident, element) map_entry = @identifier_map[ident] if map_entry if map_entry.is_a?(Array) map_entry << element else @identifier_map[ident] = [map_entry, element] end else @identifier_map[ident] = element end end # Tries to resolve the given +unresolved_refs+. If resolution is successful, the proxy object # will be removed, otherwise there will be an error description in the problems array. # In case the resolved target element's type is not valid for the given feature, the # +target_type_error+ flag will be set on the unresolved reference. # Returns an array of the references which are still unresolved. Options: # # :problems # an array to which problems will be appended # # :on_resolve # a proc which will be called for every sucessful resolution, receives the unresolved # reference as well as to new target element # # :use_target_type # use the expected target type to narrow the set of possible targets # (i.e. ignore targets with wrong type) # # :failed_resolutions # a Hash which will receive an entry for each failed resolution for which at least one # target element was found (wrong target type, or target not unique). # hash key is the uref, hash value is the target element or the Array of target elements # def resolve(unresolved_refs, options={}) problems = options[:problems] || [] still_unresolved_refs = [] failed_resolutions = options[:failed_resolutions] || {} unresolved_refs.each do |ur| if @identifier_resolver target = @identifier_resolver.call(ur.proxy.targetIdentifier) else target = @identifier_map[ur.proxy.targetIdentifier] end target = [target].compact unless target.is_a?(Array) if options[:use_target_type] feature = ur.element.class.ecore.eAllReferences.find{|r| r.name == ur.feature_name} target = target.select{|e| e.is_a?(feature.eType.instanceClass)} end if target.size == 1 status = ResolutionHelper.set_uref_target(ur, target[0]) if status == :success options[:on_resolve] && options[:on_resolve].call(ur, target[0]) elsif status == :type_error ur.target_type_error = true problems << type_error_message(target[0]) still_unresolved_refs << ur failed_resolutions[ur] = target[0] end elsif target.size > 1 problems << "identifier #{ur.proxy.targetIdentifier} not uniq" still_unresolved_refs << ur failed_resolutions[ur] = target else problems << "identifier #{ur.proxy.targetIdentifier} not found" still_unresolved_refs << ur end end still_unresolved_refs end private def type_error_message(target) "invalid target type #{target.class}" end end end end rgen-0.8.0/lib/rgen/instantiator/abstract_instantiator.rb0000644000004100000410000000327512642233643023665 0ustar www-datawww-datamodule 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 endrgen-0.8.0/lib/rgen/instantiator/resolution_helper.rb0000644000004100000410000000207312642233643023020 0ustar www-datawww-datamodule RGen module Instantiator module ResolutionHelper # sets the target of an unresolved reference in the model # returns :type_error if the target is of wrong type, otherwise :success # def self.set_uref_target(uref, target) refs = uref.element.getGeneric(uref.feature_name) if refs.is_a?(Array) index = refs.index(uref.proxy) uref.element.removeGeneric(uref.feature_name, uref.proxy) begin uref.element.addGeneric(uref.feature_name, target, index) rescue StandardError => e if is_type_error?(e) uref.element.addGeneric(uref.feature_name, uref.proxy, index) return :type_error else raise end end else begin # this will replace the proxy uref.element.setGeneric(uref.feature_name, target) rescue StandardError => e if is_type_error?(e) return :type_error else raise end end end :success end def self.is_type_error?(e) e.message =~ /Can not use a .* where a .* is expected/ end end end end rgen-0.8.0/lib/rgen/instantiator/xmi11_instantiator.rb0000644000004100000410000001147712642233643023024 0ustar www-datawww-datarequire 'rgen/ecore/ecore' require 'rgen/instantiator/abstract_xml_instantiator' require 'rgen/array_extensions' class XMI11Instantiator < AbstractXMLInstantiator include RGen::ECore ResolverDescription = Struct.new(:object, :attribute, :value, :many) INFO = 0 WARN = 1 ERROR = 2 def initialize(env, fix_map={}, loglevel=ERROR) @env = env @fix_map = fix_map @loglevel = loglevel @rolestack = [] @elementstack = [] end def add_metamodel(ns, mod) @ns_module_map ||={} @ns_module_map[ns] = mod end def instantiate(str) @resolver_descs = [] @element_by_id = {} super(str, 1000) @resolver_descs.each do |rd| if rd.many newval = rd.value.split(" ").collect{|v| @element_by_id[v]} else newval = @element_by_id[rd.value] end log WARN, "Could not resolve reference #{rd.attribute} on #{rd.object}" unless newval begin rd.object.setGeneric(rd.attribute,newval) rescue Exception log WARN, "Could not set reference #{rd.attribute} on #{rd.object}" end end end def start_tag(prefix, tag, namespaces, attributes) if tag =~ /\w+\.(\w+)/ # XMI role role_name = map_feature_name($1) || $1 eRef = @elementstack.last && eAllReferences(@elementstack.last).find{|r|r.name == role_name} log WARN, "No reference found for #{role_name} on #{@elementstack.last}" unless eRef @rolestack.push eRef elsif attributes["xmi.idref"] # reference rd = ResolverDescription.new rd.object = @elementstack.last rd.attribute = @rolestack.last.name rd.value = attributes["xmi.idref"] rd.many = @rolestack.last.many @resolver_descs << rd @elementstack.push nil else # model element value = map_tag(tag, attributes) || tag if value.is_a?(String) mod = @ns_module_map[namespaces[prefix]] unless mod log WARN, "Ignoring tag #{tag}" return end value = mod.const_get(value).new end @env << value eRef = @rolestack.last if eRef && eRef.many @elementstack.last.addGeneric(eRef.name, value) elsif eRef @elementstack.last.setGeneric(eRef.name, value) end @elementstack.push value end end def end_tag(prefix, tag) if tag =~ /\w+\.(\w+)/ @rolestack.pop else @elementstack.pop end end def set_attribute(attr, value) return unless @elementstack.last if attr == "xmi.id" @element_by_id[value] = @elementstack.last else attr_name = map_feature_name(attr) || attr eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr_name} unless eFeat log WARN, "No structural feature found for #{attr_name} on #{@elementstack.last}" return end if eFeat.is_a?(RGen::ECore::EReference) if map_feature_value(attr_name, value).is_a?(eFeat.eType.instanceClass) @elementstack.last.setGeneric(attr_name, map_feature_value(attr_name, value)) else rd = ResolverDescription.new rd.object = @elementstack.last rd.attribute = attr_name rd.value = value rd.many = eFeat.many @resolver_descs << rd end else value = map_feature_value(attr_name, value) || value value = true if value == "true" && eFeat.eType == EBoolean value = false if value == "false" && eFeat.eType == EBoolean value = value.to_i if eFeat.eType == EInt || eFeat.eType == ELong value = value.to_f if eFeat.eType == EFloat value = value.to_sym if eFeat.eType.is_a?(EEnum) @elementstack.last.setGeneric(attr_name, value) end end end private def map_tag(tag, attributes) tag_map = @fix_map[:tags] || {} value = tag_map[tag] if value.is_a?(Proc) value.call(tag, attributes) else value end end def map_feature_name(name) name_map = @fix_map[:feature_names] || {} name_map[name] end def map_feature_value(attr_name, value) value_map = @fix_map[:feature_values] || {} map = value_map[attr_name] if map.is_a?(Hash) map[value] elsif map.is_a?(Proc) map.call(value) end end def log(level, msg) puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel end def eAllReferences(element) @eAllReferences ||= {} @eAllReferences[element.class] ||= element.class.ecore.eAllReferences end def eAllStructuralFeatures(element) @eAllStructuralFeatures ||= {} @eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures end end rgen-0.8.0/lib/rgen/array_extensions.rb0000644000004100000410000000302412642233643020131 0ustar www-datawww-data# RGen Framework # (c) Martin Thiede, 2006 require 'rgen/metamodel_builder' class Array def >>(method) compact.inject([]) { |r,e| r | ( (o=e.send(method)).is_a?(Array) ? o : [o] ) } end def method_missing(m, *args) # This extensions has the side effect that it allows to call any method on any # empty array with an empty array as the result. This behavior is required for # navigating models. # # This is a problem for Hash[] called with an (empty) array of tupels. # It will call to_hash expecting a Hash as the result. When it gets an array instead, # it fails with an exception. Make sure it gets a NoMethodException as without this # extension and it will catch that and return an empty hash as expected. # # Similar problems exist for other Ruby built-in methods which are expected to fail. # return super unless (size == 0 && m != :to_hash && m != :to_str) || compact.any?{|e| e.is_a? RGen::MetamodelBuilder::MMBase} # use an array to build the result to achiev similar ordering result = [] inResult = {} compact.each do |e| if e.is_a? RGen::MetamodelBuilder::MMBase ((o=e.send(m)).is_a?(Array) ? o : [o] ).each do |v| next if inResult[v.object_id] inResult[v.object_id] = true result << v end else raise StandardError.new("Trying to call a method on an array element not a RGen MMBase") end end result.compact end end rgen-0.8.0/lib/rgen/transformer.rb0000644000004100000410000005066612642233643017114 0ustar www-datawww-datarequire '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 endrgen-0.8.0/lib/metamodels/0000755000004100000410000000000012642233643015407 5ustar www-datawww-datargen-0.8.0/lib/metamodels/uml13_metamodel.rb0000644000004100000410000004545112642233643020735 0ustar www-datawww-datarequire 'rgen/metamodel_builder' module UML13 extend RGen::MetamodelBuilder::ModuleExtension include RGen::MetamodelBuilder::DataTypes AggregationKind = Enum.new(:name => "AggregationKind", :literals =>[ :none, :aggregate, :composite ]) ChangeableKind = Enum.new(:name => "ChangeableKind", :literals =>[ :changeable, :frozen, :addOnly ]) OperationDirectionKind = Enum.new(:name => "OperationDirectionKind", :literals =>[ ]) ParameterDirectionKind = Enum.new(:name => "ParameterDirectionKind", :literals =>[ :in, :inout, :out, :return ]) MessageDirectionKind = Enum.new(:name => "MessageDirectionKind", :literals =>[ ]) ScopeKind = Enum.new(:name => "ScopeKind", :literals =>[ :instance, :classifier ]) VisibilityKind = Enum.new(:name => "VisibilityKind", :literals =>[ :public, :protected, :private ]) PseudostateKind = Enum.new(:name => "PseudostateKind", :literals =>[ :initial, :deepHistory, :shallowHistory, :join, :fork, :branch, :junction, :final ]) CallConcurrencyKind = Enum.new(:name => "CallConcurrencyKind", :literals =>[ :sequential, :guarded, :concurrent ]) OrderingKind = Enum.new(:name => "OrderingKind", :literals =>[ :unordered, :ordered, :sorted ]) class Element < RGen::MetamodelBuilder::MMBase end class ModelElement < Element has_attr 'name', String has_attr 'visibility', UML13::VisibilityKind, :defaultValueLiteral => "public" has_attr 'isSpecification', Boolean end class Namespace < ModelElement end class GeneralizableElement < ModelElement has_attr 'isRoot', Boolean has_attr 'isLeaf', Boolean has_attr 'isAbstract', Boolean end class Classifier < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Namespace) end class Class < Classifier has_attr 'isActive', Boolean end class DataType < Classifier end class Feature < ModelElement has_attr 'ownerScope', UML13::ScopeKind, :defaultValueLiteral => "instance" end class StructuralFeature < Feature has_attr 'changeability', UML13::ChangeableKind, :defaultValueLiteral => "changeable" has_attr 'targetScope', UML13::ScopeKind, :defaultValueLiteral => "instance" end class AssociationEnd < ModelElement has_attr 'isNavigable', Boolean, :defaultValueLiteral => "false" has_attr 'ordering', UML13::OrderingKind, :defaultValueLiteral => "unordered" has_attr 'aggregation', UML13::AggregationKind, :defaultValueLiteral => "none" has_attr 'targetScope', UML13::ScopeKind, :defaultValueLiteral => "instance" has_attr 'changeability', UML13::ChangeableKind, :defaultValueLiteral => "changeable" end class Interface < Classifier end class Constraint < ModelElement end class Relationship < ModelElement end class Association < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Relationship) end class Attribute < StructuralFeature end class BehavioralFeature < Feature has_attr 'isQuery', Boolean end class Operation < BehavioralFeature has_attr 'concurrency', UML13::CallConcurrencyKind, :defaultValueLiteral => "sequential" has_attr 'isRoot', Boolean has_attr 'isLeaf', Boolean has_attr 'isAbstract', Boolean end class Parameter < ModelElement has_attr 'kind', UML13::ParameterDirectionKind, :defaultValueLiteral => "inout" end class Method < BehavioralFeature end class Generalization < Relationship has_attr 'discriminator', String end class AssociationClass < RGen::MetamodelBuilder::MMMultiple(Class, Association) end class Dependency < Relationship end class Abstraction < Dependency end class PresentationElement < Element end class Usage < Dependency end class Binding < Dependency end class Component < Classifier end class Node < Classifier end class Permission < Dependency end class Comment < ModelElement has_attr 'body', String end class Flow < Relationship end class TemplateParameter < RGen::MetamodelBuilder::MMBase end class ElementResidence < RGen::MetamodelBuilder::MMBase has_attr 'visibility', UML13::VisibilityKind, :defaultValueLiteral => "public" end class Multiplicity < RGen::MetamodelBuilder::MMBase end class Expression < RGen::MetamodelBuilder::MMBase has_attr 'language', String has_attr 'body', String end class ObjectSetExpression < Expression end class TimeExpression < Expression end class BooleanExpression < Expression end class ActionExpression < Expression end class MultiplicityRange < RGen::MetamodelBuilder::MMBase has_attr 'lower', String has_attr 'upper', String end class Structure < DataType end class Primitive < DataType end class Enumeration < DataType end class EnumerationLiteral < RGen::MetamodelBuilder::MMBase has_attr 'name', String end class ProgrammingLanguageType < DataType end class IterationExpression < Expression end class TypeExpression < Expression end class ArgListsExpression < Expression end class MappingExpression < Expression end class ProcedureExpression < Expression end class Stereotype < GeneralizableElement has_attr 'icon', String has_attr 'baseClass', String end class TaggedValue < Element has_attr 'tag', String has_attr 'value', String end class UseCase < Classifier end class Actor < Classifier end class Instance < ModelElement end class UseCaseInstance < Instance end class Extend < Relationship end class Include < Relationship end class ExtensionPoint < ModelElement has_attr 'location', String end class StateMachine < ModelElement end class Event < ModelElement end class StateVertex < ModelElement end class State < StateVertex end class TimeEvent < Event end class CallEvent < Event end class SignalEvent < Event end class Transition < ModelElement end class CompositeState < State has_attr 'isConcurrent', Boolean end class ChangeEvent < Event end class Guard < ModelElement end class Pseudostate < StateVertex has_attr 'kind', UML13::PseudostateKind, :defaultValueLiteral => "initial" end class SimpleState < State end class SubmachineState < CompositeState end class SynchState < StateVertex has_attr 'bound', Integer end class StubState < StateVertex has_attr 'referenceState', String end class FinalState < State end class Collaboration < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Namespace) end class ClassifierRole < Classifier end class AssociationRole < Association end class AssociationEndRole < AssociationEnd end class Message < ModelElement end class Interaction < ModelElement end class Signal < Classifier end class Action < ModelElement has_attr 'isAsynchronous', Boolean end class CreateAction < Action end class DestroyAction < Action end class UninterpretedAction < Action end class AttributeLink < ModelElement end class Object < Instance end class Link < ModelElement end class LinkObject < RGen::MetamodelBuilder::MMMultiple(Object, Link) end class DataValue < Instance end class CallAction < Action end class SendAction < Action end class ActionSequence < Action end class Argument < ModelElement end class Reception < BehavioralFeature has_attr 'isPolymorphic', Boolean has_attr 'specification', String end class LinkEnd < ModelElement end class Call < RGen::MetamodelBuilder::MMBase end class ReturnAction < Action end class TerminateAction < Action end class Stimulus < ModelElement end class ActionInstance < RGen::MetamodelBuilder::MMBase end class Exception < Signal end class AssignmentAction < Action end class ComponentInstance < Instance end class NodeInstance < Instance end class ActivityGraph < StateMachine end class Partition < ModelElement end class SubactivityState < SubmachineState has_attr 'isDynamic', Boolean end class ActionState < SimpleState has_attr 'isDynamic', Boolean end class CallState < ActionState end class ObjectFlowState < SimpleState has_attr 'isSynch', Boolean end class ClassifierInState < Classifier end class Package < RGen::MetamodelBuilder::MMMultiple(GeneralizableElement, Namespace) end class Model < Package end class Subsystem < RGen::MetamodelBuilder::MMMultiple(Classifier, Package) has_attr 'isInstantiable', Boolean end class ElementImport < RGen::MetamodelBuilder::MMBase has_attr 'visibility', UML13::VisibilityKind, :defaultValueLiteral => "public" has_attr 'alias', String end class DiagramElement < PresentationElement has_attr 'geometry', String has_attr 'style', String end class Diagram < PresentationElement has_attr 'name', String has_attr 'toolName', String has_attr 'diagramType', String has_attr 'style', String end end UML13::Classifier.many_to_many 'participant', UML13::AssociationEnd, 'specification' UML13::Classifier.one_to_many 'associationEnd', UML13::AssociationEnd, 'type' UML13::Classifier.contains_many 'feature', UML13::Feature, 'owner' UML13::StructuralFeature.contains_one_uni 'multiplicity', UML13::Multiplicity UML13::StructuralFeature.has_one 'type', UML13::Classifier, :lowerBound => 1 UML13::Namespace.contains_many 'ownedElement', UML13::ModelElement, 'namespace' UML13::AssociationEnd.contains_one_uni 'multiplicity', UML13::Multiplicity UML13::AssociationEnd.contains_many 'qualifier', UML13::Attribute, 'associationEnd' UML13::Association.contains_many 'connection', UML13::AssociationEnd, 'association', :lowerBound => 2 UML13::Constraint.contains_one_uni 'body', UML13::BooleanExpression UML13::Constraint.many_to_many 'constrainedElement', UML13::ModelElement, 'constraint', :lowerBound => 1 UML13::GeneralizableElement.one_to_many 'specialization', UML13::Generalization, 'parent' UML13::GeneralizableElement.one_to_many 'generalization', UML13::Generalization, 'child' UML13::Attribute.contains_one_uni 'initialValue', UML13::Expression UML13::Operation.one_to_many 'occurrence', UML13::CallEvent, 'operation' UML13::Operation.one_to_many 'method', UML13::Method, 'specification' UML13::Parameter.contains_one_uni 'defaultValue', UML13::Expression UML13::Parameter.many_to_many 'state', UML13::ObjectFlowState, 'parameter' UML13::Parameter.has_one 'type', UML13::Classifier, :lowerBound => 1 UML13::Method.contains_one_uni 'body', UML13::ProcedureExpression UML13::BehavioralFeature.many_to_many 'raisedSignal', UML13::Signal, 'context' UML13::BehavioralFeature.contains_many_uni 'parameter', UML13::Parameter UML13::ModelElement.one_to_many 'behavior', UML13::StateMachine, 'context' UML13::ModelElement.many_to_one 'stereotype', UML13::Stereotype, 'extendedElement' UML13::ModelElement.one_to_many 'elementResidence', UML13::ElementResidence, 'resident' UML13::ModelElement.many_to_many 'sourceFlow', UML13::Flow, 'source' UML13::ModelElement.many_to_many 'targetFlow', UML13::Flow, 'target' UML13::ModelElement.many_to_many 'presentation', UML13::PresentationElement, 'subject' UML13::ModelElement.many_to_many 'supplierDependency', UML13::Dependency, 'supplier', :lowerBound => 1 UML13::ModelElement.contains_many 'taggedValue', UML13::TaggedValue, 'modelElement' UML13::ModelElement.contains_many_uni 'templateParameter', UML13::TemplateParameter UML13::ModelElement.many_to_many 'clientDependency', UML13::Dependency, 'client', :lowerBound => 1 UML13::ModelElement.many_to_many 'comment', UML13::Comment, 'annotatedElement' UML13::ModelElement.one_to_many 'elementImport', UML13::ElementImport, 'modelElement' UML13::Abstraction.contains_one_uni 'mapping', UML13::MappingExpression UML13::Binding.has_many 'argument', UML13::ModelElement, :lowerBound => 1 UML13::Component.contains_many 'residentElement', UML13::ElementResidence, 'implementationLocation' UML13::Component.many_to_many 'deploymentLocation', UML13::Node, 'resident' UML13::TemplateParameter.has_one 'modelElement', UML13::ModelElement UML13::TemplateParameter.has_one 'defaultElement', UML13::ModelElement UML13::Multiplicity.contains_many_uni 'range', UML13::MultiplicityRange, :lowerBound => 1 UML13::Enumeration.contains_many_uni 'literal', UML13::EnumerationLiteral, :lowerBound => 1 UML13::ProgrammingLanguageType.contains_one_uni 'type', UML13::TypeExpression UML13::Stereotype.has_many 'requiredTag', UML13::TaggedValue UML13::UseCase.has_many 'extensionPoint', UML13::ExtensionPoint UML13::UseCase.one_to_many 'include', UML13::Include, 'base' UML13::UseCase.one_to_many 'extend', UML13::Extend, 'extension' UML13::Extend.contains_one_uni 'condition', UML13::BooleanExpression UML13::Extend.has_many 'extensionPoint', UML13::ExtensionPoint, :lowerBound => 1 UML13::Extend.has_one 'base', UML13::UseCase, :lowerBound => 1 UML13::Include.has_one 'addition', UML13::UseCase, :lowerBound => 1 UML13::StateMachine.contains_many_uni 'transitions', UML13::Transition UML13::StateMachine.contains_one_uni 'top', UML13::State, :lowerBound => 1 UML13::Event.contains_many_uni 'parameters', UML13::Parameter UML13::State.contains_one_uni 'doActivity', UML13::Action UML13::State.contains_many_uni 'internalTransition', UML13::Transition UML13::State.has_many 'deferrableEvent', UML13::Event UML13::State.contains_one_uni 'exit', UML13::Action UML13::State.contains_one_uni 'entry', UML13::Action UML13::TimeEvent.contains_one_uni 'when', UML13::TimeExpression UML13::SignalEvent.many_to_one 'signal', UML13::Signal, 'occurrence', :lowerBound => 1 UML13::Transition.many_to_one 'target', UML13::StateVertex, 'incoming', :lowerBound => 1 UML13::Transition.many_to_one 'source', UML13::StateVertex, 'outgoing', :lowerBound => 1 UML13::Transition.has_one 'trigger', UML13::Event UML13::Transition.contains_one_uni 'effect', UML13::Action UML13::Transition.contains_one_uni 'guard', UML13::Guard UML13::CompositeState.contains_many 'subvertex', UML13::StateVertex, 'container', :lowerBound => 1 UML13::ChangeEvent.contains_one_uni 'changeExpression', UML13::BooleanExpression UML13::Guard.contains_one_uni 'expression', UML13::BooleanExpression UML13::SubmachineState.has_one 'submachine', UML13::StateMachine, :lowerBound => 1 UML13::Collaboration.has_one 'representedOperation', UML13::Operation UML13::Collaboration.has_one 'representedClassifier', UML13::Classifier UML13::Collaboration.has_many 'constrainingElement', UML13::ModelElement UML13::Collaboration.contains_many 'interaction', UML13::Interaction, 'context' UML13::ClassifierRole.contains_one_uni 'multiplicity', UML13::Multiplicity UML13::ClassifierRole.has_many 'availableContents', UML13::ModelElement UML13::ClassifierRole.has_many 'availableFeature', UML13::Feature UML13::ClassifierRole.has_one 'base', UML13::Classifier, :lowerBound => 1 UML13::AssociationRole.contains_one_uni 'multiplicity', UML13::Multiplicity UML13::AssociationRole.has_one 'base', UML13::Association UML13::AssociationEndRole.has_many 'availableQualifier', UML13::Attribute UML13::AssociationEndRole.has_one 'base', UML13::AssociationEnd UML13::Message.has_one 'action', UML13::Action, :lowerBound => 1 UML13::Message.has_one 'communicationConnection', UML13::AssociationRole UML13::Message.has_many 'predecessor', UML13::Message UML13::Message.has_one 'receiver', UML13::ClassifierRole, :lowerBound => 1 UML13::Message.has_one 'sender', UML13::ClassifierRole, :lowerBound => 1 UML13::Message.has_one 'activator', UML13::Message UML13::Interaction.contains_many 'message', UML13::Message, 'interaction', :lowerBound => 1 UML13::Interaction.contains_many_uni 'link', UML13::Link UML13::Instance.contains_many_uni 'slot', UML13::AttributeLink UML13::Instance.one_to_many 'linkEnd', UML13::LinkEnd, 'instance' UML13::Instance.has_many 'classifier', UML13::Classifier, :lowerBound => 1 UML13::Signal.one_to_many 'reception', UML13::Reception, 'signal' UML13::CreateAction.has_one 'instantiation', UML13::Classifier, :lowerBound => 1 UML13::Action.contains_one_uni 'recurrence', UML13::IterationExpression UML13::Action.contains_one_uni 'target', UML13::ObjectSetExpression UML13::Action.contains_one_uni 'script', UML13::ActionExpression UML13::Action.contains_many_uni 'actualArgument', UML13::Argument UML13::AttributeLink.has_one 'value', UML13::Instance, :lowerBound => 1 UML13::AttributeLink.has_one 'attribute', UML13::Attribute, :lowerBound => 1 UML13::CallAction.has_one 'operation', UML13::Operation, :lowerBound => 1 UML13::SendAction.has_one 'signal', UML13::Signal, :lowerBound => 1 UML13::ActionSequence.contains_many_uni 'action', UML13::Action UML13::Argument.contains_one_uni 'value', UML13::Expression UML13::Link.contains_many_uni 'connection', UML13::LinkEnd, :lowerBound => 2 UML13::Link.has_one 'association', UML13::Association, :lowerBound => 1 UML13::LinkEnd.has_one 'associationEnd', UML13::AssociationEnd, :lowerBound => 1 UML13::LinkEnd.has_one 'participant', UML13::Instance, :lowerBound => 1 UML13::Stimulus.has_one 'dispatchAction', UML13::Action, :lowerBound => 1 UML13::Stimulus.has_one 'communicationLink', UML13::Link UML13::Stimulus.has_one 'receiver', UML13::Instance, :lowerBound => 1 UML13::Stimulus.has_one 'sender', UML13::Instance, :lowerBound => 1 UML13::Stimulus.has_many 'argument', UML13::Instance UML13::ComponentInstance.has_many 'resident', UML13::Instance UML13::NodeInstance.has_many 'resident', UML13::ComponentInstance UML13::ActivityGraph.contains_many_uni 'partition', UML13::Partition UML13::Partition.has_many 'contents', UML13::ModelElement UML13::SubactivityState.contains_one_uni 'dynamicArguments', UML13::ArgListsExpression UML13::ObjectFlowState.has_one 'type', UML13::Classifier, :lowerBound => 1 UML13::ObjectFlowState.has_one 'available', UML13::Parameter, :lowerBound => 1 UML13::ClassifierInState.has_one 'type', UML13::Classifier, :lowerBound => 1 UML13::ClassifierInState.has_many 'inState', UML13::State UML13::ActionState.contains_one_uni 'dynamicArguments', UML13::ArgListsExpression UML13::Package.contains_many 'importedElement', UML13::ElementImport, 'package' UML13::Diagram.contains_many 'element', UML13::DiagramElement, 'diagram' UML13::Diagram.has_one 'owner', UML13::ModelElement, :lowerBound => 1 rgen-0.8.0/lib/metamodels/uml13_metamodel_ext.rb0000644000004100000410000000116412642233643021606 0ustar www-datawww-data# Some handy extensions to the UML13 metamodel # module UML13 module AssociationEnd::ClassModule def otherEnd association.connection.find{|c| c != self} end end module Classifier::ClassModule def localCompositeEnd associationEnd.select{|e| e.aggregation == :composite} end def remoteCompositeEnd associationEnd.otherEnd.select{|e| e.aggregation == :composite} end def localNavigableEnd associationEnd.select{|e| e.isNavigable} end def remoteNavigableEnd associationEnd.otherEnd.select{|e| e.isNavigable} end end end rgen-0.8.0/lib/mmgen/0000755000004100000410000000000012642233643014360 5ustar www-datawww-datargen-0.8.0/lib/mmgen/templates/0000755000004100000410000000000012642233643016356 5ustar www-datawww-datargen-0.8.0/lib/mmgen/templates/metamodel_generator.tpl0000644000004100000410000001546112642233643023123 0ustar www-datawww-data <% define 'GenerateMetamodel', :for => EPackage do |filename| %> <% file filename do %> require 'rgen/metamodel_builder' <%nl%> <% if needClassReorder? %> <% expand 'GeneratePackagesOnly' %> <% expand 'GenerateClassesReordered' %> <% else %> <% expand 'GeneratePackage' %> <% end %> <%nl%> <% expand 'GenerateAssocs' %> <% end %> <% end %> <% define 'GeneratePackage', :for => EPackage do %> module <%= moduleName %><% iinc %> extend RGen::MetamodelBuilder::ModuleExtension include RGen::MetamodelBuilder::DataTypes <% expand 'annotations::Annotations' %> <%nl%> <% expand 'EnumTypes' %> <% for c in ownClasses %><%nl%> <% expand 'ClassHeader', this, :for => c %><%iinc%> <% if c.abstract %>abstract<% end %> <% expand 'annotations::Annotations', :for => c %> <% expand 'Attribute', this, :foreach => c.eAttributes %> <%idec%> end <% end %><%nl%> <% for p in eSubpackages %> <%nl%><% expand 'GeneratePackage', :for => p %> <% end %><%idec%> end <% end %> <% define 'GenerateClassesReordered', :for => EPackage do %> <% for c in allClassesSorted %><%nl%> <% expand 'ClassHeaderFullyQualified', this, :for => c %><%iinc%> <% if c.abstract %>abstract<% end %> <% expand 'annotations::Annotations', :for => c %> <% expand 'Attribute', this, :foreach => c.eAttributes %> <%idec%> end <% end %><%nl%> <% end %> <% define 'GeneratePackagesOnly', :for => EPackage do %> module <%= moduleName %><% iinc %> extend RGen::MetamodelBuilder::ModuleExtension include RGen::MetamodelBuilder::DataTypes <% expand 'annotations::Annotations' %> <%nl%> <% expand 'EnumTypes' %> <% for p in eSubpackages %> <%nl%><% expand 'GeneratePackagesOnly', :for => p %> <% end %><%idec%> end <% end %> <% define 'Attribute', :for => EAttribute do |rootp| %> <% if upperBound == 1%>has_attr<% else %>has_many_attr<% end %> '<%= name %>', <%nows%> <% if eType.is_a?(EEnum) %><%nows%> <%= eType.qualifiedClassifierName(rootp) %><%nows%> <% else %><%nows%> <%= eType && eType.instanceClass.to_s %><%nows%> <% end %><%nows%> <% for p in RGen::MetamodelBuilder::Intermediate::Attribute.properties %> <% unless p == :name || (p == :upperBound && (upperBound == 1 || upperBound == -1)) || RGen::MetamodelBuilder::Intermediate::Attribute.default_value(p) == getGeneric(p) %> , :<%=p%> => <%nows%> <% if getGeneric(p).is_a?(String) %> "<%= getGeneric(p) %>"<%nows%> <% elsif getGeneric(p).is_a?(Symbol) %> :<%= getGeneric(p) %><%nows%> <% else %> <%= getGeneric(p) %><%nows%> <% end %> <% end %> <% end %> <%ws%><% expand 'annotations::Annotations' %><%nl%> <% end %> <% define 'EnumTypes', :for => EPackage do %> <% for enum in eClassifiers.select{|c| c.is_a?(EEnum)} %> <%= enum.name %> = Enum.new(:name => '<%= enum.name %>', :literals =>[ <%nows%> <%= enum.eLiterals.collect { |lit| ":"+(lit.name =~ /^\d|\W/ ? "'"+lit.name+"'" : lit.name) }.join(', ') %> ]) <% end %> <% end %> <% define 'GenerateAssocs', :for => EPackage do %> <% refDone = {} %> <% for ref in eAllClassifiers.select{|c| c.is_a?(EClass)}.eReferences %> <% if !refDone[ref] && ref.eOpposite %> <% ref = ref.eOpposite if ref.eOpposite.containment %> <% refDone[ref] = refDone[ref.eOpposite] = true %> <% if !ref.many && !ref.eOpposite.many %> <% if ref.containment %> <% expand 'Reference', "contains_one", this, :for => ref %> <% else %> <% expand 'Reference', "one_to_one", this, :for => ref %> <% end %> <% elsif !ref.many && ref.eOpposite.many %> <% expand 'Reference', "many_to_one", this, :for => ref %> <% elsif ref.many && !ref.eOpposite.many %> <% if ref.containment %> <% expand 'Reference', "contains_many", this, :for => ref %> <% else %> <% expand 'Reference', "one_to_many", this, :for => ref %> <% end %> <% elsif ref.many && ref.eOpposite.many %> <% expand 'Reference', "many_to_many", this, :for => ref %> <% end %> <% expand 'annotations::Annotations', :for => ref %><%nl%> <% elsif !refDone[ref] %> <% refDone[ref] = true %> <% if !ref.many %> <% if ref.containment %> <% expand 'Reference', "contains_one_uni", this, :for => ref %> <% else %> <% expand 'Reference', "has_one", this, :for => ref %> <% end %> <% elsif ref.many %> <% if ref.containment %> <% expand 'Reference', "contains_many_uni", this, :for => ref %> <% else %> <% expand 'Reference', "has_many", this, :for => ref %> <% end %> <% end %> <% expand 'annotations::Annotations', :for => ref %><%nl%> <% end %> <% end %> <% end %> <% define 'Reference', :for => EReference do |cmd, rootpackage| %> <%= eContainingClass.qualifiedClassifierName(rootpackage) %>.<%= cmd %> '<%= name %>', <%= eType && eType.qualifiedClassifierName(rootpackage) %><%nows%> <% if eOpposite %><%nows%> , '<%= eOpposite.name%>'<%nows%> <% end %><%nows%> <% pset = RGen::MetamodelBuilder::Intermediate::Reference.properties.reject{|p| p == :name || p == :upperBound || p == :containment} %> <% for p in pset.reject{|p| RGen::MetamodelBuilder::Intermediate::Reference.default_value(p) == getGeneric(p)} %> , :<%=p%> => <%=getGeneric(p)%><%nows%> <% end %> <% if eOpposite %> <% for p in pset.reject{|p| RGen::MetamodelBuilder::Intermediate::Reference.default_value(p) == eOpposite.getGeneric(p)} %> , :opposite_<%=p%> => <%=eOpposite.getGeneric(p)%><%nows%> <% end %> <% end %><%ws%> <% end %> <% define 'ClassHeader', :for => EClass do |rootp| %> class <%= classifierName %> < <% nows %> <% if eSuperTypes.size > 1 %><% nows %> RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.qualifiedClassifierNameIfRequired(rootp)}.join(', ') %>) <% elsif eSuperTypes.size > 0 %><% nows %> <%= eSuperTypes.first.qualifiedClassifierNameIfRequired(rootp) %> <% else %><% nows %> RGen::MetamodelBuilder::MMBase <% end %> <% end %> <% define 'ClassHeaderFullyQualified', :for => EClass do |rootp| %> class <%= qualifiedClassifierName(rootp) %> < <% nows %> <% if eSuperTypes.size > 1 %><% nows %> RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.qualifiedClassifierName(rootp)}.join(', ') %>) <% elsif eSuperTypes.size > 0 %><% nows %> <%= eSuperTypes.first.qualifiedClassifierName(rootp) %> <% else %><% nows %> RGen::MetamodelBuilder::MMBase <% end %> <% end %> rgen-0.8.0/lib/mmgen/templates/annotations.tpl0000644000004100000410000000235312642233643021437 0ustar www-datawww-data<% define 'Annotations', :for => EPackage do %> <% for a in eAnnotations %> annotation <% expand 'AnnotationArgs', :for => a %> <% end %> <% end %> <% define 'Annotations', :for => EClass do %> <% for a in eAnnotations %> annotation <% expand 'AnnotationArgs', :for => a %> <% end %> <% end %> <% define 'Annotations', :for => EStructuralFeature do %> <% oppositeAnnotations = (this.respond_to?(:eOpposite) && eOpposite && eOpposite.eAnnotations) || [] %> <% if eAnnotations.size > 0 || oppositeAnnotations.size > 0 %> do<%iinc%> <% for a in eAnnotations %> annotation <% expand 'AnnotationArgs', :for => a %> <% end %> <% for a in oppositeAnnotations %> opposite_annotation <% expand 'AnnotationArgs', :for => a %> <% end %><%idec%> end<%nows%> <% end %> <% end %> <% define 'AnnotationArgs', :for => EAnnotation do %> <% if source.nil? %> <% expand 'Details' %> <% else %> :source => "<%= source.to_s %>", :details => {<% expand 'Details' %>}<%nows%> <% end %> <% end %> <% define 'Details', :for => EAnnotation do %> <%= details.sort{|a,b| a.key<=>b.key}.collect{ |d| "\'" + d.key + "\' => \'"+ (d.value || "").gsub('\'','\\\'').to_s + "\'"}.join(', ') %><%nows%> <% end %>rgen-0.8.0/lib/mmgen/mm_ext/0000755000004100000410000000000012642233643015651 5ustar www-datawww-datargen-0.8.0/lib/mmgen/mm_ext/ecore_mmgen_ext.rb0000644000004100000410000000473312642233643021345 0ustar www-datawww-datarequire 'rgen/util/name_helper' module RGen module ECore module EPackage::ClassModule include RGen::Util::NameHelper def moduleName firstToUpper(name) end def qualifiedModuleName(rootPackage) return moduleName unless eSuperPackage and self != rootPackage eSuperPackage.qualifiedModuleName(rootPackage) + "::" + moduleName end def ancestorPackages return [] unless eSuperPackage [eSuperPackage] + eSuperPackage.ancestorPackages end def ownClasses eClassifiers.select{|c| c.is_a?(EClass)} end def classesInGenerationOrdering ownClasses + eSubpackages.collect{|s| s.classesInGenerationOrdering}.flatten end def needClassReorder? classesInGenerationOrdering != inheritanceOrderClasses(classesInGenerationOrdering) end def allClassesSorted inheritanceOrderClasses(classesInGenerationOrdering) end def inheritanceOrderClasses(cls) sortArray = cls.dup i1 = 0 while i1 < sortArray.size-1 again = false for i2 in i1+1..sortArray.size-1 e2 = sortArray[i2] if sortArray[i1].eSuperTypes.include?(e2) sortArray.delete(e2) sortArray.insert(i1,e2) again = true break end end i1 += 1 unless again end sortArray end end module EClassifier::ClassModule include RGen::Util::NameHelper def classifierName firstToUpper(name) end def qualifiedClassifierName(rootPackage) (ePackage ? ePackage.qualifiedModuleName(rootPackage) + "::" : "") + classifierName end def ancestorPackages return [] unless ePackage [ePackage] + ePackage.ancestorPackages end def qualifiedClassifierNameIfRequired(package) if ePackage != package commonSuper = (package.ancestorPackages & ancestorPackages).first qualifiedClassifierName(commonSuper) else classifierName end end end module EAttribute::ClassModule def RubyType typeMap = {'float' => 'Float', 'int' => 'Integer'} (self.getType && typeMap[self.getType.downcase]) || 'String' end end end end rgen-0.8.0/lib/mmgen/metamodel_generator.rb0000644000004100000410000000104412642233643020721 0ustar www-datawww-datarequire '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 endrgen-0.8.0/lib/mmgen/mmgen.rb0000644000004100000410000000134612642233643016014 0ustar www-datawww-data$:.unshift File.join(File.dirname(__FILE__),"..") require 'ea/xmi_ecore_instantiator' require 'mmgen/metamodel_generator' include MMGen::MetamodelGenerator unless ARGV.length >= 2 puts "Usage: mmgen.rb ()*" exit else file_name = ARGV.shift root_package_name = ARGV.shift modules = ARGV out_file = file_name.gsub(/\.\w+$/,'.rb') puts out_file end env = RGen::Environment.new File.open(file_name) { |f| puts "instantiating ..." XMIECoreInstantiator.new.instantiateECoreModel(env, f.read) } rootPackage = env.find(:class => RGen::ECore::EPackage, :name => root_package_name).first puts "generating ..." generateMetamodel(rootPackage, out_file, modules) rgen-0.8.0/metadata.yml0000644000004100000410000002335612642233644015024 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: rgen version: !ruby/object:Gem::Version version: 0.8.0 prerelease: platform: ruby authors: - Martin Thiede autorequire: bindir: bin cert_chain: [] date: 2015-11-15 00:00:00.000000000 Z dependencies: [] description: RGen is a framework for Model Driven Software Development (MDSD) in Ruby. This means that it helps you build Metamodels, instantiate Models, modify and transform Models and finally generate arbitrary textual content from it. email: martin dot thiede at gmx de executables: [] extensions: [] extra_rdoc_files: - README.rdoc - CHANGELOG - MIT-LICENSE files: - lib/ea_support/ea_support.rb - lib/ea_support/id_store.rb - lib/ea_support/uml13_ea_metamodel.rb - lib/ea_support/uml13_ea_metamodel_ext.rb - lib/ea_support/uml13_ea_metamodel_generator.rb - lib/ea_support/uml13_ea_to_uml13.rb - lib/ea_support/uml13_to_uml13_ea.rb - lib/metamodels/uml13_metamodel.rb - lib/metamodels/uml13_metamodel_ext.rb - lib/mmgen/metamodel_generator.rb - lib/mmgen/mm_ext/ecore_mmgen_ext.rb - lib/mmgen/mmgen.rb - lib/mmgen/templates/annotations.tpl - lib/mmgen/templates/metamodel_generator.tpl - lib/rgen/array_extensions.rb - lib/rgen/ecore/ecore.rb - lib/rgen/ecore/ecore_builder_methods.rb - lib/rgen/ecore/ecore_ext.rb - lib/rgen/ecore/ecore_interface.rb - lib/rgen/ecore/ecore_to_ruby.rb - lib/rgen/ecore/ruby_to_ecore.rb - lib/rgen/environment.rb - lib/rgen/fragment/dump_file_cache.rb - lib/rgen/fragment/fragmented_model.rb - lib/rgen/fragment/model_fragment.rb - lib/rgen/instantiator/abstract_instantiator.rb - lib/rgen/instantiator/abstract_xml_instantiator.rb - lib/rgen/instantiator/default_xml_instantiator.rb - lib/rgen/instantiator/ecore_xml_instantiator.rb - lib/rgen/instantiator/json_instantiator.rb - lib/rgen/instantiator/json_parser.rb - lib/rgen/instantiator/json_parser.y - lib/rgen/instantiator/nodebased_xml_instantiator.rb - lib/rgen/instantiator/qualified_name_resolver.rb - lib/rgen/instantiator/reference_resolver.rb - lib/rgen/instantiator/resolution_helper.rb - lib/rgen/instantiator/xmi11_instantiator.rb - lib/rgen/metamodel_builder.rb - lib/rgen/metamodel_builder/builder_extensions.rb - lib/rgen/metamodel_builder/builder_runtime.rb - lib/rgen/metamodel_builder/constant_order_helper.rb - lib/rgen/metamodel_builder/data_types.rb - lib/rgen/metamodel_builder/intermediate/annotation.rb - lib/rgen/metamodel_builder/intermediate/feature.rb - lib/rgen/metamodel_builder/mm_multiple.rb - lib/rgen/metamodel_builder/module_extension.rb - lib/rgen/model_builder.rb - lib/rgen/model_builder/builder_context.rb - lib/rgen/model_builder/model_serializer.rb - lib/rgen/model_builder/reference_resolver.rb - lib/rgen/serializer/json_serializer.rb - lib/rgen/serializer/opposite_reference_filter.rb - lib/rgen/serializer/qualified_name_provider.rb - lib/rgen/serializer/xmi11_serializer.rb - lib/rgen/serializer/xmi20_serializer.rb - lib/rgen/serializer/xml_serializer.rb - lib/rgen/template_language.rb - lib/rgen/template_language/directory_template_container.rb - lib/rgen/template_language/output_handler.rb - lib/rgen/template_language/template_container.rb - lib/rgen/template_language/template_helper.rb - lib/rgen/transformer.rb - lib/rgen/util/auto_class_creator.rb - lib/rgen/util/cached_glob.rb - lib/rgen/util/file_cache_map.rb - lib/rgen/util/file_change_detector.rb - lib/rgen/util/method_delegation.rb - lib/rgen/util/model_comparator.rb - lib/rgen/util/model_comparator_base.rb - lib/rgen/util/model_dumper.rb - lib/rgen/util/name_helper.rb - lib/rgen/util/pattern_matcher.rb - lib/transformers/ecore_to_uml13.rb - lib/transformers/uml13_to_ecore.rb - test/array_extensions_test.rb - test/coverage/assets/0.10.0/application.css - test/coverage/assets/0.10.0/application.js - test/coverage/assets/0.10.0/colorbox/border.png - test/coverage/assets/0.10.0/colorbox/controls.png - test/coverage/assets/0.10.0/colorbox/loading.gif - test/coverage/assets/0.10.0/colorbox/loading_background.png - test/coverage/assets/0.10.0/favicon_green.png - test/coverage/assets/0.10.0/favicon_red.png - test/coverage/assets/0.10.0/favicon_yellow.png - test/coverage/assets/0.10.0/loading.gif - test/coverage/assets/0.10.0/magnify.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png - test/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png - test/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png - test/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png - test/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png - test/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png - test/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png - test/coverage/index.html - test/ea_instantiator_test.rb - test/ea_serializer_test.rb - test/ecore_self_test.rb - test/ecore_to_ruby_test.rb - test/environment_test.rb - test/json_test.rb - test/metamodel_builder_test.rb - test/metamodel_from_ecore_test.rb - test/metamodel_order_test.rb - test/metamodel_roundtrip_test.rb - test/metamodel_roundtrip_test/TestModel.rb - test/metamodel_roundtrip_test/TestModel_Regenerated.rb - test/metamodel_roundtrip_test/houseMetamodel.ecore - test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore - test/metamodel_roundtrip_test/houseMetamodel_from_ecore.rb - test/metamodel_roundtrip_test/using_builtin_types.ecore - test/metamodel_roundtrip_test/using_builtin_types_serialized.ecore - test/method_delegation_test.rb - test/model_builder/builder_context_test.rb - test/model_builder/builder_test.rb - test/model_builder/ecore_internal.rb - test/model_builder/ecore_original.rb - test/model_builder/ecore_original_regenerated.rb - test/model_builder/reference_resolver_test.rb - test/model_builder/serializer_test.rb - test/model_builder/statemachine_metamodel.rb - test/model_builder/test_model/statemachine1.rb - test/model_builder_test.rb - test/model_fragment_test.rb - test/output_handler_test.rb - test/qualified_name_provider_test.rb - test/qualified_name_resolver_test.rb - test/reference_resolver_test.rb - test/rgen_test.rb - test/template_language_test.rb - test/template_language_test/expected_result1.txt - test/template_language_test/expected_result2.txt - test/template_language_test/expected_result3.txt - test/template_language_test/indentStringTestDefaultIndent.out - test/template_language_test/indentStringTestTabIndent.out - test/template_language_test/templates/callback_indent_test/a.tpl - test/template_language_test/templates/callback_indent_test/b.tpl - test/template_language_test/templates/code/array.tpl - test/template_language_test/templates/content/author.tpl - test/template_language_test/templates/content/chapter.tpl - test/template_language_test/templates/define_local_test/local.tpl - test/template_language_test/templates/define_local_test/test.tpl - test/template_language_test/templates/evaluate_test/test.tpl - test/template_language_test/templates/indent_nonl_at_eof_test/test.tpl - test/template_language_test/templates/indent_same_line_sub/test.tpl - test/template_language_test/templates/indent_string_test.tpl - test/template_language_test/templates/index/c/cmod.tpl - test/template_language_test/templates/index/chapter.tpl - test/template_language_test/templates/no_backslash_r_test.tpl - test/template_language_test/templates/no_indent_test/no_indent.tpl - test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl - test/template_language_test/templates/no_indent_test/test.tpl - test/template_language_test/templates/no_indent_test/test2.tpl - test/template_language_test/templates/no_indent_test/test3.tpl - test/template_language_test/templates/null_context_test.tpl - test/template_language_test/templates/root.tpl - test/template_language_test/templates/template_resolution_test/sub1.tpl - test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl - test/template_language_test/templates/template_resolution_test/test.tpl - test/template_language_test/testout.txt - test/testmodel/class_model_checker.rb - test/testmodel/ea_testmodel.eap - test/testmodel/ea_testmodel.xml - test/testmodel/ea_testmodel_partial.xml - test/testmodel/ea_testmodel_regenerated.xml - test/testmodel/ecore_model_checker.rb - test/testmodel/manual_testmodel.xml - test/testmodel/object_model_checker.rb - test/transformer_test.rb - test/util/file_cache_map_test.rb - test/util/file_cache_map_test/testdir/fileA - test/util/pattern_matcher_test.rb - test/util_test.rb - test/xml_instantiator_test.rb - test/xml_instantiator_test/simple_ecore_model_checker.rb - test/xml_instantiator_test/simple_xmi_ecore_instantiator.rb - test/xml_instantiator_test/simple_xmi_metamodel.rb - test/xml_instantiator_test/simple_xmi_to_ecore.rb - README.rdoc - CHANGELOG - MIT-LICENSE - Rakefile homepage: http://ruby-gen.org licenses: [] post_install_message: rdoc_options: - --main - README.rdoc - -x - test - -x - metamodels - -x - ea_support/uml13* require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rgen rubygems_version: 1.8.24 signing_key: specification_version: 3 summary: Ruby Modelling and Generator Framework test_files: [] rgen-0.8.0/test/0000755000004100000410000000000012642233644013467 5ustar www-datawww-datargen-0.8.0/test/reference_resolver_test.rb0000644000004100000410000001022412642233644020731 0ustar www-datawww-data$:.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 rgen-0.8.0/test/template_language_test.rb0000644000004100000410000001540012642233644020531 0ustar www-datawww-data$:.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 def test_indent_nonl_at_eof tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR) tc.load(TEMPLATES_DIR) assert_equal(" Sub\n", tc.expand('indent_nonl_at_eof_test/test::Test', :for => :dummy)) end def test_indent_same_line_sub tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new([MyMM, CCodeMM], OUTPUT_DIR) tc.load(TEMPLATES_DIR) assert_equal(" Start Sub2\r\n Sub\r\n", tc.expand('indent_same_line_sub/test::Test', :for => :dummy)) end end rgen-0.8.0/test/coverage/0000755000004100000410000000000012642233644015262 5ustar www-datawww-datargen-0.8.0/test/coverage/assets/0000755000004100000410000000000012642233643016563 5ustar www-datawww-datargen-0.8.0/test/coverage/assets/0.10.0/0000755000004100000410000000000012642233644017300 5ustar www-datawww-datargen-0.8.0/test/coverage/assets/0.10.0/favicon_red.png0000644000004100000410000000176112642233644022272 0ustar www-datawww-data‰PNG  IHDR(-StEXtSoftwareAdobe ImageReadyqÉe<fiTXtXML:com.adobe.xmp [æPLTE™ŸþDIDATxÚb`È À;BBKIEND®B`‚rgen-0.8.0/test/coverage/assets/0.10.0/application.js0000644000004100000410000065210012642233644022145 0ustar www-datawww-data/*! * jQuery JavaScript Library v1.6.2 * http://jquery.com/ * * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Thu Jun 30 14:16:56 2011 -0400 */ (function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j )}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(/"}while(x.length||y.length){var u=t().splice(0,1)[0];v+=l(w.substr(q,u.offset-q));q=u.offset;if(u.event=="start"){v+=r(u.node);s.push(u.node)}else{if(u.event=="stop"){var p=s.length;do{p--;var o=s[p];v+=("")}while(o!=u.node);s.splice(p,1);while(p'+l(K[0])+""}else{M+=l(K[0])}O=N.lR.lastIndex;K=N.lR.exec(L)}M+=l(L.substr(O,L.length-O));return M}function J(r,L){if(L.sL&&d[L.sL]){var K=f(L.sL,r);s+=K.keyword_count;return K.value}else{return E(r,L)}}function H(L,r){var K=L.cN?'':"";if(L.rB){p+=K;L.buffer=""}else{if(L.eB){p+=l(r)+K;L.buffer=""}else{p+=K;L.buffer=r}}B.push(L);A+=L.r}function D(N,K,P){var Q=B[B.length-1];if(P){p+=J(Q.buffer+N,Q);return false}var L=y(K,Q);if(L){p+=J(Q.buffer+N,Q);H(L,K);return L.rB}var r=v(B.length-1,K);if(r){var M=Q.cN?"":"";if(Q.rE){p+=J(Q.buffer+N,Q)+M}else{if(Q.eE){p+=J(Q.buffer+N,Q)+M+l(K)}else{p+=J(Q.buffer+N+K,Q)+M}}while(r>1){M=B[B.length-2].cN?"":"";p+=M;r--;B.length--}var O=B[B.length-1];B.length--;B[B.length-1].buffer="";if(O.starts){H(O.starts,"")}return Q.rE}if(w(K,Q)){throw"Illegal"}}var G=d[I];var B=[G.dM];var A=0;var s=0;var p="";try{var u=0;G.dM.buffer="";do{var x=q(C,u);var t=D(x[0],x[1],x[2]);u+=x[0].length;if(!t){u+=x[1].length}}while(!x[2]);if(B.length>1){throw"Illegal"}return{language:I,r:A,keyword_count:s,value:p}}catch(F){if(F=="Illegal"){return{language:null,r:0,keyword_count:0,value:l(C)}}else{throw F}}}function h(){function o(t,s,u){if(t.compiled){return}if(!u){t.bR=c(s,t.b?t.b:"\\B|\\b");if(!t.e&&!t.eW){t.e="\\B|\\b"}if(t.e){t.eR=c(s,t.e)}}if(t.i){t.iR=c(s,t.i)}if(t.r==undefined){t.r=1}if(t.k){t.lR=c(s,t.l||hljs.IR,true)}for(var r in t.k){if(!t.k.hasOwnProperty(r)){continue}if(t.k[r] instanceof Object){t.kG=t.k}else{t.kG={keyword:t.k}}break}if(!t.c){t.c=[]}t.compiled=true;for(var q=0;qx.keyword_count+x.r){x=u}if(u.keyword_count+u.r>w.keyword_count+w.r){x=w;w=u}}}var s=t.className;if(!s.match(w.language)){s=s?(s+" "+w.language):w.language}var o=b(t);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=k(o,b(q),A)}if(y){w.value=w.value.replace(/^((<[^>]+>|\t)+)/gm,function(B,E,D,C){return E.replace(/\t/g,y)})}if(p){w.value=w.value.replace(/\n/g,"
")}if(/MSIE [678]/.test(navigator.userAgent)&&t.tagName=="CODE"&&t.parentNode.tagName=="PRE"){var q=t.parentNode;var v=document.createElement("div");v.innerHTML="
"+w.value+"
";t=v.firstChild.firstChild;v.firstChild.cN=q.cN;q.parentNode.replaceChild(v.firstChild,q)}else{t.innerHTML=w.value}t.className=s;t.dataset={};t.dataset.result={language:w.language,kw:w.keyword_count,re:w.r};if(x&&x.language){t.dataset.second_best={language:x.language,kw:x.keyword_count,re:x.r}}}function j(){if(j.called){return}j.called=true;e();var q=document.getElementsByTagName("pre");for(var o=0;o|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\.",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.inherit=function(o,r){var q={};for(var p in o){q[p]=o[p]}if(r){for(var p in r){q[p]=r[p]}}return q}}();hljs.LANGUAGES.ruby=function(){var g="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var a="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var n={keyword:{and:1,"false":1,then:1,defined:1,module:1,"in":1,"return":1,redo:1,"if":1,BEGIN:1,retry:1,end:1,"for":1,"true":1,self:1,when:1,next:1,until:1,"do":1,begin:1,unless:1,END:1,rescue:1,nil:1,"else":1,"break":1,undef:1,not:1,"super":1,"class":1,"case":1,require:1,yield:1,alias:1,"while":1,ensure:1,elsif:1,or:1,def:1},keymethods:{__id__:1,__send__:1,abort:1,abs:1,"all?":1,allocate:1,ancestors:1,"any?":1,arity:1,assoc:1,at:1,at_exit:1,autoload:1,"autoload?":1,"between?":1,binding:1,binmode:1,"block_given?":1,call:1,callcc:1,caller:1,capitalize:1,"capitalize!":1,casecmp:1,"catch":1,ceil:1,center:1,chomp:1,"chomp!":1,chop:1,"chop!":1,chr:1,"class":1,class_eval:1,"class_variable_defined?":1,class_variables:1,clear:1,clone:1,close:1,close_read:1,close_write:1,"closed?":1,coerce:1,collect:1,"collect!":1,compact:1,"compact!":1,concat:1,"const_defined?":1,const_get:1,const_missing:1,const_set:1,constants:1,count:1,crypt:1,"default":1,default_proc:1,"delete":1,"delete!":1,delete_at:1,delete_if:1,detect:1,display:1,div:1,divmod:1,downcase:1,"downcase!":1,downto:1,dump:1,dup:1,each:1,each_byte:1,each_index:1,each_key:1,each_line:1,each_pair:1,each_value:1,each_with_index:1,"empty?":1,entries:1,eof:1,"eof?":1,"eql?":1,"equal?":1,"eval":1,exec:1,exit:1,"exit!":1,extend:1,fail:1,fcntl:1,fetch:1,fileno:1,fill:1,find:1,find_all:1,first:1,flatten:1,"flatten!":1,floor:1,flush:1,for_fd:1,foreach:1,fork:1,format:1,freeze:1,"frozen?":1,fsync:1,getc:1,gets:1,global_variables:1,grep:1,gsub:1,"gsub!":1,"has_key?":1,"has_value?":1,hash:1,hex:1,id:1,include:1,"include?":1,included_modules:1,index:1,indexes:1,indices:1,induced_from:1,inject:1,insert:1,inspect:1,instance_eval:1,instance_method:1,instance_methods:1,"instance_of?":1,"instance_variable_defined?":1,instance_variable_get:1,instance_variable_set:1,instance_variables:1,"integer?":1,intern:1,invert:1,ioctl:1,"is_a?":1,isatty:1,"iterator?":1,join:1,"key?":1,keys:1,"kind_of?":1,lambda:1,last:1,length:1,lineno:1,ljust:1,load:1,local_variables:1,loop:1,lstrip:1,"lstrip!":1,map:1,"map!":1,match:1,max:1,"member?":1,merge:1,"merge!":1,method:1,"method_defined?":1,method_missing:1,methods:1,min:1,module_eval:1,modulo:1,name:1,nesting:1,"new":1,next:1,"next!":1,"nil?":1,nitems:1,"nonzero?":1,object_id:1,oct:1,open:1,pack:1,partition:1,pid:1,pipe:1,pop:1,popen:1,pos:1,prec:1,prec_f:1,prec_i:1,print:1,printf:1,private_class_method:1,private_instance_methods:1,"private_method_defined?":1,private_methods:1,proc:1,protected_instance_methods:1,"protected_method_defined?":1,protected_methods:1,public_class_method:1,public_instance_methods:1,"public_method_defined?":1,public_methods:1,push:1,putc:1,puts:1,quo:1,raise:1,rand:1,rassoc:1,read:1,read_nonblock:1,readchar:1,readline:1,readlines:1,readpartial:1,rehash:1,reject:1,"reject!":1,remainder:1,reopen:1,replace:1,require:1,"respond_to?":1,reverse:1,"reverse!":1,reverse_each:1,rewind:1,rindex:1,rjust:1,round:1,rstrip:1,"rstrip!":1,scan:1,seek:1,select:1,send:1,set_trace_func:1,shift:1,singleton_method_added:1,singleton_methods:1,size:1,sleep:1,slice:1,"slice!":1,sort:1,"sort!":1,sort_by:1,split:1,sprintf:1,squeeze:1,"squeeze!":1,srand:1,stat:1,step:1,store:1,strip:1,"strip!":1,sub:1,"sub!":1,succ:1,"succ!":1,sum:1,superclass:1,swapcase:1,"swapcase!":1,sync:1,syscall:1,sysopen:1,sysread:1,sysseek:1,system:1,syswrite:1,taint:1,"tainted?":1,tell:1,test:1,"throw":1,times:1,to_a:1,to_ary:1,to_f:1,to_hash:1,to_i:1,to_int:1,to_io:1,to_proc:1,to_s:1,to_str:1,to_sym:1,tr:1,"tr!":1,tr_s:1,"tr_s!":1,trace_var:1,transpose:1,trap:1,truncate:1,"tty?":1,type:1,ungetc:1,uniq:1,"uniq!":1,unpack:1,unshift:1,untaint:1,untrace_var:1,upcase:1,"upcase!":1,update:1,upto:1,"value?":1,values:1,values_at:1,warn:1,write:1,write_nonblock:1,"zero?":1,zip:1}};var h={cN:"yardoctag",b:"@[A-Za-z]+"};var d={cN:"comment",b:"#",e:"$",c:[h]};var c={cN:"comment",b:"^\\=begin",e:"^\\=end",c:[h],r:10};var b={cN:"comment",b:"^__END__",e:"\\n$"};var u={cN:"subst",b:"#\\{",e:"}",l:g,k:n};var p=[hljs.BE,u];var s={cN:"string",b:"'",e:"'",c:p,r:0};var r={cN:"string",b:'"',e:'"',c:p,r:0};var q={cN:"string",b:"%[qw]?\\(",e:"\\)",c:p,r:10};var o={cN:"string",b:"%[qw]?\\[",e:"\\]",c:p,r:10};var m={cN:"string",b:"%[qw]?{",e:"}",c:p,r:10};var l={cN:"string",b:"%[qw]?<",e:">",c:p,r:10};var k={cN:"string",b:"%[qw]?/",e:"/",c:p,r:10};var j={cN:"string",b:"%[qw]?%",e:"%",c:p,r:10};var i={cN:"string",b:"%[qw]?-",e:"-",c:p,r:10};var t={cN:"string",b:"%[qw]?\\|",e:"\\|",c:p,r:10};var e={cN:"function",b:"\\bdef\\s+",e:" |$|;",l:g,k:n,c:[{cN:"title",b:a,l:g,k:n},{cN:"params",b:"\\(",e:"\\)",l:g,k:n},d,c,b]};var f={cN:"identifier",b:g,l:g,k:n,r:0};var v=[d,c,b,s,r,q,o,m,l,k,j,i,t,{cN:"class",b:"\\b(class|module)\\b",e:"$|;",k:{"class":1,module:1},c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+hljs.IR+"::)?"+hljs.IR}]},d,c,b]},e,{cN:"constant",b:"(::)?([A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:[s,r,q,o,m,l,k,j,i,t,f],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},f,{b:"("+hljs.RSR+")\\s*",c:[d,c,b,{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[hljs.BE]}],r:0}];u.c=v;e.c[1].c=v;return{dM:{l:g,k:n,c:v}}}(); /*! Colorbox v1.5.13 - 2014-08-04 jQuery lightbox and modal window plugin (c) 2014 Jack Moore - http://www.jacklmoore.com/colorbox license: http://www.opensource.org/licenses/mit-license.php */ (function ($, document, window) { var // Default settings object. // See http://jacklmoore.com/colorbox for details. defaults = { // data sources html: false, photo: false, iframe: false, inline: false, // behavior and appearance transition: "elastic", speed: 300, fadeOut: 300, width: false, initialWidth: "600", innerWidth: false, maxWidth: false, height: false, initialHeight: "450", innerHeight: false, maxHeight: false, scalePhotos: true, scrolling: true, opacity: 0.9, preloading: true, className: false, overlayClose: true, escKey: true, arrowKey: true, top: false, bottom: false, left: false, right: false, fixed: false, data: undefined, closeButton: true, fastIframe: true, open: false, reposition: true, loop: true, slideshow: false, slideshowAuto: true, slideshowSpeed: 2500, slideshowStart: "start slideshow", slideshowStop: "stop slideshow", photoRegex: /\.(gif|png|jp(e|g|eg)|bmp|ico|webp|jxr|svg)((#|\?).*)?$/i, // alternate image paths for high-res displays retinaImage: false, retinaUrl: false, retinaSuffix: '@2x.$1', // internationalization current: "image {current} of {total}", previous: "previous", next: "next", close: "close", xhrError: "This content failed to load.", imgError: "This image failed to load.", // accessbility returnFocus: true, trapFocus: true, // callbacks onOpen: false, onLoad: false, onComplete: false, onCleanup: false, onClosed: false, rel: function() { return this.rel; }, href: function() { // using this.href would give the absolute url, when the href may have been inteded as a selector (e.g. '#container') return $(this).attr('href'); }, title: function() { return this.title; } }, // Abstracting the HTML and event identifiers for easy rebranding colorbox = 'colorbox', prefix = 'cbox', boxElement = prefix + 'Element', // Events event_open = prefix + '_open', event_load = prefix + '_load', event_complete = prefix + '_complete', event_cleanup = prefix + '_cleanup', event_closed = prefix + '_closed', event_purge = prefix + '_purge', // Cached jQuery Object Variables $overlay, $box, $wrap, $content, $topBorder, $leftBorder, $rightBorder, $bottomBorder, $related, $window, $loaded, $loadingBay, $loadingOverlay, $title, $current, $slideshow, $next, $prev, $close, $groupControls, $events = $(''), // $({}) would be prefered, but there is an issue with jQuery 1.4.2 // Variables for cached values or use across multiple functions settings, interfaceHeight, interfaceWidth, loadedHeight, loadedWidth, index, photo, open, active, closing, loadingTimer, publicMethod, div = "div", requests = 0, previousCSS = {}, init; // **************** // HELPER FUNCTIONS // **************** // Convenience function for creating new jQuery objects function $tag(tag, id, css) { var element = document.createElement(tag); if (id) { element.id = prefix + id; } if (css) { element.style.cssText = css; } return $(element); } // Get the window height using innerHeight when available to avoid an issue with iOS // http://bugs.jquery.com/ticket/6724 function winheight() { return window.innerHeight ? window.innerHeight : $(window).height(); } function Settings(element, options) { if (options !== Object(options)) { options = {}; } this.cache = {}; this.el = element; this.value = function(key) { var dataAttr; if (this.cache[key] === undefined) { dataAttr = $(this.el).attr('data-cbox-'+key); if (dataAttr !== undefined) { this.cache[key] = dataAttr; } else if (options[key] !== undefined) { this.cache[key] = options[key]; } else if (defaults[key] !== undefined) { this.cache[key] = defaults[key]; } } return this.cache[key]; }; this.get = function(key) { var value = this.value(key); return $.isFunction(value) ? value.call(this.el, this) : value; }; } // Determine the next and previous members in a group. function getIndex(increment) { var max = $related.length, newIndex = (index + increment) % max; return (newIndex < 0) ? max + newIndex : newIndex; } // Convert '%' and 'px' values to integers function setSize(size, dimension) { return Math.round((/%/.test(size) ? ((dimension === 'x' ? $window.width() : winheight()) / 100) : 1) * parseInt(size, 10)); } // Checks an href to see if it is a photo. // There is a force photo option (photo: true) for hrefs that cannot be matched by the regex. function isImage(settings, url) { return settings.get('photo') || settings.get('photoRegex').test(url); } function retinaUrl(settings, url) { return settings.get('retinaUrl') && window.devicePixelRatio > 1 ? url.replace(settings.get('photoRegex'), settings.get('retinaSuffix')) : url; } function trapFocus(e) { if ('contains' in $box[0] && !$box[0].contains(e.target) && e.target !== $overlay[0]) { e.stopPropagation(); $box.focus(); } } function setClass(str) { if (setClass.str !== str) { $box.add($overlay).removeClass(setClass.str).addClass(str); setClass.str = str; } } function getRelated(rel) { index = 0; if (rel && rel !== false && rel !== 'nofollow') { $related = $('.' + boxElement).filter(function () { var options = $.data(this, colorbox); var settings = new Settings(this, options); return (settings.get('rel') === rel); }); index = $related.index(settings.el); // Check direct calls to Colorbox. if (index === -1) { $related = $related.add(settings.el); index = $related.length - 1; } } else { $related = $(settings.el); } } function trigger(event) { // for external use $(document).trigger(event); // for internal use $events.triggerHandler(event); } var slideshow = (function(){ var active, className = prefix + "Slideshow_", click = "click." + prefix, timeOut; function clear () { clearTimeout(timeOut); } function set() { if (settings.get('loop') || $related[index + 1]) { clear(); timeOut = setTimeout(publicMethod.next, settings.get('slideshowSpeed')); } } function start() { $slideshow .html(settings.get('slideshowStop')) .unbind(click) .one(click, stop); $events .bind(event_complete, set) .bind(event_load, clear); $box.removeClass(className + "off").addClass(className + "on"); } function stop() { clear(); $events .unbind(event_complete, set) .unbind(event_load, clear); $slideshow .html(settings.get('slideshowStart')) .unbind(click) .one(click, function () { publicMethod.next(); start(); }); $box.removeClass(className + "on").addClass(className + "off"); } function reset() { active = false; $slideshow.hide(); clear(); $events .unbind(event_complete, set) .unbind(event_load, clear); $box.removeClass(className + "off " + className + "on"); } return function(){ if (active) { if (!settings.get('slideshow')) { $events.unbind(event_cleanup, reset); reset(); } } else { if (settings.get('slideshow') && $related[1]) { active = true; $events.one(event_cleanup, reset); if (settings.get('slideshowAuto')) { start(); } else { stop(); } $slideshow.show(); } } }; }()); function launch(element) { var options; if (!closing) { options = $(element).data(colorbox); settings = new Settings(element, options); getRelated(settings.get('rel')); if (!open) { open = active = true; // Prevents the page-change action from queuing up if the visitor holds down the left or right keys. setClass(settings.get('className')); // Show colorbox so the sizes can be calculated in older versions of jQuery $box.css({visibility:'hidden', display:'block', opacity:''}); $loaded = $tag(div, 'LoadedContent', 'width:0; height:0; overflow:hidden; visibility:hidden'); $content.css({width:'', height:''}).append($loaded); // Cache values needed for size calculations interfaceHeight = $topBorder.height() + $bottomBorder.height() + $content.outerHeight(true) - $content.height(); interfaceWidth = $leftBorder.width() + $rightBorder.width() + $content.outerWidth(true) - $content.width(); loadedHeight = $loaded.outerHeight(true); loadedWidth = $loaded.outerWidth(true); // Opens inital empty Colorbox prior to content being loaded. var initialWidth = setSize(settings.get('initialWidth'), 'x'); var initialHeight = setSize(settings.get('initialHeight'), 'y'); var maxWidth = settings.get('maxWidth'); var maxHeight = settings.get('maxHeight'); settings.w = (maxWidth !== false ? Math.min(initialWidth, setSize(maxWidth, 'x')) : initialWidth) - loadedWidth - interfaceWidth; settings.h = (maxHeight !== false ? Math.min(initialHeight, setSize(maxHeight, 'y')) : initialHeight) - loadedHeight - interfaceHeight; $loaded.css({width:'', height:settings.h}); publicMethod.position(); trigger(event_open); settings.get('onOpen'); $groupControls.add($title).hide(); $box.focus(); if (settings.get('trapFocus')) { // Confine focus to the modal // Uses event capturing that is not supported in IE8- if (document.addEventListener) { document.addEventListener('focus', trapFocus, true); $events.one(event_closed, function () { document.removeEventListener('focus', trapFocus, true); }); } } // Return focus on closing if (settings.get('returnFocus')) { $events.one(event_closed, function () { $(settings.el).focus(); }); } } var opacity = parseFloat(settings.get('opacity')); $overlay.css({ opacity: opacity === opacity ? opacity : '', cursor: settings.get('overlayClose') ? 'pointer' : '', visibility: 'visible' }).show(); if (settings.get('closeButton')) { $close.html(settings.get('close')).appendTo($content); } else { $close.appendTo('
'); // replace with .detach() when dropping jQuery < 1.4 } load(); } } // Colorbox's markup needs to be added to the DOM prior to being called // so that the browser will go ahead and load the CSS background images. function appendHTML() { if (!$box && document.body) { init = false; $window = $(window); $box = $tag(div).attr({ id: colorbox, 'class': $.support.opacity === false ? prefix + 'IE' : '', // class for optional IE8 & lower targeted CSS. role: 'dialog', tabindex: '-1' }).hide(); $overlay = $tag(div, "Overlay").hide(); $loadingOverlay = $([$tag(div, "LoadingOverlay")[0],$tag(div, "LoadingGraphic")[0]]); $wrap = $tag(div, "Wrapper"); $content = $tag(div, "Content").append( $title = $tag(div, "Title"), $current = $tag(div, "Current"), $prev = $('