package fr.tikione.jacocoverage.plugin.util; import fr.tikione.jacocoexec.analyzer.JavaClass; import fr.tikione.jacocoverage.plugin.anno.AbstractCoverageAnnotation; import fr.tikione.jacocoverage.plugin.anno.CoverageAnnotation; import fr.tikione.jacocoverage.plugin.anno.CoverageGlyphedAnnotation; import fr.tikione.jacocoverage.plugin.anno.EditorCoverageStateEnum; import fr.tikione.jacocoverage.plugin.config.Config; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import javax.swing.text.StyledDocument; import org.netbeans.api.java.classpath.GlobalPathRegistry; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectUtils; import org.openide.awt.HtmlBrowser; import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.modules.InstalledFileLocator; import org.openide.nodes.Node; import org.openide.text.Line; import org.openide.text.NbDocument; import org.openide.util.Exceptions; import org.openide.util.Utilities; import org.openide.windows.IOProvider; /** * Some NetBeans related utilities. * * @author Jonathan Lermitage * @author Graeme Ingleby */ public class NBUtils { /** * Close a NetBeans console tab. * * @param tabName the name on the tab. * @throws IOException if an I/O error occurs. */ public static void closeConsoleTab(String tabName) throws IOException { IOProvider.getDefault().getIO(tabName, false).closeInputOutput(); } /** * Color (in editor) all the document representing the Java class. * * @param project the project containing the Java class. * @param jclass the Java class informations and coverage data. * @param multiLnInst enable coloring of multi-lines instructions. * @param srcDir the folder containing Java sources. Needed only if {@code multiLnInst} is true, otherwise you can use {@code null}. */ @SuppressWarnings({"AssignmentToForLoopParameter", "UnnecessaryLabelOnBreakStatement"}) public static void colorDoc(Project project, JavaClass jclass, boolean multiLnInst, File srcDir) { String classResource = jclass.getPackageName() + jclass.getClassName(); String prjId = getProjectId(project); int theme = Config.getTheme(); FIND_JAVA_FO: for (FileObject curRoot : GlobalPathRegistry.getDefault().getSourceRoots()) { FileObject fileObject = curRoot.getFileObject(classResource); if (fileObject != null && "java".equalsIgnoreCase(fileObject.getExt())) { try { DataObject dataObject = DataObject.find(fileObject); Node node = dataObject.getNodeDelegate(); EditorCookie editorCookie = node.getLookup().lookup(EditorCookie.class); Map<Integer, fr.tikione.jacocoexec.analyzer.CoverageStateEnum> coverage = jclass.getCoverage(); Map<Integer, String> coverageDesc = jclass.getCoverageDesc(); if (editorCookie != null) { StyledDocument doc = editorCookie.openDocument(); if (doc != null) { int startLine = 0; int endLine = NbDocument.findLineNumber(doc, doc.getLength()); Line.Set lineset = editorCookie.getLineSet(); for (int covIdx : coverage.keySet()) { if (covIdx >= startLine && covIdx <= endLine) { Line line = lineset.getOriginal(covIdx); EditorCoverageStateEnum coverageState; switch (coverage.get(covIdx)) { case COVERED: coverageState = EditorCoverageStateEnum.COVERED; break; case NOT_COVERED: coverageState = EditorCoverageStateEnum.NOT_COVERED; break; case PARTIALLY_COVERED: coverageState = EditorCoverageStateEnum.PARTIALLY_COVERED; break; default: coverageState = EditorCoverageStateEnum.COVERED; } AbstractCoverageAnnotation annotation; if (coverageDesc.containsKey(covIdx)) { annotation = new CoverageGlyphedAnnotation( coverageState, prjId, jclass.getPackageName() + jclass.getClassName(), covIdx, coverageDesc.get(covIdx), theme); } else { annotation = new CoverageAnnotation( coverageState, prjId, jclass.getPackageName() + jclass.getClassName(), covIdx, theme); } annotation.attach(line); line.addPropertyChangeListener(annotation); } } if (multiLnInst) { // Patch by GWI // old: File javafile = new File(srcDir, jclass.getPackageName() + jclass.getClassName()); // new: File javafile = new File(fileObject.getPath()); File javafile = new File(fileObject.getPath()); List<String> javalines = org.apache.commons.io.FileUtils.readLines(javafile); int nblines = javalines.size(); for (int lineIdx = 0; lineIdx < nblines; lineIdx++) { boolean isCovered = coverage.containsKey(lineIdx); boolean isCoveredDesc = coverageDesc.containsKey(lineIdx); if ((isCovered || isCoveredDesc) && (lineIdx + 1 < nblines) && (!coverage.containsKey(lineIdx + 1) && !coverageDesc.containsKey(lineIdx + 1)) && (!Utils.isIntructionFinished(javalines.get(lineIdx)))) { EditorCoverageStateEnum coverageState; switch (coverage.get(lineIdx)) { case COVERED: coverageState = EditorCoverageStateEnum.COVERED; break; case NOT_COVERED: coverageState = EditorCoverageStateEnum.NOT_COVERED; break; case PARTIALLY_COVERED: coverageState = EditorCoverageStateEnum.PARTIALLY_COVERED; break; default: coverageState = EditorCoverageStateEnum.COVERED; } coverage.put(lineIdx + 1, coverage.get(lineIdx)); AbstractCoverageAnnotation annotation = new CoverageAnnotation( coverageState, prjId, jclass.getPackageName() + jclass.getClassName(), lineIdx + 1, theme); Line line = lineset.getOriginal(lineIdx + 1); annotation.attach(line); line.addPropertyChangeListener(annotation); } } } } break FIND_JAVA_FO; } } catch (DataObjectNotFoundException ex) { Exceptions.printStackTrace(ex); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } } } /** * launch the default browser to display an URL. * * @param url the URL to display. */ public static void extBrowser(String url) { try { HtmlBrowser.URLDisplayer.getDefault().showURL(new URL(url)); } catch (MalformedURLException ex) { Exceptions.printStackTrace(ex); } } /** * Get the JaCoCo-Agent JAR file that is registered in the IDE. * * @return the JaCoCo-Agent JAR. */ public static File getJacocoAgentJar() { if (Config.isUseCustomJacocoJar()) { File jacocoagent = new File(Config.getCustomJacocoJarPath()); if (jacocoagent.exists()) { return jacocoagent; } } return InstalledFileLocator.getDefault().locate("modules/ext/jacocoagent.jar", "fr.tikione.jacoco.lib", false); } // /** // * Get the JaCoCo-Ant JAR file that is registered in the IDE. // * // * @return the JaCoCo-Ant JAR. // */ // public static File getJacocoAntJar() { // return InstalledFileLocator.getDefault().locate("modules/ext/jacocoant.jar", "fr.tikione.jacoco.lib", false); // } /** * Get the full path of the directory containing a given project. * * @param project the project. * @return the directory containing the project. */ public static String getProjectDir(Project project) { return FileUtil.getFileDisplayName(project.getProjectDirectory()); } /** * Generate a string representing the project. Two different projects should have different representation. * * @param project the project. * @return a representation of the project. */ public static String getProjectId(Project project) { return getProjectDir(project) + '_' + project.toString(); } /** * Retrieve the list of Java packages of a given project. * Each element is a fully qualified package name, e.g. <code>foo</code>, <code>foo.bar</code> and <code>foo.bar.too</code>. * * @param project the project to list Java packages. * @param prjProps the project properties. * @return a list of Java package names. */ public static List<String> getProjectJavaPackages(Project project, Properties prjProps) { List<String> packages = new ArrayList<>(8); String srcFolderName = Utils.getProperty(prjProps, "src.dir"); List<File> packagesAsFolders = Utils.listFolders( new File(NBUtils.getProjectDir(project) + File.separator + srcFolderName + File.separator)); int rootDirnameLen = NBUtils.getProjectDir(project).length() + srcFolderName.length() + 2; for (File srcPackage : packagesAsFolders) { packages.add(srcPackage.getAbsolutePath() .substring(rootDirnameLen) .replaceAll(Matcher.quoteReplacement(File.separator), ".")); } return packages; } /** * Retrieve the list of Java packages of a given project. * Each element is a fully qualified package name, e.g. <code>foo</code>, <code>foo.bar</code> and <code>foo.bar.too</code>. * Elements are separated with a given separator string. * * @param project the project to list Java packages. * @param prjProps the project properties. * @param separator the separator string. * @param prefix a prefix to append to the end of each package name (can be empty). * @return a list of Java package names. */ public static String getProjectJavaPackagesAsStr(Project project, Properties prjProps, String separator, String prefix) { List<String> packagesList = getProjectJavaPackages(project, prjProps); StringBuilder packages = new StringBuilder(256); if (packagesList.isEmpty()) { packages.append("*"); } else { boolean first = true; for (String pack : packagesList) { if (!first) { packages.append(separator); } packages.append(pack).append(prefix); first = false; } } return packages.toString(); } /** * Get the name of a project. * * @param project the project. * @return the project's name. */ public static String getProjectName(Project project) { return ProjectUtils.getInformation(project).getName(); } /** * Get the selected project. Return null if multiple projects are selected. * * @return the selected project or null if none or many. */ public static Project getSelectedProject() { Project project; Collection<? extends Project> prjs = getAllSelectedProjects(); if (prjs.size() == 1) { project = prjs.iterator().next(); } else { project = null; } return project; } /** * Get every selected projects. * * @return the selected projects. */ public static Collection<? extends Project> getAllSelectedProjects() { return Utilities.actionsGlobalContext().lookupAll(Project.class); } private NBUtils() { } }