| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: Command.java,v 1.1.1.1.2.1 2004/07/16 23:32:03 vlad_r Exp $ |
| */ |
| package com.vladium.emma; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.Properties; |
| |
| import com.vladium.logging.ILogLevels; |
| import com.vladium.util.IConstants; |
| import com.vladium.util.Property; |
| import com.vladium.util.Strings; |
| import com.vladium.util.XProperties; |
| import com.vladium.util.args.IOptsParser; |
| import com.vladium.emma.data.mergeCommand; |
| import com.vladium.emma.instr.instrCommand; |
| import com.vladium.emma.report.reportCommand; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| abstract class Command |
| { |
| // public: ................................................................ |
| |
| |
| public static Command create (final String name, final String usageName, final String [] args) |
| { |
| final Command tool; |
| |
| // TODO: dynamic load here? |
| |
| if ("run".equals (name)) |
| tool = new runCommand (usageName, args); |
| else if ("instr".equals (name)) |
| tool = new instrCommand (usageName, args); |
| else if ("report".equals (name)) |
| tool = new reportCommand (usageName, args); |
| else if ("merge".equals (name)) |
| tool = new mergeCommand (usageName, args); |
| else |
| throw new IllegalArgumentException ("unknown command: [" + name + "]"); |
| |
| tool.initialize (); |
| |
| return tool; |
| } |
| |
| public abstract void run (); |
| |
| // protected: ............................................................. |
| |
| |
| protected Command (final String usageToolName, final String [] args) |
| { |
| m_usageToolName = usageToolName; |
| m_args = args != null ? (String []) args.clone () : IConstants.EMPTY_STRING_ARRAY; |
| } |
| |
| protected abstract String usageArgsMsg (); |
| |
| // TODO: is this useful (separate from <init>)? |
| protected void initialize () |
| { |
| m_exit = false; |
| |
| if (m_out != null) try { m_out.flush (); } catch (Throwable ignore) {} |
| m_out = new PrintWriter (System.out, true); |
| } |
| |
| protected final String getToolName () |
| { |
| // TODO: embed build number etc |
| final String clsName = getClass ().getName (); |
| |
| return clsName.substring (0, clsName.length () - 7); |
| } |
| |
| protected final IOptsParser getOptParser (final ClassLoader loader) |
| { |
| return IOptsParser.Factory.create (usageResName (getToolName ()), loader, |
| usageMsgPrefix (m_usageToolName), USAGE_OPT_NAMES); |
| } |
| |
| protected final boolean processOpt (final IOptsParser.IOpt opt) |
| { |
| final String on = opt.getCanonicalName (); |
| |
| if ("exit".equals (on)) // 'exit' should always be first in this else-if chain |
| { |
| m_exit = getOptionalBooleanOptValue (opt); |
| return true; |
| } |
| else if ("p".equals (on)) |
| { |
| m_propertyFile = new File (opt.getFirstValue ()); |
| return true; |
| } |
| else if ("verbose".equals (on)) |
| { |
| setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.VERBOSE_STRING); |
| return true; |
| } |
| else if ("quiet".equals (on)) |
| { |
| setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.WARNING_STRING); |
| return true; |
| } |
| else if ("silent".equals (on)) |
| { |
| setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.SEVERE_STRING); |
| return true; |
| } |
| else if ("debug".equals (on)) |
| { |
| if (opt.getValueCount () == 0) |
| setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.TRACE1_STRING); |
| else |
| setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, opt.getFirstValue ()); |
| |
| return true; |
| } |
| else if ("debugcls".equals (on)) |
| { |
| setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_FILTER, Strings.toListForm (Strings.merge (opt.getValues (), COMMA_DELIMITERS, true), ',')); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| protected final void processCmdPropertyOverrides (final IOptsParser.IOpts parsedopts) |
| { |
| final IOptsParser.IOpt [] popts = parsedopts.getOpts (EMMAProperties.GENERIC_PROPERTY_OVERRIDE_PREFIX); |
| if ((popts != null) && (popts.length != 0)) |
| { |
| final Properties cmdOverrides = new XProperties (); |
| |
| for (int o = 0; o < popts.length; ++ o) |
| { |
| final IOptsParser.IOpt opt = popts [o]; |
| final String on = opt.getName ().substring (opt.getPatternPrefix ().length ()); |
| |
| // TODO: support mergeable prefixed opts? |
| |
| cmdOverrides.setProperty (on, opt.getFirstValue ()); |
| } |
| |
| // command line user overrides are have highest precedence: |
| m_propertyOverrides = Property.combine (cmdOverrides, m_propertyOverrides); |
| } |
| } |
| |
| protected final boolean processFilePropertyOverrides () |
| { |
| if (m_propertyFile != null) |
| { |
| final Properties fileOverrides; |
| |
| try |
| { |
| fileOverrides = Property.getPropertiesFromFile (m_propertyFile); |
| } |
| catch (IOException ioe) |
| { |
| exit (true, "property override file [" + m_propertyFile.getAbsolutePath () + "] could not be read", ioe, RC_USAGE); |
| return false; |
| } |
| |
| // props file overrides have second highest precendence: |
| m_propertyOverrides = Property.combine (m_propertyOverrides, fileOverrides); |
| } |
| |
| return true; |
| } |
| |
| protected final void usageexit (final IOptsParser parser, final int level, final String msg) |
| { |
| if (msg != null) |
| { |
| m_out.print (usageMsgPrefix (m_usageToolName)); |
| m_out.println (msg); |
| } |
| |
| if (parser != null) |
| { |
| m_out.println (); |
| m_out.print (usageMsgPrefix (m_usageToolName)); |
| m_out.println (toolNameToCommandName (m_usageToolName) + " " + usageArgsMsg () + ","); |
| m_out.println (" where options include:"); |
| m_out.println (); |
| parser.usage (m_out, level, STDOUT_WIDTH); |
| } |
| |
| m_out.println (); |
| exit (true, null, null, RC_USAGE); |
| } |
| |
| protected final void exit (final boolean showBuildID, final String msg, final Throwable t, final int rc) |
| throws EMMARuntimeException |
| { |
| if (showBuildID) |
| { |
| m_out.println (IAppConstants.APP_USAGE_BUILD_ID); |
| } |
| |
| if (msg != null) |
| { |
| m_out.print (toolNameToCommandName (m_usageToolName) + ": "); m_out.println (msg); |
| } |
| |
| if (rc != RC_OK) |
| { |
| // error exit: |
| |
| //if ((showBuildID) || (msg != null)) m_out.println (); |
| |
| if (m_exit) |
| { |
| if (t != null) t.printStackTrace (m_out); |
| System.exit (rc); |
| } |
| else |
| { |
| if (t instanceof EMMARuntimeException) |
| throw (EMMARuntimeException) t; |
| else if (t != null) |
| throw msg != null ? new EMMARuntimeException (msg, t) : new EMMARuntimeException ("unexpected failure: ", t); |
| } |
| } |
| else |
| { |
| // normal exit: 't' is ignored |
| |
| if (m_exit) |
| { |
| System.exit (0); |
| } |
| } |
| } |
| |
| protected static boolean getOptionalBooleanOptValue (final IOptsParser.IOpt opt) |
| { |
| if (opt.getValueCount () == 0) |
| return true; |
| else |
| { |
| final String v = opt.getFirstValue ().toLowerCase (); |
| |
| return Property.toBoolean (v); |
| } |
| } |
| |
| protected static String [] getListOptValue (final IOptsParser.IOpt opt, final String delimiters, final boolean processAtFiles) |
| throws IOException |
| { |
| return Strings.mergeAT (opt.getValues (), delimiters, processAtFiles); |
| } |
| |
| protected static String usageMsgPrefix (final String toolName) |
| { |
| return toolNameToCommandName (toolName).concat (" usage: "); |
| } |
| |
| protected static String usageResName (final String toolName) |
| { |
| return toolName.replace ('.', '/').concat ("_usage.res"); |
| } |
| |
| protected static String toolNameToCommandName (final String toolName) |
| { |
| final int lastDot = toolName.lastIndexOf ('.'); |
| |
| return lastDot > 0 ? toolName.substring (lastDot + 1) : toolName; |
| } |
| |
| |
| protected final String m_usageToolName; |
| protected final String [] m_args; |
| |
| protected File m_propertyFile; |
| protected Properties m_propertyOverrides; |
| protected boolean m_exit; |
| protected PrintWriter m_out; // this is set independently from Logger by design |
| |
| protected static final String COMMA_DELIMITERS = "," + Strings.WHITE_SPACE; |
| protected static final String PATH_DELIMITERS = ",".concat (File.pathSeparator); |
| |
| protected static final String [] USAGE_OPT_NAMES = new String [] {"h", "help"}; |
| protected static final int STDOUT_WIDTH = 80; |
| |
| // return codes used with System.exit(): |
| protected static final int RC_OK = 0; |
| protected static final int RC_USAGE = 1; |
| protected static final int RC_UNEXPECTED = 2; |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| /* |
| * Lazily instantiates m_propertyOverrides if necessary. |
| */ |
| private void setPropertyOverride (final String key, final String value) |
| { |
| Properties propertyOverrides = m_propertyOverrides; |
| if (propertyOverrides == null) |
| { |
| m_propertyOverrides = propertyOverrides = new XProperties (); |
| } |
| |
| propertyOverrides.setProperty (key, value); |
| } |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |