diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc849f..a8bd947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,105 +1,108 @@ -## JaCoCoverage Change Log - -### Version 1.3.8.20140919.2148 (2014-09-19) -* updated JaCoCo library to 0.7.2 (see its [changelog](http://www.eclemma.org/jacoco/trunk/doc/changes.html)). -* preparation for Java Web/EE/EJB/EAR projects support (will be activated once [NetBeans bug 246072](https://netbeans.org/bugzilla/show_bug.cgi?id=246072) is fixed). -* *built with NetBeans 8.0.1FCS and Oracle JDK7.* - -### Version 1.3.7.20140512.1900 (2014-05-12) -* updated JaCoCo library to 0.7.1 (see its [changelog](http://www.eclemma.org/jacoco/trunk/doc/changes.html)). - -### Version 1.3.6.20140430.2331 (2014-04-30) -* updated Apache Commons Lang3 library to 3.3.2. -* updated FasterXML Jackson Annotations library to 2.2.3. -* updated FasterXML Jackson Core library to 2.2.3. -* updated FasterXML Jackson Databind library to 2.2.3. -* *built with NetBeans 8.0FCS-patch1 and Oracle JDK7.* - -### Version 1.3.5.20140430.2035 (2014-04-30) -* GitHub #18 removed error while tests fail. - -### Version 1.3.4.20140322.1836 (2014-03-22) -* updated JaCoCo library to 0.7.0 to support Java8. -* *built with NetBeans 8.0FCS and Oracle JDK7.* - -### Version 1.3.4.20140306.1000 (2014-03-06) -* updated JaCoCo library to 0.6.5. -* *built with NetBeans 8.0RC1 and Oracle JDK7.* - -### Version 1.3.3.20131214.1257 (2013-12-14) -* GitHub #17 added an action at project's level to remove coverage data from editor (Reset coverage data). -* added the default package to the list of covered packages. -* updated JaCoCo library to 0.6.4. -* *built with NetBeans 7.4 and Oracle JDK7.* - -### Version 1.3.2.20130911.1322 (2013-09-11) -* GitHub #16 will now look for a "nbbuild.xml" file when the "build.xml" file is missing. -* *built with NetBeans 7.3.1 and Oracle JDK7.* - -### Version 1.3.1.20130908.1301 (2013-09-08) -* changed license to WTFPL (Do What the Fuck You Want to Public License, see http://www.wtfpl.net). -* fixed: another unclosed input stream on project's properties. -* Internal: some code reworked. -* added a About dialog. -* *built with NetBeans 7.3.1 and Oracle JDK6 (a Git branch has been created for NetBeans 7.3.1 and JDK6 support).* - -### Version 1.3.0.20130815.1437 (2013-08-15) -* fixed: unclosed input stream on project's properties. -* GitHub #12 added a package filter at project level. -* *built with NetBeans 7.3.1 and Oracle JDK7.* - -### Version 1.2.9.20130804.1649 (2013-08-04) -* GitHub #11 added configuration at project level. -* GitHub #14 fixed MissingResourceException when assertions (-ea switch) are enabled. -* disabled NetBeans Module projects support (will be fixed later). -* coloration of multi-line instructions is now enabled in default configuration. -* removed actions from NetBeans toolbar. -* removed actions icons. -* internal: library modules are now hidden in Plugins Manager. - -### Version 1.2.2.20130617.1157 (2013-06-17) -* GitHub #9 fixed JavaAgent didn't generate report if its path contained comma. -* minor UI fixes in options panel (relevant on systems with large fonts, like Ubuntu). -* added coloration of multi-line instructions (can be enabled in options panel). - -### Version 1.2.1.20130612.1950 (2013-06-12) -* fixed: hide action menu items when disabled. -* added an option to keep JaCoCo binary and XML report files, in their original form or compressed with Zip format. -* updated JaCoCo library to 0.6.3. -* removed Lombok 0.11.8 lib since it is not compatible with JDK8 compiler. -* removed registration of JaCoCo Ant Library into NetBeans. -* disabled JaCoCoverage action items when multiple projects are selected. -* *built with NetBeans 7.3.1 and Oracle JDK6.* - -### Version 1.2.0.20130602.325 (2013-06-02) -* improved code highlighting with glyphs in left margin and additional information in lines tooltips. -* stabilization and optimization: JaCoCoverage task now runs with a RequestProcessor limited to 3 maximum threads. -* fixed a bug: jacoco.exec file was generated into the project's working directory, not the project's root. -* added support of detailed JaCoCo HTML reports. -* added a copy of JaCoCo XML report file in project's directory (.jacocoverage/jacoco.latest.xml file). -* added a warning message when Ant Task or JaCoCo Agent fails. -* added a progress bar while JaCoCoverage is loading JaCoCo report and applying code highlighting. -* added color themes for code highlighting. -* updated coverage colors (they are now based on JaCoCo HTML reports). -* removed duplicated JAR files in JaCoCo Library module. -* configuration panel: removed unused options. -* configuration panel: added social icons and a link to the online help. -* added French translation in plugin's description (available for "fr" locales). -* some minor UI improvements. - -### Version 1.1.1 (2013-05-06) -* GitHub #2 #3 integrated Jan Lahoda's patch (preventing NPE when right-clicking on a project that does not have nbproject/project.properties). -* classes displayed in minimal coverage reports are now sorted. -* installing plugins doesn't require to restart NetBeans. - -### Version 1.1.0 (2013-04-28) -* added support of NetBeans Modules projects. -* added a "Run Project with JaCoCoverage" action menu and removed the configuration of the Ant task. -* added configuration of code highlighting and reporting (enable/disable). -* added icons on action menus. -* minor enhancement of the minimal coverage report. -* fixed errors when the JaCoCo JavaAgent doesn't generate JaCoCo report file (jacoco.exec). - -### Version 1.0.0 (2013-04-22) -* first stable version. Supports Java7 code coverage, code highlighting and minimal coverage report. -* *built with NetBeans 7.3 and Oracle JDK6.* +## JaCoCoverage Change Log + +### Version 1.4.0.20141122.2340 (2014-09-19) +* GitHub #24 add Java Web/EE/EJB/EAR projects support. + +### Version 1.3.8.20140919.2148 (2014-09-19) +* updated JaCoCo library to 0.7.2 (see its [changelog](http://www.eclemma.org/jacoco/trunk/doc/changes.html)). +* preparation for Java Web/EE/EJB/EAR projects support (will be activated once [NetBeans bug 246072](https://netbeans.org/bugzilla/show_bug.cgi?id=246072) is fixed). +* *built with NetBeans 8.0.1FCS and Oracle JDK7.* + +### Version 1.3.7.20140512.1900 (2014-05-12) +* updated JaCoCo library to 0.7.1 (see its [changelog](http://www.eclemma.org/jacoco/trunk/doc/changes.html)). + +### Version 1.3.6.20140430.2331 (2014-04-30) +* updated Apache Commons Lang3 library to 3.3.2. +* updated FasterXML Jackson Annotations library to 2.2.3. +* updated FasterXML Jackson Core library to 2.2.3. +* updated FasterXML Jackson Databind library to 2.2.3. +* *built with NetBeans 8.0FCS-patch1 and Oracle JDK7.* + +### Version 1.3.5.20140430.2035 (2014-04-30) +* GitHub #18 removed error while tests fail. + +### Version 1.3.4.20140322.1836 (2014-03-22) +* updated JaCoCo library to 0.7.0 to support Java8. +* *built with NetBeans 8.0FCS and Oracle JDK7.* + +### Version 1.3.4.20140306.1000 (2014-03-06) +* updated JaCoCo library to 0.6.5. +* *built with NetBeans 8.0RC1 and Oracle JDK7.* + +### Version 1.3.3.20131214.1257 (2013-12-14) +* GitHub #17 added an action at project's level to remove coverage data from editor (Reset coverage data). +* added the default package to the list of covered packages. +* updated JaCoCo library to 0.6.4. +* *built with NetBeans 7.4 and Oracle JDK7.* + +### Version 1.3.2.20130911.1322 (2013-09-11) +* GitHub #16 will now look for a "nbbuild.xml" file when the "build.xml" file is missing. +* *built with NetBeans 7.3.1 and Oracle JDK7.* + +### Version 1.3.1.20130908.1301 (2013-09-08) +* changed license to WTFPL (Do What the Fuck You Want to Public License, see http://www.wtfpl.net). +* fixed: another unclosed input stream on project's properties. +* Internal: some code reworked. +* added a About dialog. +* *built with NetBeans 7.3.1 and Oracle JDK6 (a Git branch has been created for NetBeans 7.3.1 and JDK6 support).* + +### Version 1.3.0.20130815.1437 (2013-08-15) +* fixed: unclosed input stream on project's properties. +* GitHub #12 added a package filter at project level. +* *built with NetBeans 7.3.1 and Oracle JDK7.* + +### Version 1.2.9.20130804.1649 (2013-08-04) +* GitHub #11 added configuration at project level. +* GitHub #14 fixed MissingResourceException when assertions (-ea switch) are enabled. +* disabled NetBeans Module projects support (will be fixed later). +* coloration of multi-line instructions is now enabled in default configuration. +* removed actions from NetBeans toolbar. +* removed actions icons. +* internal: library modules are now hidden in Plugins Manager. + +### Version 1.2.2.20130617.1157 (2013-06-17) +* GitHub #9 fixed JavaAgent didn't generate report if its path contained comma. +* minor UI fixes in options panel (relevant on systems with large fonts, like Ubuntu). +* added coloration of multi-line instructions (can be enabled in options panel). + +### Version 1.2.1.20130612.1950 (2013-06-12) +* fixed: hide action menu items when disabled. +* added an option to keep JaCoCo binary and XML report files, in their original form or compressed with Zip format. +* updated JaCoCo library to 0.6.3. +* removed Lombok 0.11.8 lib since it is not compatible with JDK8 compiler. +* removed registration of JaCoCo Ant Library into NetBeans. +* disabled JaCoCoverage action items when multiple projects are selected. +* *built with NetBeans 7.3.1 and Oracle JDK6.* + +### Version 1.2.0.20130602.325 (2013-06-02) +* improved code highlighting with glyphs in left margin and additional information in lines tooltips. +* stabilization and optimization: JaCoCoverage task now runs with a RequestProcessor limited to 3 maximum threads. +* fixed a bug: jacoco.exec file was generated into the project's working directory, not the project's root. +* added support of detailed JaCoCo HTML reports. +* added a copy of JaCoCo XML report file in project's directory (.jacocoverage/jacoco.latest.xml file). +* added a warning message when Ant Task or JaCoCo Agent fails. +* added a progress bar while JaCoCoverage is loading JaCoCo report and applying code highlighting. +* added color themes for code highlighting. +* updated coverage colors (they are now based on JaCoCo HTML reports). +* removed duplicated JAR files in JaCoCo Library module. +* configuration panel: removed unused options. +* configuration panel: added social icons and a link to the online help. +* added French translation in plugin's description (available for "fr" locales). +* some minor UI improvements. + +### Version 1.1.1 (2013-05-06) +* GitHub #2 #3 integrated Jan Lahoda's patch (preventing NPE when right-clicking on a project that does not have nbproject/project.properties). +* classes displayed in minimal coverage reports are now sorted. +* installing plugins doesn't require to restart NetBeans. + +### Version 1.1.0 (2013-04-28) +* added support of NetBeans Modules projects. +* added a "Run Project with JaCoCoverage" action menu and removed the configuration of the Ant task. +* added configuration of code highlighting and reporting (enable/disable). +* added icons on action menus. +* minor enhancement of the minimal coverage report. +* fixed errors when the JaCoCo JavaAgent doesn't generate JaCoCo report file (jacoco.exec). + +### Version 1.0.0 (2013-04-22) +* first stable version. Supports Java7 code coverage, code highlighting and minimal coverage report. +* *built with NetBeans 7.3 and Oracle JDK6.* diff --git a/README.md b/README.md index 298c8b2..98fb58e 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ * The plugin works as a transparent additional service that colors all java files according to the unit tests coverage information. With code coverage enabled user continues to work with his/her project in the usual way but can easily view the test coverage of the project classes.
The code coverage plugin will update the code coverage data and refresh editors markup every time a unit test (or any selected -Ant target) is executed for the project. Currently the Java Application, Java Library, Java Project with Existing Sources are supported. __Maven support__ with JaCoCo is already integrated in NetBeans base installation, please check [online how-to](http://wiki.netbeans.org/MavenCodeCoverage) for details. +Ant target) is executed for the project. Currently the __Java Application__, __Java Library__, __Java Web and Java EE__ (not tested with EAR and EJB projects but may work), Java Project with Existing Sources are supported. __Maven support__ with JaCoCo is already integrated in NetBeans base installation, please check [online how-to](http://wiki.netbeans.org/MavenCodeCoverage) for details. * Coverage collections are based on JaCoCo in order to **support Java 5, 6, 7 and Java 8 bytecode**. Take it as a modern alternative to the EMMA and Cobertura based plugins. * JaCoCo is a free code coverage library for Java, which has been created by the [EclEmma team](http://www.eclemma.org/jacoco/). diff --git a/tikione-jacocoverage-plugin/manifest.mf b/tikione-jacocoverage-plugin/manifest.mf index 84641c5..e72b604 100644 --- a/tikione-jacocoverage-plugin/manifest.mf +++ b/tikione-jacocoverage-plugin/manifest.mf @@ -1,6 +1,6 @@ -Manifest-Version: 1.0 -OpenIDE-Module: fr.tikione.jacocoverage.plugin/1 -OpenIDE-Module-Layer: fr/tikione/jacocoverage/plugin/layer.xml -OpenIDE-Module-Localizing-Bundle: fr/tikione/jacocoverage/plugin/Bundle.properties -OpenIDE-Module-Specification-Version: 1.3.8.20140919.2148 -OpenIDE-Module-Needs: org.openide.filesystems.FileUtil.toFileObject +Manifest-Version: 1.0 +OpenIDE-Module: fr.tikione.jacocoverage.plugin/1 +OpenIDE-Module-Layer: fr/tikione/jacocoverage/plugin/layer.xml +OpenIDE-Module-Localizing-Bundle: fr/tikione/jacocoverage/plugin/Bundle.properties +OpenIDE-Module-Specification-Version: 1.4.0.20141122.2340 +OpenIDE-Module-Needs: org.openide.filesystems.FileUtil.toFileObject diff --git a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ActionJacocoOnAntTaskJ2SE.java b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ActionJacocoOnAntTaskJ2SE.java index 12c87fc..46b670b 100644 --- a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ActionJacocoOnAntTaskJ2SE.java +++ b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ActionJacocoOnAntTaskJ2SE.java @@ -1,283 +1,298 @@ -package fr.tikione.jacocoverage.plugin.action; - -import fr.tikione.jacocoexec.analyzer.JaCoCoReportAnalyzer; -import fr.tikione.jacocoexec.analyzer.JaCoCoXmlReportParser; -import fr.tikione.jacocoexec.analyzer.JavaClass; -import fr.tikione.jacocoverage.plugin.anno.AbstractCoverageAnnotation; -import fr.tikione.jacocoverage.plugin.config.Globals; -import fr.tikione.jacocoverage.plugin.config.ProjectConfig; -import fr.tikione.jacocoverage.plugin.util.NBUtils; -import fr.tikione.jacocoverage.plugin.util.Utils; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.AbstractAction; -import javax.xml.parsers.ParserConfigurationException; -import org.apache.tools.ant.module.api.AntProjectCookie; -import org.apache.tools.ant.module.api.AntTargetExecutor; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.ProgressHandleFactory; -import org.netbeans.api.project.Project; -import org.openide.DialogDisplayer; -import org.openide.NotifyDescriptor; -import org.openide.awt.HtmlBrowser; -import org.openide.execution.ExecutorTask; -import org.openide.filesystems.FileObject; -import org.openide.loaders.DataObject; -import org.openide.util.Exceptions; -import org.openide.util.RequestProcessor; -import org.openide.util.Utilities; -import org.xml.sax.SAXException; - -/** - * A toolkit that launches Ant tasks with the JaCoCo JavaAgent, colorizes Java source files and shows a coverage report. - *
See DevFaqRequestProcessor for NetBeans threading tweaks. - *
See DevFaqActionContextSensitive for context action tweaks. - *
See DevFaqAddGlobalContext for global context and project tweaks. - * - * @author Jonathan Lermitage - */ -@SuppressWarnings("CloneableImplementsClone") -public abstract class ActionJacocoOnAntTaskJ2SE - extends AbstractAction - implements ActionListener { - - private static final long serialVersionUID = 1L; - - private static final Logger LOGGER = Logger.getLogger(ActionJacocoOnAntTaskJ2SE.class.getName()); - - /** The Ant task to launch. */ - private final String antTask; - - /** Additional properties passed to the Ant task. */ - private final Properties addAntTargetProps = new Properties(); - - /** - * Enable the context action on supported projects only. - * - * @param antTask additional properties passed to the Ant task. - */ - public ActionJacocoOnAntTaskJ2SE(String antTask) { - this.antTask = antTask; - } - - @Override - public void actionPerformed(ActionEvent ae) { - new RequestProcessor("JaCoCoverage Preparation Task", 3, true).post(new Runnable() { - @Override - public void run() { - try { - runJacocoJavaagent(NBUtils.getSelectedProject()); - } catch (IllegalArgumentException | IOException ex) { - Exceptions.printStackTrace(ex); - } - } - }); - } - - /** - * Run an Ant task with a JaCoCo Java Agent, collect and display coverage data. - * - * @param project the project to launch Ant target from. - * @throws IOException if an I/O error occurs. - */ - private void runJacocoJavaagent(final Project project) - throws IOException { - // Retrieve JaCoCoverage preferences. - final ProjectConfig cfg = ProjectConfig.forFile(new File(NBUtils.getProjectDir(project), Globals.PRJ_CFG)); - final boolean enblHighlight = cfg.isEnblHighlighting(); - final boolean enblConsoleReport = cfg.isEnblConsoleReport(); - final boolean enblHtmlReport = cfg.isEnblHtmlReport(); - final boolean openHtmlReport = cfg.isOpenHtmlReport(); - - if (enblHighlight || enblConsoleReport || enblHtmlReport) { - // Retrieve project properties. - final String prjDir = NBUtils.getProjectDir(project) + File.separator; - FileObject prjPropsFo = project.getProjectDirectory().getFileObject("nbproject/project.properties"); - final Properties prjProps = new Properties(); - try (InputStream insPrjProps = prjPropsFo.getInputStream()) { - prjProps.load(insPrjProps); - } - - final File xmlreport = Utils.getJacocoXmlReportfile(project); - final File binreport = Utils.getJacocoBinReportFile(project); - if (binreport.exists() && !binreport.delete() || xmlreport.exists() && !xmlreport.delete()) { - String msg = "Cannot delete the previous JaCoCo report files, please delete them manually:\n" - + binreport.getAbsolutePath() + " and/or\n" - + xmlreport.getAbsolutePath(); - NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE); - DialogDisplayer.getDefault().notify(nd); - } else { - // Apply JaCoCo JavaAgent customization. - final String antTaskJavaagentParam; - List excludeList = cfg.getPkgclssExclude(); - StringBuilder exclude = new StringBuilder((excludeList.size() + 1) * 20); - if (cfg.isOverrideGlobals() && !excludeList.isEmpty()) { - exclude.append(",excludes="); - boolean first = true; - for (String pkg : excludeList) { - if (!first) { - exclude.append(':'); - } - exclude.append(pkg).append(".*"); - first = false; - } - } - antTaskJavaagentParam = "\"" + NBUtils.getJacocoAgentJar().getAbsolutePath() - //+ "\"=includes=*:" + NBUtils.getProjectJavaPackagesAsStr(project, prjProps, ":", ".*") - //+ ",destfile=\"" + binreport.getAbsolutePath() + "\"" + exclude.toString(); - + "\"=destfile=\"" + binreport.getAbsolutePath() + "\"" + exclude.toString(); // TODO revert once j2ee works - - System.out.println("binreport=" + binreport.getAbsolutePath()); - - - FileObject scriptToExecute = project.getProjectDirectory().getFileObject("build", "xml"); - if (scriptToExecute == null) { // Fix for GitHub #16. - scriptToExecute = project.getProjectDirectory().getFileObject("nbbuild", "xml"); - } - DataObject dataObj = DataObject.find(scriptToExecute); - AntProjectCookie antCookie = dataObj.getLookup().lookup(AntProjectCookie.class); - - AntTargetExecutor.Env env = new AntTargetExecutor.Env(); - AntTargetExecutor executor = AntTargetExecutor.createTargetExecutor(env); - - // Add the customized JaCoCo JavaAgent to the JVM arguments given to the Ant task. The JaCoCo JavaAgent is - // appended to the existing list of JVM arguments that is given to the Ant task. - Properties targetProps = env.getProperties(); - targetProps.putAll(addAntTargetProps); - String prjJvmArgs = Utils.getProperty(prjProps, "run.jvmargs"); - targetProps.put("run.jvmargs", prjJvmArgs + " -javaagent:" + antTaskJavaagentParam); - env.setProperties(targetProps); - - // Launch the Ant task with the JaCoCo JavaAgent. - final ExecutorTask execute = executor.execute(antCookie, new String[]{antTask}); - - new RequestProcessor("JaCoCoverage Collection Task", 3, true).post(new Runnable() { - @Override - public void run() { - ProgressHandle progr = ProgressHandleFactory.createHandle("JaCoCoverage Collection Task"); - try { - progr.setInitialDelay(400); - progr.start(); - progr.switchToIndeterminate(); - - // Wait for the end of the Ant task execution. We do it in a new thread otherwise it would - // freeze the current one. This is a workaround for a known and old NetBeans bug: the ExecutorTask - // object provided by the NetBeans platform is not correctly wrapped. - int executeRes = execute.result(); - if (binreport.exists()) { - long st = System.currentTimeMillis(); - // Load the generated JaCoCo coverage report. - File classDir = new File(prjDir + Utils.getProperty(prjProps, "build.classes.dir") + File.separator); - File srcDir = new File(prjDir + Utils.getProperty(prjProps, "src.dir") + File.separator); - JaCoCoReportAnalyzer.toXmlReport(binreport, xmlreport, classDir, srcDir); - final Map coverageData = JaCoCoXmlReportParser.getCoverageData(xmlreport); - new File(prjDir + Globals.JACOCOVERAGE_DATA_DIR).mkdirs(); - - // Remove existing highlighting (from a previous coverage task), show reports and apply - // highlighting on each Java source file. - AbstractCoverageAnnotation.removeAll(NBUtils.getProjectId(project)); - String prjname = NBUtils.getProjectName(project); - if (enblConsoleReport) { - JaCoCoReportAnalyzer.toConsoleReport(coverageData, prjname + Globals.TXTREPORT_TABNAME); - } - File reportdir = new File(prjDir + Globals.HTML_REPORT_DIR); - if (reportdir.exists()) { - org.apache.commons.io.FileUtils.deleteDirectory(reportdir); - } - if (enblHtmlReport) { - reportdir.mkdirs(); - String report = JaCoCoReportAnalyzer.toHtmlReport(binreport, reportdir, classDir, srcDir, prjname); - if (openHtmlReport) { - try { - HtmlBrowser.URLDisplayer.getDefault().showURL(Utilities.toURI(new File(report)).toURL()); - } catch (MalformedURLException ex) { - Exceptions.printStackTrace(ex); - } - } - } - if (enblHighlight) { - for (final JavaClass jclass : coverageData.values()) { - NBUtils.colorDoc(project, jclass, cfg.isEnblHighlightingExtended(), srcDir); - } - } - keepJaCoCoWorkfiles(binreport, xmlreport, prjDir, cfg.getJaCoCoWorkfilesRule()); - - long et = System.currentTimeMillis(); - LOGGER.log(Level.INFO, "Coverage Collection Task took: {0} ms", et - st); - } else { - AbstractCoverageAnnotation.removeAll(NBUtils.getProjectId(project)); - NBUtils.closeConsoleTab(Globals.TXTREPORT_TABNAME); - String msg = "Ant Task or JaCoCo Agent failed, JaCoCoverage can't process data.\n" - + "(AntExitCode=" + executeRes + ", JacocoBinReportFound=" + binreport.exists() + ")"; - NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.WARNING_MESSAGE); - DialogDisplayer.getDefault().notify(nd); - } - } catch (FileNotFoundException ex) { - Exceptions.printStackTrace(ex); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } catch (ParserConfigurationException | SAXException ex) { - Exceptions.printStackTrace(ex); - } finally { - progr.finish(); - } - } - }); - } - } else { - String msg = "Please enable at least one JaCoCoverage feature first (highlighting or reporting)."; - NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE); - DialogDisplayer.getDefault().notify(nd); - } - } - - /** - * Apply retention policy on JaCoCo workfiles. - * - * @param binreport JaCoCo binary report file. - * @param xmlreport JaCoCo XML report file. - * @param prjDir project directory. - * @param wfrule retention policy. - * @throws IOException if workfiles can't be removed or moved. - */ - private void keepJaCoCoWorkfiles(File binreport, File xmlreport, String prjDir, int wfrule) - throws IOException { - File xmlreportCpy = new File(prjDir + Globals.XML_BACKUP_REPORT); - File xmlreportZip = new File(prjDir + Globals.XMLZIP_BACKUP_REPORT); - File binreportCpy = new File(prjDir + Globals.BIN_BACKUP_REPORT); - File binreportZip = new File(prjDir + Globals.BINZIP_BACKUP_REPORT); - xmlreportCpy.delete(); - xmlreportZip.delete(); - binreportCpy.delete(); - binreportZip.delete(); - switch (wfrule) { - case 0: - org.apache.commons.io.FileUtils.moveFile(binreport, binreportCpy); - org.apache.commons.io.FileUtils.moveFile(xmlreport, xmlreportCpy); - break; - case 1: - Utils.zip(binreport, binreportZip, Globals.BINZIP_BACKUP_REPORT_ENTRY, false); - Utils.zip(xmlreport, xmlreportZip, Globals.XMLZIP_BACKUP_REPORT_ENTRY, false); - break; - case 2: - break; - } - binreport.delete(); - xmlreport.delete(); - } - - @SuppressWarnings("ReturnOfCollectionOrArrayField") - public Properties getAddAntTargetProps() { - return addAntTargetProps; - } -} +package fr.tikione.jacocoverage.plugin.action; + +import fr.tikione.jacocoexec.analyzer.JaCoCoReportAnalyzer; +import fr.tikione.jacocoexec.analyzer.JaCoCoXmlReportParser; +import fr.tikione.jacocoexec.analyzer.JavaClass; +import fr.tikione.jacocoverage.plugin.anno.AbstractCoverageAnnotation; +import fr.tikione.jacocoverage.plugin.config.Globals; +import fr.tikione.jacocoverage.plugin.config.ProjectConfig; +import fr.tikione.jacocoverage.plugin.util.NBProjectTypeEnum; +import fr.tikione.jacocoverage.plugin.util.NBUtils; +import fr.tikione.jacocoverage.plugin.util.Utils; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.tools.ant.module.api.AntProjectCookie; +import org.apache.tools.ant.module.api.AntTargetExecutor; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.netbeans.api.project.Project; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.awt.HtmlBrowser; +import org.openide.execution.ExecutorTask; +import org.openide.filesystems.FileObject; +import org.openide.loaders.DataObject; +import org.openide.util.Exceptions; +import org.openide.util.RequestProcessor; +import org.openide.util.Utilities; +import org.xml.sax.SAXException; + +/** + * A toolkit that launches Ant tasks with the JaCoCo JavaAgent, colorizes Java source files and shows a coverage report. + *
See DevFaqRequestProcessor for NetBeans threading tweaks. + *
See DevFaqActionContextSensitive for context action tweaks. + *
See DevFaqAddGlobalContext for global context and project tweaks. + * + * @author Jonathan Lermitage + */ +@SuppressWarnings("CloneableImplementsClone") +public abstract class ActionJacocoOnAntTaskJ2SE + extends AbstractAction + implements ActionListener { + + private static final long serialVersionUID = 1L; + + private static final Logger LOGGER = Logger.getLogger(ActionJacocoOnAntTaskJ2SE.class.getName()); + + /** The Ant task to launch. */ + private final String antTask; + + /** Additional properties passed to the Ant task. */ + private final Properties addAntTargetProps = new Properties(); + + /** + * Enable the context action on supported projects only. + * + * @param antTask additional properties passed to the Ant task. + */ + public ActionJacocoOnAntTaskJ2SE(String antTask) { + this.antTask = antTask; + } + + @Override + public void actionPerformed(ActionEvent ae) { + new RequestProcessor("JaCoCoverage Preparation Task", 3, true).post(new Runnable() { + @Override + public void run() { + try { + runJacocoJavaagent(NBUtils.getSelectedProject()); + } catch (IllegalArgumentException | IOException ex) { + Exceptions.printStackTrace(ex); + } + } + }); + } + + /** + * Run an Ant task with a JaCoCo Java Agent, collect and display coverage data. + * + * @param project the project to launch Ant target from. + * @throws IOException if an I/O error occurs. + */ + private void runJacocoJavaagent(final Project project) + throws IOException { + // Retrieve JaCoCoverage preferences. + final ProjectConfig cfg = ProjectConfig.forFile(new File(NBUtils.getProjectDir(project), Globals.PRJ_CFG)); + final boolean enblHighlight = cfg.isEnblHighlighting(); + final boolean enblConsoleReport = cfg.isEnblConsoleReport(); + final boolean enblHtmlReport = cfg.isEnblHtmlReport(); + final boolean openHtmlReport = cfg.isOpenHtmlReport(); + + if (enblHighlight || enblConsoleReport || enblHtmlReport) { + // Retrieve project properties. + final String prjDir = NBUtils.getProjectDir(project) + File.separator; + FileObject prjPropsFo = project.getProjectDirectory().getFileObject("nbproject/project.properties"); + final Properties prjProps = new Properties(); + try (InputStream insPrjProps = prjPropsFo.getInputStream()) { + prjProps.load(insPrjProps); + } + + final File xmlreport = Utils.getJacocoXmlReportfile(project); + final File binreport = Utils.getJacocoBinReportFile(project); + if (binreport.exists() && !binreport.delete() || xmlreport.exists() && !xmlreport.delete()) { + String msg = "Cannot delete the previous JaCoCo report files, please delete them manually:\n" + + binreport.getAbsolutePath() + " and/or\n" + + xmlreport.getAbsolutePath(); + NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notify(nd); + } else { + // Apply JaCoCo JavaAgent customization. + final String antTaskJavaagentParam; + List excludeList = cfg.getPkgclssExclude(); + StringBuilder exclude = new StringBuilder((excludeList.size() + 1) * 20); + if (cfg.isOverrideGlobals() && !excludeList.isEmpty()) { + exclude.append(",excludes="); + boolean first = true; + for (String pkg : excludeList) { + if (!first) { + exclude.append(':'); + } + exclude.append(pkg).append(".*"); + first = false; + } + } + + antTaskJavaagentParam = "\"" + NBUtils.getJacocoAgentJar().getAbsolutePath() + + "\"=includes=*:" + NBUtils.getProjectJavaPackagesAsStr(project, prjProps, ":", ".*") + + ",destfile=\"" + binreport.getAbsolutePath() + "\"" + exclude.toString(); + + FileObject scriptToExecute = project.getProjectDirectory().getFileObject("build", "xml"); + if (scriptToExecute == null) { // Fix for GitHub #16. + scriptToExecute = project.getProjectDirectory().getFileObject("nbbuild", "xml"); + } + DataObject dataObj = DataObject.find(scriptToExecute); + AntProjectCookie antCookie = dataObj.getLookup().lookup(AntProjectCookie.class); + + AntTargetExecutor.Env env = new AntTargetExecutor.Env(); + AntTargetExecutor executor = AntTargetExecutor.createTargetExecutor(env); + + // Add the customized JaCoCo JavaAgent to the JVM arguments given to the Ant task. The JaCoCo JavaAgent is + // appended to the existing list of JVM arguments that is given to the Ant task. + Properties targetProps = env.getProperties(); + targetProps.putAll(addAntTargetProps); + + // Specify jvm args. Special case for Java Web projects. + String prjJvmArgs; + final boolean isJ2EE = Utils.isProjectSupported(NBUtils.getSelectedProject(), + NBProjectTypeEnum.J2EE, NBProjectTypeEnum.J2EE_EAR, NBProjectTypeEnum.J2EE_EJB, NBProjectTypeEnum.J2EE_WEB); + if (isJ2EE) { + prjJvmArgs = Utils.getProperty(prjProps, "runmain.jvmargs"); + targetProps.put("runmain.jvmargs", prjJvmArgs + " -javaagent:" + antTaskJavaagentParam); + } else { + prjJvmArgs = Utils.getProperty(prjProps, "run.jvmargs"); + targetProps.put("run.jvmargs", prjJvmArgs + " -javaagent:" + antTaskJavaagentParam); + } + + env.setProperties(targetProps); + + // Launch the Ant task with the JaCoCo JavaAgent. + final ExecutorTask execute = executor.execute(antCookie, new String[]{antTask}); + + new RequestProcessor("JaCoCoverage Collection Task", 3, true).post(new Runnable() { + @Override + public void run() { + ProgressHandle progr = ProgressHandleFactory.createHandle("JaCoCoverage Collection Task"); + try { + progr.setInitialDelay(400); + progr.start(); + progr.switchToIndeterminate(); + + // Wait for the end of the Ant task execution. We do it in a new thread otherwise it would + // freeze the current one. This is a workaround for a known and old NetBeans bug: the ExecutorTask + // object provided by the NetBeans platform is not correctly wrapped. + int executeRes = execute.result(); + if (binreport.exists()) { + long st = System.currentTimeMillis(); + // Load the generated JaCoCo coverage report. Special case for Java Web projects. + File classDir; + if (isJ2EE) { + classDir = new File(prjDir + File.separator + "build" + File.separator + "web" + + File.separator + "WEB-INF" + File.separator + "classes" + File.separator); + } else { + classDir = new File(prjDir + Utils.getProperty(prjProps, "build.classes.dir") + File.separator); + } + File srcDir = new File(prjDir + Utils.getProperty(prjProps, "src.dir") + File.separator); + JaCoCoReportAnalyzer.toXmlReport(binreport, xmlreport, classDir, srcDir); + final Map coverageData = JaCoCoXmlReportParser.getCoverageData(xmlreport); + new File(prjDir + Globals.JACOCOVERAGE_DATA_DIR).mkdirs(); + + // Remove existing highlighting (from a previous coverage task), show reports and apply + // highlighting on each Java source file. + AbstractCoverageAnnotation.removeAll(NBUtils.getProjectId(project)); + String prjname = NBUtils.getProjectName(project); + if (enblConsoleReport) { + JaCoCoReportAnalyzer.toConsoleReport(coverageData, prjname + Globals.TXTREPORT_TABNAME); + } + File reportdir = new File(prjDir + Globals.HTML_REPORT_DIR); + if (reportdir.exists()) { + org.apache.commons.io.FileUtils.deleteDirectory(reportdir); + } + if (enblHtmlReport) { + reportdir.mkdirs(); + String report = JaCoCoReportAnalyzer.toHtmlReport(binreport, reportdir, classDir, srcDir, prjname); + if (openHtmlReport) { + try { + HtmlBrowser.URLDisplayer.getDefault().showURL(Utilities.toURI(new File(report)).toURL()); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } + } + } + if (enblHighlight) { + for (final JavaClass jclass : coverageData.values()) { + NBUtils.colorDoc(project, jclass, cfg.isEnblHighlightingExtended(), srcDir); + } + } + keepJaCoCoWorkfiles(binreport, xmlreport, prjDir, cfg.getJaCoCoWorkfilesRule()); + + long et = System.currentTimeMillis(); + LOGGER.log(Level.INFO, "Coverage Collection Task took: {0} ms", et - st); + } else { + AbstractCoverageAnnotation.removeAll(NBUtils.getProjectId(project)); + NBUtils.closeConsoleTab(Globals.TXTREPORT_TABNAME); + String msg = "Ant Task or JaCoCo Agent failed, JaCoCoverage can't process data.\n" + + "(AntExitCode=" + executeRes + ", JacocoBinReportFound=" + binreport.exists() + ")"; + NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.WARNING_MESSAGE); + DialogDisplayer.getDefault().notify(nd); + } + } catch (FileNotFoundException ex) { + Exceptions.printStackTrace(ex); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } catch (ParserConfigurationException | SAXException ex) { + Exceptions.printStackTrace(ex); + } finally { + progr.finish(); + } + } + }); + } + } else { + String msg = "Please enable at least one JaCoCoverage feature first (highlighting or reporting)."; + NotifyDescriptor nd = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notify(nd); + } + } + + /** + * Apply retention policy on JaCoCo workfiles. + * + * @param binreport JaCoCo binary report file. + * @param xmlreport JaCoCo XML report file. + * @param prjDir project directory. + * @param wfrule retention policy. + * @throws IOException if workfiles can't be removed or moved. + */ + private void keepJaCoCoWorkfiles(File binreport, File xmlreport, String prjDir, int wfrule) + throws IOException { + File xmlreportCpy = new File(prjDir + Globals.XML_BACKUP_REPORT); + File xmlreportZip = new File(prjDir + Globals.XMLZIP_BACKUP_REPORT); + File binreportCpy = new File(prjDir + Globals.BIN_BACKUP_REPORT); + File binreportZip = new File(prjDir + Globals.BINZIP_BACKUP_REPORT); + xmlreportCpy.delete(); + xmlreportZip.delete(); + binreportCpy.delete(); + binreportZip.delete(); + switch (wfrule) { + case 0: + org.apache.commons.io.FileUtils.moveFile(binreport, binreportCpy); + org.apache.commons.io.FileUtils.moveFile(xmlreport, xmlreportCpy); + break; + case 1: + Utils.zip(binreport, binreportZip, Globals.BINZIP_BACKUP_REPORT_ENTRY, false); + Utils.zip(xmlreport, xmlreportZip, Globals.XMLZIP_BACKUP_REPORT_ENTRY, false); + break; + case 2: + break; + } + binreport.delete(); + xmlreport.delete(); + } + + @SuppressWarnings("ReturnOfCollectionOrArrayField") + public Properties getAddAntTargetProps() { + return addAntTargetProps; + } +} diff --git a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntRunJ2SE.java b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntRunJ2SE.java index ed43809..285735c 100644 --- a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntRunJ2SE.java +++ b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntRunJ2SE.java @@ -22,7 +22,7 @@ import org.openide.util.actions.Presenter; /** - * The "Run with JaCoCoverage" contextual action registration for J2SE projects. + * The "Run with JaCoCoverage" contextual action registration for Java projects. * Start the "run" Ant task with the JaCoCo JavaAgent correctly configured, colorize Java source files and show a coverage report. * * @author Jonathan Lermitage diff --git a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntTestJ2SE.java b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntTestJ2SE.java index 6a5e48c..70764cc 100644 --- a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntTestJ2SE.java +++ b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/action/ProjectAntTestJ2SE.java @@ -16,7 +16,7 @@ import org.openide.util.actions.Presenter; /** - * The "Test with JaCoCoverage" contextual action registration for J2SE projects. + * The "Test with JaCoCoverage" contextual action registration for Java projects. * Start the "test" Ant task with the JaCoCo JavaAgent correctly configured, colorize Java source files and show a coverage report. * * @author Jonathan Lermitage @@ -39,9 +39,9 @@ public class ProjectAntTestJ2SE public ProjectAntTestJ2SE() { super("test"); - setEnabled(Utils.isProjectSupported(NBUtils.getSelectedProject(), NBProjectTypeEnum.J2SE/*, + setEnabled(Utils.isProjectSupported(NBUtils.getSelectedProject(), NBProjectTypeEnum.J2SE, NBProjectTypeEnum.J2EE, NBProjectTypeEnum.J2EE_EAR, - NBProjectTypeEnum.J2EE_EJB, NBProjectTypeEnum.J2EE_WEB*/)); + NBProjectTypeEnum.J2EE_EJB, NBProjectTypeEnum.J2EE_WEB)); putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true); putValue(Action.NAME, Bundle.CTL_ProjectAntTestJ2SE()); } diff --git a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/util/Utils.java b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/util/Utils.java index 71aeede..5ab0ea6 100644 --- a/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/util/Utils.java +++ b/tikione-jacocoverage-plugin/src/fr/tikione/jacocoverage/plugin/util/Utils.java @@ -1,288 +1,289 @@ -package fr.tikione.jacocoverage.plugin.util; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; -import org.apache.commons.io.IOUtils; -import org.netbeans.api.project.Project; -import org.openide.util.Exceptions; - -/** - * Some utilities. - * - * @author Jonathan Lermitage - */ -public class Utils { - - /** - * Regular expression to recognize "${key}" patterns in Properties files used by NetBeans projects. - */ - private static final String PA_NBPROPKEY_SHORTCUT = "\\$\\{([^\\}]+)\\}"; - - /** - * Compiled regular expression to recognize "${key}" patterns in Properties files used by NetBeans projects. - */ - private static final Pattern CPA_NBPROPKEY_SHORTCUT = Pattern.compile(PA_NBPROPKEY_SHORTCUT); - - /** - * The white-space character ({@code \u0020}). - */ - public static final char SPACE = '\u0020'; - - /** - * The tabulation character ({@code \u0009}). - */ - public static final char TAB = '\u0009'; - - /** - * File extension(s) of Java files. - */ - private static final String[] JAVA_EXT_ARR = new String[]{"java"}; - - /** - * Check a regular expression. - * - * @param src the text to examine. - * @param pattern the compiled regular expression, targeted area are enclosed with parenthesis. - * @return true if the regular expression is checked, otherwise false. - */ - public static boolean checkRegex(String src, Pattern pattern) { - return pattern.matcher(src).find(); - } - - /** - * Return the capturing groups from the regular expression in the string. - * - * @param src the string to search in. - * @param pattern the compiled pattern. - * @param expectedNbMatchs the expected tokens matched, for performance purpose. - * @return all the strings matched (in an ArrayList). - */ - public static List getGroupsFromRegex(String src, Pattern pattern, int expectedNbMatchs) { - List res = new ArrayList<>(expectedNbMatchs); - Matcher matcher = pattern.matcher(src); - while (matcher.find()) { - for (int group = 1; group <= matcher.groupCount(); group++) { - res.add(matcher.group(group)); - } - } - return res; - } - - /** - * Get the JaCoCo binary report file of the given project. - * - * @param project the project to get JaCoCo report file. - * @return the JaCoCo report file. - */ - public static File getJacocoBinReportFile(Project project) { - String prjdir = NBUtils.getProjectDir(project); - String bindir; - if (prjdir.contains(",") || prjdir.contains(";") || prjdir.contains("=")) { - // FIXED GitHub #9 JavaAgent doesn't allow comma in report file's path (comma is used to separate parameters). - // FIXED 20130625 extended GitHub #9 principle to other sensible characters. - bindir = System.getProperty("java.io.tmpdir"); - } else { - bindir = prjdir; - } - return new File(bindir, "jacoco.exec-" + System.nanoTime()); - } - - /** - * Get the JaCoCo XML report file of the given project. - * - * @param project the project to get JaCoCo report file. - * @return the JaCoCo report file. - */ - public static File getJacocoXmlReportfile(Project project) { - String jacocoXmlReportPath = NBUtils.getProjectDir(project) + File.separator + "jacocoverage.report.xml"; - return new File(jacocoXmlReportPath); - } - - /** - * Get a key value from a Properties object, with support of NetBeans key references (aka "${key}"). - * - * @param props the Properties object to load key value from. - * @param key the key value to get value. - * @return the key value. - */ - @SuppressWarnings("ValueOfIncrementOrDecrementUsed") - public static String getProperty(Properties props, String key) { - String value = props.getProperty(key, ""); - int security = 0; - while (security++ < 80 && checkRegex(value, CPA_NBPROPKEY_SHORTCUT)) { - List refs = getGroupsFromRegex(value, CPA_NBPROPKEY_SHORTCUT, 3); - for (String ref : refs) { - value = value.replaceFirst(PA_NBPROPKEY_SHORTCUT, props.getProperty(ref, "")); - } - } - return value; - } - - /** - * Indicate if a Java line describes a finished instruction, otherwise a part of a multi-line instruction (or no instruction). - * - * @param inst the Java line. - * @return {@code true} if instruction is finished, otherwise {@code false}. - */ - public static boolean isIntructionFinished(String inst) { - String trim = org.apache.commons.lang3.StringUtils.strip(inst); - boolean finished = trim.endsWith(";") || trim.endsWith("}") || trim.endsWith("{"); - if (!finished && (trim.contains(";") || trim.contains("}") || trim.contains("{"))) { - // Remove strings (a string could contains a semi-colon) and comments and check again. - trim = trim.replaceAll("\\\\\"", "").replaceAll("\".*\"", ""); - trim = org.apache.commons.lang3.StringUtils.strip(trim.replaceAll(";[^;]*//.*$", ";").replaceAll("/\\*.*\\*/", "")); - finished = trim.endsWith(";") || trim.endsWith("}") || trim.endsWith("{"); - } - return finished; - } - - /** - * Indicate if a project is about a certain type. - *
See DevFaqActionAllAvailableProjectTypes for help. - * - * @param project the project. - * @param prjtype the targeted project type. - * @return true if supported, otherwise false. - */ - @SuppressWarnings("UnnecessaryLabelOnBreakStatement") - public static boolean isProjectSupported(Project project, NBProjectTypeEnum... prjtype) { - boolean supported = false; - if (null != project) { - String projectClass = project.getClass().getName(); - PRJ: - for (NBProjectTypeEnum type : prjtype) { - if (type.isStrict() ? projectClass.equals(type.qname()) : projectClass.startsWith(type.qname())) { - supported = true; - break PRJ; - } - } - } - return supported; - } - - /** - * Get a list of every subfolder contained in a given folder. - * - * @param root the root folder. - * @return a list of subfolders. - */ - public static List listFolders(File root) { - List folders = new ArrayList<>(16); - File[] subfolders = root.listFiles(new FileFilter() { - @Override - public boolean accept(File pathname) { - return pathname.isDirectory(); - } - }); - folders.addAll(Arrays.asList(subfolders)); - for (File subfolder : subfolders) { - folders.addAll(listFolders(subfolder)); - } - return folders; - } - - /** - * Get a list of non-empty (ie that contains Java files) packages contained in a given folder and its subfolders. - * - * @param root a folder that contains Java sources. - * @return a list of packages and classes. - */ - public static List listNonEmptyPkgs(File root) { - List resPkgs = new ArrayList<>(16); - List pkgs = listFolders(root); - Collections.sort(pkgs); - for (File pkg : pkgs) { - List classes = new ArrayList<>(org.apache.commons.io.FileUtils.listFiles(pkg, JAVA_EXT_ARR, false)); - if (!classes.isEmpty()) { - resPkgs.add(pkg); - } - } - return resPkgs; - } - - /** - * Load an internal resource. - * - * @param internalResource the path of internal resource. - * @return the loaded resource. - * @throws IOException if an I/O error occurs. - */ - public static byte[] toBytes(String internalResource) - throws IOException { - byte[] content = null; - InputStream is = Utils.class.getResourceAsStream(internalResource); - try { - content = IOUtils.toByteArray(is); - } finally { - if (is != null) { - is.close(); - } - } - return content; - } - - /** - * Compress a file to Zip. The archive contains an entry named as the source file. - * - * @param src the source file to compress. - * @param dst the zipped output file. - * @param entryname the name of the entry stored in the zipped file. - * @param async if {@code true}, the compression process will be done in a separate parallel thread, otherwise the current thread. - */ - public static void zip(final File src, final File dst, final String entryname, boolean async) { - if (async) { - new Thread(new Runnable() { - @Override - public void run() { - zip(src, dst, entryname); - } - }).start(); - } else { - zip(src, dst, entryname); - } - } - - /** - * Compress a file to Zip. The archive contains an entry named as the source file - * - * @param src the source file to compress. - * @param dst the zipped output file. - * @param entryname the name of the entry stored in the zipped file. - * @throws FileNotFoundException if the source file doesn't exist. - */ - @SuppressWarnings("NestedAssignment") - private static void zip(File src, File dst, String entryname) { - byte[] buffer = new byte[512]; - try { - try (FileOutputStream dstStrm = new FileOutputStream(dst); ZipOutputStream zipStrm = new ZipOutputStream(dstStrm)) { - try (FileInputStream srcStrm = new FileInputStream(src)) { - ZipEntry entry = new ZipEntry(entryname); - zipStrm.putNextEntry(entry); - int len; - while ((len = srcStrm.read(buffer)) > 0) { - zipStrm.write(buffer, 0, len); - } - } - zipStrm.closeEntry(); - } - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - } - - private Utils() { - } -} +package fr.tikione.jacocoverage.plugin.util; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.commons.io.IOUtils; +import org.netbeans.api.project.Project; +import org.openide.util.Exceptions; + +/** + * Some utilities. + * + * @author Jonathan Lermitage + */ +public class Utils { + + /** + * Regular expression to recognize "${key}" patterns in Properties files used by NetBeans projects. + */ + private static final String PA_NBPROPKEY_SHORTCUT = "\\$\\{([^\\}]+)\\}"; + + /** + * Compiled regular expression to recognize "${key}" patterns in Properties files used by NetBeans projects. + */ + private static final Pattern CPA_NBPROPKEY_SHORTCUT = Pattern.compile(PA_NBPROPKEY_SHORTCUT); + + /** + * The white-space character ({@code \u0020}). + */ + public static final char SPACE = '\u0020'; + + /** + * The tabulation character ({@code \u0009}). + */ + public static final char TAB = '\u0009'; + + /** + * File extension(s) of Java files. + */ + private static final String[] JAVA_EXT_ARR = new String[]{"java"}; + + /** + * Check a regular expression. + * + * @param src the text to examine. + * @param pattern the compiled regular expression, targeted area are enclosed with parenthesis. + * @return true if the regular expression is checked, otherwise false. + */ + public static boolean checkRegex(String src, Pattern pattern) { + return pattern.matcher(src).find(); + } + + /** + * Return the capturing groups from the regular expression in the string. + * + * @param src the string to search in. + * @param pattern the compiled pattern. + * @param expectedNbMatchs the expected tokens matched, for performance purpose. + * @return all the strings matched (in an ArrayList). + */ + public static List getGroupsFromRegex(String src, Pattern pattern, int expectedNbMatchs) { + List res = new ArrayList<>(expectedNbMatchs); + Matcher matcher = pattern.matcher(src); + while (matcher.find()) { + for (int group = 1; group <= matcher.groupCount(); group++) { + res.add(matcher.group(group)); + } + } + return res; + } + + /** + * Get the JaCoCo binary report file of the given project. + * + * @param project the project to get JaCoCo report file. + * @return the JaCoCo report file. + */ + public static File getJacocoBinReportFile(Project project) { + String prjdir = NBUtils.getProjectDir(project); + String bindir; + if (prjdir.contains(",") || prjdir.contains(";") || prjdir.contains("=")) { + // FIXED GitHub #9 JavaAgent doesn't allow comma in report file's path (comma is used to separate parameters). + // FIXED 20130625 extended GitHub #9 principle to other sensible characters. + bindir = System.getProperty("java.io.tmpdir"); + } else { + bindir = prjdir; + } + return new File(bindir, "jacoco.exec-" + System.nanoTime()); + } + + /** + * Get the JaCoCo XML report file of the given project. + * + * @param project the project to get JaCoCo report file. + * @return the JaCoCo report file. + */ + public static File getJacocoXmlReportfile(Project project) { + String jacocoXmlReportPath = NBUtils.getProjectDir(project) + File.separator + "jacocoverage.report.xml"; + return new File(jacocoXmlReportPath); + } + + /** + * Get a key value from a Properties object, with support of NetBeans key references (aka "${key}"). + * TODO: fix crash with javaee 'build.classes.dir' property + * + * @param props the Properties object to load key value from. + * @param key the key value to get value. + * @return the key value. + */ + @SuppressWarnings("ValueOfIncrementOrDecrementUsed") + public static String getProperty(Properties props, String key) { + String value = props.getProperty(key, ""); + int security = 0; + while (security++ < 80 && checkRegex(value, CPA_NBPROPKEY_SHORTCUT)) { + List refs = getGroupsFromRegex(value, CPA_NBPROPKEY_SHORTCUT, 3); + for (String ref : refs) { + value = value.replaceFirst(PA_NBPROPKEY_SHORTCUT, props.getProperty(ref, "")); + } + } + return value; + } + + /** + * Indicate if a Java line describes a finished instruction, otherwise a part of a multi-line instruction (or no instruction). + * + * @param inst the Java line. + * @return {@code true} if instruction is finished, otherwise {@code false}. + */ + public static boolean isIntructionFinished(String inst) { + String trim = org.apache.commons.lang3.StringUtils.strip(inst); + boolean finished = trim.endsWith(";") || trim.endsWith("}") || trim.endsWith("{"); + if (!finished && (trim.contains(";") || trim.contains("}") || trim.contains("{"))) { + // Remove strings (a string could contains a semi-colon) and comments and check again. + trim = trim.replaceAll("\\\\\"", "").replaceAll("\".*\"", ""); + trim = org.apache.commons.lang3.StringUtils.strip(trim.replaceAll(";[^;]*//.*$", ";").replaceAll("/\\*.*\\*/", "")); + finished = trim.endsWith(";") || trim.endsWith("}") || trim.endsWith("{"); + } + return finished; + } + + /** + * Indicate if a project is about a certain type. + *
See DevFaqActionAllAvailableProjectTypes for help. + * + * @param project the project. + * @param prjtype the targeted project type. + * @return true if supported, otherwise false. + */ + @SuppressWarnings("UnnecessaryLabelOnBreakStatement") + public static boolean isProjectSupported(Project project, NBProjectTypeEnum... prjtype) { + boolean supported = false; + if (null != project) { + String projectClass = project.getClass().getName(); + PRJ: + for (NBProjectTypeEnum type : prjtype) { + if (type.isStrict() ? projectClass.equals(type.qname()) : projectClass.startsWith(type.qname())) { + supported = true; + break PRJ; + } + } + } + return supported; + } + + /** + * Get a list of every subfolder contained in a given folder. + * + * @param root the root folder. + * @return a list of subfolders. + */ + public static List listFolders(File root) { + List folders = new ArrayList<>(16); + File[] subfolders = root.listFiles(new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isDirectory(); + } + }); + folders.addAll(Arrays.asList(subfolders)); + for (File subfolder : subfolders) { + folders.addAll(listFolders(subfolder)); + } + return folders; + } + + /** + * Get a list of non-empty (ie that contains Java files) packages contained in a given folder and its subfolders. + * + * @param root a folder that contains Java sources. + * @return a list of packages and classes. + */ + public static List listNonEmptyPkgs(File root) { + List resPkgs = new ArrayList<>(16); + List pkgs = listFolders(root); + Collections.sort(pkgs); + for (File pkg : pkgs) { + List classes = new ArrayList<>(org.apache.commons.io.FileUtils.listFiles(pkg, JAVA_EXT_ARR, false)); + if (!classes.isEmpty()) { + resPkgs.add(pkg); + } + } + return resPkgs; + } + + /** + * Load an internal resource. + * + * @param internalResource the path of internal resource. + * @return the loaded resource. + * @throws IOException if an I/O error occurs. + */ + public static byte[] toBytes(String internalResource) + throws IOException { + byte[] content = null; + InputStream is = Utils.class.getResourceAsStream(internalResource); + try { + content = IOUtils.toByteArray(is); + } finally { + if (is != null) { + is.close(); + } + } + return content; + } + + /** + * Compress a file to Zip. The archive contains an entry named as the source file. + * + * @param src the source file to compress. + * @param dst the zipped output file. + * @param entryname the name of the entry stored in the zipped file. + * @param async if {@code true}, the compression process will be done in a separate parallel thread, otherwise the current thread. + */ + public static void zip(final File src, final File dst, final String entryname, boolean async) { + if (async) { + new Thread(new Runnable() { + @Override + public void run() { + zip(src, dst, entryname); + } + }).start(); + } else { + zip(src, dst, entryname); + } + } + + /** + * Compress a file to Zip. The archive contains an entry named as the source file + * + * @param src the source file to compress. + * @param dst the zipped output file. + * @param entryname the name of the entry stored in the zipped file. + * @throws FileNotFoundException if the source file doesn't exist. + */ + @SuppressWarnings("NestedAssignment") + private static void zip(File src, File dst, String entryname) { + byte[] buffer = new byte[512]; + try { + try (FileOutputStream dstStrm = new FileOutputStream(dst); ZipOutputStream zipStrm = new ZipOutputStream(dstStrm)) { + try (FileInputStream srcStrm = new FileInputStream(src)) { + ZipEntry entry = new ZipEntry(entryname); + zipStrm.putNextEntry(entry); + int len; + while ((len = srcStrm.read(buffer)) > 0) { + zipStrm.write(buffer, 0, len); + } + } + zipStrm.closeEntry(); + } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + private Utils() { + } +}