Permalink
Browse files

Merge pull request #814 from Shikib/ruby/php-codegen

implemented custom code injection functionality/parameter based injection for ruby/php
  • Loading branch information...
2 parents 2a5bd57 + 3f37e96 commit 9bb16a461ef386aa29e15b2bc5cce2413e80509d @TimLethbridge TimLethbridge committed Jun 3, 2016
Showing with 1,050 additions and 1 deletion.
  1. +125 −0 UmpleToPhp/UmpleTLTemplates/class_MethodDeclaration.ump
  2. +93 −1 UmpleToRuby/UmpleTLTemplates/class_MethodDeclaration.ump
  3. +39 −0 cruise.umple/test/cruise/umple/implementation/ClassTemplateTest.java
  4. +52 −0 cruise.umple/test/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsBasic.php.txt
  5. +65 −0 cruise.umple/test/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsComments.php.txt
  6. +58 −0 cruise.umple/test/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsNoBraces.php.txt
  7. +53 −0 ...mple/test/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsParametersBasic.php.txt
  8. +68 −0 ...mple/test/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsParametersMulti.php.txt
  9. +57 −0 ...est/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsParametersUnspecified.php.txt
  10. +60 −0 cruise.umple/test/cruise/umple/implementation/php/ClassTemplateTest_CodeInjectionsSingleLine.php.txt
  11. +1 −0 cruise.umple/test/cruise/umple/implementation/php/PhpClassTemplateTest.java
  12. +49 −0 cruise.umple/test/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsBasic.ruby.txt
  13. +59 −0 cruise.umple/test/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsComments.ruby.txt
  14. +52 −0 cruise.umple/test/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsNoBraces.ruby.txt
  15. +50 −0 ...le/test/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsParametersBasic.ruby.txt
  16. +61 −0 ...le/test/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsParametersMulti.ruby.txt
  17. +52 −0 ...t/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsParametersUnspecified.ruby.txt
  18. +56 −0 ...e.umple/test/cruise/umple/implementation/ruby/ClassTemplateTest_CodeInjectionsSingleLine.ruby.txt
@@ -10,6 +10,8 @@ class UmpleToPhp {
String methodImplementationModifier = aMethod.getIsAbstract() ? " abstract" : "";
String methodName = aMethod.getName();
String methodType = "function";
+ String customBeforeInjectionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjectionsCustomMethod("before", aMethod.getName(), aMethod.getMethodParameters()));
+ String customAfterInjectionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjectionsCustomMethod("after", aMethod.getName(), aMethod.getMethodParameters()));
String customPreconditionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjections("before", aMethod.getName()+"Precondition"));String customPostconditionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjections("before", aMethod.getName()+"Postcondition"));
customPostconditionCode = customPostconditionCode==null?"":customPostconditionCode;
@@ -72,7 +74,130 @@ class UmpleToPhp {
{
appendln(realSb, "\n {");
if (customPreconditionCode != null) { append(realSb, "\n{0}\n",GeneratorHelper.doIndent(customPreconditionCode, " "));}
+ if (customBeforeInjectionCode != null) { append(realSb, "\n{0}\n",GeneratorHelper.doIndent(customBeforeInjectionCode, " "));}
+
+ if(properMethodBody.contains("return"))
+ {
+ if(customAfterInjectionCode != null) {
+ // Do some pre-processing to handle returns not being on a new line. Doing this allows us to maintain suitable indentation.
+ String[] properMethodLines = properMethodBody.split("\\n");
+ String fixedProperMethodBody = "";
+ for(int i = 0; i < properMethodLines.length; i++) {
+ if(properMethodLines[i].contains("return") && !properMethodLines[i].trim().substring(0, 6).equals("return")) {
+ String[] splitLines = properMethodLines[i].split("return", 2);
+ // Determine indentation of return by adding indentation amount to previous line
+ String returnIndent = "";
+ int j = 0;
+ while(splitLines[0].charAt(j) == ' ') {
+ returnIndent += " ";
+ j++;
+
+ }
+
+ fixedProperMethodBody += returnIndent + splitLines[0].trim() + "\n";
+
+ String[] returnLines = splitLines[1].split(";");
+ if(returnLines.length > 1 && returnLines[1].trim().length() > 0) {
+ fixedProperMethodBody += returnIndent + " return " + returnLines[0].trim() + ";\n" + returnIndent + returnLines[1].trim() + "\n";
+ } else {
+ fixedProperMethodBody += returnIndent + " return " + splitLines[1].trim() + "\n";
+ }
+ } else {
+ fixedProperMethodBody += properMethodLines[i] + "\n";
+ }
+ }
+
+ properMethodBody = fixedProperMethodBody;
+
+ String properMethodIndent = "";
+ int indentIndex = 0;
+ while(indentIndex < properMethodBody.length() && properMethodBody.charAt(indentIndex) == ' ') {
+ properMethodIndent += " ";
+ indentIndex++;
+ }
+
+ // inject the after injection code after every return, while appropriate indentation
+ for(int i = -1; (i = properMethodBody.indexOf("return", i + 1)) != -1; ) {
+ // determine the indentation of the return
+ String indent = "";
+ while(i >= 1 && properMethodBody.charAt(--i) == ' ') {
+ indent += " ";
+ }
+
+ // Need to determine if block has braces surrounding it. To do this, take the previous
+ // lines of code and apply some regex to remove all of the comments.
+ String[] previousLinesOfCode = properMethodBody.substring(0, i+1).replaceAll("\\/\\*([\\S\\s]+?)\\*\\/", "").replaceAll("(?s)/\\*.*?\\*/", "").replaceAll("//.*$", "").replaceAll("#.*$", "").split("\\n");
+ int commentLineCount = properMethodBody.substring(0, i+1).split("\\n").length - previousLinesOfCode.length;
+
+ // set previousLine to be the first non-empty line
+ int previousLine = -1;
+ for(int j = previousLinesOfCode.length - 1; j >= 0; j--) {
+ if(previousLinesOfCode[j].trim().length() > 0) {
+ previousLine = j;
+ break;
+ }
+ }
+
+ String previousLineStr = previousLinesOfCode[previousLine].trim();
+
+ // Need to subtract the number of lines of comments between the return and the previous line of code
+ while(!properMethodBody.split("\\n")[previousLine + commentLineCount].trim().equals(previousLineStr)) {
+ commentLineCount--;
+ }
+
+ // If we need to, insert braces, otherwise continue as normal
+ String indentedCustomAfterInjectionCode = GeneratorHelper.doIndent("\n" + customAfterInjectionCode, indent);
+ String braceIndent = "";
+ String brace = "";
+ String braceNewLine = "";
+ if(previousLine != -1 && (previousLineStr.charAt(previousLineStr.length()-1) == ')' || (previousLineStr.length() >= 4 && previousLineStr.substring(previousLineStr.length()-4).equals("else")))) {
+ String[] methodLines = properMethodBody.split("\\n");
+ previousLine += commentLineCount;
+
+ // determine how indented the brace is
+ int j = 0;
+ while(j < methodLines[previousLine].length() && methodLines[previousLine].charAt(j) == ' ') {
+ braceIndent += " ";
+ j++;
+ }
+
+ methodLines[previousLine] = braceIndent + methodLines[previousLine].trim() + " {";
+
+ // Set properMethodBody to be String.join(methodLines, "\\n")
+ String newProperMethodBody = "";
+ for(int k = 0; k < methodLines.length; k++) {
+ newProperMethodBody += methodLines[k];
+ if(k != methodLines.length - 1) {
+ newProperMethodBody += "\n";
+ }
+ }
+ properMethodBody = newProperMethodBody;
+
+ brace = "}";
+ braceNewLine = "\n";
+ }
+
+ i += indent.length() + 1;
+ String[] returnAndRest = properMethodBody.substring(i).split(";", 2);
+ properMethodBody = properMethodIndent + properMethodBody.substring(0, i).trim() + indentedCustomAfterInjectionCode + "\n" + indent + returnAndRest[0].trim() + ";" + braceNewLine + braceIndent + brace + returnAndRest[1];
+ i += indentedCustomAfterInjectionCode.length() + braceIndent.length() + 7;
+ }
+
+ // if the last line isn't a return, insert the injection at the very end
+ String[] lines = properMethodBody.split("\\n");
+ if(!lines[lines.length-1].contains("return")) {
+ properMethodBody += GeneratorHelper.doIndent("\n" + customAfterInjectionCode, " ");
+ }
+ }
+ }
+
appendln(realSb, properMethodBody);
+
+ if(!properMethodBody.contains("return"))
+ {
+ if (customAfterInjectionCode != null) { append(realSb, "{0}\n",GeneratorHelper.doIndent(customAfterInjectionCode, " "));}
+ }
+
appendln(realSb, " }");
}
}
@@ -6,6 +6,8 @@ class UmpleToRuby {
{
String methodName = aMethod.getName();
String methodType = aMethod.getType();
+ String customBeforeInjectionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjectionsCustomMethod("before", aMethod.getName(), aMethod.getMethodParameters()));
+ String customAfterInjectionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjectionsCustomMethod("after", aMethod.getName(), aMethod.getMethodParameters()));
String customPreconditionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjections("before", aMethod.getName()+"Precondition"));
String customPostconditionCode = GeneratorHelper.toCode(uClass.getApplicableCodeInjections("before", aMethod.getName()+"Postcondition"));
customPostconditionCode = customPostconditionCode==null?"":customPostconditionCode;
@@ -18,7 +20,6 @@ class UmpleToRuby {
}
String properMethodBody = " " + methodBody;
-
String finalParams = "";
StringBuilder parameters = new StringBuilder();
if (aMethod.hasMethodParameters())
@@ -55,8 +56,99 @@ class UmpleToRuby {
}
if (customPreconditionCode != null) { append(realSb, "\n{0}\n",GeneratorHelper.doIndent(customPreconditionCode, " "));}
+ if (customBeforeInjectionCode != null) { append(realSb, "{0}\n",GeneratorHelper.doIndent(customBeforeInjectionCode, " "));}
+
+ if(properMethodBody.contains("return")) {
+ if(customAfterInjectionCode != null) {
+ // Do some pre-processing to handle returns not being on a new line. Doing this allows us to maintain suitable indentation.
+ String[] properMethodLines = properMethodBody.split("\\n");
+ String fixedProperMethodBody = "";
+ for(int i = 0; i < properMethodLines.length; i++) {
+ if(properMethodLines[i].contains("return") && !properMethodLines[i].trim().substring(0, 6).equals("return")) {
+ String[] splitLines = properMethodLines[i].split("return", 2);
+ // Determine indentation of return by adding indentation amount to previous line
+ String returnIndent = "";
+ int j = 0;
+ while(splitLines[0].charAt(j) == ' ') {
+ returnIndent += " ";
+ j++;
+ }
+
+ fixedProperMethodBody += returnIndent + splitLines[0].trim() + "\n";
+
+ String[] returnLines = splitLines[1].split(";");
+ if(returnLines.length > 1 && returnLines[1].trim().length() > 0) {
+ fixedProperMethodBody += returnIndent + " return " + returnLines[0].trim() + ";\n" + returnIndent + returnLines[1].trim() + "\n";
+ } else {
+ fixedProperMethodBody += returnIndent + " return " + splitLines[1].trim() + "\n";
+ }
+ } else {
+ fixedProperMethodBody += properMethodLines[i] + "\n";
+ }
+ }
+
+ properMethodBody = fixedProperMethodBody;
+
+ String properMethodIndent = "";
+ int indentIndex = 0;
+ while(indentIndex < properMethodBody.length() && properMethodBody.charAt(indentIndex) == ' ') {
+ properMethodIndent += " ";
+ indentIndex++;
+ }
+
+ // inject the after injection code after every return, while appropriate indentation
+ for(int i = -1; (i = properMethodBody.indexOf("return", i + 1)) != -1; ) {
+ // determine the indentation of the return
+ String indent = "";
+ while(i >= 1 && properMethodBody.charAt(--i) == ' ') {
+ indent += " ";
+ }
+
+ // Need to determine if block has braces surrounding it. To do this, take the previous
+ // lines of code and apply some regex to remove all of the comments.
+ String[] previousLinesOfCode = properMethodBody.substring(0, i+1).replaceAll("\\/\\*([\\S\\s]+?)\\*\\/", "").replaceAll("(?s)/\\*.*?\\*/", "").replaceAll("//.*$", "").split("\\n");
+ int commentLineCount = properMethodBody.substring(0, i+1).split("\\n").length - previousLinesOfCode.length;
+
+ // set previousLine to be the first non-empty line
+ int previousLine = -1;
+ for(int j = previousLinesOfCode.length - 1; j >= 0; j--) {
+ if(previousLinesOfCode[j].trim().length() > 0) {
+ previousLine = j;
+ break;
+ }
+ }
+
+ String previousLineStr = previousLinesOfCode[previousLine].trim();
+
+ // Need to subtract the number of lines of comments between the return and the previous line of code
+ while(!properMethodBody.split("\\n")[previousLine + commentLineCount].trim().equals(previousLineStr)) {
+ commentLineCount--;
+ }
+
+ // If we need to, insert braces, otherwise continue as normal
+ String indentedCustomAfterInjectionCode = GeneratorHelper.doIndent("\n" + customAfterInjectionCode, indent);
+
+ i += indent.length() + 1;
+ String[] returnAndRest = properMethodBody.substring(i).split("\n", 2);
+ properMethodBody = properMethodIndent + properMethodBody.substring(0, i).trim() + indentedCustomAfterInjectionCode + "\n" + indent + returnAndRest[0].trim() + "\n" + returnAndRest[1];
+ i += indentedCustomAfterInjectionCode.length() + 4;
+ }
+
+ // if the last line isn't a return, insert the injection at the very end
+ String[] lines = properMethodBody.split("\\n");
+ if(!lines[lines.length-1].contains("return")) {
+ properMethodBody += GeneratorHelper.doIndent("\n" + customAfterInjectionCode, " ");
+ }
+ }
+ }
appendln(realSb, properMethodBody);
+
+ if(!properMethodBody.contains("return"))
+ {
+ if (customAfterInjectionCode != null) { append(realSb, "{0}\n",GeneratorHelper.doIndent(customAfterInjectionCode, " "));}
+ }
+
appendln(realSb, " end");
}
@@ -116,7 +116,46 @@ public void NoPackageForClass()
}
+ @Test
+ public void ClassCodeInjections_Basic()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsBasic.ump", languagePath + "/ClassTemplateTest_CodeInjectionsBasic." + languagePath + ".txt", "Student");
+ }
+
+ @Test
+ public void ClassCodeInjections_NoBraces()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsNoBraces.ump", languagePath + "/ClassTemplateTest_CodeInjectionsNoBraces." + languagePath + ".txt", "Student");
+ }
+ @Test
+ public void ClassCodeInjections_Comments()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsComments.ump", languagePath + "/ClassTemplateTest_CodeInjectionsComments." + languagePath + ".txt", "Student");
+ }
+
+ @Test
+ public void ClassCodeInjections_SingleLine()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsSingleLine.ump", languagePath + "/ClassTemplateTest_CodeInjectionsSingleLine." + languagePath + ".txt", "Student");
+ }
+
+ public void ClassCodeInjections_ParametersBasic()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsParametersBasic.ump", languagePath + "/ClassTemplateTest_CodeInjectionsParametersBasic." + languagePath + ".txt", "Student");
+ }
+
+ @Test
+ public void ClassCodeInjections_ParametersUnspecified()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsParametersUnspecified.ump", languagePath + "/ClassTemplateTest_CodeInjectionsParametersUnspecified." + languagePath + ".txt", "Student");
+ }
+
+ @Test
+ public void ClassCodeInjections_ParametersMulti()
+ {
+ assertUmpleTemplateFor("ClassTemplateTest_CodeInjectionsParametersMulti.ump", languagePath + "/ClassTemplateTest_CodeInjectionsParametersMulti." + languagePath + ".txt", "Student");
+ }
@Test
@@ -0,0 +1,52 @@
+<?php
+/*PLEASE DO NOT EDIT THIS CODE*/
+/*This code was generated using the UMPLE @UMPLE_VERSION@ modeling language!*/
+
+class Student
+{
+
+ //------------------------
+ // MEMBER VARIABLES
+ //------------------------
+
+ //------------------------
+ // CONSTRUCTOR
+ //------------------------
+
+ public function __construct()
+ {}
+
+ //------------------------
+ // INTERFACE
+ //------------------------
+
+ public function equals($compareTo)
+ {
+ return $this == $compareTo;
+ }
+
+ public function delete()
+ {}
+
+ public function foo(int a)
+ {
+
+ System.out.println("Starting foo with argument: " + a);
+ if(a < 0) {
+ System.out.println("Returning from foo!");
+ return 0;
+ }
+
+ for(int i = 0; i < a; i++) {
+ if(i == a/4) {
+ System.out.println("Returning from foo!");
+ return a;
+ }
+ }
+ System.out.println("Returning from foo!");
+ return 4;
+
+ }
+
+}
+?>
Oops, something went wrong.

0 comments on commit 9bb16a4

Please sign in to comment.