/* * ****************************************************************************** * MontiCore Language Workbench * Copyright (c) 2015, MontiCore, All rights reserved. * * This project is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this project. If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************** */ package de.monticore.generating.templateengine.reporting; import java.nio.file.Path; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import de.monticore.ast.ASTNode; import de.monticore.generating.templateengine.HookPoint; import de.monticore.generating.templateengine.TemplateController; import de.monticore.generating.templateengine.reporting.commons.ReportManager; import de.monticore.generating.templateengine.reporting.commons.ReportManager.ReportManagerFactory; import de.monticore.symboltable.Scope; import de.se_rwth.commons.logging.Log; import de.se_rwth.commons.logging.Slf4jLog; /** * Facade for all reporting activities. Invoking a report method causes all * AReporter implementing this method to execute it. * * @author (last commit) $Author$ * @version $Revision$, $Date$ */ public class Reporting extends Slf4jLog { /** * Map of model names to actual report managers. */ private Map<String, ReportManager> reportManagers = new HashMap<>(); /** * Where reports will be written to. */ private String outputDirectory; /** * For creating report managers on-demand for newly processed models. */ private ReportManagerFactory factory; /** * Constructor for de.monticore.generating.templateengine.reporting.Reporting * * @param outputDirectory for storing the reports * @param factory for creating specific report manager configurations */ private Reporting(String outputDirectory, ReportManagerFactory factory) { this.outputDirectory = outputDirectory; this.factory = factory; } private Map<String, ReportManager> getReportManagers() { return this.reportManagers; } private String getOutputDirectory() { return this.outputDirectory; } private ReportManagerFactory getFactory() { return this.factory; } private ReportManager getReportManager(String modelName) { if (!this.getReportManagers().containsKey(modelName)) { ReportManager repoMan = this.getFactory().provide(modelName); this.getReportManagers().put(modelName, repoMan); } return this.getReportManagers().get(modelName); } // ######################### // some log overriding magic /** * @see de.se_rwth.commons.logging.ILog#warn(java.lang.String) */ @Override public void doWarn(String msg) { reportWarning(msg); super.doWarn(msg); } /** * @see de.se_rwth.commons.logging.ILog#warn(java.lang.String, * java.lang.Throwable) */ @Override public void doWarn(String msg, Throwable t) { reportWarning(msg); super.doWarn(msg, t); } /** * @see de.se_rwth.commons.logging.ILog#error(java.lang.String) */ @Override public void doError(String msg) { this.doError(msg, Optional.empty()); } /** * @see de.se_rwth.commons.logging.ILog#error(java.lang.String, * java.lang.Throwable) */ @Override public void doError(String msg, Throwable t) { this.doError(msg, Optional.ofNullable(t)); } private void doError(String msg, Optional<Throwable> t) { // we need to know whether we wanted to fail immediately boolean wantsToFailQuick = Log.isFailQuickEnabled(); if (wantsToFailQuick) { // if so, then temporary deactivate fail quick to allow proper logging Log.enableFailQuick(false); } // report the error reportError(msg); // and log the error if (t.isPresent()) { super.doError(msg, t.get()); } else { super.doError(msg); } // now if we wanted to fail quick, we need to flush the reports without // causing a crash, i.e., we need to catch exceptions if (wantsToFailQuick) { try { // TODO TGR check if null can be provided in this case flush(null); } catch (Exception e) { // this is rather generic but it'll probably do for now super.doError("0xA4055 Error during error reporting. Enable debug for more details."); super.doDebug("Error during error reporting", e, ReportManager.class.getName()); } } // eventually, if we wanted to fail quick we do it now if (wantsToFailQuick) { Log.enableFailQuick(true); } } // end of the log overriding magic // ######################### /* the singleton */ private static Reporting singleton; /* whether reporting is enabled at the moment */ private static boolean enabled = false; /* the currently active model for which reporting takes place */ private static String currentModel; /** * @return the single reporting instance */ private static Reporting get() { return singleton; } public static void init(String outputDirectory, ReportManagerFactory factory) { if (outputDirectory == null || outputDirectory.isEmpty()) { Log.error("0xA4050 Output directory must not be null or empty."); } if (factory == null) { Log.error("0xA4051 Report manager factory must not be null."); } singleton = new Reporting(outputDirectory, factory); Log.setLog(singleton); } /** * @return whether reporting was properly initialized * @see Reporting#init(String, ReportManagerFactory) */ public static boolean isInitialized() { return get() != null; } /** * @return whether reporting is currently enabled */ public static boolean isEnabled() { if (isInitialized()) { return enabled; } // if it's not initialized it's also not enabled return false; } /** * Enable reporting for the given model name. * * @param modelName for which to enable reporting * @return whether reporting is enabled or not (reporting will not be enabled * if reporting was not previously initialized) */ public static boolean on(String modelName) { if (modelName == null || modelName.isEmpty()) { Log.error("0xA4052 Must specify valid model name for reporting."); } if (!isInitialized()) { Log.warn("0xA4053 You must initialize reporting before enabling it."); return false; } currentModel = modelName; enabled = true; return enabled; } /** * Disable reporting entirely. * * @return the currently active model name for which reporting was active */ public static String off() { if (isInitialized()) { enabled = false; if (currentModel != null) { return currentModel; } } return ""; } /** * @return the currently active/responsible report manager instance */ private static ReportManager getReportManager() { // must only be used internally and with preceeding checks fo initialization return get().getReportManager(currentModel); } public static void reportTransformationStart(String transformationName) { if (isEnabled()) { getReportManager().reportTransformationStart(transformationName); } } public static void reportTransformationObjectMatch(String transformationName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTransformationObjectMatch(transformationName, ast); } } public static void reportTransformationOldValue(String transformationName, ASTNode ast){ if (isEnabled()) { getReportManager().reportTransformationOldValue(transformationName, ast); } } public static void reportTransformationNewValue(String transformationName, ASTNode ast){ if (isEnabled()) { getReportManager().reportTransformationNewValue(transformationName, ast); } } public static void reportTransformationOldValue(String transformationName, String value){ if (isEnabled()) { getReportManager().reportTransformationOldValue(transformationName, value); } } public static void reportTransformationNewValue(String transformationName, String value){ if (isEnabled()) { getReportManager().reportTransformationNewValue(transformationName, value); } } public static void reportTransformationOldValue(String transformationName, boolean value){ if (isEnabled()) { getReportManager().reportTransformationOldValue(transformationName, value); } } public static void reportTransformationNewValue(String transformationName, boolean value){ if (isEnabled()) { getReportManager().reportTransformationNewValue(transformationName, value); } } public static void reportTransformationObjectChange(String transformationName, ASTNode ast, String attributeName) { if (isEnabled()) { getReportManager().reportTransformationObjectChange(transformationName, ast, attributeName); } } public static void reportTransformationObjectCreation(String transformationName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTransformationObjectCreation(transformationName, ast); } } public static void reportTransformationObjectDeletion(String transformationName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTransformationObjectDeletion(transformationName, ast); } } public static void reportModelStart(ASTNode ast, String modelName, String fileName) { if (isEnabled()) { getReportManager().reportModelStart(ast, modelName, fileName); } } /** * Reports the execution of templates * * @param templateName * @param ast */ /* handwritten templates, and templates within Template Hookpoints and AST * specific Template Hookpoints */ public static void reportTemplateStart(String templateName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTemplateStart(templateName, ast); } } /** * Reports the execution of a standard template that is wrapped into a * template hook point via the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) * getTemplateForwardings} method. The template is wrapped into the template * hook point only if there is no other template forwarding. * * @param templateName * @param ast */ public static void reportExecuteStandardTemplate(String templateName, ASTNode ast) { if (isEnabled()) { getReportManager().reportExecuteStandardTemplate(templateName, ast); } } /** * Reports a template based file creation via the * {@link de.monticore.generating.templateengine.TemplateController#writeArgs(String, String, String, ASTNode, List) * writeArgs} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController}. * * @param templateName * @param path * @param ast */ public static void reportFileCreation(String templateName, Path path, ASTNode ast) { if (isEnabled()) { getReportManager().reportFileCreation(templateName, path, ast); } } /** * Reports the end of a file creation (file finalization). * * @param templateName * @param path * @param ast */ public static void reportFileFinalization(String templateName, Path path, ASTNode ast) { if (isEnabled()) { getReportManager().reportFileFinalization(templateName, path, ast); } } /** * Reports a template based file creation. * * @param templateName * @param qualifiedFilename * @param fileExtension * @param ast */ public static void reportFileCreation(String templateName, String qualifiedFilename, String fileExtension, ASTNode ast) { if (isEnabled()) { getReportManager().reportFileCreation(templateName, qualifiedFilename, fileExtension, ast); } } /** * Reports the end of a file creation (file finalization). * * @param templateName * @param qualifiedFilename * @param fileExtension * @param ast */ public static void reportFileFinalization(String templateName, String qualifiedFilename, String fileExtension, ASTNode ast) { if (isEnabled()) { getReportManager() .reportFileFinalization(templateName, qualifiedFilename, fileExtension, ast); } } /** * Reports the end of a template execution. * * @param templateName * @param ast */ public static void reportTemplateEnd(String templateName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTemplateEnd(templateName, ast); } } public static void reportModelEnd(String modelName, String fileName) { if (isEnabled()) { getReportManager().reportModelEnd(modelName, fileName); } } public static void reportModelLoad(String qualifiedName) { if (isEnabled()) { getReportManager().reportModelLoad(qualifiedName); } } public static void reportSetValue(String name, Object value) { if (isEnabled()) { getReportManager().reportSetValue(name, value); } } public static void reportInstantiate(String className, List<Object> params) { if (isEnabled()) { getReportManager().reportInstantiate(className, params); } } public static void reportMethodCall(String className, String methodName, List<Object> params) { if (isEnabled()) { getReportManager().reportMethodCall(className, methodName, params); } } /** * Reports the template inclusion via the * {@link de.monticore.generating.templateengine.TemplateController#logTemplateCallOrInclude(String, ASTNode) * logTemplateCallOrInclude} method called by the * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) * processTemplate} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController} after calculating all forwardings. * * @param templateName * @param ast */ public static void reportTemplateInclude(String templateName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTemplateInclude(templateName, ast); } } /** * Reports the template write via the * {@link de.monticore.generating.templateengine.TemplateController#logTemplateCallOrInclude(String, ASTNode) * logTemplateCallOrInclude} method called by the * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) * processTemplate} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController}. TemplateWrite does not calculate forwardings, it * processes the template instantly. * * @param templateName * @param ast */ public static void reportTemplateWrite(String templateName, ASTNode ast) { if (isEnabled()) { getReportManager().reportTemplateWrite(templateName, ast); } } /** * Reports the registration of a hook point via the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#setHookPoint(String, HookPoint) * setHookPoint} method of the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement * GlobalExtensionManagement}. * * @param hookName * @param hp */ public static void reportSetHookPoint(String hookName, HookPoint hp) { if (isEnabled()) { getReportManager().reportSetHookPoint(hookName, hp); } } /** * Reports the execution of a hook point via the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#callHookPoint(TemplateController, String, ASTNode) * callHookPoint} method. This does not include the execution of hook points * registered by the setBefore, setAfter or replaceTemplate Methods, nor the * execution of AST specific hook points. * * @param oldTemplate * @param beforeHps * @param ast */ public static void reportCallHookPointStart(String hookName, HookPoint hp, ASTNode ast) { if (isEnabled()) { getReportManager().reportCallHookPointStart(hookName, hp, ast); } } /** * Reports the end of the execution of a hook point via the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#callHookPoint(TemplateController, String, ASTNode) * callHookPoint} method. * * @param hookName */ public static void reportCallHookPointEnd(String hookName) { if (isEnabled()) { getReportManager().reportCallHookPointEnd(hookName); } } /** * Reports the execution of hook points via the * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) * include} or * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) * includeArgs} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController}. This includes the execution of all hook points * registered by setAfter. This method is called in the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) * getTemplateForwardings} method triggered by the * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) * processTemplate} method. * * @param oldTemplate * @param afterHps * @param ast */ public static void reportCallAfterHookPoint(String oldTemplate, Collection<HookPoint> afterHps, ASTNode ast) { if (isEnabled()) { getReportManager().reportCallAfterHookPoint(oldTemplate, afterHps, ast); } } /** * Reports the execution of hook points via the * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) * include} or * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) * includeArgs} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController}. This includes the execution of all hook points * registered by setBefore. This method is called in the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) * getTemplateForwardings} method triggered by the * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) * processTemplate} method. * * @param oldTemplate * @param beforeHps * @param ast */ public static void reportCallBeforeHookPoint(String oldTemplate, Collection<HookPoint> beforeHps, ASTNode ast) { if (isEnabled()) { getReportManager().reportCallBeforeHookPoint(oldTemplate, beforeHps, ast); } } /** * Reports the execution of hook points via the * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) * include} or * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) * includeArgs} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController}. This includes the execution of all hook points * registered by setReplace. These hook points replace a template. This method * is called in the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardingsX(String , ASTNode) * getTemplateForwardingsX} method triggered by the * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) * processTemplate} method. * * @param oldTemplate * @param hps * @param ast */ public static void reportCallReplacementHookPoint(String oldTemplate, List<HookPoint> hps, ASTNode ast) { if (isEnabled()) { getReportManager().reportCallReplacementHookPoint(oldTemplate, hps, ast); } } /** * Reports the execution of hook points via the * {@link de.monticore.generating.templateengine.TemplateController#include(List, List) * include} or * {@link de.monticore.generating.templateengine.TemplateController#includeArgs(String, List) * includeArgs} method of the * {@link de.monticore.generating.templateengine.TemplateController * TemplateController}. This includes the execution of all hook points * registered by setASTSpecificReplacement. This method is called in the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#getTemplateForwardings(String , ASTNode) * getTemplateForwardings} method triggered by the * {@link de.monticore.generating.templateengine.TemplateController#processTemplate(String, ASTNode, List) * processTemplate} method. * * @param oldTemplate * @param hps * @param ast */ public static void reportCallSpecificReplacementHookPoint(String oldTemplate, List<HookPoint> hps, ASTNode ast) { if (isEnabled()) { getReportManager().reportCallSpecificReplacementHookPoint(oldTemplate, hps, ast); } } /** * Reports the replacement of a template by an AST hook point via the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#replaceTemplate(String , ASTNode , HookPoint ) * replaceTemplate} method of the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement * GlobalExtensionManagement}. This does not include any other assignment or * replacement. * * @param oldTemplate * @param node * @param newHp */ public static void reportASTSpecificTemplateReplacement(String oldTemplate, ASTNode node, HookPoint newHp) { if (isEnabled()) { getReportManager().reportASTSpecificTemplateReplacement(oldTemplate, node, newHp); } } /** * Reports the replacement of a template by hook points via the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#replaceTemplate(String , List ) * replaceTemplate} method of the * {@link de.monticore.generating.templateengine.GlobalExtensionManagement * GlobalExtensionManagement}. This does not include any other assignment or * replacement. * * @param oldTemplate * @param newHps */ public static void reportTemplateReplacement(String oldTemplate, List<? extends HookPoint> newHps) { if (isEnabled()) { getReportManager().reportTemplateReplacement(oldTemplate, newHps); } } /** * Reports the assignment of hook points to a template via * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#setBeforeTemplate(String , List )} * . This does not include any other assignment or replacement. * * @param template * @param beforeHps */ public static void reportSetBeforeTemplate(String template, List<? extends HookPoint> beforeHps) { if (isEnabled()) { getReportManager().reportSetBeforeTemplate(template, beforeHps); } } /** * Reports the assignment of hook points to a template via * {@link de.monticore.generating.templateengine.GlobalExtensionManagement#setAfterTemplate(String , List )} * . This does not include any other assignment or replacement. * * @param template * @param afterHps */ public static void reportSetAfterTemplate(String template, List<? extends HookPoint> afterHps) { if (isEnabled()) { getReportManager().reportSetAfterTemplate(template, afterHps); } } public static void reportUseHandwrittenCodeFile(Path parentDir, Path fileName) { if (isEnabled()) { getReportManager().reportUseHandwrittenCodeFile(parentDir, fileName); } } public static void reportAddValue(String name, Object value, int size) { if (isEnabled()) { getReportManager().reportAddValue(name, value, size); } } /** * Invoking this method causes a report of value to DetailedReporter. * * @param line that will be reported in DetailedReporter */ public static void reportToDetailed(String value) { if (isEnabled()) { getReportManager().reportDetailed(value); } } /** * This method is called when an input file is opened which is obtained via * model resolution. Such files typically are dependency models (e.g., super * grammars, super CDs, ...). * * @param parentPath * @param file */ public static void reportOpenInputFile(Path parentPath, Path file) { if (isEnabled()) { getReportManager().reportOpenInputFile(parentPath, file); } } /** * This method is called when an input file is parsed; i.e., this report hook * point is designed for the main input artifacts only. E.g., files that are * loaded on demand during further processing should not report using this * method but {@link #reportOpenInputFile(Path, Path)} instead. * * @param inputFilePath * @param modelName */ public static void reportParseInputFile(Path inputFilePath, String modelName) { if (isEnabled()) { getReportManager().reportParseInputFile(inputFilePath, modelName); } } /** * This method is called to report the content of the symbol table. The method * should only be called once during the execution of the generator. * * @param scope */ public static void reportSymbolTableScope(Scope scope) { if (isEnabled()) { getReportManager().reportSymbolTableScope(scope); } } /** * Invoking this method causes all AReporter to close their files and all * OneTimeReporter to write their content into files. * * @param ast the root node of the reported ast */ public static void flush(ASTNode ast) { if (isEnabled()) { getReportManager().flush(ast); } } public static void reportWarning(String message) { if (isEnabled()) { getReportManager().reportWarning(message); } } public static void reportError(String msg) { if (isEnabled()) { getReportManager().reportError(msg); } } public static String getOutputDir() { if (isInitialized()) { return get().getOutputDirectory(); } return ""; } }