1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: Command.java,v 1.1.1.1.2.1 2004/07/16 23:32:03 vlad_r Exp $ 8 */ 9 package com.vladium.emma; 10 11 import java.io.File; 12 import java.io.IOException; 13 import java.io.PrintWriter; 14 import java.util.Properties; 15 16 import com.vladium.logging.ILogLevels; 17 import com.vladium.util.IConstants; 18 import com.vladium.util.Property; 19 import com.vladium.util.Strings; 20 import com.vladium.util.XProperties; 21 import com.vladium.util.args.IOptsParser; 22 import com.vladium.emma.data.mergeCommand; 23 import com.vladium.emma.instr.instrCommand; 24 import com.vladium.emma.report.reportCommand; 25 26 // ---------------------------------------------------------------------------- 27 /** 28 * @author Vlad Roubtsov, (C) 2003 29 */ 30 public 31 abstract class Command 32 { 33 // public: ................................................................ 34 35 create(final String name, final String usageName, final String [] args)36 public static Command create (final String name, final String usageName, final String [] args) 37 { 38 final Command tool; 39 40 // TODO: dynamic load here? 41 42 if ("run".equals (name)) 43 tool = new runCommand (usageName, args); 44 else if ("instr".equals (name)) 45 tool = new instrCommand (usageName, args); 46 else if ("report".equals (name)) 47 tool = new reportCommand (usageName, args); 48 else if ("merge".equals (name)) 49 tool = new mergeCommand (usageName, args); 50 else 51 throw new IllegalArgumentException ("unknown command: [" + name + "]"); 52 53 tool.initialize (); 54 55 return tool; 56 } 57 run()58 public abstract void run (); 59 60 // protected: ............................................................. 61 62 Command(final String usageToolName, final String [] args)63 protected Command (final String usageToolName, final String [] args) 64 { 65 m_usageToolName = usageToolName; 66 m_args = args != null ? (String []) args.clone () : IConstants.EMPTY_STRING_ARRAY; 67 } 68 usageArgsMsg()69 protected abstract String usageArgsMsg (); 70 71 // TODO: is this useful (separate from <init>)? initialize()72 protected void initialize () 73 { 74 m_exit = false; 75 76 if (m_out != null) try { m_out.flush (); } catch (Throwable ignore) {} 77 m_out = new PrintWriter (System.out, true); 78 } 79 getToolName()80 protected final String getToolName () 81 { 82 // TODO: embed build number etc 83 final String clsName = getClass ().getName (); 84 85 return clsName.substring (0, clsName.length () - 7); 86 } 87 getOptParser(final ClassLoader loader)88 protected final IOptsParser getOptParser (final ClassLoader loader) 89 { 90 return IOptsParser.Factory.create (usageResName (getToolName ()), loader, 91 usageMsgPrefix (m_usageToolName), USAGE_OPT_NAMES); 92 } 93 processOpt(final IOptsParser.IOpt opt)94 protected final boolean processOpt (final IOptsParser.IOpt opt) 95 { 96 final String on = opt.getCanonicalName (); 97 98 if ("exit".equals (on)) // 'exit' should always be first in this else-if chain 99 { 100 m_exit = getOptionalBooleanOptValue (opt); 101 return true; 102 } 103 else if ("p".equals (on)) 104 { 105 m_propertyFile = new File (opt.getFirstValue ()); 106 return true; 107 } 108 else if ("verbose".equals (on)) 109 { 110 setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.VERBOSE_STRING); 111 return true; 112 } 113 else if ("quiet".equals (on)) 114 { 115 setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.WARNING_STRING); 116 return true; 117 } 118 else if ("silent".equals (on)) 119 { 120 setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.SEVERE_STRING); 121 return true; 122 } 123 else if ("debug".equals (on)) 124 { 125 if (opt.getValueCount () == 0) 126 setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, ILogLevels.TRACE1_STRING); 127 else 128 setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_LEVEL, opt.getFirstValue ()); 129 130 return true; 131 } 132 else if ("debugcls".equals (on)) 133 { 134 setPropertyOverride (AppLoggers.PROPERTY_VERBOSITY_FILTER, Strings.toListForm (Strings.merge (opt.getValues (), COMMA_DELIMITERS, true), ',')); 135 return true; 136 } 137 138 return false; 139 } 140 processCmdPropertyOverrides(final IOptsParser.IOpts parsedopts)141 protected final void processCmdPropertyOverrides (final IOptsParser.IOpts parsedopts) 142 { 143 final IOptsParser.IOpt [] popts = parsedopts.getOpts (EMMAProperties.GENERIC_PROPERTY_OVERRIDE_PREFIX); 144 if ((popts != null) && (popts.length != 0)) 145 { 146 final Properties cmdOverrides = new XProperties (); 147 148 for (int o = 0; o < popts.length; ++ o) 149 { 150 final IOptsParser.IOpt opt = popts [o]; 151 final String on = opt.getName ().substring (opt.getPatternPrefix ().length ()); 152 153 // TODO: support mergeable prefixed opts? 154 155 cmdOverrides.setProperty (on, opt.getFirstValue ()); 156 } 157 158 // command line user overrides are have highest precedence: 159 m_propertyOverrides = Property.combine (cmdOverrides, m_propertyOverrides); 160 } 161 } 162 processFilePropertyOverrides()163 protected final boolean processFilePropertyOverrides () 164 { 165 if (m_propertyFile != null) 166 { 167 final Properties fileOverrides; 168 169 try 170 { 171 fileOverrides = Property.getPropertiesFromFile (m_propertyFile); 172 } 173 catch (IOException ioe) 174 { 175 exit (true, "property override file [" + m_propertyFile.getAbsolutePath () + "] could not be read", ioe, RC_USAGE); 176 return false; 177 } 178 179 // props file overrides have second highest precendence: 180 m_propertyOverrides = Property.combine (m_propertyOverrides, fileOverrides); 181 } 182 183 return true; 184 } 185 usageexit(final IOptsParser parser, final int level, final String msg)186 protected final void usageexit (final IOptsParser parser, final int level, final String msg) 187 { 188 if (msg != null) 189 { 190 m_out.print (usageMsgPrefix (m_usageToolName)); 191 m_out.println (msg); 192 } 193 194 if (parser != null) 195 { 196 m_out.println (); 197 m_out.print (usageMsgPrefix (m_usageToolName)); 198 m_out.println (toolNameToCommandName (m_usageToolName) + " " + usageArgsMsg () + ","); 199 m_out.println (" where options include:"); 200 m_out.println (); 201 parser.usage (m_out, level, STDOUT_WIDTH); 202 } 203 204 m_out.println (); 205 exit (true, null, null, RC_USAGE); 206 } 207 exit(final boolean showBuildID, final String msg, final Throwable t, final int rc)208 protected final void exit (final boolean showBuildID, final String msg, final Throwable t, final int rc) 209 throws EMMARuntimeException 210 { 211 if (showBuildID) 212 { 213 m_out.println (IAppConstants.APP_USAGE_BUILD_ID); 214 } 215 216 if (msg != null) 217 { 218 m_out.print (toolNameToCommandName (m_usageToolName) + ": "); m_out.println (msg); 219 } 220 221 if (rc != RC_OK) 222 { 223 // error exit: 224 225 //if ((showBuildID) || (msg != null)) m_out.println (); 226 227 if (m_exit) 228 { 229 if (t != null) t.printStackTrace (m_out); 230 System.exit (rc); 231 } 232 else 233 { 234 if (t instanceof EMMARuntimeException) 235 throw (EMMARuntimeException) t; 236 else if (t != null) 237 throw msg != null ? new EMMARuntimeException (msg, t) : new EMMARuntimeException ("unexpected failure: ", t); 238 } 239 } 240 else 241 { 242 // normal exit: 't' is ignored 243 244 if (m_exit) 245 { 246 System.exit (0); 247 } 248 } 249 } 250 getOptionalBooleanOptValue(final IOptsParser.IOpt opt)251 protected static boolean getOptionalBooleanOptValue (final IOptsParser.IOpt opt) 252 { 253 if (opt.getValueCount () == 0) 254 return true; 255 else 256 { 257 final String v = opt.getFirstValue ().toLowerCase (); 258 259 return Property.toBoolean (v); 260 } 261 } 262 getListOptValue(final IOptsParser.IOpt opt, final String delimiters, final boolean processAtFiles)263 protected static String [] getListOptValue (final IOptsParser.IOpt opt, final String delimiters, final boolean processAtFiles) 264 throws IOException 265 { 266 return Strings.mergeAT (opt.getValues (), delimiters, processAtFiles); 267 } 268 usageMsgPrefix(final String toolName)269 protected static String usageMsgPrefix (final String toolName) 270 { 271 return toolNameToCommandName (toolName).concat (" usage: "); 272 } 273 usageResName(final String toolName)274 protected static String usageResName (final String toolName) 275 { 276 return toolName.replace ('.', '/').concat ("_usage.res"); 277 } 278 toolNameToCommandName(final String toolName)279 protected static String toolNameToCommandName (final String toolName) 280 { 281 final int lastDot = toolName.lastIndexOf ('.'); 282 283 return lastDot > 0 ? toolName.substring (lastDot + 1) : toolName; 284 } 285 286 287 protected final String m_usageToolName; 288 protected final String [] m_args; 289 290 protected File m_propertyFile; 291 protected Properties m_propertyOverrides; 292 protected boolean m_exit; 293 protected PrintWriter m_out; // this is set independently from Logger by design 294 295 protected static final String COMMA_DELIMITERS = "," + Strings.WHITE_SPACE; 296 protected static final String PATH_DELIMITERS = ",".concat (File.pathSeparator); 297 298 protected static final String [] USAGE_OPT_NAMES = new String [] {"h", "help"}; 299 protected static final int STDOUT_WIDTH = 80; 300 301 // return codes used with System.exit(): 302 protected static final int RC_OK = 0; 303 protected static final int RC_USAGE = 1; 304 protected static final int RC_UNEXPECTED = 2; 305 306 // package: ............................................................... 307 308 // private: ............................................................... 309 310 311 /* 312 * Lazily instantiates m_propertyOverrides if necessary. 313 */ setPropertyOverride(final String key, final String value)314 private void setPropertyOverride (final String key, final String value) 315 { 316 Properties propertyOverrides = m_propertyOverrides; 317 if (propertyOverrides == null) 318 { 319 m_propertyOverrides = propertyOverrides = new XProperties (); 320 } 321 322 propertyOverrides.setProperty (key, value); 323 } 324 325 } // end of class 326 // ----------------------------------------------------------------------------