Repository: SpoonLabs/gumtree-spoon-ast-diff (java)

Issue:
Throw exception when insert `static` at a method head
#### Input
```java
AstComparator comp = new AstComparator();
        Diff diff = comp.compare("public class Calculator {\n" +
                "    public int add(int a, int b){\n" +
                "        return a + b;\n" +
                "    }\n" +
                "}", "public class Calculator {\n" +
                "    public static int add(int a, int b){\n" +
                "        return a + b;\n" +
                "    }\n" +
                "}");
```

#### Result
The operation `Delete class` is wrong since the class name is not changed!

The move operation is wired and throws exception!

<img width="1205" alt="Screen Shot 2019-04-08 at 4 12 48 PM" src="https://user-images.githubusercontent.com/23020574/55708831-7da29500-5a19-11e9-8e81-76e2ace81855.png">


Pull request:
[bugfix] fixing #86. Matching 'compatible' modifiers
We add to the label of nodes "Modifiers" the class of the modifiable class. So, for instance, the modifiers of a class can be only matched with those from another class, (preventing matching
with modifiables of parameters, methods, etc).
Detailed explanation:
I found the bug and a possible solution.
Bug: before we had GumTree nodes Modifiers that grouped the modifiers of a CtModifiable (Method, class, param, field, etc). The last change I introduced added a Virtual Node for those modifiers that are not included in the spoon model, but that's not related to the bug (I think).
The problem is when matching modifiers: GT finds several of Trees with similar shape (i.e., Modifier root with child public), so in the buggy code it matches the modifiers of one CtElement (e.g., method) with those from another kind of elements (e.g., field).
Solution: to change the type of the GumTree nodes (and to the new virtual as well) that represent "Modifiers": instead of having only type Modifiers we add the class name of the CtModifiable. As consequence, Gumtree only will match modifiers that belong to the same CtElement.
WDYT?

Fix #87 
Fix #86 


Patch:
diff --git a/src/main/java/gumtree/spoon/builder/CtVirtualElement.java b/src/main/java/gumtree/spoon/builder/CtVirtualElement.java
index b908d06..63e1606 100644
--- a/src/main/java/gumtree/spoon/builder/CtVirtualElement.java
+++ b/src/main/java/gumtree/spoon/builder/CtVirtualElement.java
@@ -27,7 +27,7 @@ public class CtVirtualElement extends CtWrapper<String> {
 
 	@Override
 	public String toString() {
-		return "Virtual Element " + ((value != null) ? ("with label: " + value.toString()) : null);
+		return "VE: " + ((value != null) ? (value.toString()) : null);
 	}
 
 	@Override
diff --git a/src/main/java/gumtree/spoon/builder/NodeCreator.java b/src/main/java/gumtree/spoon/builder/NodeCreator.java
index 7b03b9a..e43d697 100644
--- a/src/main/java/gumtree/spoon/builder/NodeCreator.java
+++ b/src/main/java/gumtree/spoon/builder/NodeCreator.java
@@ -25,10 +25,12 @@ class NodeCreator extends CtInheritanceScanner {
 
 	@Override
 	public void scanCtModifiable(CtModifiable m) {
-		ITree modifiers = builder.createNode("Modifiers", "");
+		// We add the type of modifiable element
+		String type = "Modifiers_" + getClassName(m.getClass().getSimpleName());
+		ITree modifiers = builder.createNode(type, "");
 
-		//We create a virtual node
-		modifiers.setMetadata(SpoonGumTreeBuilder.SPOON_OBJECT, new CtVirtualElement("Modifiers", m, m.getModifiers()));
+		// We create a virtual node
+		modifiers.setMetadata(SpoonGumTreeBuilder.SPOON_OBJECT, new CtVirtualElement(type, m, m.getModifiers()));
 
 		// ensuring an order (instead of hashset)
 		// otherwise some flaky tests in CI
@@ -43,13 +45,19 @@ class NodeCreator extends CtInheritanceScanner {
 		for (ModifierKind kind : modifiers1) {
 			ITree modifier = builder.createNode("Modifier", kind.toString());
 			modifiers.addChild(modifier);
-			//We wrap the modifier (which is not a ctelement)
+			// We wrap the modifier (which is not a ctelement)
 			modifier.setMetadata(SpoonGumTreeBuilder.SPOON_OBJECT, new CtWrapper(kind, m));
 		}
 		builder.addSiblingNode(modifiers);
 
 	}
 
+	private String getClassName(String simpleName) {
+		if (simpleName == null)
+			return "";
+		return simpleName.replace("Ct", "").replace("Impl", "");
+	}
+
 	@Override
 	public <T> void scanCtVariable(CtVariable<T> e) {
 		CtTypeReference<T> type = e.getType();


Test patch:
diff --git a/src/test/java/gumtree/spoon/AstComparatorTest.java b/src/test/java/gumtree/spoon/AstComparatorTest.java
index 802c841..ed1ccde 100644
--- a/src/test/java/gumtree/spoon/AstComparatorTest.java
+++ b/src/test/java/gumtree/spoon/AstComparatorTest.java
@@ -14,8 +14,12 @@ import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
 
+import com.github.gumtreediff.matchers.Mapping;
+import com.github.gumtreediff.tree.ITree;
+
 import gumtree.spoon.builder.SpoonGumTreeBuilder;
 import gumtree.spoon.diff.Diff;
+import gumtree.spoon.diff.DiffImpl;
 import gumtree.spoon.diff.operations.MoveOperation;
 import gumtree.spoon.diff.operations.Operation;
 import gumtree.spoon.diff.operations.OperationKind;
@@ -1641,4 +1645,17 @@ public class AstComparatorTest {
 		assertTrue(result.containsOperation(OperationKind.Insert, "Invocation"));
 		System.setProperty("nolabel", "false");
 	}
+
+	@Test
+	public void testInsertStaticFromMethodHead() throws Exception {
+		AstComparator diff = new AstComparator();
+		Diff result = diff.compare(
+				"public class Calculator {\n" + "    public int add(int a, int b){\n" + "        return a + b;\n"
+						+ "    }\n" + "}",
+				"public class Calculator {\n" + "    public static int add(int a, int b){\n" + "        return a + b;\n"
+						+ "    }\n" + "}");
+
+		assertEquals(1, result.getRootOperations().size());
+
+	}
 }
diff --git a/src/test/resources/examples/spoon.json b/src/test/resources/examples/spoon.json
index ac5cfac..3afb0a8 100644
--- a/src/test/resources/examples/spoon.json
+++ b/src/test/resources/examples/spoon.json
@@ -8,7 +8,7 @@
       "children": [
         {
           "label": "",
-          "type": "Modifiers",
+          "type": "Modifiers_Class",
           "children": [
             {
               "label": "public",
@@ -28,7 +28,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "final",
@@ -70,7 +70,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "private",
@@ -92,7 +92,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "private",
@@ -114,7 +114,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "private",
@@ -136,7 +136,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "private",
@@ -158,7 +158,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "private",
@@ -180,7 +180,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Field",
               "children": [
                 {
                   "label": "private",
@@ -197,7 +197,7 @@
           "children": [
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Constructor",
               "children": [
                 {
                   "label": "public",
@@ -217,7 +217,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_Parameter",
                   "children": []
                 }
               ]
@@ -233,7 +233,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_Parameter",
                   "children": []
                 }
               ]
@@ -879,7 +879,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_LocalVariable",
                   "children": []
                 },
                 {
@@ -978,7 +978,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1032,7 +1032,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1081,7 +1081,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1101,7 +1101,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_Parameter",
                   "children": []
                 }
               ]
@@ -1173,7 +1173,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "private",
@@ -1193,7 +1193,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_LocalVariable",
                   "children": []
                 },
                 {
@@ -1376,7 +1376,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_LocalVariable",
                   "children": []
                 },
                 {
@@ -1483,7 +1483,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1558,7 +1558,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1634,7 +1634,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1731,7 +1731,7 @@
                     },
                     {
                       "label": "",
-                      "type": "Modifiers",
+                      "type": "Modifiers_LocalVariable",
                       "children": []
                     },
                     {
@@ -1807,7 +1807,7 @@
                         },
                         {
                           "label": "",
-                          "type": "Modifiers",
+                          "type": "Modifiers_CatchVariable",
                           "children": []
                         }
                       ]
@@ -1893,7 +1893,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -1913,7 +1913,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_LocalVariable",
                   "children": []
                 },
                 {
@@ -2129,7 +2129,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "public",
@@ -2229,7 +2229,7 @@
             },
             {
               "label": "",
-              "type": "Modifiers",
+              "type": "Modifiers_Method",
               "children": [
                 {
                   "label": "private",
@@ -2322,7 +2322,7 @@
                 },
                 {
                   "label": "",
-                  "type": "Modifiers",
+                  "type": "Modifiers_LocalVariable",
                   "children": []
                 },
                 {
@@ -2381,7 +2381,7 @@
                     },
                     {
                       "label": "",
-                      "type": "Modifiers",
+                      "type": "Modifiers_LocalVariable",
                       "children": []
                     },
                     {
@@ -2408,7 +2408,7 @@
                     },
                     {
                       "label": "",
-                      "type": "Modifiers",
+                      "type": "Modifiers_LocalVariable",
                       "children": []
                     }
                   ]
@@ -2533,7 +2533,7 @@
                         },
                         {
                           "label": "",
-                          "type": "Modifiers",
+                          "type": "Modifiers_CatchVariable",
                           "children": []
                         }
                       ]
@@ -2615,7 +2615,7 @@
                         },
                         {
                           "label": "",
-                          "type": "Modifiers",
+                          "type": "Modifiers_CatchVariable",
                           "children": []
                         }
                       ]