package org.netbeans.gradle.project.output; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.net.URL; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jtrim.utils.ExceptionHelper; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.queries.SourceForBinaryQuery; import org.netbeans.api.project.Project; import org.netbeans.gradle.project.java.query.GradleClassPathProvider; import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation; import org.openide.filesystems.FileObject; import org.openide.filesystems.URLMapper; public final class StackTraceConsumer implements OutputLinkFinder { private static final Logger LOGGER = Logger.getLogger(StackTraceConsumer.class.getName()); private static final Pattern LINE_PATTERN = Pattern.compile("(?:\\[catch\\])?\\sat (.*)\\((.*)\\.java\\:(\\d+)\\)"); private final Project project; private final ClassPath classPath; public StackTraceConsumer(Project project) { ExceptionHelper.checkNotNullArgument(project, "project"); this.project = project; this.classPath = getClassPathFromProject(project); } private static ClassPath getClassPathFromProject(Project project) { GradleClassPathProvider classPaths = project.getLookup().lookup(GradleClassPathProvider.class); if (classPaths == null) { LOGGER.log(Level.WARNING, "No class path provider for project: {0}", project.getProjectDirectory()); return ClassPath.EMPTY; } ClassPath classPath = classPaths.getAllRuntimeClassPaths(); if (classPath == null) { LOGGER.log(Level.WARNING, "No runtime class path for project: {0}", project.getProjectDirectory()); return ClassPath.EMPTY; } return classPath; } private OpenEditorOutputListener tryCreateLinkListener( SourceForBinaryQuery.Result sourceForBinary, String path, String lineNum) { FileObject[] roots = sourceForBinary.getRoots(); for (FileObject root: roots) { FileObject javaFo = root.getFileObject(path); if (javaFo != null) { int lineInt = -1; try { lineInt = Integer.parseInt(lineNum); } catch (NumberFormatException ex) { } return OpenEditorOutputListener.tryCreateListener(javaFo, lineInt); } } return null; } public ActionListener tryGetOpenEditorAction(String line) { final OutputLinkDef linkDef = tryFindLink(line); if (linkDef != null) { return new ActionListener() { @Override public void actionPerformed(ActionEvent e) { linkDef.getAction().run(); } }; } else { return null; } } // This method is based on // org.netbeans.modules.maven.api.output.OutputUtils.matchStackTraceLine @Override public OutputLinkDef tryFindLink(String line) { Matcher match = LINE_PATTERN.matcher(line); if (!match.matches()) { return null; } String method = match.group(1); String file = match.group(2); String lineNum = match.group(3); int index = method.indexOf(file); if (index < 0) { return null; } String packageName = method.substring(0, index).replace('.', '/'); String resourceName = packageName + file + ".class"; FileObject resource = classPath.findResource(resourceName); if (resource == null) { return null; } String path = packageName + file + ".java"; FileObject root = classPath.findOwnerRoot(resource); if (root == null) { return null; } URL url = URLMapper.findURL(root, URLMapper.INTERNAL); for (SourceForBinaryQueryImplementation query: project.getLookup().lookupAll(SourceForBinaryQueryImplementation.class)) { SourceForBinaryQuery.Result sourceForBinary = query.findSourceRoots(url); if (sourceForBinary != null) { OpenEditorOutputListener result = tryCreateLinkListener(sourceForBinary, path, lineNum); if (result != null) { return new OutputLinkDef(match.start(), match.end(), result); } } } SourceForBinaryQuery.Result sourceForBinary = SourceForBinaryQuery.findSourceRoots(url); if (sourceForBinary == null) { return null; } OpenEditorOutputListener result = tryCreateLinkListener(sourceForBinary, path, lineNum); return result != null ? new OutputLinkDef(match.start(), match.end(), result) : null; } }