| --/******************************************************************************* |
| -- * Copyright (c) 2015 Vienna University of Technology. |
| -- * All rights reserved. This program and the accompanying materials |
| -- * are made available under the terms of the Eclipse Public License v1.0 |
| -- * which accompanies this distribution, and is available at |
| -- * http://www.eclipse.org/legal/epl-v10.html |
| -- * |
| -- * Contributors: |
| -- * Alexander Bergmayr (TU Wien) - initial API and implementation |
| -- * Manuel Wimmer (TU Wien) - initial API and implementation |
| -- * |
| -- * Initially developed in the context of ARTIST EU project www.artist-project.eu |
| -- *******************************************************************************/ |
| |
| -- @nsURI UMLMM=http://www.eclipse.org/uml2/4.0.0/UML |
| -- @path JMM=http://www.eclipse.org/MoDisco/Java/0.2.incubation/java |
| -- @path TMM=pathmap://METAMODEL/Trace.ecore |
| -- @path CFGMM=/eu.artist.migration.mdt.jump.cm2up/metamodel/jump-configuration.ecore |
| |
| -- Discovers UML Profiles from Java application code, frameworks and libraries. |
| |
| module CodeModel2UMLProfile;-- creates UP the UML Profile and TM the Trace Model |
| -- from CM the Code Model discovered from a Java application / library |
| -- JP a Java-based UML Profile that provides Stereotypes for the parameterized types |
| -- and proxies |
| -- JPT the Java Primitive Types library provided by the UML implementation |
| -- MC the UML meta-classes in terms of a UML model |
| -- UPT the UML Primitive Types library provided by the UML implementation |
| create UP: UMLMM, TM: TMM from CM: JMM, JP: UMLMM, JPT: UMLMM, MC: UMLMM, UPT: UMLMM, |
| EPT: UMLMM, CFG : CFGMM; |
| |
| -- TODO: default value of Strings - default values need to be generally revised |
| -- TODO: Code-generator: ID annotation nachziehen! |
| |
| |
| -- helpers for Java-based processing |
| uses javaLibrary; |
| -- helpers for Stereotype application |
| uses profileLibrary; |
| |
| helper def: getAnnotationTypeMemberDeclarations: |
| Sequence(JMM!AnnotationTypeMemberDeclaration) = |
| JMM!AnnotationTypeMemberDeclaration.allInstancesFrom('CM'); |
| |
| helper def: repeatingStereotypesCfg: |
| OclAny = |
| CFGMM!ProfileConfigurationParameter.allInstancesFrom('CFG') -> first().repeatingStereotypes; |
| |
| helper def: getRepeatableAnnotations: |
| Sequence(JMM!Annotation) = |
| JMM!Annotation.allInstancesFrom('CM') -> select(e | e.type.type.name = 'Repeatable'); |
| |
| -- check if the annotation type is repeatable |
| helper context JMM!AnnotationTypeDeclaration def : isRepeatable : Boolean = |
| self.annotations -> exists(e | e.type.type.name = 'Repeatable'); |
| |
| -- check if the annotation type is a container annotation |
| helper context JMM!AnnotationTypeDeclaration def : isContainerAnnotation : Boolean = |
| thisModule.getRepeatableAnnotations -> collect(e | e.values) -> flatten() -> exists(e | e.value.type.type = self); |
| |
| helper context JMM!AnnotationTypeDeclaration def : requiresContainerAnnotation : Boolean = |
| -- thisModule.repeatingStereotypesCfg = 'native' -> we don't need a container stereotype |
| thisModule.repeatingStereotypesCfg = #composition or thisModule.repeatingStereotypesCfg = #emulation; |
| |
| helper def: getStereotypes: Sequence(UMLMM!Stereotype) = |
| UMLMM!Stereotype.allInstancesFrom('JP'); |
| |
| helper def: profile: UMLMM!Profile = |
| OclUndefined; |
| |
| helper def: stereotype: UMLMM!Stereotype = |
| OclUndefined; |
| |
| helper def: proxyPackage: UMLMM!Package = |
| OclUndefined; |
| |
| helper def: trace: TMM!Trace = |
| OclUndefined; |
| |
| -- ensure that the trace model is generated |
| entrypoint rule createTrace() { |
| to |
| tr: TMM!Trace ( |
| ) |
| do { |
| thisModule.trace <- tr; |
| } |
| } |
| |
| -- init package for proxy elements |
| lazy rule initProxyPackage { |
| from |
| s1: JMM!Type |
| to |
| t1: UMLMM!Package ( |
| name <- 'proxy' |
| ) |
| do { |
| thisModule.profile.packagedElement <- t1; |
| thisModule.proxyPackage <- t1; |
| |
| t1.applyProfile(UMLMM!Profile.allInstancesFrom('JP').first()); |
| } |
| } |
| |
| -- create the profile |
| rule Model2Profile { |
| from |
| s1: JMM!Model |
| to |
| t1: UMLMM!Profile ( |
| -- packageImport <- Sequence{jpt, mc, upt}, |
| packageImport <- Sequence{mc, |
| upt}, |
| metamodelReference <- mc, |
| name <- s1.name, |
| packagedElement <- s1.ownedElements -> select(e | not e.proxy and e. |
| isAnnotationTypeContainer) |
| ), |
| -- java primitive types |
| -- jpt : UMLMM!PackageImport( |
| -- importedPackage <- UMLMM!Model.allInstancesFrom('JPT').first() |
| -- ), |
| -- uml meta-classes |
| mc: UMLMM!PackageImport ( |
| importedPackage <- UMLMM!Model.allInstancesFrom('MC').first() |
| ), |
| -- uml primitive types |
| upt: UMLMM!PackageImport ( |
| importedPackage <- UMLMM!Model.allInstancesFrom('UPT').first() |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'Model2Profile', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- global access to the profile |
| thisModule.profile <- t1; |
| -- apply the profile for java generics / proxies |
| t1.applyProfile(UMLMM!Profile.allInstancesFrom('JP').first()); |
| thisModule.stereotype <- UMLMM!Stereotype.allInstancesFrom('JP') -> any(e | e.name = 'JProfile'); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule Package2Package { |
| from |
| s1: JMM!Package ( |
| not s1.proxy and s1.isAnnotationTypeContainer |
| ) |
| to |
| t1: UMLMM!Package ( |
| name <- s1.name, |
| packagedElement <- s1.ownedPackages -> select(e | not e.proxy and |
| e.isAnnotationTypeContainer) --, |
| -- instead of selecting all required elements here let's do it in the |
| -- corresponding rules |
| -- of ClassDeclaration, EnumDeclaration, InterfaceDeclaration, |
| -- AnnotationTypeDeclaration |
| -- packagedElement <- s1.ownedElements |
| |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'Package2Package', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| -- but only enumerations that are required for the profile |
| rule EnumDeclaration2Enumeration { |
| from |
| s1: JMM!EnumDeclaration ( |
| s1.isEnumerationRelevant |
| ) |
| to |
| t1: UMLMM!Enumeration ( |
| name <- if (s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) then |
| s1.name |
| else |
| s1.name.concat('From').concat(s1.refImmediateComposite().name) |
| endif, |
| ownedLiteral <- s1.enumConstants -> select(e | not e.proxy or e. |
| refImmediateComposite().isEnumerationRelevant), |
| -- TODO: new CHECK !! |
| ownedAttribute <- s1.bodyDeclarations -> select(e | e. |
| oclIsTypeOf(JMM!FieldDeclaration)), |
| -- with 'not e.proxy' we exclude the 'valueOf' method generated by the java |
| -- compiler |
| ownedOperation <- s1.bodyDeclarations -> select(e | e. |
| oclIsTypeOf(JMM!MethodDeclaration) and not e.proxy) |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'EnumDeclaration2Enumeration', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- if the required class declaration is a proxy then the proxy stereotype is |
| -- applied |
| if(s1.proxy = true) { |
| if(thisModule.proxyPackage.oclIsUndefined()) { |
| thisModule -> initProxyPackage(s1); |
| } |
| thisModule.proxyPackage.packagedElement <- t1; |
| |
| -- apply the proxy stereotype |
| thisModule.stereotype <- thisModule.getStereotypes -> select(e | e.name = |
| 'JProxyType').first(); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| -- set the namepace of the proxy element if available |
| if(s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) { |
| t1.setValue(thisModule.stereotype, 'namespace', s1. |
| refImmediateComposite().getFullyQualifiedPackageName); |
| } |
| else { |
| t1.setValue(thisModule.stereotype, 'namespace', 'NOT_AVAILABLE'); |
| } |
| } |
| |
| -- elements are contained either by packages or classifiers (nested elements) |
| else { |
| if(not s1.package.oclIsUndefined()) { |
| thisModule.resolveTemp(s1.refImmediateComposite(), 't1').packagedElement |
| <- t1; |
| } |
| else { |
| -- so let's get the package of the owner and put the stereotype there |
| -- ... however |
| -- there is a problem when we generate code !!!! |
| thisModule.resolveTemp(s1.refImmediateComposite().getJavaPackage(), 't1'). |
| packagedElement <- t1; |
| } |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule FieldDeclaration2Property { |
| from |
| s1: JMM!FieldDeclaration ( |
| not s1.proxy and s1.refImmediateComposite().oclIsTypeOf(JMM!EnumDeclaration) |
| and s1.refImmediateComposite().isEnumerationRelevant |
| ) |
| to |
| t1: UMLMM!Property ( |
| -- TODO: fragments is 0..* - check the exact behaviour! |
| name <- s1.fragments.first().name, |
| type <- s1.type.getType(), |
| isStatic <- s1.modifier.static |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'FieldDeclaration2Property', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule MethodDeclaration2Operation { |
| from |
| s1: JMM!MethodDeclaration ( |
| not s1.proxy and s1.refImmediateComposite().oclIsTypeOf(JMM!EnumDeclaration) |
| and s1.refImmediateComposite().isEnumerationRelevant |
| ) |
| to |
| t1: UMLMM!Operation ( |
| name <- s1.name, |
| ownedParameter <- s1.parameters -> collect(e | thisModule.createParameter(e)) |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'MethodDeclaration2Operation', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- thisModule.applyStereotypes(s1, t1); |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| lazy rule createParameter { |
| from |
| s1: JMM!SingleVariableDeclaration |
| to |
| t1: UMLMM!Parameter ( |
| name <- s1.name, |
| type <- s1.type.getType(), |
| direction <- #"in" |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'SingleVariableDeclaration2Paramter', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule EnumConstantDeclaration2EnumerationLiteral { |
| from |
| s1: JMM!EnumConstantDeclaration ( |
| s1.refImmediateComposite().isEnumerationRelevant |
| ) |
| to |
| t1: UMLMM!EnumerationLiteral ( |
| name <- s1.name |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'EnumConstantDeclaration2EnumerationLiteral', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| -- but only classes that are required for the profile. Such classes can only be |
| -- bounded type parameters of java.lang.class |
| rule ClassDeclaration2Class { |
| from |
| -- since java.lang.Class is mapped to UML class, we need to exclude it here as |
| -- well |
| s1: JMM!ClassDeclaration ( |
| (s1.refImmediateComposite().getFullyQualifiedPackageName.concat('.'). |
| concat(s1.name) <> 'java.lang.Class') and (thisModule. |
| getAnnotationTypeMemberDeclarations -> select(e | not e.type. |
| oclIsUndefined()) -> select(e | e.type.type. |
| oclIsTypeOf(JMM!ParameterizedType)) -> exists(f | f.type.type. |
| typeArguments -> exists(g | g.type = s1))) |
| ) |
| to |
| t1: UMLMM!Class ( |
| name <- if (s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) then |
| s1.name |
| else |
| s1.name.concat('From').concat(s1.refImmediateComposite().name) |
| endif |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'ClassDeclaration2Class', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- if the required class declaration is a proxy then the proxy stereotype is |
| -- applied |
| if(s1.proxy = true) { |
| if(thisModule.proxyPackage.oclIsUndefined()) { |
| thisModule -> initProxyPackage(s1); |
| } |
| thisModule.proxyPackage.packagedElement <- t1; |
| |
| -- apply the proxy stereotype |
| thisModule.stereotype <- UMLMM!Stereotype.allInstancesFrom('JP') -> |
| select(e | e.name = 'JProxyType').first(); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| -- set the namepace of the proxy element if available |
| if(s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) { |
| t1.setValue(thisModule.stereotype, 'namespace', s1. |
| refImmediateComposite().getFullyQualifiedPackageName); |
| } |
| else { |
| t1.setValue(thisModule.stereotype, 'namespace', 'NOT_AVAILABLE'); |
| } |
| } |
| |
| -- nested classifiers are contained by classifiers rather than packages ;) |
| else { |
| if(thisModule.resolveTemp(s1.refImmediateComposite(), 't1'). |
| oclIsTypeOf(UMLMM!Package)) { |
| thisModule.resolveTemp(s1.refImmediateComposite(), 't1').packagedElement |
| <- t1; |
| } |
| else { |
| -- so let's get the package of the owner and put the stereotype there |
| -- ... however |
| -- there is a problem when we generate code !!!! |
| thisModule.resolveTemp(s1.refImmediateComposite().getJavaPackage(), 't1'). |
| packagedElement <- t1; |
| } |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| -- but only interfaces that are required for the profile. Such classes can only be |
| -- bounded type parameters of java.lang.class |
| rule InterfaceDeclaration2Interface { |
| from |
| s1: JMM!InterfaceDeclaration ( |
| thisModule.getAnnotationTypeMemberDeclarations -> select(e | not e.type. |
| oclIsUndefined()) -> select(e | e.type.type. |
| oclIsTypeOf(JMM!ParameterizedType)) -> exists(f | f.type.type. |
| typeArguments -> exists(g | g.type = s1)) |
| ) |
| to |
| t1: UMLMM!Interface ( |
| name <- if (s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) then |
| s1.name |
| else |
| s1.name.concat('From').concat(s1.refImmediateComposite().name) |
| endif |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'InterfaceDeclaration2Interface', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- if the required class declaration is a proxy then the proxy stereotype is |
| -- applied |
| if(s1.proxy = true) { |
| if(thisModule.proxyPackage.oclIsUndefined()) { |
| thisModule.initProxyPackage(s1); |
| } |
| thisModule.proxyPackage.packagedElement <- t1; |
| |
| -- apply the proxy stereotype |
| thisModule.stereotype <- UMLMM!Stereotype.allInstancesFrom('JP') -> select(e |
| | e.name = 'JProxyType').first(); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| -- set the namepace of the proxy element if available |
| if(s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) { |
| t1.setValue(thisModule.stereotype, 'namespace', s1. |
| refImmediateComposite().getFullyQualifiedPackageName); |
| } |
| else { |
| t1.setValue(thisModule.stereotype, 'namespace', 'NOT_AVAILABLE'); |
| } |
| } |
| |
| -- nested classifiers are contained by classifiers rather than packages ;) |
| else { |
| if(thisModule.resolveTemp(s1.refImmediateComposite(), 't1'). |
| oclIsTypeOf(UMLMM!Package)) { |
| thisModule.resolveTemp(s1.refImmediateComposite(), 't1').packagedElement |
| <- t1; |
| } |
| else { |
| -- so let's get the package of the owner and put the stereotype there |
| -- ... however |
| -- there is a problem when we generate code !!!! |
| thisModule.resolveTemp(s1.refImmediateComposite().getJavaPackage(), 't1'). |
| packagedElement <- t1; |
| } |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule AnnotationTypeDeclaration2Stereotype { |
| from |
| -- only those AnnotationTypeDeclarations that are not proxies or return value of |
| -- a AnnotationTypeMemberDeclaration |
| s1: JMM!AnnotationTypeDeclaration ( |
| -- isn't a proxy |
| not s1.proxy |
| |
| -- is a container stereotype required |
| and ((s1.isContainerAnnotation and s1.requiresContainerAnnotation) or not s1.isContainerAnnotation) |
| |
| -- TODO: document this case |
| or thisModule.getAnnotationTypeMemberDeclarations -> select(e | not e.type.oclIsUndefined()) -> |
| select(e | e.type.type.oclIsTypeOf(JMM!ParameterizedType)) -> exists(f | f.type.type.typeArguments -> |
| exists(g | g.type = s1)) |
| |
| -- we do have a proxy element that has been resolved. This happens if a |
| -- missing library has been added to the build path. If this |
| -- library is not added then an UnresolvedTypeDeclaration |
| -- is discovered by MoDisco. |
| or (s1.proxy and s1.isRelevantForAnnotationTypes) |
| ) |
| to |
| t1: UMLMM!Stereotype ( |
| name <- if (s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) then |
| s1.name |
| else |
| s1.name.concat('From').concat(s1.refImmediateComposite().name) |
| endif, |
| -- can be set to public as no other visibility modifier is allowed for |
| -- annotation types |
| visibility <- #public, |
| -- should not be done since then the stereotype cannot be instantiated (Java |
| -- Feature or Bug ;) ) |
| -- isAbstract <- if s1.modifier.inheritance.toString() = 'abstract' then true |
| -- else false endif, |
| |
| -- TODO : According to JLS, an annotation type declaration can also contain |
| -- constant declarations |
| -- class declarations etc. ... even annotation type declarations -> inner |
| -- annotations |
| -- this means bodyDeclarations may also be of type JMM!FieldDeclaration |
| |
| -- in findbugs haben wir den Fall einer ClassDeclaration innerhabl eienr |
| -- AnnotationTypeDeclaration, |
| -- see: edu.umd.cs.findbugs.internalAnnotations.SlashedClassName |
| ownedAttribute <- s1.bodyDeclarations -> select(e | e. |
| oclIsTypeOf(JMM!AnnotationTypeMemberDeclaration)) |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'AnnotationTypeDeclaration2Stereotype', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| -- if the required class declaration is a proxy then the proxy stereotype is |
| -- applied |
| -- and the stereotype is added to a dedicated proxy package |
| if(s1.proxy) { |
| if(thisModule.proxyPackage.oclIsUndefined()) { |
| thisModule.initProxyPackage(s1); |
| } |
| thisModule.proxyPackage.packagedElement <- t1; |
| |
| -- apply the proxy stereotype |
| thisModule.stereotype <- UMLMM!Stereotype.allInstancesFrom('JP') -> select(e |
| | e.name = 'JProxyType').first(); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| -- set the namepace of the proxy element if available |
| if(s1.refImmediateComposite().oclIsTypeOf(JMM!Package)) { |
| t1.setValue(thisModule.stereotype, 'namespace', s1. |
| refImmediateComposite().getFullyQualifiedPackageName); |
| } |
| else { |
| t1.setValue(thisModule.stereotype, 'namespace', 'NOT_AVAILABLE'); |
| } |
| } |
| |
| -- TODO: nested classifiers are contained by classifiers rather than packages ;) |
| -- ... however nested |
| -- stereotypes are not actuall supported |
| else { |
| if(thisModule.resolveTemp(s1.refImmediateComposite(), 't1'). |
| oclIsTypeOf(UMLMM!Package)) { |
| thisModule.resolveTemp(s1.refImmediateComposite(), 't1').packagedElement |
| <- t1; |
| } |
| else { |
| -- so let's get the package of the owner and put the stereotype there |
| -- ... however |
| -- there is a problem when we generate code !!!! |
| thisModule.resolveTemp(s1.refImmediateComposite().getJavaPackage(), 't1'). |
| packagedElement <- t1; |
| } |
| } |
| |
| -- if the target annotation is not set at all then we have to trigger a lazy rule |
| -- that produces extensions to |
| -- all possible candidates |
| if(not s1.proxy and not (s1.annotations -> exists(a | a.type.type.name = |
| 'Target'))) { |
| for(javaElementType in thisModule.umlTarget.getKeys()) { |
| for(umlElementType in thisModule.umlTarget.get(javaElementType)) { |
| thisModule.createExtension(s1, t1, umlElementType, javaElementType); |
| } |
| } |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule createExtension(annotationType: JMM!AnnotationTypeDeclaration, stereotype: |
| UMLMM!Stereotype, |
| metaClass: UMLMM!Element, elementType: String) { |
| to |
| -- the extension relationship |
| t1: UMLMM!Extension ( |
| name <- 'extension_'.concat(stereotype.name).concat('_').concat(metaClass. |
| name), |
| memberEnd <- Sequence{t2, t3}, |
| ownedEnd <- t3 |
| ), |
| -- the properties of the relationship (end points) |
| t2: UMLMM!Property ( |
| name <- 'base_'.concat(metaClass.name), |
| type <- metaClass, |
| -- we set the lower bound to 0 because if the stereotype extends |
| -- several metaclasses then we would get validation errors with |
| -- a lower bound 1 |
| lower <- 0, |
| upper <- 1 |
| ), |
| t3: UMLMM!ExtensionEnd ( |
| name <- 'extension_'.concat(metaClass.name), |
| aggregation <- #composite, |
| type <- stereotype, |
| lower <- 0, |
| upper <- if(annotationType.isRepeatable) then (0-1) else 1 endif |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'createExtension', |
| targetElements <- Sequence{t1, |
| t2, |
| t3} |
| ) |
| do { |
| -- refers to the ownedAttribute of the stereotype |
| stereotype.ownedAttribute <- t2; |
| |
| -- refers to the profile that contains the stereotype |
| stereotype.refImmediateComposite().packagedElement <- t1; |
| |
| -- if we create an extension of Type then we need a constraint as |
| -- UML:Type is more general then Java:Type |
| |
| if(elementType = 'TYPE') { |
| thisModule.createTypeConstraint(stereotype); |
| } |
| |
| else if(elementType = 'CONSTRUCTOR') { |
| thisModule.createConstructorConstraint(stereotype); |
| } |
| |
| else if(elementType = 'METHOD') { |
| thisModule.createMethodConstraint(stereotype); |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{annotationType}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule SingleVariableAccessMultiValued2Extension { |
| from |
| s1: JMM!SingleVariableAccess ( |
| s1.isMultiValuedAnnotationTarget |
| ) |
| using { |
| s2: JMM!AnnotationTypeDeclaration = s1.refImmediateComposite(). |
| refImmediateComposite().refImmediateComposite().refImmediateComposite(); |
| } |
| to |
| -- the extension relationship |
| t1: UMLMM!Extension ( |
| name <- 'extension_'.concat(thisModule.resolveTemp(s2, 't1').name). |
| concat('_').concat(thisModule.umlTarget.get(s1.variable.name).first().name), |
| memberEnd <- Sequence{t2, |
| t3}, |
| ownedEnd <- t3 |
| ), |
| -- the properties of the relationship (end points) |
| t2: UMLMM!Property ( |
| name <- 'base_'.concat(thisModule.umlTarget.get(s1.variable.name).first(). |
| name), |
| type <- thisModule.umlTarget.get(s1.variable.name).first(), |
| lower <- 0, |
| upper <- 1 |
| ), |
| t3: UMLMM!ExtensionEnd ( |
| name <- 'extension_'.concat(thisModule.umlTarget.get(s1.variable.name). |
| first().name), |
| aggregation <- #composite, |
| type <- thisModule.resolveTemp(s2, 't1') |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'SingleVariableAccessMultiValued2Extension', |
| targetElements <- Sequence{t1, |
| t2, |
| t3} |
| ) |
| do { |
| -- refers to the ownedAttribute of the stereotype |
| thisModule.resolveTemp(s2, 't1').ownedAttribute <- t2; |
| |
| -- refers to the profile that contains the stereotype |
| thisModule.resolveTemp(s2.getPackage(), 't1').packagedElement <- t1; |
| |
| -- if the target is of type FIELD, we do have to create an extension for |
| -- EnumDeclaration as well; |
| -- if we use Associatons, we may have to add another extension |
| if(s1.variable.name = 'FIELD') { |
| thisModule.createExtension(s2, thisModule.resolveTemp(s2, 't1'), thisModule. |
| umlTarget.get(s1.variable.name).at(2), s1.variable.name); |
| } |
| |
| -- if the target is of type METHOD, we do have to create an extension for |
| -- Property as well as methods in annotationtypedeclrations are translated |
| -- to property |
| if(s1.variable.name = 'METHOD') { |
| thisModule.createExtension(s2, thisModule.resolveTemp(s2, 't1'), thisModule. |
| umlTarget.get(s1.variable.name).at(2), s1.variable.name); |
| } |
| |
| -- in case of TYPE we do have to create the type constraint |
| if(s1.variable.name = 'TYPE') { |
| thisModule.createTypeConstraint(thisModule.resolveTemp(s2, 't1')); |
| } |
| |
| -- in case of CONSTRUCTOR we do have to create the type constraint |
| else if(s1.variable.name = 'CONSTRUCTOR') { |
| thisModule.createConstructorConstraint(thisModule.resolveTemp(s2, 't1')); |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule SingleVariableAccessSingleValued2Extension { |
| from |
| s1: JMM!SingleVariableAccess ( |
| s1.isSingleValuedAnnotationTarget |
| ) |
| using { |
| s2: JMM!AnnotationTypeDeclaration = s1.refImmediateComposite(). |
| refImmediateComposite().refImmediateComposite(); |
| } |
| to |
| -- the extension relationship |
| t1: UMLMM!Extension ( |
| name <- 'extension_'.concat(thisModule.resolveTemp(s2, 't1').name). |
| concat('_').concat(thisModule.umlTarget.get(s1.variable.name).first().name), |
| memberEnd <- Sequence{t2, |
| t3}, |
| ownedEnd <- t3 |
| ), |
| -- the properties of the relationship (end points) |
| t2: UMLMM!Property ( |
| name <- 'base_'.concat(thisModule.umlTarget.get(s1.variable.name).first(). |
| name), |
| type <- thisModule.umlTarget.get(s1.variable.name).first(), |
| lower <- 0, |
| upper <- 1 |
| ), |
| t3: UMLMM!ExtensionEnd ( |
| name <- 'extension_'.concat(thisModule.umlTarget.get(s1.variable.name). |
| first().name), |
| aggregation <- #composite, |
| type <- thisModule.resolveTemp(s2, 't1') |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'SingleVariableAccessSingleValued2Extension', |
| targetElements <- Sequence{t1, |
| t2, |
| t3} |
| ) |
| do { |
| -- refers to the ownedAttribute of the stereotype |
| thisModule.resolveTemp(s2, 't1').ownedAttribute <- t2; |
| |
| -- refers to the profile that contains the stereotype |
| thisModule.resolveTemp(s2.getPackage(), 't1').packagedElement <- t1; |
| |
| -- if the target is of type FIELD, we do have to create an extension for |
| -- EnumDeclaration as well; |
| -- if we use Associatons, we may have to add another extension |
| if(s1.variable.name = 'FIELD') { |
| thisModule.createExtension(s2, thisModule.resolveTemp(s2, 't1'), thisModule. |
| umlTarget.get(s1.variable.name).at(2), s1.variable.name); |
| } |
| |
| -- if the target is of type METHOD, we do have to create an extension for |
| -- Property as well as methods in annotationtypedeclrations are translated |
| -- to property |
| if(s1.variable.name = 'METHOD') { |
| thisModule.createExtension(s2, thisModule.resolveTemp(s2, 't1'), thisModule. |
| umlTarget.get(s1.variable.name).at(2), s1.variable.name); |
| } |
| |
| -- in case of TYPE we do have to create the type constraint |
| if(s1.variable.name = 'TYPE') { |
| thisModule.createTypeConstraint(thisModule.resolveTemp(s2, 't1')); |
| } |
| |
| -- in case of CONSTRUCTOR we do have to create the type constraint |
| else if(s1.variable.name = 'CONSTRUCTOR') { |
| thisModule.createConstructorConstraint(thisModule.resolveTemp(s2, 't1')); |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| rule AnnotationTypeMemberDeclaration2Property { |
| from |
| s1: JMM!AnnotationTypeMemberDeclaration ( |
| -- we need to exclude unresolved annotation type member declarations |
| if(s1.oclIsTypeOf(JMM!AnnotationTypeMemberDeclaration) and not s1.proxy) then |
| |
| -- is a container stereotype required |
| ((s1.refImmediateComposite().isContainerAnnotation and s1.refImmediateComposite().requiresContainerAnnotation) |
| or not s1.refImmediateComposite().isContainerAnnotation) |
| |
| or s1.refImmediateComposite().isRelevantForAnnotationTypes |
| |
| else false |
| endif |
| ) |
| to |
| t1: UMLMM!Property ( |
| name <- s1.name, |
| -- can be set to public as no other visibility modifier is allowed for |
| -- annotation types |
| visibility <- #public, |
| -- the problem here is that we may have proxy member declaratios with no |
| -- further type information |
| type <- if (s1.type <> OclUndefined) then |
| s1.type.getType() |
| else |
| UMLMM!PrimitiveType.allInstancesFrom('EPT') -> select(e | e.name = |
| 'EJavaObject').first() |
| endif, |
| -- is done in post processing ;) |
| -- default <- if s1.default <> OclUndefined and |
| -- s1.default.oclIsTypeOf(JMM!NumberLiteral) then |
| -- s1.default.tokenValue else OclUndefined endif, |
| -- TODO: provide example for an undefined type |
| lower <- if s1.default <> OclUndefined then |
| 0 |
| else |
| if s1.type <> OclUndefined then |
| if s1.type.type.oclIsTypeOf(JMM!ArrayType) then |
| 0 |
| else |
| 1 |
| endif |
| else |
| 1 |
| endif |
| endif, |
| upper <- if s1.type <> OclUndefined then |
| if s1.type.type.oclIsTypeOf(JMM!ArrayType) then |
| -1 |
| else |
| 1 |
| endif |
| else |
| 1 |
| endif, |
| -- if stereotypes are composed by other stereotypes |
| aggregation <- if s1.type <> OclUndefined |
| -- if the annotation member is of type Class then aggregation needs to be set to #none |
| then if(s1.type.isComplexType() and not s1.type.getType().oclIsTypeOf(UMLMM!Class)) then #composite else #none |
| endif |
| else #none |
| endif |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'AnnotationTypeMemberDeclaration2Property', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| |
| -- TODO : Here we should also consider the discussion about OCL constraints for |
| -- the allowed types as specified by the binding Class<T> |
| -- same as above: the problem here is that we may have proxy member declaratios |
| -- with no further type information |
| if(s1.type <> OclUndefined) { |
| if (s1.type.type.oclIsTypeOf(JMM!ParameterizedType)) { |
| thisModule.stereotype <- thisModule.getStereotypes -> any(e | e.name = |
| 'JGenericType'); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| t1.setValue(thisModule.stereotype, 'type', 'upperBounded'); |
| |
| -- set the type of the 'binding' ... unbounded (default), upperbounded, |
| -- lowerbounded, bounded; |
| if (s1.type.type.typeArguments.first().type. |
| oclIsTypeOf(JMM!WildCardType)) { |
| |
| -- if the type parameterized type is unresolved then create a proxy |
| -- element |
| -- design decision: it seems to be rather hard to express this part |
| -- declaratively ;( |
| |
| if(not s1.type.type.typeArguments.first().type.bound. |
| oclIsUndefined()) { |
| if(s1.type.type.typeArguments.first().type.bound.type. |
| oclIsTypeOf(JMM!UnresolvedTypeDeclaration)) { |
| |
| -- s1.refImmediateComposite().name.debug(); |
| -- s1.name.debug(); |
| thisModule.UnresolvedTypeDeclaration2Type(s1.type.type. |
| typeArguments.first().type.bound.type); |
| } |
| } |
| |
| -- upperBounded case |
| if (s1.type.type.typeArguments.first().type.name.startsWith('?' + '' |
| + ' extends')) { |
| thisModule.stereotype <- thisModule.getStereotypes -> any(e | e. |
| name = 'JGenericType'); |
| t1.setValue(thisModule.stereotype, 'type', 'upperBounded'); |
| |
| --t1.setValue(thisModule.stereotype, 'type', 'upperBounded'); |
| --t1.setValue(thisModule.stereotype, 'parameter', |
| -- thisModule.resolveTemp(s1.type.type.typeArguments.first().type.bound.typ |
| -- e |
| -- , |
| -- 't1').debug()); |
| } |
| -- lowerBounded case |
| else if (s1.type.type.typeArguments.first().type.name.toString(). |
| startsWith('? super')) { |
| t1.setValue(thisModule.stereotype, 'type', 'lowerBounded'); |
| -- t1.setValue(thisModule.stereotype, 'parameter', |
| -- thisModule.resolveTemp(s1.type.type.typeArguments.first().type.bound.typ |
| -- e |
| -- , |
| -- 't1').debug()); |
| } |
| } |
| |
| -- bounded case |
| else if (not s1.type.type.typeArguments.first().type.oclIsUndefined()) { |
| |
| -- s1.name.debug(); |
| t1.setValue(thisModule.stereotype, 'type', 'bounded'); |
| t1.setValue(thisModule.stereotype, 'parameter', thisModule. |
| resolveTemp(s1.type.type.typeArguments.first().type, |
| 't1')); |
| } |
| } |
| |
| if (s1.type.isComplexType()) { |
| thisModule.AnnotationTypeMemberDeclaration2Association(s1); |
| } |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| -- if we have UML properties with a complex type then we should create an UML association |
| lazy rule AnnotationTypeMemberDeclaration2Association { |
| from |
| s1: JMM!AnnotationTypeMemberDeclaration |
| to |
| t1: UMLMM!Association ( |
| name <- if (not s1.type.getType().name.oclIsUndefined()) then |
| s1.name.concat('_').concat(s1.refImmediateComposite().name).concat('_').concat(s1.type.getType().name) |
| else |
| s1.name.concat('_').concat(s1.refImmediateComposite().name).concat('_').concat(s1.type.type.name) |
| endif, |
| memberEnd <- s1, |
| memberEnd <- t2 |
| ), |
| t2: UMLMM!Property ( |
| -- the idea is to use the name of the Java field plus the classifier the |
| -- association points to |
| name <- s1.name.concat('_').concat(s1.refImmediateComposite().name), |
| -- can be set to public as no other visibility modifier is allowed for |
| -- annotation types |
| visibility <- #public, |
| -- TODO: Think about the multiplicity |
| lower <- 0, |
| upper <- 1, |
| type <- s1.refImmediateComposite() |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'AnnotationTypeMemberDeclaration2Association', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| thisModule.resolveTemp(s1.refImmediateComposite().getPackage(), 't1'). |
| packagedElement <- t1; |
| |
| -- case java.lang.Class / java.lang.Class[]: we do not want to change the |
| -- meta-class UML Class |
| -- so the produced property is contained by the association |
| if( (s1.type.type.oclIsTypeOf(JMM!ParameterizedType)) or |
| (s1.type.type.refImmediateComposite().getFullyQualifiedPackageName.toString().concat('.').concat(s1.type.type.name).toString() |
| = 'java.lang.Class') or ( -- in case the Array is parameterized the name |
| -- string also contains '<...>' |
| s1.type.type.name.toString().startsWith('java.lang.Class') and s1.type. |
| type.name.toString().endsWith('[]')) ) { |
| |
| t1.ownedEnd <- t2; |
| } |
| -- we follow the standard procedure and put the produced property into the |
| -- associated element |
| else { |
| if(s1.type.type.oclIsTypeOf(JMM!ArrayType)) { |
| |
| if(s1.type.type.elementType.type. |
| oclIsTypeOf(JMM!UnresolvedTypeDeclaration)) { |
| t1.ownedEnd <- t2; |
| } |
| |
| |
| thisModule.resolveTemp(s1.type.type.elementType.type, 't1'). |
| ownedAttribute <- t2; |
| } |
| else { |
| thisModule.resolveTemp(s1.type.type, 't1').ownedAttribute <- t2; |
| } |
| } |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| |
| } |
| } |
| |
| -- Refers to Types that cannot be resolved. This happens if a library references elements |
| -- of another library for which we do not have access to the source. Then, we get only the |
| -- type information and the type access but not further information. |
| -- The idea is to transform those UnresolvedTypes that are used in the declaration of |
| -- of annotations, i.e., AnnotationTypeMemberDeclarations. This seems to be essential |
| -- to declare the AnnotationTypeMemberDeclaration. |
| rule UnresolvedTypeDeclaration2Type { |
| from |
| s1: JMM!UnresolvedTypeDeclaration ( |
| s1.usagesInTypeAccess -> exists(e | e.refImmediateComposite(). |
| oclIsTypeOf(JMM!AnnotationTypeMemberDeclaration)) or |
| -- ArrayTypes need to be handled differently |
| s1.usagesInTypeAccess -> collect(e | e.refImmediateComposite()) -> |
| select(e | e.oclIsTypeOf(JMM!ArrayType)) -> exists(e | e.usagesInTypeAccess -> |
| exists(f | f.refImmediateComposite().oclIsTypeOf(JMM!AnnotationTypeMemberDeclaration))) |
| ) |
| to |
| t1: UMLMM!Class ( |
| name <- s1.name |
| ), |
| -- trace information |
| tl: TMM!TraceLink ( |
| ruleName <- 'UnresolvedTypeDeclaration2Type', |
| targetElements <- Sequence{t1} |
| ) |
| do { |
| if(thisModule.proxyPackage.oclIsUndefined()) { |
| thisModule -> initProxyPackage(s1); |
| } |
| thisModule.proxyPackage.packagedElement <- t1; |
| |
| -- apply the proxy stereotype |
| thisModule.stereotype <- UMLMM!Stereotype.allInstancesFrom('JP') -> |
| select(e | e.name = 'JProxyType').first(); |
| t1.applyStereotype(thisModule.stereotype); |
| |
| -- trace information |
| tl.refSetValue('sourceElements', Sequence{s1}); |
| thisModule.trace.traceLinks <- tl; |
| } |
| } |
| |
| -- we do need a constraint for the Type extension as only Class, Interface, Enumeration |
| -- and Stereotype |
| -- should be targets for the produced Stereotype |
| rule createTypeConstraint(stereotype: UMLMM!Stereotype) { |
| to |
| t1: UMLMM!Constraint ( |
| name <- 'typeConstraint', |
| constrainedElement <- stereotype, |
| context <- stereotype, |
| specification <- t2 |
| ), |
| t2: UMLMM!OpaqueExpression ( |
| name <- 'typeConstraintExpression', |
| -- the type constraint |
| -- body <- 'self.base_Type.oclIsUndefined() or |
| -- (self.base_Type.oclIsTypeOf(uml::Class) or |
| -- self.base_Type.oclIsTypeOf(uml::Interface) or |
| -- self.base_Type.oclIsTypeOf(uml::Enumeration) or |
| -- self.base_Type.oclIsTypeOf(uml::Stereotype))', |
| body <- 'not self.base_Type.oclIsUndefined() implies Set{uml::Stereotype, |
| uml::Class, uml::Enumeration, uml::Interface} |
| -> includes(self.base_Type.oclType())', |
| -- the language we use to express the constraint |
| language <- 'OCL' |
| ) |
| } |
| |
| -- furthermore, we do need a constraint for the Operation extension if the Java element |
| -- type Constructor |
| -- is used |
| rule createConstructorConstraint(stereotype: UMLMM!Stereotype) { |
| to |
| t1: UMLMM!Constraint ( |
| name <- 'constructorConstraint', |
| constrainedElement <- stereotype, |
| context <- stereotype, |
| specification <- t2 |
| ), |
| t2: UMLMM!OpaqueExpression ( |
| name <- 'constructorConstraintExpression', |
| -- the type constraint |
| -- body <- 'self.base_Operation.oclIsUndefined() or |
| -- self.base_Operation.name = |
| -- self.base_Operation.oclContainer().oclAsType(uml::Classifier).name' |
| -- , |
| body <- 'not self.base_Operation.oclIsUndefined() implies |
| self.base_Operation.name = |
| self.base_Operation.oclContainer().oclAsType(uml::Classifier).name', |
| -- the language we use to express the constraint |
| language <- 'OCL' |
| ) |
| } |
| |
| -- since we translation methods of annotationtypedeclarations to properties, we do have |
| -- to take care |
| -- that such properties can be stereotyped if ElementType.Method is set |
| rule createMethodConstraint(stereotype: UMLMM!Stereotype) { |
| to |
| t1: UMLMM!Constraint ( |
| name <- 'methodConstraint', |
| constrainedElement <- stereotype, |
| context <- stereotype, |
| specification <- t2 |
| ), |
| t2: UMLMM!OpaqueExpression ( |
| name <- 'methodConstraintExpression', |
| -- the type constraint |
| -- body <- 'self.base_Property.oclIsUndefined() or |
| -- self.base_Property.oclContainer().oclIsTypeOf(uml::Stereotype)', |
| body <- 'not self.base_Property.oclIsUndefined() implies |
| self.base_Property.oclContainer().oclIsTypeOf(uml::Stereotype)', |
| -- the language we use to express the constraint |
| language <- 'OCL' |
| ) |
| } |
| |
| -- currently we do not use UML's support for templates and template binding! |
| --rule ParameterizedType2Class { |
| -- from |
| -- s1 : JMM!ParameterizedType |
| -- to |
| -- t1 : UMLMM!Class ( |
| -- name <- s1.name, |
| -- templateBinding <- s1.typeArguments |
| -- ) |
| --} |
| -- |
| --rule TypeAccessOfParameterizedType2TemplateBinding { |
| -- from |
| -- s1 : JMM!TypeAccess( |
| -- if s1.refImmediateComposite().oclIsTypeOf(JMM!ParameterizedType) |
| -- then s1.refImmediateComposite().typeArguments -> exists(e | e = s1) |
| -- else false endif) |
| -- to |
| -- t1 : UMLMM!TemplateBinding ( |
| -- signature <- |
| -- s1.refImmediateComposite().getParameterizedClassFromJavaLibrary().ownedTemplateSignatur |
| -- e |
| -- , |
| -- |
| -- parameterSubstitution <- t2 |
| -- ), |
| -- |
| -- t2 : UMLMM!TemplateParameterSubstitution ( |
| -- -- TODO : the assumption here is that we have only one parameter |
| -- formal <- |
| -- s1.refImmediateComposite().getParameterizedClassFromJavaLibrary().ownedTemplateSignature.ownedParameter.first( |
| -- ) |
| -- , |
| -- |
| -- actual <- if s1.type.oclIsTypeOf(JMM!AnnotationTypeDeclaration) then |
| -- s1.type else s1.getActualParameterFromModelLibrary() endif |
| -- -- TODO : in RSA the property ownedActual is set as well ... but then the |
| --- define step of UML2 plugin breaks! |
| -- ) |
| --} |