Command.java revision a921fd048da6858dc24d4370e3bba15fc9cc69ca
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 */
9package com.vladium.emma;
10
11import java.io.File;
12import java.io.IOException;
13import java.io.PrintWriter;
14import java.util.Properties;
15
16import com.vladium.logging.ILogLevels;
17import com.vladium.util.IConstants;
18import com.vladium.util.Property;
19import com.vladium.util.Strings;
20import com.vladium.util.XProperties;
21import com.vladium.util.args.IOptsParser;
22import com.vladium.emma.data.mergeCommand;
23import com.vladium.emma.instr.instrCommand;
24import com.vladium.emma.report.reportCommand;
25
26// ----------------------------------------------------------------------------
27/**
28 * @author Vlad Roubtsov, (C) 2003
29 */
30public
31abstract class Command
32{
33    // public: ................................................................
34
35
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
58    public abstract void run ();
59
60    // protected: .............................................................
61
62
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
69    protected abstract String usageArgsMsg ();
70
71    // TODO: is this useful (separate from <init>)?
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
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
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
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
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
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
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
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
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
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
269    protected static String usageMsgPrefix (final String toolName)
270    {
271        return toolNameToCommandName (toolName).concat (" usage: ");
272    }
273
274    protected static String usageResName (final String toolName)
275    {
276        return toolName.replace ('.', '/').concat ("_usage.res");
277    }
278
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     */
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// ----------------------------------------------------------------------------