diff --git a/src/main/java/com/appland/appmap/output/v1/CodeObject.java b/src/main/java/com/appland/appmap/output/v1/CodeObject.java index 6b0e1369..059a0af1 100644 --- a/src/main/java/com/appland/appmap/output/v1/CodeObject.java +++ b/src/main/java/com/appland/appmap/output/v1/CodeObject.java @@ -1,18 +1,14 @@ package com.appland.appmap.output.v1; import com.alibaba.fastjson.annotation.JSONField; - import com.appland.appmap.util.Logger; - import javassist.CtBehavior; import javassist.CtClass; +import javassist.bytecode.SourceFileAttribute; import java.lang.reflect.Modifier; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Collections; +import java.util.*; +import java.util.regex.Matcher; /** * Represents a package, class or method. @@ -79,7 +75,7 @@ public CodeObject setLocation(String location) { public List getChildren() { return this.children; } - + /** * Return the list of children, or an empty list if there are * none. {@code getChildren} behaves differently to satisfy @@ -100,15 +96,15 @@ private CodeObject setFile(String file) { this.file = file; return this; } - + @JSONField(serialize = false, deserialize = false) private Integer lineno; private CodeObject setLineno(Integer lineno) { this.lineno = lineno; return this; } - - + + @Override public boolean equals(Object obj) { if (obj == null) { @@ -129,7 +125,7 @@ public boolean equals(Object obj) { && codeObject.isStatic == this.isStatic && (codeObject.file == null? this.file == null : codeObject.file.equals(this.file)) && codeObject.lineno == this.lineno; - + } /** @@ -194,12 +190,25 @@ public CodeObject(CodeObject src) { * @return An estimated source file path */ public static String getSourceFilePath(CtClass classType) { - String[] parts = { - "src", "main", "java", - classType.getPackageName().replace('.', '/'), - classType.getClassFile().getSourceFile() - }; - return String.join("/", parts); + String sourceFilePath = null; + if (classType.getClassFile2().getAttribute("SourceFile") != null) { + sourceFilePath = getSourceFilePathWithDebugInfo(classType); + } else { + sourceFilePath = getSourceCodePath(classType); + } + return sourceFilePath; + } + + private static String getSourceFilePathWithDebugInfo(CtClass classType) { + return classType.getName().substring(0, classType.getName().lastIndexOf(".")) + .replaceAll("\\.", Matcher.quoteReplacement("/")) + + "/" + ((SourceFileAttribute) classType.getClassFile2() + .getAttribute("SourceFile")).getFileName(); + } + + private static String getSourceCodePath(CtClass classType) { + return classType.getName().replaceAll("\\.", Matcher.quoteReplacement("/")) + .replaceAll("\\$\\w+", "") + ".java"; } /** @@ -321,7 +330,7 @@ public CodeObject get(String path) { public CodeObject findChild(String name, Boolean isStatic, int lineNumber) { for (CodeObject child : this.safeGetChildren()) { if (child.name.equals(name) - && child.isStatic == isStatic + && child.isStatic == isStatic && child.lineno == lineNumber) { return child; } @@ -357,7 +366,7 @@ private boolean equalBySubstring(String s1, String s2, int start, int end) { return true; } - + public CodeObject findChildBySubstring(String name, int start, int end) { for (CodeObject child : this.safeGetChildren()) { if (equalBySubstring(child.name, name, start, end)) { @@ -367,7 +376,7 @@ public CodeObject findChildBySubstring(String name, int start, int end) { return null; } - + /** * Add an immediate child to this CodeObject. * @param child The child to be added diff --git a/src/test/java/com/appland/appmap/output/v1/CodeObjectTest.java b/src/test/java/com/appland/appmap/output/v1/CodeObjectTest.java new file mode 100644 index 00000000..1c6edbdb --- /dev/null +++ b/src/test/java/com/appland/appmap/output/v1/CodeObjectTest.java @@ -0,0 +1,31 @@ +package com.appland.appmap.output.v1; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class CodeObjectTest { + + @Test + public void getSourceFilePath_for_RegularClass() throws NotFoundException { + CtClass testCtClass = ClassPool.getDefault().get("com.appland.appmap.ExampleClass"); + assertEquals("com/appland/appmap/ExampleClass.java", CodeObject.getSourceFilePath(testCtClass)); + } + + @Test + public void getSourceFilePath_for_InnerClass_ResultInBaseClass() throws NotFoundException { + CtClass testCtClass = ClassPool.getDefault().get("com.appland.appmap.output.v1.testclasses.ExampleInnerClass$StaticFinalInnerClass"); + assertEquals("com/appland/appmap/output/v1/testclasses/ExampleInnerClass.java", CodeObject.getSourceFilePath(testCtClass)); + } + + @Test + public void getSourceFilePath_for_AnonymousClass_ResultInBaseClass() throws NotFoundException { + CtClass testCtClass = ClassPool.getDefault().get("com.appland.appmap.output.v1.testclasses.Anonymous$1"); + assertEquals("com/appland/appmap/output/v1/testclasses/Anonymous.java", CodeObject.getSourceFilePath(testCtClass)); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/appland/appmap/output/v1/testclasses/Anonymous.java b/src/test/java/com/appland/appmap/output/v1/testclasses/Anonymous.java new file mode 100644 index 00000000..d76e3793 --- /dev/null +++ b/src/test/java/com/appland/appmap/output/v1/testclasses/Anonymous.java @@ -0,0 +1,13 @@ +package com.appland.appmap.output.v1.testclasses; + +public class Anonymous { + + public static Runnable getAnonymousImpl(){ + return new Runnable() { + @Override + public void run() { + System.err.println("Hello Anonymous!"); + } + }; + } +} diff --git a/src/test/java/com/appland/appmap/output/v1/testclasses/ExampleInnerClass.java b/src/test/java/com/appland/appmap/output/v1/testclasses/ExampleInnerClass.java new file mode 100644 index 00000000..6495e09d --- /dev/null +++ b/src/test/java/com/appland/appmap/output/v1/testclasses/ExampleInnerClass.java @@ -0,0 +1,11 @@ +package com.appland.appmap.output.v1.testclasses; + +import com.appland.appmap.ExampleClass; + +public class ExampleInnerClass extends ExampleClass { + + private static final class StaticFinalInnerClass { + + } + +} diff --git a/src/test/java/com/appland/appmap/test/util/MethodBuilderTest.java b/src/test/java/com/appland/appmap/test/util/MethodBuilderTest.java index b4320377..b92e29fb 100644 --- a/src/test/java/com/appland/appmap/test/util/MethodBuilderTest.java +++ b/src/test/java/com/appland/appmap/test/util/MethodBuilderTest.java @@ -32,7 +32,7 @@ public void testBuild() throws Exception { Method myMethod = testClass.getMethod("myMethod"); myMethod.invoke(obj); - assertEquals(myMethodMessage + "\n", systemOutRule.getLog()); + assertEquals(myMethodMessage + System.getProperty("line.separator"), systemOutRule.getLog()); } @Test @@ -52,7 +52,7 @@ public void testMultiParameter() throws Exception { double y = 12.0; myMethod.invoke(obj, x, y); - assertEquals(x + " " + y + "\n", systemOutRule.getLog()); + assertEquals(x + " " + y + System.getProperty("line.separator"), systemOutRule.getLog()); assertEquals(2, myMethod.getParameterCount()); assertArrayEquals(new Class[]{ int.class, double.class }, myMethod.getParameterTypes());