diff --git a/cruise.umple/src/Generator.ump b/cruise.umple/src/Generator.ump index 9a52392a99..c1c2bbfa67 100755 --- a/cruise.umple/src/Generator.ump +++ b/cruise.umple/src/Generator.ump @@ -78,6 +78,7 @@ use EventSequenceGenerator, FeatureDiagramCo, EntityRelationshipCon, Simulate use XtextGenerator, JsonGenerator, JsonMixedGenerator, StructureDiagramGenerator , GvStateDiagramGenerator; use StateTableGenerator, SuperGvGeneratorGenerator, HtmlGenerator, UmpleModelWalkerGenerator, CodeAnalysisGenerator; use AlloyGenerator, NuSMVGenerators, NuSMVGenerator, NuSMVOptimizerGenerator, SimpleMetricsGenerator, CodeGvClassDiagramGenerator; +use UmpleAnnotaiveToCompositionGenerator; //End // Base case generator, allows compilation without generation for testing command line, since Java is default @@ -124,5 +125,6 @@ mixset SuperGvGeneratorGenerator{ use generators/Generator_SuperGvGenerator.ump; mixset HtmlGenerator{ use generators/Generator_Html.ump;} mixset UmpleModelWalkerGenerator{ use generators/Generator_UmpleModelWalker.ump;} mixset CodeAnalysisGenerator{ use generators/Generator_CodeAnalysis.ump;} +mixset UmpleAnnotaiveToCompositionGenerator { use generators/Generator_CodeAnnotaiveToComposition.ump; } //End diff --git a/cruise.umple/src/class/UmpleInternalParser_CodeClass.ump b/cruise.umple/src/class/UmpleInternalParser_CodeClass.ump index 532b5923fe..84fd0dcf5a 100755 --- a/cruise.umple/src/class/UmpleInternalParser_CodeClass.ump +++ b/cruise.umple/src/class/UmpleInternalParser_CodeClass.ump @@ -3287,7 +3287,7 @@ private Boolean checkIsDistributed(UmpleInterface uInterface) String methodCode = token.getValue(); // process mixsets inside code mixset Mixset { - methodCode = processInlineMixset(token.getValue()); + methodCode = processInlineMixset(token.getValue(), meth); } // end of MIXSET cb.setCode(methodCode); aMethod.setCodePosition(token.getPosition()); @@ -4107,7 +4107,7 @@ private Boolean checkIsDistributed(UmpleInterface uInterface) { String aspectCode = sub.getValue(); mixset Mixset { - aspectCode = processInlineMixset(sub.getValue()); + aspectCode = processInlineMixset(sub.getValue(), null); } if(langs.size()==0) { diff --git a/cruise.umple/src/generators/Generator_CodeAnnotaiveToComposition.ump b/cruise.umple/src/generators/Generator_CodeAnnotaiveToComposition.ump new file mode 100644 index 0000000000..3e21aaf8ed --- /dev/null +++ b/cruise.umple/src/generators/Generator_CodeAnnotaiveToComposition.ump @@ -0,0 +1,135 @@ +/* + +Copyright: All contributers to the Umple Project + +This file is made available subject to the open source license found at: +http://umple.org/license + + */ + +namespace cruise.umple.compiler; + +class UmpleAnnotaiveToCompositionGenerator +{ + isA CodeGeneratorWithSubptions; + depend java.io.*; + depend java.util.*; + depend cruise.umple.util.*; + depend cruise.umple.compiler.exceptions.*; + depend java.util.stream.Collectors; + UmpleModel model = null; + String output = ""; + + fileExtension= ".ump"; + + // CENTRAL GENERATE METHOD + + public void generate(){ + StringBuilder code = new StringBuilder(); + List mixsetList = model.getMixsetOrFiles().stream().filter(mixset -> mixset.getIsMixset()).map(obj -> (Mixset)obj).collect(Collectors.toList()); + code.append("/* \n"); + code.append("Annotaive fragments are refactored into composition fragments. \n"); + code.append("Total mixsts definitions: "+mixsetList.size()); + code.append("\n*/ \n"); + code.append("//*** \n"); + code.append("//Coarse-grained variability : \n"); + + for (Mixset aMixset: mixsetList) + { + code.append("\n"); + code.append("mixset "+aMixset.getMixsetName() + " { ") ; + for (MixsetFragment mFragment : aMixset.getMixsetFragments()) + { + code.append("\n// Fragment source file: "+mFragment.getOriginalUmpFile().getFileName()); + code.append("\n// Line : "+mFragment.getOriginalUmpLine()); + code.append("\n"+mFragment.getBody()); + code.append("\n"); + } + code.append("\n}\n") ; + } + // + code.append("//*** \n"); + code.append("//Fine-grained variability : \n"); + // + int labelCount= 1; + List umpleClasses = model.getUmpleClasses().stream().filter(classM -> classM.getMethods().size() > 0).collect(Collectors.toList()); + for (UmpleClass aClass : umpleClasses) + { + String className = aClass.getName(); + String classInlineMixset =""; + try + { + List methodsWithInlineMixset = aClass.getMethods().stream().filter(classM -> classM.getMethodBody() != null).collect(Collectors.toList()); + for (Method method : methodsWithInlineMixset) + { + List mixsetInMethodList = method.getMethodBody().getMixsetInMethods(); + for (MixsetInMethod mixsetInMethod : mixsetInMethodList) + { + // prepare parameter types + String injectionParameters = ""; + List paramList = method.getMethodParameters().stream().map(aParam -> aParam.getType()).collect(Collectors.toList()); + String paramListCommaSeparated = String.join(",", paramList); + if(paramList.size() > 0) + injectionParameters = "("+paramListCommaSeparated+ ")"; + // define a labeled aspect injection. + String mixsetBody = mixsetInMethod.getMixsetFragment(); + mixsetBody = mixsetBody.substring(mixsetBody.indexOf("{")); + classInlineMixset+="mixset " + mixsetInMethod.getMixsetName() + " { \n"; + classInlineMixset+=" class " + className + " { \n"; // start class definition + classInlineMixset+=" before "+ "Lable_"+mixsetInMethod.getMixsetName()+"_"+labelCount+":"+ method.getName()+ injectionParameters+"\n "+ mixsetBody; + classInlineMixset+=" } \n"; + classInlineMixset+="} \n"; // end class definition. + labelCount ++; + // two empty line after each aspect injection. + classInlineMixset+="\n\n"; + } + } + if(classInlineMixset.trim().length() > 1) + { + code.append(classInlineMixset); + } + } + catch(NullPointerException ex) + { + continue; + } + } + code.append("\n"); + model.setCode(code.toString()); + writeModel(); + } + + + private String spacer(int num) + { + StringBuilder space = new StringBuilder(); + for (int i = 0; i < num; i++) + { + space.append(" "); + } + return space.toString(); + } + + private void writeModel() + { + try + { + String path = model.getUmpleFile().getPath(); + File file = new File(path); + file.mkdirs(); + String modelFilename = path + File.separator + model.getUmpleFile().getSimpleFileName()+"_refactoredToComposition" + this.fileExtension ; + BufferedWriter bw = new BufferedWriter(new FileWriter(modelFilename)); + bw.write(model.getCode()); + bw.flush(); + bw.close(); + } + catch (Exception e) + { + throw new UmpleCompilerException("There was a problem with generating UmpleSelf code." + e, e); + } + } + + + +} + diff --git a/cruise.umple/src/mixset/UmpleInternalParser_CodeMixset.ump b/cruise.umple/src/mixset/UmpleInternalParser_CodeMixset.ump index 34e2211246..bbe6b0d881 100644 --- a/cruise.umple/src/mixset/UmpleInternalParser_CodeMixset.ump +++ b/cruise.umple/src/mixset/UmpleInternalParser_CodeMixset.ump @@ -368,6 +368,17 @@ class UmpleInternalParser mixsetFragmentLineNumber = mixsetFragmentPosition.getLineNumber(); UmpleFile mixsetFragmentUmpleFile = model.getUmpleFile(); // where the mixset keyword is encountered. Not the use statement + // Start: update the fragment file if the fragment came from a file included by use statement. + Token lookForUseStToken = token; + while((lookForUseStToken = lookForUseStToken.getParentToken()) != null) + { + if(lookForUseStToken.is("useStatement")) + { + mixsetFragmentUmpleFile = new UmpleFile(lookForUseStToken.getSubToken("use").getValue()); + break; + } + } + //End MixsetFragment mixsetFragment = new MixsetFragment(mixsetFragmentUmpleFile, mixsetFragmentLineNumber, mixsetBody); return mixsetFragment; } @@ -452,14 +463,18 @@ class UmpleInternalParser } } // a helper method that process pure code to either remove it or to let mixset content stay. - private String processInlineMixset(String methodCode) { - // process mixsets inside code - ArrayList mixsetInCodeList = MethodBody.getMixsetsFromCode(methodCode) ; - for (MixsetInMethod aMixsetInMethod : mixsetInCodeList) - { - // update the code according to use statements (of mixsets) - methodCode = MethodBody.handelMixsetInsideMethod(this.getModel(), aMixsetInMethod, methodCode); - } + private String processInlineMixset(String methodCode, MethodBody methodBody) { + // process mixsets inside code + ArrayList mixsetInCodeList = MethodBody.getMixsetsFromCode(methodCode) ; + for (MixsetInMethod aMixsetInMethod : mixsetInCodeList) + { + // update the code according to use statements (of mixsets) + methodCode = MethodBody.handelMixsetInsideMethod(this.getModel(), aMixsetInMethod, methodCode); + if(methodBody !=null) + { + methodBody.addMixsetInMethod(aMixsetInMethod); + } + } return methodCode; } } diff --git a/cruise.umple/test/cruise/umple/compiler/mixset/UmpleMixsetTest.java b/cruise.umple/test/cruise/umple/compiler/mixset/UmpleMixsetTest.java index 387be0f00f..c130f95bb7 100644 --- a/cruise.umple/test/cruise/umple/compiler/mixset/UmpleMixsetTest.java +++ b/cruise.umple/test/cruise/umple/compiler/mixset/UmpleMixsetTest.java @@ -14,6 +14,8 @@ import cruise.umple.compiler.exceptions.*; import cruise.umple.compiler.php.PhpClassGenerator; import java.io.File; +import cruise.umple.compiler.UmpleAnnotaiveToCompositionGenerator; + public class UmpleMixsetTest { @@ -697,5 +699,25 @@ public void inlineMixsetsInsideAspectCode() //delete generated file SampleFileWriter.destroy(umpleParserTest.pathToInput+"/AspectClass.java"); } + @Test + public void refactorInlineMixsetIntoCompMixset() + { + UmpleFile umpleFile = new UmpleFile(umpleParserTest.pathToInput,"parseRefactorInlineMixsetIntoCompMixset.ump"); + UmpleModel umpModel = new UmpleModel(umpleFile); + umpModel.setShouldGenerate(true); + umpModel.run(); + UmpleAnnotaiveToCompositionGenerator umpleAnnotaiveToCompositionGenerator = new UmpleAnnotaiveToCompositionGenerator(); + umpleAnnotaiveToCompositionGenerator.setModel(umpModel); + umpleAnnotaiveToCompositionGenerator.generate(); + String generatedFile = umpleParserTest.pathToInput+"/parseRefactorInlineMixsetIntoCompMixset_refactoredToComposition.ump"; + SampleFileWriter.assertFileExists(generatedFile); + String templateGeneratedCode = SampleFileWriter.readContent(new File(generatedFile)); + // included mixsets + Assert.assertTrue(templateGeneratedCode.contains("class Bank { 1 -- 1..* Branch; }")); + SampleFileWriter.destroy(generatedFile); + SampleFileWriter.destroy(umpleParserTest.pathToInput+"/Bank.java"); + SampleFileWriter.destroy(umpleParserTest.pathToInput+"/Account.java"); + SampleFileWriter.destroy(umpleParserTest.pathToInput+"/Branch.java"); + } } diff --git a/cruise.umple/test/cruise/umple/compiler/mixset/parseRefactorInlineMixsetIntoCompMixset.ump b/cruise.umple/test/cruise/umple/compiler/mixset/parseRefactorInlineMixsetIntoCompMixset.ump new file mode 100644 index 0000000000..566e310776 --- /dev/null +++ b/cruise.umple/test/cruise/umple/compiler/mixset/parseRefactorInlineMixsetIntoCompMixset.ump @@ -0,0 +1,29 @@ +class Bank { + 1 -- * Account; + mixset Multibranch + { + 1 -- 1..* Branch; + } + + /* + void withdraw(int amount){ + if (amount > 0) + //some code + mixset Multibranch + { + amount = amount - someFees; + } + } + */ +} + +mixset Multibranch { + class Branch { + Integer id; String address; + } +} + +class Account { + owner; Integer number; Integer balance; + mixset Multibranch { * -- 1 Branch;} +} diff --git a/umpleonline/scripts/compiler_config.php b/umpleonline/scripts/compiler_config.php index 82a7a5e34b..e53f598576 100644 --- a/umpleonline/scripts/compiler_config.php +++ b/umpleonline/scripts/compiler_config.php @@ -313,6 +313,8 @@ function generateMenu($buttonSuffix) + +