package fr.tikione.jacocoverage.plugin.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.openide.DialogDisplayer; import org.openide.NotifyDescriptor; /** * Plugin's configuration handler at project level. * * @author Jonathan Lermitage */ public class ProjectConfig { private static final Logger LOGGER = Logger.getLogger(ProjectConfig.class.getName()); /** A general cache for project configuration instances. */ private static final Map<File, ProjectConfig> prjCfgs = Collections.synchronizedMap(new HashMap<File, ProjectConfig>(8)); /** Key for map of configuration properties: commons properties. */ private static final String JSON_GENERAL = "preferences"; /** Key for map of configuration properties: packages and classes filter. */ private static final String JSON_PKGFILTER = "pkgclss.excludelist"; /** * Get project's configuration handler. * * @param prjCfgFile the project to get configuration handler from. * @return project's configuration handler. * @throws IOException if cannot load configuration. */ public static ProjectConfig forFile(File prjCfgFile) throws IOException { ProjectConfig pc; if (prjCfgs.containsKey(prjCfgFile)) { pc = prjCfgs.get(prjCfgFile); } else { pc = new ProjectConfig(prjCfgFile); pc.load(); prjCfgs.put(prjCfgFile, pc); } return pc; } /** The map of all configuration properties. Element {@link #JSON_GENERAL} contains common properties (in a {@code Properties} object). Element {@link #JSON_PKGFILTER} contains configuration for packages and classes filter (an {@code ArrayList} of excluded elements). */ private final Map<String, Object> pref = new HashMap<>(4); /** The file used for configuration persistence. */ private final File prjCfgFile; /** JSon mapper. */ private final ObjectMapper mapper = new ObjectMapper(); private ProjectConfig(File prjCfgFile) { this.prjCfgFile = prjCfgFile; pref.put(JSON_GENERAL, new Properties()); pref.put(JSON_PKGFILTER, new ArrayList<String>(16)); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); mapper.enable(SerializationFeature.EAGER_SERIALIZER_FETCH); } private Properties getInternalPref() { return (Properties) pref.get(JSON_GENERAL); } @SuppressWarnings("unchecked") public List<String> getPkgclssExclude() { ArrayList<String> pkgfilter = (ArrayList<String>) pref.get(JSON_PKGFILTER); return pkgfilter == null ? Collections.<String>emptyList() : pkgfilter; } /** * Load project's configuration. * * @throws IOException if cannot load configuration. */ @SuppressWarnings("unchecked") public void load() throws IOException { getInternalPref().clear(); getPkgclssExclude().clear(); prjCfgFile.getParentFile().mkdirs(); if (prjCfgFile.exists()) { try { getInternalPref().putAll((Map<Object, Object>) mapper.readValue(prjCfgFile, Map.class).get(JSON_GENERAL)); getPkgclssExclude().addAll((Collection<? extends String>) mapper.readValue(prjCfgFile, Map.class).get(JSON_PKGFILTER)); } catch (IOException ex) { LOGGER.log(Level.INFO, "Project's JaCoCoverage configuration file format is outdated or invalid. Reset cause:", ex); String msg = "The project's JaCoCoverage configuration file format is outdated or invalid.\n" + "The configuration file has been reset to support new format."; DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(msg, NotifyDescriptor.WARNING_MESSAGE)); } } } /** * Store project's configuration. * * @throws IOException if cannot store configuration. */ public void store() throws IOException { prjCfgFile.getParentFile().mkdirs(); if (prjCfgFile.exists() && !prjCfgFile.delete()) { throw new IOException("Cannot write project's jacocoverage config to: " + prjCfgFile); } else { mapper.writeValue(prjCfgFile, pref); } } /** * Get project's configuration value: Indicate if project overrides plugin's globals options. * * @return configuration value. */ public boolean isOverrideGlobals() { return Boolean.parseBoolean( getInternalPref().getProperty( Globals.PROP_PRJ_OVERRIDE_GLOBALS, Boolean.toString(Globals.DEF_PRJ_OVERRIDE_GLOBALS))); } /** * Set project's configuration value: Indicate if project overrides plugin's globals options. * * @param enbl configuration value. */ public void setOverrideGlobals(boolean enbl) { getInternalPref().setProperty(Globals.PROP_PRJ_OVERRIDE_GLOBALS, Boolean.toString(enbl)); } /** * Get configuration value: show a minimal textual JaCoCo report in a NetBeans console tab. * * @return configuration value. */ public boolean isEnblConsoleReport() { boolean res; if (isOverrideGlobals()) { res = Boolean.parseBoolean(getInternalPref().getProperty( Globals.PROP_ENABLE_CONSOLE_REPORT, Boolean.toString(Globals.DEF_ENABLE_CONSOLE_REPORT))); } else { res = Config.isEnblConsoleReport(); } return res; } /** * Get configuration value: enable code highlighting. * * @return configuration value. */ public boolean isEnblHighlighting() { boolean res; if (isOverrideGlobals()) { res = Boolean.parseBoolean(getInternalPref().getProperty( Globals.PROP_ENABLE_HIGHLIGHT, Boolean.toString(Globals.DEF_ENABLE_HIGHLIGHT))); } else { res = Config.isEnblHighlighting(); } return res; } /** * Get configuration value: enable extended code highlighting. * * @return configuration value. */ public boolean isEnblHighlightingExtended() { boolean res; if (isOverrideGlobals()) { res = Boolean.parseBoolean(getInternalPref().getProperty( Globals.PROP_ENABLE_HIGHLIGHTEXTENDED, Boolean.toString(Globals.DEF_ENABLE_HIGHLIGHTEXTENDED))); } else { res = Config.isEnblHighlightingExtended(); } return res; } /** * Get configuration value: generate a complete HTML JaCoCo report. * * @return configuration value. */ public boolean isEnblHtmlReport() { boolean res; if (isOverrideGlobals()) { res = Boolean.parseBoolean(getInternalPref().getProperty( Globals.PROP_ENABLE_HTML_REPORT, Boolean.toString(Globals.DEF_ENABLE_HTML_REPORT))); } else { res = Config.isEnblHtmlReport(); } return res; } /** * Get configuration value: automatically open generated complete HTML JaCoCo report. * * @return configuration value. */ public boolean isOpenHtmlReport() { boolean res; if (isOverrideGlobals()) { res = Boolean.parseBoolean(getInternalPref().getProperty( Globals.PROP_AUTOOPEN_HTML_REPORT, Boolean.toString(Globals.DEF_AUTOOPEN_HTML_REPORT))); } else { res = Config.isOpenHtmlReport(); } return res; } /** * Get configuration value: JaCoCoverage themePrefix. * * @return configuration value. */ public int getTheme() { int res; if (isOverrideGlobals()) { res = Integer.parseInt(getInternalPref().getProperty( Globals.PROP_THEME, Integer.toString(Globals.DEF_THEME))); } else { res = Config.getTheme(); } return res; } /** * Get configuration value: what to do with JaCoCo workfiles. * * @return configuration value. */ public int getJaCoCoWorkfilesRule() { int res; if (isOverrideGlobals()) { res = Integer.parseInt(getInternalPref().getProperty( Globals.PROP_JACOCOWORKFILES_RULE, Integer.toString(Globals.DEF_JACOCOWORKFILES_RULE))); } else { res = Config.getJaCoCoWorkfilesRule(); } return res; } /** * Set configuration value: show a minimal textual JaCoCo report in a NetBeans console tab. * * @param enbl configuration value. */ public void setEnblConsoleReport(boolean enbl) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_ENABLE_CONSOLE_REPORT, Boolean.toString(enbl)); } else { Config.setEnblConsoleReport(enbl); } } /** * Set configuration value: enable code highlighting. * * @param enbl configuration value. */ public void setEnblHighlighting(boolean enbl) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_ENABLE_HIGHLIGHT, Boolean.toString(enbl)); } else { Config.setEnblHighlighting(enbl); } } /** * Set configuration value: enable extended code highlighting. * * @param enbl configuration value. */ public void setEnblHighlightingExtended(boolean enbl) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_ENABLE_HIGHLIGHTEXTENDED, Boolean.toString(enbl)); } else { Config.setEnblHighlightingExtended(enbl); } } /** * Set configuration value: generate a complete HTML JaCoCo report. * * @param enbl configuration value. */ public void setEnblHtmlReport(boolean enbl) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_ENABLE_HTML_REPORT, Boolean.toString(enbl)); } else { Config.setEnblHtmlReport(enbl); } } /** * Set configuration value: automatically open generated complete HTML JaCoCo report. * * @param enbl configuration value. */ public void setOpenHtmlReport(boolean enbl) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_AUTOOPEN_HTML_REPORT, Boolean.toString(enbl)); } else { Config.setOpenHtmlReport(enbl); } } /** * Set configuration value: JaCoCoverage themePrefix. * * @param theme configuration value. */ public void setTheme(int theme) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_THEME, Integer.toString(theme)); } else { Config.setTheme(theme); } } /** * Set configuration value: what to do with JaCoCo workfiles. * * @param rule configuration value. */ public void setJaCoCoWorkfilesRule(int rule) { if (isOverrideGlobals()) { getInternalPref().setProperty(Globals.PROP_JACOCOWORKFILES_RULE, Integer.toString(rule)); } else { Config.setJaCoCoWorkfilesRule(rule); } } }