package de.lmu.ifi.dbs.elki.application; /* This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures Copyright (C) 2011 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import java.io.File; import java.util.Collection; import de.lmu.ifi.dbs.elki.logging.Logging; import de.lmu.ifi.dbs.elki.logging.LoggingConfiguration; import de.lmu.ifi.dbs.elki.logging.LoggingUtil; import de.lmu.ifi.dbs.elki.utilities.ClassGenericsUtil; import de.lmu.ifi.dbs.elki.utilities.FormatUtil; import de.lmu.ifi.dbs.elki.utilities.exceptions.AbortException; import de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException; import de.lmu.ifi.dbs.elki.utilities.optionhandling.AbstractParameterizer; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionID; import de.lmu.ifi.dbs.elki.utilities.optionhandling.OptionUtil; import de.lmu.ifi.dbs.elki.utilities.optionhandling.ParameterException; import de.lmu.ifi.dbs.elki.utilities.optionhandling.Parameterizable; import de.lmu.ifi.dbs.elki.utilities.optionhandling.UnspecifiedParameterException; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.Parameterization; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.SerializedParameterization; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameterization.TrackParameters; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassParameter; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.FileParameter; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Flag; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.Parameter; import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.StringParameter; import de.lmu.ifi.dbs.elki.utilities.pairs.Pair; /** * AbstractApplication sets the values for flags verbose and help. *

* Any Wrapper class that makes use of these flags may extend this class. Beware * to make correct use of parameter settings via optionHandler as commented with * constructor and methods. * * @author Elke Achtert * @author Erich Schubert * * @apiviz.uses LoggingConfiguration */ public abstract class AbstractApplication implements Parameterizable { /** * We need a static logger in this class, for code used in "main" methods. */ protected static Logging STATIC_LOGGER = Logging.getLogger(AbstractApplication.class); /** * The newline string according to system. */ private static final String NEWLINE = System.getProperty("line.separator"); /** * Information for citation and version. */ public static final String INFORMATION = "ELKI Version 0.4 (2011, August)" + NEWLINE + NEWLINE + "published in:" + NEWLINE + "E. Achtert, A. Hettab, H.-P. Kriegel, E. Schubert, A. Zimek:" + NEWLINE + "Spatial Outlier Detection: Data, Algorithms, Visualizations." + NEWLINE + "In Proceedings of the 12th International Symposium on" + NEWLINE + "Spatial and Temporal Databases (SSTD), Minneapolis, MN, 2011." + NEWLINE; /** * Parameter that specifies the name of the output file. *

* Key: {@code -app.out} *

*/ public static final OptionID OUTPUT_ID = OptionID.getOrCreateOptionID("app.out", ""); /** * Parameter that specifies the name of the input file. *

* Key: {@code -app.in} *

*/ public static final OptionID INPUT_ID = OptionID.getOrCreateOptionID("app.in", ""); /** * Value of verbose flag. */ private boolean verbose; /** * Constructor. * * @param verbose Verbose flag. */ public AbstractApplication(boolean verbose) { if(verbose) { // Note: do not unset verbose if not --verbose - someone else might // have set it intentionally. So don't setVerbose(verbose)! LoggingConfiguration.setVerbose(true); } } /** * Returns whether verbose messages should be printed while executing the * application. * * @return whether verbose messages should be printed while executing the * application */ public final boolean isVerbose() { return verbose; } /** * Generic command line invocation. * * Refactored to have a central place for outermost exception handling. * * @param cls Application class to run. * @param args the arguments to run this application with */ public static void runCLIApplication(Class cls, String[] args) { final Flag HELP_FLAG = new Flag(OptionID.HELP); final Flag HELP_LONG_FLAG = new Flag(OptionID.HELP_LONG); final ClassParameter DESCRIPTION_PARAM = new ClassParameter(OptionID.DESCRIPTION, Object.class, true); final StringParameter DEBUG_PARAM = new StringParameter(OptionID.DEBUG, true); SerializedParameterization params = new SerializedParameterization(args); try { params.grab(HELP_FLAG); params.grab(HELP_LONG_FLAG); params.grab(DESCRIPTION_PARAM); params.grab(DEBUG_PARAM); if(DESCRIPTION_PARAM.isDefined()) { params.clearErrors(); printDescription(DESCRIPTION_PARAM.getValue()); return; } // Fail silently on errors. if(params.getErrors().size() > 0) { params.logAndClearReportedErrors(); return; } if(DEBUG_PARAM.isDefined()) { LoggingUtil.parseDebugParameter(DEBUG_PARAM); } } catch(Exception e) { printErrorMessage(e); return; } try { TrackParameters config = new TrackParameters(params); AbstractApplication task = ClassGenericsUtil.tryInstantiate(AbstractApplication.class, cls, config); if((HELP_FLAG.isDefined() && HELP_FLAG.getValue()) || (HELP_LONG_FLAG.isDefined() && HELP_LONG_FLAG.getValue())) { LoggingConfiguration.setVerbose(true); STATIC_LOGGER.verbose(usage(config.getAllParameters())); } else { params.logUnusedParameters(); if(params.getErrors().size() > 0) { LoggingConfiguration.setVerbose(true); STATIC_LOGGER.verbose("The following configuration errors prevented execution:\n"); for(ParameterException e : params.getErrors()) { STATIC_LOGGER.verbose(e.getMessage()); } STATIC_LOGGER.verbose("\n"); STATIC_LOGGER.verbose("Stopping execution because of configuration errors."); } else { task.run(); } } } catch(Exception e) { printErrorMessage(e); } } /** * Returns a usage message, explaining all known options * * @param options Options to show in usage. * @return a usage message explaining all known options */ public static String usage(Collection>> options) { StringBuffer usage = new StringBuffer(); usage.append(INFORMATION); // Collect options usage.append(NEWLINE).append("Parameters:").append(NEWLINE); OptionUtil.formatForConsole(usage, FormatUtil.getConsoleWidth(), " ", options); // FIXME: re-add constraints! return usage.toString(); } /** * Print an error message for the given error. * * @param e Error Exception. */ protected static void printErrorMessage(Exception e) { if(e instanceof AbortException) { // ensure we actually show the message: LoggingConfiguration.setVerbose(true); STATIC_LOGGER.verbose(e.getMessage()); } else if(e instanceof UnspecifiedParameterException) { STATIC_LOGGER.error(e.getMessage()); } else if(e instanceof ParameterException) { STATIC_LOGGER.error(e.getMessage()); } else { STATIC_LOGGER.exception(e); } } /** * Print the description for the given parameter */ private static void printDescription(Class descriptionClass) { if(descriptionClass != null) { LoggingConfiguration.setVerbose(true); STATIC_LOGGER.verbose(OptionUtil.describeParameterizable(new StringBuffer(), descriptionClass, FormatUtil.getConsoleWidth(), " ").toString()); } } /** * Runs the application. * * @throws de.lmu.ifi.dbs.elki.utilities.exceptions.UnableToComplyException if * an error occurs during running the application */ public abstract void run() throws UnableToComplyException; /** * Parameterization class. * * @author Erich Schubert * * @apiviz.exclude */ public static abstract class Parameterizer extends AbstractParameterizer { /** * Verbose flag */ protected boolean verbose = false; @Override protected void makeOptions(Parameterization config) { super.makeOptions(config); configVerbose(config); // Note: we do not run the other methods by default. // Only verbose will always be present! } /** * Get the verbose parameter. * * @param config Parameterization */ protected void configVerbose(Parameterization config) { final Flag verboseF = new Flag(OptionID.VERBOSE_FLAG); if(config.grab(verboseF)) { verbose = verboseF.getValue(); } } /** * Get the output file parameter. * * @param config Options * @return Output file */ protected File getParameterOutputFile(Parameterization config) { return getParameterOutputFile(config, "Output filename."); } /** * Get the output file parameter. * * @param config Options * @param description Short description * @return Output file */ protected File getParameterOutputFile(Parameterization config, String description) { final FileParameter outputP = new FileParameter(OUTPUT_ID, FileParameter.FileType.OUTPUT_FILE); outputP.setShortDescription(description); if(config.grab(outputP)) { return outputP.getValue(); } return null; } /** * Get the input file parameter. * * @param config Options * @return Input file */ protected File getParameterInputFile(Parameterization config) { return getParameterInputFile(config, "Input filename."); } /** * Get the input file parameter * * @param config Options * @param description Description * @return Input file */ protected File getParameterInputFile(Parameterization config, String description) { final FileParameter inputP = new FileParameter(INPUT_ID, FileParameter.FileType.INPUT_FILE); inputP.setShortDescription(description); if(config.grab(inputP)) { return inputP.getValue(); } return null; } @Override protected abstract AbstractApplication makeInstance(); } }