1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.tool;
  28 
  29 import java.net.URL;
  30 import java.util.Iterator;
  31 import java.util.ListIterator;
  32 import java.util.Vector;
  33 
  34 import com.sun.javatest.InterviewParameters;
  35 import com.sun.javatest.util.I18NResourceBundle;
  36 
  37 /**
  38  * A class to represent a command to be executed.
  39  * Commands are typically read from the command line or from command files.
  40  */
  41 public abstract class Command
  42 {
  43     /**
  44      * This exception is used to report problems with a specific command.
  45      */
  46     public class Fault extends Exception
  47     {
  48         /**
  49          * Create a Fault.
  50          * @param i18n A resource bundle in which to find the detail message.
  51          * @param s The key for the detail message.
  52          */
  53         public Fault(I18NResourceBundle i18n, String s) {
  54             super(i18n.getString(s));
  55         }
  56 
  57         /**
  58          * Create a Fault.
  59          * @param i18n A resource bundle in which to find the detail message.
  60          * @param s The key for the detail message.
  61          * @param o An argument to be formatted with the detail message by
  62          * {@link java.text.MessageFormat#format}
  63          */
  64         public Fault(I18NResourceBundle i18n, String s, Object o) {
  65             super(i18n.getString(s, o));
  66         }
  67 
  68         /**
  69          * Create a Fault.
  70          * @param i18n A resource bundle in which to find the detail message.
  71          * @param s The key for the detail message.
  72          * @param o An array of arguments to be formatted with the detail message by
  73          * {@link java.text.MessageFormat#format}
  74          */
  75         public Fault(I18NResourceBundle i18n, String s, Object[] o) {
  76             super(i18n.getString(s, o));
  77         }
  78 
  79         /**
  80          * Create a Fault, by wrapping a CommandContext Fault.
  81          * The message string will be propagated directly;
  82          * the argument fault will be set as the cause for this fault.
  83          * @param e A CommandContext.Fault to wrap.
  84          */
  85         public Fault(CommandContext.Fault e) {
  86             super(e.getMessage(), e);
  87         }
  88 
  89         /**
  90          * Get the command that created this fault.
  91          * @return the command that created this fault
  92          */
  93         public Command getCommand() {
  94             return Command.this;
  95         }
  96 
  97     }
  98 
  99     /**
 100      * Create an instance of a command.
 101      * @param name The name for this command.
 102      * The name will be saved as the first entry as the argument array.
 103      */
 104     protected Command(String name) {
 105         args = new Vector<>();
 106         args.add(name);
 107     }
 108 
 109     /**
 110      * Record another argument in the argument array.
 111      * @param arg the argument to be added
 112      */
 113     protected void addArg(String arg) {
 114         args.add(arg);
 115     }
 116 
 117     /**
 118      * Get another argument from the iterator, and add it to the argument array.
 119      * @param argIter the iterator from which to get the next argument
 120      * @return the next argument from the iterator
 121      */
 122     protected String nextArg(Iterator<String> argIter) {
 123         String s = argIter.next();
 124         addArg(s);
 125         return s;
 126     }
 127 
 128     /**
 129      * Back up the iterator to reject an argument, and remove the corresponding
 130      * entry from the argument array.
 131      * @param argIter the iterator from which teh argument was obtained
 132      */
 133     protected void putbackArg(ListIterator<String> argIter) {
 134         argIter.previous();
 135         args.remove(args.size() - 1);
 136     }
 137 
 138     /**
 139      * Get the array of arguments for this command.
 140      * The first element in the array will be the command name;
 141      * the subsequent arguments will be the ones added by the addArg method.
 142      * @return the array of arguments for this command
 143      */
 144     public String[] getArgs() {
 145         String[] a = new String[args.size()];
 146         args.copyInto(a);
 147         return a;
 148     }
 149 
 150     /**
 151      * Get a printable representation of this command.
 152      * The string is composed of the entries in the argument array.
 153      * @return a printable representation of this command
 154      */
 155     public String toString() {
 156         StringBuffer sb = new StringBuffer();
 157         for (int i = 0; i < args.size(); i++) {
 158             if (sb.length() > 0)
 159                 sb.append(' ');
 160             String arg = args.elementAt(i);
 161             boolean hasSpace = (arg.indexOf(' ') != -1);
 162             boolean hasQuote = (arg.indexOf('"') != -1);
 163             boolean hasEscape = (arg.indexOf('\\') != -1);
 164             if (hasSpace)
 165                 sb.append('"');
 166             if (hasQuote || hasEscape) {
 167                 for (int ci = 0; ci < arg.length(); ci++) {
 168                     char c = arg.charAt(ci);
 169                     if (c == '"' || c == '\\')
 170                         sb.append('\\');
 171                     sb.append(c);
 172                 }
 173             }
 174             else
 175                 sb.append(arg);
 176             if (hasSpace)
 177                 sb.append('"');
 178         }
 179         return sb.toString();
 180     }
 181 
 182     /**
 183      * Get the desktop mode for this command. Valid responses are one of
 184      * DEFAULT_DTMODE, DESKTOP_NOT_REQUIRED_DTMODE, DESKTOP_REQUIRED_DTMODE.
 185      * The default is DESKTOP_NOT_REQUIRED_DTMODE if isActionCommand is true,
 186      * and DESKTOP_DEFAULT_DTMODE otherwise.
 187      * @return a value indicating the desktop mode for this command
 188      * @see #DEFAULT_DTMODE
 189      * @see #DESKTOP_NOT_REQUIRED_DTMODE
 190      * @see #DESKTOP_REQUIRED_DTMODE
 191      */
 192     public int getDesktopMode() {
 193         return (isActionCommand() ? DESKTOP_NOT_REQUIRED_DTMODE : DEFAULT_DTMODE);
 194     }
 195 
 196     /**
 197      * Get the classpath to load the custom splash screen from.
 198      * At this location, it is expected that a resource bundle prefixed with
 199      * "splash" will be available.  The search strategy given in ResourceBundle
 200      * will be used, with the returned File as the classpath for the class loader.
 201      * The limited classpath/classloader is used to make this operation as fast
 202      * as possible, rather than requiring that the command's entire context be
 203      * loaded.
 204      *
 205      * In the resource bundle, there should be a property named
 206      * <code>startup.icon</code>.
 207      * @return the location of the splash screen resource bundle
 208      * @see java.util.ResourceBundle
 209      * @since 4.0
 210      */
 211     public URL getCustomSplash() {
 212         return null;
 213     }
 214 
 215     ClassLoader getCustomHelpLoader() {
 216         return null;
 217     }
 218 
 219     /**
 220      * A value to indicate that a command accepts the default desktop mode.
 221      * This means that it neither requires nor discourages the use of a desktop
 222      * for its use.
 223      * @see #getDesktopMode
 224      * @see #DESKTOP_NOT_REQUIRED_DTMODE
 225      * @see #DESKTOP_REQUIRED_DTMODE
 226      */
 227     public static final int DEFAULT_DTMODE = 0;
 228 
 229     /**
 230      * A value to indicate that a command does not require the use of
 231      * a desktop to function.
 232      * @see #getDesktopMode
 233      * @see #DEFAULT_DTMODE
 234      * @see #DESKTOP_REQUIRED_DTMODE
 235      */
 236     public static final int DESKTOP_NOT_REQUIRED_DTMODE = 1;
 237 
 238     /**
 239      * A value to indicate that a command requires the use of
 240      * a desktop to function.
 241      * @see #getDesktopMode
 242      * @see #DEFAULT_DTMODE
 243      * @see #DESKTOP_NOT_REQUIRED_DTMODE
 244      */
 245     public static final int DESKTOP_REQUIRED_DTMODE = 2;
 246 
 247     /**
 248      * Check whether this command is an action command or not. Action commands
 249      * are those that do work such as running tests, writing a report, etc.
 250      * The default implementation is to return false.
 251      * @return true if this command is an action command, and false otherwise
 252      */
 253     public boolean isActionCommand() {
 254         return false;
 255     }
 256 
 257     /**
 258      * Execute the work embodied by this command, using the given command context.
 259      * @param ctx context information that may be set up by preceding commands.
 260      * @throws Command.Fault if there is an error while executing this command
 261      */
 262     public abstract void run(CommandContext ctx) throws Fault;
 263 
 264     /**
 265      * A convenience method to get the configuration from a command context,
 266      * and rewrapping any exception that might occur.
 267      * @param ctx the command context from which to get the configuration
 268      * @return the current configuration from the command context
 269      * @throws Command.Fault if there is a problem obtaining or evaluating
 270      * the configuration.
 271      */
 272     protected InterviewParameters getConfig(CommandContext ctx)
 273         throws Command.Fault
 274     {
 275         try {
 276             return ctx.getConfig();
 277         }
 278         catch (CommandContext.Fault e) {
 279             throw new Fault(e);
 280         }
 281     }
 282 
 283     private Vector<String> args;
 284 }