1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2004, 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.io.BufferedInputStream;
  30 import java.io.File;
  31 import java.io.FileInputStream;
  32 import java.io.FileNotFoundException;
  33 import java.io.IOException;
  34 import java.io.InputStream;
  35 import java.net.URL;
  36 import java.net.URLClassLoader;
  37 import java.text.NumberFormat;
  38 import java.text.ParsePosition;
  39 import java.util.Iterator;
  40 import java.util.ListIterator;
  41 import java.util.Locale;
  42 import java.util.Map;
  43 import java.util.MissingResourceException;
  44 import java.util.Properties;
  45 import java.util.ResourceBundle;
  46 import java.util.Set;
  47 import java.util.Vector;
  48 
  49 import com.sun.interview.Interview;
  50 import com.sun.interview.NullQuestion;
  51 import com.sun.interview.CompositeQuestion;
  52 import com.sun.interview.Question;
  53 import com.sun.javatest.FileParameters;
  54 import com.sun.javatest.InterviewParameters;
  55 import com.sun.javatest.Parameters;
  56 import com.sun.javatest.Status;
  57 import com.sun.javatest.TestSuite;
  58 import com.sun.javatest.WorkDirectory;
  59 import com.sun.javatest.util.DirectoryClassLoader;
  60 //import com.sun.javatest.util.PathClassLoader;
  61 import com.sun.javatest.util.HelpTree;
  62 import com.sun.javatest.util.I18NResourceBundle;
  63 import java.util.HashMap;
  64 import java.util.Map.Entry;
  65 
  66 /**
  67  * A manager for all the various configuration commands.
  68  */
  69 public class ConfigManager
  70     extends CommandManager
  71 {
  72     public HelpTree.Node getHelp() {
  73         Object[] childData = {
  74             ConcurrencyCommand.getName(),
  75             ConfigCommand.getName(),
  76             EnvCommand.getName(),
  77             EnvFilesCommand.getNames(),
  78             ExcludeListCommand.getName(),
  79             KeywordsCommand.getName(),
  80             KflCommand.getName(),
  81             OpenCommand.getName(),
  82             ParamsCommand.getHelp(),
  83             PriorStatusCommand.getName(),
  84             SetCommand.getName(),
  85             TestsCommand.getName(),
  86             TestSuiteCommand.getNames(),
  87             TimeoutFactorCommand.getName(),
  88             WorkDirectoryCommand.getNames(),
  89             WriteConfigCommand.getName()
  90         };
  91 
  92         return getHelp(i18n, "cnfg", childData);
  93     }
  94 
  95     HelpTree.Node getHelp(I18NResourceBundle i18n, String prefix, Object[] childData) {
  96         Vector<HelpTree.Node> v = new Vector<>();
  97         for (int i = 0; i < childData.length; i++) {
  98             Object data = childData[i];
  99             if (data instanceof HelpTree.Node)
 100                 v.add((HelpTree.Node)data);
 101             else if (data instanceof String)
 102                 v.add(new HelpTree.Node(i18n, prefix + "." + data));
 103             else if (data instanceof String[]) {
 104                 String[] names = (String[]) data;
 105                 for (int j = 0; j < names.length; j++)
 106                     v.add(new HelpTree.Node(i18n, prefix + "." + names[j]));
 107             }
 108             else
 109                 throw new IllegalArgumentException();
 110         }
 111         HelpTree.Node[] childNodes = new HelpTree.Node[v.size()];
 112         v.copyInto(childNodes);
 113         return new HelpTree.Node(i18n, prefix, childNodes);
 114     }
 115 
 116     public boolean parseCommand(String cmd, ListIterator<String> argIter, CommandContext ctx)
 117         throws Command.Fault
 118     {
 119         if (isMatch(cmd, ConcurrencyCommand.getName())) {
 120             ctx.addCommand(new ConcurrencyCommand(argIter));
 121             return true;
 122         }
 123 
 124         if (isMatch(cmd, ConfigCommand.getName())) {
 125             ctx.addCommand(new ConfigCommand(argIter));
 126             return true;
 127         }
 128 
 129         if (isMatch(cmd, EnvCommand.getName())) {
 130             ctx.addCommand(new EnvCommand(argIter));
 131             return true;
 132         }
 133 
 134         if (isMatch(cmd, EnvFilesCommand.getNames())) {
 135             ctx.addCommand(new EnvFilesCommand(argIter));
 136             return true;
 137         }
 138 
 139         if (isMatch(cmd, ExcludeListCommand.getName())) {
 140             ctx.addCommand(new ExcludeListCommand(argIter));
 141             return true;
 142         }
 143 
 144         if (isMatch(cmd, OpenCommand.getName())) {
 145             ctx.addCommand(new OpenCommand(argIter));
 146             return true;
 147         }
 148 
 149         if (isMatch(cmd, KeywordsCommand.getName())) {
 150             ctx.addCommand(new KeywordsCommand(argIter));
 151             return true;
 152         }
 153 
 154         if (isMatch(cmd, KflCommand.getName())) {
 155             ctx.addCommand(new KflCommand(argIter));
 156             return true;
 157         }
 158 
 159         if (isMatch(cmd, ParamsCommand.getName())) {
 160             ctx.addCommand(new ParamsCommand(argIter));
 161             return true;
 162         }
 163 
 164         if (isMatch(cmd, PriorStatusCommand.getName())) {
 165             ctx.addCommand(new PriorStatusCommand(argIter));
 166             return true;
 167         }
 168 
 169         if (isMatch(cmd, SetCommand.getName())) {
 170             ctx.addCommand(new SetCommand(argIter));
 171             return true;
 172         }
 173 
 174         if (isMatch(cmd, SetXCommand.getName())) {
 175             ctx.addCommand(new SetXCommand(argIter));
 176             return true;
 177         }
 178 
 179         if (isMatch(cmd, TestsCommand.getName())) {
 180             ctx.addCommand(new TestsCommand(argIter));
 181             return true;
 182         }
 183 
 184         if (isMatch(cmd, TestSuiteCommand.getNames())) {
 185             ctx.addCommand(new TestSuiteCommand(argIter));
 186             return true;
 187         }
 188 
 189         if (isMatch(cmd, TimeoutFactorCommand.getName())) {
 190             ctx.addCommand(new TimeoutFactorCommand(argIter));
 191             return true;
 192         }
 193 
 194         if (isMatch(cmd, WorkDirectoryCommand.getNames())) {
 195             ctx.addCommand(new WorkDirectoryCommand(argIter));
 196             return true;
 197         }
 198 
 199         if (isMatch(cmd, WriteConfigCommand.getName())) {
 200             ctx.addCommand(new WriteConfigCommand(argIter));
 201             return true;
 202         }
 203 
 204 
 205         return false;
 206     }
 207 
 208     static Command getOpenCommand(File file)
 209         throws Command.Fault
 210     {
 211         return new OpenCommand(file);
 212     }
 213 
 214     private static Map commandFactory;
 215     private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(ConfigManager.class);
 216 
 217     //--------------------------------------------------------------------------
 218 
 219     private static class ConcurrencyCommand extends Command
 220     {
 221         static String getName() {
 222             return "concurrency";
 223         }
 224 
 225         ConcurrencyCommand(ListIterator<String> argIter) throws Fault {
 226             super(getName());
 227 
 228             if (!argIter.hasNext())
 229                 throw new Fault(i18n, "cnfg.conc.missingArg");
 230 
 231             String arg = nextArg(argIter);
 232 
 233             NumberFormat fmt = NumberFormat.getIntegerInstance(); // will be locale-specific
 234             ParsePosition pos = new ParsePosition(0);
 235             Number num = fmt.parse(arg, pos);
 236             if (num != null && (pos.getIndex() == arg.length())) {
 237                 value = num.intValue();
 238                 if (value < Parameters.ConcurrencyParameters.MIN_CONCURRENCY
 239                     || value > Parameters.ConcurrencyParameters.MAX_CONCURRENCY) {
 240                     throw new Fault(i18n, "cnfg.conc.badRange",
 241                                     new Object[] {
 242                                         arg,
 243                                         new Integer(Parameters.ConcurrencyParameters.MIN_CONCURRENCY),
 244                                         new Integer(Parameters.ConcurrencyParameters.MAX_CONCURRENCY) });
 245                 }
 246             }
 247             else
 248                 throw new Fault(i18n, "cnfg.conc.badValue", arg);
 249         }
 250 
 251         public void run(CommandContext ctx) throws Fault {
 252             InterviewParameters p = getConfig(ctx);
 253             if (p.getConcurrencyParameters() instanceof Parameters.MutableConcurrencyParameters) {
 254                 Parameters.MutableConcurrencyParameters cParams =
 255                     (Parameters.MutableConcurrencyParameters) (p.getConcurrencyParameters());
 256                 cParams.setConcurrency(value);
 257             }
 258             else
 259                 throw new Fault(i18n, "cnfg.conc.notEditable");
 260         }
 261 
 262         private int value;
 263     }
 264 
 265 
 266     //--------------------------------------------------------------------------
 267 
 268     private static class ConfigCommand extends Command
 269     {
 270         static String getName() {
 271             return "config";
 272         }
 273 
 274         ConfigCommand(ListIterator<String> argIter) throws Fault {
 275             super(getName());
 276 
 277             if (!argIter.hasNext())
 278                 throw new Fault(i18n, "cnfg.conf.missingArg");
 279 
 280             path = new File(nextArg(argIter));
 281         }
 282 
 283         ConfigCommand(File path) {
 284             super(path.getPath());
 285 
 286             this.path = path;
 287         }
 288 
 289         public void run(CommandContext ctx) throws Fault {
 290             /*OLD
 291             try {
 292                 InterviewParameters p = getConfig(ctx);
 293                 if (p == null)
 294                     ctx.setInterviewParameters(InterviewParameters.open(path));
 295                 else {
 296                     // should check for compatibility?
 297                     p.load(path);
 298                 }
 299 
 300                 if (ctx.getWorkDirectory() == null)
 301                     ctx.setWorkDirectory(p.getWorkDirectory());
 302 
 303                 ctx.setAutoRunReportDir(null);
 304             }
 305             catch (TestSuite.Fault e) {
 306                 throw new Fault(i18n, "cnfg.cantSetParameters", e.getMessage());
 307             }
 308             catch (FileNotFoundException e) {
 309                 throw new Fault(i18n, "cnfg.cantFindFile", path);
 310             }
 311             catch (IOException e) {
 312                 throw new Fault(i18n, "cnfg.cantReadFile", new Object[] { path, e} );
 313             }
 314             catch (InterviewParameters.Fault e) {
 315                 throw new Fault(i18n, "cnfg.cantOpenConfig", new Object[] { path, e} );
 316             }
 317             */
 318 
 319             try {
 320                 ctx.setConfig(path);
 321                 ctx.setAutoRunReportDir(null);
 322             }
 323             catch (CommandContext.Fault e) {
 324                 throw new Fault(e);
 325             }
 326         }
 327 
 328         private File path;
 329     }
 330 
 331     //--------------------------------------------------------------------------
 332 
 333     private static class WriteConfigCommand extends Command
 334     {
 335         static String getName() {
 336             return "writeConfig";
 337         }
 338 
 339         WriteConfigCommand(ListIterator<String> argIter) throws Fault {
 340             super(getName());
 341 
 342             if (!argIter.hasNext())
 343                 throw new Fault(i18n, "cnfg.conf.missingArg");
 344                 // XXX could provide a better error message, perhaps including the value of
 345                 //     getName(), because the missingArg error message is general purpose
 346                 //     EX: throw new Fault(i18n, "cnfg.conf.missingArg", getName());
 347 
 348             path = new File(nextArg(argIter));
 349         }
 350 
 351         WriteConfigCommand(File path) {
 352             super(path.getPath());
 353 
 354             this.path = path;
 355         }
 356 
 357         public void run(CommandContext ctx) throws Fault {
 358             try {
 359                 InterviewParameters p = getConfig(ctx);
 360                 p.saveAs(path, true, true);
 361             }
 362             catch (IOException e) {
 363                 if (!path.canWrite())
 364                     throw new Fault(i18n, "cnfg.writeConfig.cantWrite", path.getPath());
 365                 else
 366                     throw new Fault(i18n, "cnfg.writeConfig.writeErr", new Object[] { path, e } );
 367             }
 368             catch (Interview.Fault e) {
 369                 throw new Fault(i18n, "cnfg.writeConfig.badConfig", new Object[] { path, e.getMessage() } );
 370             }   // catch
 371         }
 372 
 373         private File path;
 374     }
 375 
 376     //--------------------------------------------------------------------------
 377 
 378     private static class EnvCommand extends Command
 379     {
 380         static String getName() {
 381             return "env";
 382         }
 383 
 384         EnvCommand(ListIterator<String> argIter) throws Fault {
 385             super(getName());
 386 
 387             if (!argIter.hasNext())
 388                 throw new Fault(i18n, "cnfg.env.missingArg");
 389 
 390             name = nextArg(argIter);
 391         }
 392 
 393         public void run(CommandContext ctx) throws Fault {
 394             InterviewParameters p = getConfig(ctx);
 395             if (p.getEnvParameters() instanceof Parameters.LegacyEnvParameters) {
 396                 Parameters.LegacyEnvParameters eParams =
 397                     (Parameters.LegacyEnvParameters) (p.getEnvParameters());
 398                 eParams.setEnvName(name);
 399             }
 400             else
 401                 throw new Fault(i18n, "cnfg.env.notEditable");
 402         }
 403 
 404         private String name;
 405     }
 406 
 407     //--------------------------------------------------------------------------
 408 
 409     private static class EnvFilesCommand extends Command
 410     {
 411         static String[] getNames() {
 412             return new String[] { "envfile", "envfiles" };
 413         }
 414 
 415         EnvFilesCommand(ListIterator<String> argIter) throws Fault {
 416             super(getNames()[0]);
 417 
 418             Vector<File> v = new Vector<>();
 419 
 420             while (argIter.hasNext()) {
 421                 String arg = nextArg(argIter);
 422                 if (arg.startsWith("-")) {
 423                     putbackArg(argIter);
 424                     break;
 425                 }
 426                 else
 427                     v.add(new File(arg));
 428             }
 429 
 430             if (v.size() == 0)
 431                 throw new Fault(i18n, "cnfg.envFiles.noFiles");
 432 
 433             files = new File[v.size()];
 434             v.toArray(files);
 435         }
 436 
 437         public void run(CommandContext ctx) throws Fault {
 438             InterviewParameters p = getConfig(ctx);
 439             if (p.getEnvParameters() instanceof Parameters.LegacyEnvParameters) {
 440                 Parameters.LegacyEnvParameters eParams =
 441                     (Parameters.LegacyEnvParameters) (p.getEnvParameters());
 442                 eParams.setEnvFiles(files);
 443             }
 444             else
 445                 throw new Fault(i18n, "cnfg.envFiles.notEditable");
 446         }
 447 
 448         private File[] files;
 449     }
 450 
 451     //--------------------------------------------------------------------------
 452 
 453     private static class ExcludeListCommand extends Command
 454     {
 455         static String getName() {
 456             return "excludeList";
 457         }
 458 
 459         ExcludeListCommand(ListIterator<String> argIter) throws Fault {
 460             super(getName());
 461 
 462             // in time, we should support -none, -default, -latest etc
 463             Vector<File> v = new Vector<>();
 464 
 465             while (argIter.hasNext()) {
 466                 String arg = nextArg(argIter);
 467                 if (arg.startsWith("-")) {
 468                     putbackArg(argIter);
 469                     break;
 470                 }
 471                 else
 472                     v.add(new File(arg));
 473             }
 474 
 475             if (v.size() == 0)
 476                 throw new Fault(i18n, "cnfg.excl.noFiles");
 477 
 478             files = new File[v.size()];
 479             v.toArray(files);
 480         }
 481 
 482         public void run(CommandContext ctx) throws Fault {
 483             InterviewParameters p = getConfig(ctx);
 484             if (p.getExcludeListParameters() instanceof Parameters.MutableExcludeListParameters) {
 485                 Parameters.MutableExcludeListParameters eParams =
 486                     (Parameters.MutableExcludeListParameters) (p.getExcludeListParameters());
 487                 eParams.setExcludeMode(Parameters.MutableExcludeListParameters.CUSTOM_EXCLUDE_LIST);
 488                 eParams.setCustomExcludeFiles(files);
 489             }
 490             else
 491                 throw new Fault(i18n, "cnfg.excl.notEditable");
 492         }
 493 
 494         private File[] files;
 495     }
 496     //--------------------------------------------------------------------------
 497 
 498     private static class KflCommand extends Command
 499     {
 500         static String getName() {
 501             return "kfl";
 502         }
 503 
 504         KflCommand(ListIterator<String> argIter) throws Fault {
 505             super(getName());
 506 
 507             // in time, we should support -none, -default, -latest etc
 508             Vector<File> v = new Vector<>();
 509 
 510             while (argIter.hasNext()) {
 511                 String arg = nextArg(argIter);
 512                 if (arg.startsWith("-")) {
 513                     putbackArg(argIter);
 514                     break;
 515                 }
 516                 else
 517                     v.add(new File(arg));
 518             }
 519 
 520             if (v.size() == 0)
 521                 throw new Fault(i18n, "cnfg.kfl.noFiles");
 522 
 523             files = new File[v.size()];
 524             v.toArray(files);
 525         }
 526 
 527         public void run(CommandContext ctx) throws Fault {
 528             InterviewParameters p = getConfig(ctx);
 529             p.setKnownFailureFiles(files);
 530 //          if (p.getExcludeListParameters() instanceof Parameters.MutableExcludeListParameters) {
 531 //              Parameters.MutableExcludeListParameters eParams =
 532 //                  (Parameters.MutableExcludeListParameters) (p.getExcludeListParameters());
 533 //              eParams.setExcludeMode(Parameters.MutableExcludeListParameters.CUSTOM_EXCLUDE_LIST);
 534 //              eParams.setCustomExcludeFiles(files);
 535 //          }
 536 //          else
 537 //              throw new Fault(i18n, "cnfg.excl.notEditable");
 538         }
 539 
 540         private File[] files;
 541     }
 542     //--------------------------------------------------------------------------
 543 
 544     private static class OpenCommand extends Command
 545     {
 546         static String getName() {
 547             return "open";
 548         }
 549 
 550         OpenCommand(File file) throws Fault {
 551             super(file.getPath());
 552             cmdForFile = getCommandForFile(file);
 553         }
 554 
 555         OpenCommand(ListIterator<String> argIter) throws Fault {
 556             super(getName());
 557 
 558             if (!argIter.hasNext())
 559                 throw new Fault(i18n, "cnfg.open.missingArg");
 560 
 561             String arg = nextArg(argIter);
 562             cmdForFile = getCommandForFile(new File(arg));
 563         }
 564 
 565         public void run(CommandContext ctx) throws Fault {
 566             cmdForFile.run(ctx);
 567         }
 568 
 569         Command getCommandForFile(File file)
 570             throws Fault
 571         {
 572             if (!file.exists())
 573                 throw new Fault(i18n, "cnfg.open.cantFindFile", file);
 574 
 575             if (TestSuite.isTestSuite(file))
 576                 return new TestSuiteCommand(file);
 577 
 578             if (WorkDirectory.isWorkDirectory(file))
 579                 return new WorkDirectoryCommand(file);
 580 
 581             if (FileParameters.isParameterFile(file))
 582                 return new ParamFileCommand(file);
 583 
 584             if (InterviewParameters.isInterviewFile(file))
 585                 return new ConfigCommand(file);
 586 
 587             if (file.getPath().endsWith(".jte"))
 588                 throw new Fault(i18n, "cnfg.open.cantOpenJTE", file);
 589 
 590             if (file.getPath().endsWith(".jtt"))
 591                 throw new Fault(i18n, "cnfg.open.cantOpenJTT", file);
 592 
 593             if (file.getPath().endsWith(".jtx"))
 594                 throw new Fault(i18n, "cnfg.open.cantOpenJTX", file);
 595 
 596             throw new Fault(i18n, "cnfg.open.unknownFileType", file);
 597         }
 598 
 599         private Command cmdForFile;
 600     }
 601 
 602     //--------------------------------------------------------------------------
 603 
 604     private static class KeywordsCommand extends Command
 605     {
 606         static String getName() {
 607             return "keywords";
 608         }
 609 
 610         KeywordsCommand(ListIterator<String> argIter) throws Fault {
 611             super(getName());
 612 
 613             // could support -all -any
 614             if (!argIter.hasNext())
 615                 throw new Fault(i18n, "cnfg.keywords.missingArg");
 616 
 617             expr = nextArg(argIter);
 618         }
 619 
 620         public void run(CommandContext ctx) throws Fault {
 621             InterviewParameters p = getConfig(ctx);
 622             if (p.getKeywordsParameters() instanceof Parameters.MutableKeywordsParameters) {
 623                 Parameters.MutableKeywordsParameters kParams =
 624                     (Parameters.MutableKeywordsParameters) (p.getKeywordsParameters());
 625                 if (expr == null)
 626                     kParams.setKeywordsMode(Parameters.MutableKeywordsParameters.NO_KEYWORDS);
 627                 else {
 628                     kParams.setKeywordsMode(Parameters.MutableKeywordsParameters.MATCH_KEYWORDS);
 629                     kParams.setMatchKeywords(Parameters.MutableKeywordsParameters.EXPR, expr);
 630                 }
 631             }
 632             else
 633                 throw new Fault(i18n, "cnfg.keywords.notEditable");
 634         }
 635 
 636         private String expr;
 637     }
 638 
 639     //--------------------------------------------------------------------------
 640     // Legacy CLI support (-params, jte and jtp files)
 641     // Very deprecated.
 642 
 643     private static abstract class ParamsBaseCommand extends Command
 644     {
 645         ParamsBaseCommand(String name) {
 646             super(name);
 647         }
 648 
 649         protected void setParameters(CommandContext ctx, FileParameters fp)
 650             throws Fault
 651         {
 652             /*OLD
 653               if (ctx.getTestSuite() != null)
 654               throw new Command.Fault(i18n, "cnfg.testSuiteAlreadySet");
 655 
 656               if (ctx.getWorkDirectory() != null)
 657               throw new Command.Fault(i18n, "cnfg.workDirAlreadySet");
 658             */
 659 
 660             try {
 661                 ctx.setTestSuite(fp.getTestSuite());
 662 
 663                 if (fp.getWorkDirectory() != null)
 664                     ctx.setWorkDirectory(fp.getWorkDirectory());
 665 
 666                 getConfig(ctx).load(fp);
 667 
 668                 // support for old feature
 669                 File autoRunReportDir = fp.getReportDir();
 670                 if (autoRunReportDir == null) {
 671                     File rd = new File("reports", "report");
 672                     autoRunReportDir = ctx.getWorkDirectory().getFile(rd.getPath());
 673                 }
 674                 ctx.setAutoRunReportDir(autoRunReportDir);
 675             }
 676             catch (TestSuite.Fault e) {
 677                 throw new Fault(i18n, "cnfg.cantSetParameters", e.getMessage());
 678             }
 679             catch (CommandContext.Fault e) {
 680                 throw new Fault(e);
 681             }
 682         }
 683     }
 684 
 685     //--------------------------------------------------------------------------
 686 
 687     private static class ParamFileCommand extends ParamsBaseCommand
 688     {
 689         ParamFileCommand(File path) {
 690             super(path.getPath());
 691             this.path = path;
 692         }
 693 
 694         public void run(CommandContext ctx) throws Fault {
 695             try {
 696                 FileParameters params = new FileParameters(path);
 697                 if (!params.isValid()) {
 698                     throw new Fault(i18n, "cnfg.params.badParameterFile",
 699                                     new Object[] { path, params.getErrorMessage() } );
 700                 }
 701                 setParameters(ctx, params);
 702             }
 703             catch (FileNotFoundException e) {
 704                 throw new Fault(i18n, "cnfg.params.cantFindFile", path);
 705             }
 706             catch (IOException e) {
 707                 throw new Fault(i18n, "cnfg.params.cantReadFile",
 708                                 new Object[] { path, e } );
 709             }
 710         }
 711 
 712         private File path;
 713     }
 714 
 715     //--------------------------------------------------------------------------
 716 
 717     private static class ParamsCommand extends ParamsBaseCommand
 718     {
 719         static String getName() {
 720             return "params";
 721         }
 722 
 723         static HelpTree.Node getHelp() {
 724             String[] opts = {
 725                 "testSuite", "t",
 726                 "keywords",
 727                 "status",
 728                 "exclude",
 729                 "envfile",
 730                 "env",
 731                 "conc",
 732                 "timeout",
 733                 "report", "r",
 734                 "workdir", "w"
 735             };
 736             return new HelpTree.Node(i18n, "cnfg.params", opts);
 737         }
 738 
 739         ParamsCommand(ListIterator<String> argIter) throws Fault {
 740             super(getName());
 741 
 742             Vector<String> v = new Vector<>();
 743             while (argIter.hasNext())
 744                 v.add(nextArg(argIter));
 745             String[] args = new String[v.size()];
 746             v.copyInto(args);
 747 
 748             try {
 749                 params = new FileParameters(args);
 750             } catch (IllegalArgumentException e) {
 751                 throw new Fault(i18n, "cnfg.params.badValue", e.getMessage());
 752             }
 753 
 754             if (!params.isValid())
 755                 throw new Fault(i18n, "cnfg.params.badValue", params.getErrorMessage());
 756         }
 757 
 758         public void run(CommandContext ctx) throws Fault {
 759             setParameters(ctx, params);
 760         }
 761 
 762         private FileParameters params;
 763     }
 764 
 765     //--------------------------------------------------------------------------
 766 
 767     private static class PriorStatusCommand extends Command
 768     {
 769         static String getName() {
 770             return "priorStatus";
 771         }
 772 
 773         PriorStatusCommand(ListIterator<String> argIter) throws Fault {
 774             super(getName());
 775 
 776             if (!argIter.hasNext())
 777                 throw new Fault(i18n, "cnfg.status.missingArg");
 778 
 779             String arg = nextArg(argIter);
 780             String[] words = split(arg.toLowerCase());
 781             boolean any = false;
 782             values = new boolean[Status.NUM_STATES];
 783             if (words != null) {
 784                 for (int i = 0; i < words.length; i++) {
 785                     String w = words[i];
 786                     if (w.startsWith("pass")) {
 787                         values[Status.PASSED] = any = true;
 788                     }
 789                     else if (w.startsWith("fail")) {
 790                         values[Status.FAILED] = any = true;
 791                     }
 792                     else if (w.startsWith("error")) {
 793                         values[Status.ERROR] = any = true;
 794                     }
 795                     else if (w.startsWith("notrun")) {
 796                         values[Status.NOT_RUN] = any = true;
 797                     }
 798                     else
 799                         throw new Fault(i18n, "cnfg.status.badArg", w);
 800                 }
 801             }
 802 
 803 
 804             if (!any)
 805                 throw new Fault(i18n, "cnfg.status.noValues");
 806         }
 807 
 808         public void run(CommandContext ctx) throws Fault {
 809             InterviewParameters p = getConfig(ctx);
 810             if (p.getPriorStatusParameters() instanceof Parameters.MutablePriorStatusParameters) {
 811                 Parameters.MutablePriorStatusParameters sParams =
 812                     (Parameters.MutablePriorStatusParameters) (p.getPriorStatusParameters());
 813                 sParams.setPriorStatusMode(Parameters.MutablePriorStatusParameters.MATCH_PRIOR_STATUS);
 814                 sParams.setMatchPriorStatusValues(values);
 815             }
 816             else
 817                 throw new Fault(i18n, "cnfg.status.notEditable");
 818         }
 819 
 820         private static String[] split(String s) {
 821             if (s == null)
 822             return null;
 823 
 824             Vector<String> v = new Vector<>();
 825             int start = -1;
 826             for (int i = 0; i < s.length(); i++) {
 827                 if (Character.isLetterOrDigit(s.charAt(i))) {
 828                     if (start == -1)
 829                         start = i;
 830                 }
 831                 else {
 832                     if (start != -1)
 833                         v.addElement(s.substring(start, i));
 834                     start = -1;
 835                 }
 836             }
 837             if (start != -1)
 838             v.addElement(s.substring(start));
 839             if (v.size() == 0)
 840             return null;
 841             String[] a = new String[v.size()];
 842             v.copyInto(a);
 843             return a;
 844         }
 845 
 846         private boolean[] values;
 847     }
 848 
 849 
 850     //--------------------------------------------------------------------------
 851 
 852     private static class SetCommand extends Command
 853     {
 854         static String getName() {
 855             return "set";
 856         }
 857 
 858         SetCommand(ListIterator<String> argIter) throws Fault {
 859             super(getName());
 860 
 861             if (!argIter.hasNext())
 862                 throw new Fault(i18n, "cnfg.set.insufficientArgs");
 863 
 864             String arg = nextArg(argIter);
 865             if (arg.equals("-f") || arg.equals("-file")) {
 866                 if (!argIter.hasNext())
 867                     throw new Fault(i18n, "cnfg.set.insufficientArgs");
 868                 file = new File(nextArg(argIter));
 869             }
 870             else {
 871                 tag = arg;
 872                 if (!argIter.hasNext())
 873                     throw new Fault(i18n, "cnfg.set.insufficientArgs");
 874                 value = nextArg(argIter);
 875             }
 876         }
 877 
 878         public void run(CommandContext ctx) throws Fault {
 879             InterviewParameters p = getConfig(ctx);
 880             Question[] path = p.getPath();
 881             if (file != null) {
 882                 Map values = loadFile(file);
 883                 for (int i = 0; i < path.length; i++) {
 884                     Question q = path[i];
 885                     String v = (String) (values.get(q.getTag()));
 886                     if (v != null) {
 887                         setValue(q, v);
 888                         path = p.getPath();
 889                     }
 890                 }
 891             }
 892             else {
 893                 for (int i = 0; i < path.length; i++) {
 894                     Question q = path[i];
 895                     if (q.getTag().equals(tag)) {
 896                         setValue(q, value);
 897                         return;
 898                     }
 899                 }
 900 
 901                 // The following is not ideal but works for now.
 902                 // It is arguably bad form to return such a long detail
 903                 // string, rather than providing an extra method
 904                 // to generate the trace if required -- i.e. the Fault
 905                 // equivalent of e.printStackTrace()
 906                 throw new Fault(i18n, "cnfg.set.tagNotFound",
 907                                 new Object[] { tag, getPathTrace(path) });
 908             }
 909         }
 910 
 911         private void setValue(Question q, String value) throws Fault {
 912             try {
 913                 if (q instanceof CompositeQuestion) {
 914                     CompositeQuestion cq = (CompositeQuestion)q;
 915                     int sepIndex = value.indexOf(":");
 916                     if (sepIndex > 0) {
 917                         // decode value and send to question
 918                         // could be handled differently in the future
 919                         // text to the left of the colon in the values is the key
 920                         // all text to the right is the value
 921                         String key = value.substring(0, sepIndex);
 922                         String val;
 923                         if (sepIndex == value.length() + 1)
 924                             // handles key:
 925                             val = "";
 926                         else
 927                             // handles key:value
 928                             val = value.substring(sepIndex + 1);
 929 
 930                         cq.setValue(key, val);
 931                     }
 932                     else {
 933                         q.setValue(value);
 934                     }
 935                 }
 936                 else {
 937                     q.setValue(value);
 938                 }
 939             }
 940             catch (InterviewParameters.Fault e) {
 941                 throw new Fault(i18n, "cnfg.set.cantSetValue",
 942                                 new Object[] { q.getTag(), value, e.getMessage() });
 943             }
 944         }
 945 
 946         private static String getPathTrace(Question[] path) {
 947             String lineSep = System.getProperty("line.separator");
 948             StringBuffer sb = new StringBuffer();
 949             for (int i = 0; i < path.length; i++) {
 950                 Question q = path[i];
 951                 sb.append(q.getTag());
 952                 if (!(q instanceof NullQuestion)) {
 953                     String s = q.getStringValue();
 954                     sb.append(" (");
 955                     if (s == null)
 956                         sb.append("null");
 957                     else if (s.length() < 32)
 958                         sb.append(s);
 959                     else {
 960                         sb.append(s.substring(0, 32));
 961                         sb.append("...");
 962                     }
 963                     sb.append(")");
 964                 }
 965                 sb.append(lineSep); // arguably better to do it later when printing to terminal
 966             }
 967             return (sb.toString());
 968         }
 969 
 970         private Map loadFile(File file) throws Fault {
 971             try (FileInputStream fis = new FileInputStream(file);
 972                  InputStream in = new BufferedInputStream(fis)) {
 973                 Properties props = new Properties();
 974                 props.load(in);
 975                 return props;
 976             }
 977             catch (FileNotFoundException e) {
 978                 throw new Fault(i18n, "cnfg.set.cantFindFile", file);
 979             }
 980             catch (IOException e) {
 981                 throw new Fault(i18n, "cnfg.set.cantReadFile",
 982                                 new Object[] { file, e.getMessage() });
 983             }
 984         }
 985 
 986         private File file;
 987         private String tag;
 988         private String value;
 989     }
 990 
 991     //--------------------------------------------------------------------------
 992 
 993     /**
 994      * Sets "external" interview values from the command line.
 995      */
 996     private static class SetXCommand extends Command
 997     {
 998         static String getName() {
 999             return "setX";
1000         }
1001 
1002         SetXCommand(ListIterator<String> argIter) throws Fault {
1003             super(getName());
1004 
1005             if (!argIter.hasNext())
1006                 throw new Fault(i18n, "cnfg.set.insufficientArgs");
1007 
1008             String arg = nextArg(argIter);
1009             if (arg.equals("-f") || arg.equals("-file")) {
1010                 if (!argIter.hasNext())
1011                     throw new Fault(i18n, "cnfg.set.insufficientArgs");
1012                 file = new File(nextArg(argIter));
1013             }
1014             else {
1015                 name = arg;
1016                 if (!argIter.hasNext())
1017                     throw new Fault(i18n, "cnfg.set.insufficientArgs");
1018                 value = nextArg(argIter);
1019             }
1020         }
1021 
1022         public void run(CommandContext ctx) throws Fault {
1023             InterviewParameters p = getConfig(ctx);
1024             if (file != null) {
1025                 Map values = loadFile(file);
1026                 Set keys = values.keySet();
1027                 Iterator it = keys.iterator();
1028                 String name = null;
1029                 for (int i = 0; it.hasNext(); i++) {
1030                     name = (String)(it.next());
1031                     /*  could do it this way to reject unknown props
1032                     String v = p.retrieveProperty(name);
1033                     if (v != null) {
1034                         p.storeProperty(name, (String)(values.get(name)));
1035                     }
1036                     */
1037                     p.storeProperty(name, (String)(values.get(name)));
1038                 }
1039             }
1040             else {
1041                 p.storeProperty(name, value);
1042             }
1043         }
1044 
1045         private Map loadFile(File file) throws Fault {
1046             try (FileInputStream fis = new FileInputStream(file);
1047                 InputStream in = new BufferedInputStream(fis)) {
1048                 Properties props = new Properties();
1049                 props.load(in);
1050                 return props;
1051             }
1052             catch (FileNotFoundException e) {
1053                 throw new Fault(i18n, "cnfg.set.cantFindFile", file);
1054             }
1055             catch (IOException e) {
1056                 throw new Fault(i18n, "cnfg.set.cantReadFile",
1057                                 new Object[] { file, e.getMessage() });
1058             }
1059         }
1060 
1061         private File file;
1062         private String name;
1063         private String value;
1064     }
1065 
1066     //--------------------------------------------------------------------------
1067 
1068     private static class TestsCommand extends Command
1069     {
1070         static String getName() {
1071             return "tests";
1072         }
1073 
1074         TestsCommand(ListIterator<String> argIter) throws Fault {
1075             super(getName());
1076 
1077             Vector<String> v = new Vector<>();
1078 
1079             while (argIter.hasNext()) {
1080                 // could possibly support @file or similar syntax here for a list of tests
1081                 String arg = nextArg(argIter);
1082                 if (arg.startsWith("-")) {
1083                     putbackArg(argIter);
1084                     break;
1085                 }
1086                 else
1087                     v.add(arg);
1088             }
1089 
1090             if (v.size() == 0)
1091                 throw new Fault(i18n, "cnfg.tests.noTests");
1092 
1093             tests = new String[v.size()];
1094             v.toArray(tests);
1095         }
1096 
1097         public void run(CommandContext ctx) throws Fault {
1098             InterviewParameters p = getConfig(ctx);
1099             if (p.getTestsParameters() instanceof Parameters.MutableTestsParameters) {
1100                 Parameters.MutableTestsParameters iParams =
1101                     (Parameters.MutableTestsParameters) (p.getTestsParameters());
1102                 iParams.setTestsMode(Parameters.MutableTestsParameters.SPECIFIED_TESTS);
1103                 iParams.setSpecifiedTests(tests);
1104             }
1105         else
1106             throw new Fault(i18n, "cnfg.tests.notEditable");
1107         }
1108 
1109         private String[] tests;
1110     }
1111 
1112     //--------------------------------------------------------------------------
1113 
1114     private static class TestSuiteCommand extends Command
1115     {
1116         static String[] getNames() {
1117             return new String[] { "testsuite", "ts" };
1118         }
1119 
1120         TestSuiteCommand(ListIterator<String> argIter) throws Fault {
1121             super(getNames()[0]);
1122 
1123             while (argIter.hasNext()) {
1124                 String arg = nextArg(argIter);
1125                 if (arg.equalsIgnoreCase("-preferred"))
1126                     preferFlag = true;
1127                 else if (arg.startsWith("-"))
1128                     throw new Fault(i18n, "cnfg.ts.badArg", arg);
1129                 else {
1130                     path = new File(arg);
1131                     return;
1132                 }
1133             }   // while
1134 
1135             // if we are here, args are exhausted or invalid
1136             throw new Fault(i18n, "cnfg.ts.missingArg");
1137         }
1138 
1139         TestSuiteCommand(File path) {
1140             super(path.getPath());
1141             this.path = path;
1142         }
1143 
1144         public URL getCustomSplash() {
1145             String basePath = path.getAbsolutePath() +
1146                               File.separator + "lib";
1147             DirectoryClassLoader loader =
1148                     new DirectoryClassLoader(basePath);
1149             try {
1150                 ResourceBundle b = ResourceBundle.getBundle("splash",
1151                                         Locale.getDefault(), loader);
1152                 String icon = b.getString("splash.icon");
1153                 // could support a classpath value in the bundle
1154                 // and use PathClassLoader to generate the icon
1155                 File f = new File(icon);
1156                 if (!f.isAbsolute()) {
1157                     f = new File(basePath, icon);
1158                 }
1159                 if (f.canRead())
1160                     try {
1161                         return f.toURL();
1162                     }
1163                     catch (java.net.MalformedURLException e) {
1164                         return null;
1165                     }
1166                 else
1167                     return null;
1168             }
1169             // catch all possible exceptions from ResourceBundle
1170             catch (MissingResourceException m) {
1171                 return null;
1172             }
1173             catch (NullPointerException e) {
1174                 return null;
1175             }
1176             catch (ClassCastException e) {
1177                 return null;
1178             }
1179         }
1180 
1181         /**
1182          * Get custom help set for harness help.
1183          * A file help.properties must be in the <code>lib</code> directory of
1184          * the test suite.  An internationalization-aware search is then done
1185          * to find a properties bundle named "help".  That bundle must contain
1186          * a classpath entry, which is then used to generate the return value.
1187          * The classpath value is relative to the root of the test suite, not
1188          * the <code>lib</code> directory.
1189          */
1190         ClassLoader getCustomHelpLoader() {
1191             try {
1192                 String basePath = path.getCanonicalPath() +
1193                                   File.separator + "lib";
1194                 DirectoryClassLoader loader =
1195                         new DirectoryClassLoader(basePath);
1196 
1197                 ResourceBundle b = ResourceBundle.getBundle("help",
1198                                         Locale.getDefault(), loader);
1199                 String cp = b.getString("classpath");
1200 
1201                 // NOTES: PathClassLoader does not currently support resources
1202                 //        DirectionClassLoader cannot load from jar files
1203                 //PathClassLoader cl = new PathClassLoader(path, cp);
1204                 //DirectoryClassLoader cl = new DirectoryClassLoader(
1205                 //        path.getPath() + File.separator + cp);
1206                 File f = new File(cp);
1207                 if (!f.isAbsolute()) {
1208                     f = new File(path.getPath(), cp);
1209                 }
1210                 URLClassLoader cl = new URLClassLoader(new URL[] {f.toURL()},
1211                                             this.getClass().getClassLoader());
1212 
1213                 return cl;
1214             }
1215             // catch all possible exceptions from ResourceBundle
1216             catch (MissingResourceException m) {
1217                 return null;
1218             }
1219             catch (NullPointerException e) {
1220                 return null;
1221             }
1222             catch (ClassCastException e) {
1223                 return null;
1224             }
1225             catch (IOException e) {
1226                 return null;
1227             }
1228         }
1229 
1230         public void run(CommandContext ctx) throws Fault {
1231             /*OLD
1232             if (ctx.getTestSuite() != null)
1233                 throw new Fault(i18n, "cnfg.testSuiteAlreadySet");
1234 
1235             try {
1236                 ctx.setTestSuite(TestSuite.open(path));
1237             }
1238             catch (FileNotFoundException e) {
1239                 throw new Fault(i18n, "cnfg.ts.cantFindTestSuite", path);
1240             }
1241             catch (TestSuite.Fault e) {
1242                 throw new Fault(i18n, "cnfg.ts.cantOpenTestSuite", e.getMessage());
1243             }
1244             */
1245             if (!path.exists())
1246                 throw new Fault(i18n, "cnfg.ts.cantFindTestSuite", path);
1247 
1248             if (!TestSuite.isTestSuite(path))
1249                 throw new Fault(i18n, "cnfg.ts.notATestSuite", path);
1250 
1251             try {
1252                 ctx.setTestSuite(path);
1253 
1254                 if (preferFlag) {
1255                     // searching desktop history to find previous compatible workdir
1256                     // if none, continue as normal
1257                     // this is not the best logical place to do this, but there doesn't
1258                     // seem to be another good place in the current architecture.
1259                     // although perhaps it would be good to defer this processing to a
1260                     // little later in the command processing?
1261                     Map<String, String> p = Desktop.getPreviousDesktop(null);
1262                     String s = p.get("tool.count");
1263                     if (s != null) {
1264                         int count = Integer.parseInt(s);
1265                         for (int i = 0; i < count; i++) {
1266                             s = p.get("tool." + i + ".class");
1267                             if ("com.sun.javatest.exec.ExecTool".equals(s)) {
1268                                 s = p.get("tool." + i + ".testSuite");
1269 
1270                                 // this tool instance does not have a test suite
1271                                 if (s == null)
1272                                     continue;
1273 
1274                                 String s1 = path.getPath();
1275                                 String s2 = s;
1276                                 try {
1277                                     s1 = path.getCanonicalPath();
1278                                 }
1279                                 catch (IOException e) {
1280                                     // ignore accept default value of s1
1281                                 }
1282 
1283                                 try {
1284                                     File file = new File(s2);
1285                                     s2 = file.getCanonicalPath();
1286                                 }
1287                                 catch (IOException e) {
1288                                     // ignore accept default value of s2
1289                                 }
1290 
1291                                 if (s1.equals(s2)) {
1292                                     s = p.get("tool." + i + ".workDir");
1293                                     if (s != null &&
1294                                         WorkDirectory.isUsableWorkDirectory(
1295                                             new File(s))) {
1296                                         // found workdir to use as preferred workdir
1297                                         ctx.setDefaultWorkDir(s);
1298 
1299                                         // we found WD,
1300                                         // try to restore filter now
1301 
1302                                         s = p.get("tool." + i + ".filter");
1303                                         Map<String, String> data = collectSpecificData("tool." + i + ".", p);
1304 
1305                                         if (s != null) {
1306                                             ctx.setDesktopData(data);
1307                                         }
1308 
1309                                         break;
1310                                     }
1311                                     else {
1312                                         if (s != null)
1313                                             System.out.println(i18n.getString("cnfg.badWorkdir", s));
1314                                         continue;
1315                                     }
1316                                 }
1317                             }
1318                         }   // for
1319                     }
1320                 }
1321             }
1322             catch (CommandContext.Fault e) {
1323                 throw new Fault(e);
1324             }
1325         }
1326 
1327         private File path;
1328         private boolean preferFlag;
1329 
1330         private Map<String, String> collectSpecificData(String prefix, Map<String, String> p) {
1331             Set<Entry<String, String>> s = p.entrySet();
1332             Iterator<Entry<String, String>> it = s.iterator();
1333             Map<String, String> res = new HashMap<>();
1334             while (it.hasNext()) {
1335                 Entry<String, String> en = it.next();
1336                 if (en.getKey().toString().startsWith(prefix)) {
1337                     String key = en.getKey();
1338                     key = key.substring(prefix.length());
1339                     if ("-workDir-config-class-mgr-".contains(key)) {
1340                     //if ("-class-mgr-".contains(key)) {
1341                         continue;
1342                     }
1343                     res.put(key, en.getValue());
1344                 }
1345             }
1346             return res;
1347         }
1348     }
1349 
1350     //--------------------------------------------------------------------------
1351 
1352     private static class TimeoutFactorCommand extends Command
1353     {
1354         static String getName() {
1355             return "timeoutfactor";
1356         }
1357 
1358         TimeoutFactorCommand(ListIterator<String> argIter) throws Fault {
1359             super(getName());
1360 
1361             if (!argIter.hasNext())
1362                 throw new Fault(i18n, "cnfg.tf.missingArg");
1363 
1364             String arg = nextArg(argIter);
1365 
1366             NumberFormat fmt = NumberFormat.getNumberInstance(); // will be locale-specific
1367             ParsePosition pos = new ParsePosition(0);
1368             Number num = fmt.parse(arg, pos);
1369             if (num != null && (pos.getIndex() == arg.length())) {
1370                 value = num.floatValue();
1371                 if (value < Parameters.TimeoutFactorParameters.MIN_TIMEOUT_FACTOR
1372                     || value > Parameters.TimeoutFactorParameters.MAX_TIMEOUT_FACTOR) {
1373                     throw new Fault(i18n, "cnfg.tf.badRange",
1374                                     new Object[] {
1375                                         arg,
1376                                         new Float(Parameters.TimeoutFactorParameters.MIN_TIMEOUT_FACTOR),
1377                                         new Float(Parameters.TimeoutFactorParameters.MAX_TIMEOUT_FACTOR) });
1378                 }
1379             }
1380             else
1381                 throw new Fault(i18n, "cnfg.tf.badValue", arg);
1382         }
1383 
1384         public void run(CommandContext ctx) throws Fault {
1385             InterviewParameters p = getConfig(ctx);
1386             if (p.getTimeoutFactorParameters() instanceof Parameters.MutableTimeoutFactorParameters) {
1387                 Parameters.MutableTimeoutFactorParameters cParams =
1388                 (Parameters.MutableTimeoutFactorParameters) (p.getTimeoutFactorParameters());
1389                 cParams.setTimeoutFactor(value);
1390             }
1391             else
1392                 throw new Fault(i18n, "cnfg.tf.notEditable");
1393         }
1394 
1395         private float value;
1396     }
1397 
1398     //--------------------------------------------------------------------------
1399 
1400     private static class WorkDirectoryCommand extends Command
1401     {
1402         static String[] getNames() {
1403             return new String[] { "workdirectory", "workdir", "wd" };
1404         }
1405 
1406         WorkDirectoryCommand(ListIterator<String> argIter) throws Fault {
1407             super(getNames()[0]);
1408 
1409             while (argIter.hasNext()) {
1410                 String arg = nextArg(argIter);
1411                 if (arg.equalsIgnoreCase("-create"))
1412                     createFlag = true;
1413                 else if (arg.equalsIgnoreCase("-overwrite")) {
1414                     createFlag = true;
1415                     overwriteFlag = true;
1416                 }
1417                 else if (arg.startsWith("-"))
1418                     throw new Fault(i18n, "cnfg.wd.badArg", arg);
1419                 else {
1420                     path = new File(arg);
1421                     return;
1422                 }
1423             }
1424 
1425             // drop through if path not given
1426             throw new Fault(i18n, "cnfg.wd.missingArg");
1427         }
1428 
1429         WorkDirectoryCommand(File path) {
1430             super(path.getPath());
1431             this.path = path;
1432         }
1433 
1434         public void run(CommandContext ctx) throws Fault {
1435             /*OLD
1436             TestSuite ts = ctx.getTestSuite();
1437             WorkDirectory wd = ctx.getWorkDirectory();
1438 
1439             if (wd != null)
1440                 throw new Fault(i18n, "cnfg.workDirAlreadySet");
1441 
1442             if (createFlag)
1443                 wd = createWorkDirectory(ts);
1444             else
1445                 wd = openWorkDirectory(ts);
1446 
1447             try {
1448                 ctx.setWorkDirectory(wd);
1449             }
1450             catch (TestSuite.Fault e) {
1451                 throw new Fault(i18n, "cnfg.wd.cantOpenTestSuiteForWorkDir", e.getMessage());
1452             }
1453             */
1454             if (!createFlag) {
1455                 if (!path.exists())
1456                     throw new Fault(i18n, "cnfg.wd.cantFindWorkDir", path);
1457                 if (!WorkDirectory.isWorkDirectory(path)
1458                     && !WorkDirectory.isEmptyDirectory(path))
1459                     throw new Fault(i18n, "cnfg.wd.notAWorkDirectory", path);
1460             }
1461 
1462             if (overwriteFlag) {
1463                 remove(path);
1464                 if (path.exists())
1465                     throw new Fault(i18n, "cnfg.wd.cantRemoveWorkDir", path);
1466             }
1467 
1468             try {
1469                 ctx.setWorkDirectory(path, createFlag);
1470             }
1471             catch (CommandContext.Fault e) {
1472                 throw new Fault(e);
1473             }
1474 
1475         }
1476 
1477         /*OLD
1478         private WorkDirectory createWorkDirectory(TestSuite ts) throws Fault {
1479             if (ts == null)
1480                 throw new Fault(i18n, "cnfg.wd.cantCreateWorkDir_noTestSuite");
1481 
1482             if (overwriteFlag) {
1483                 remove(path);
1484                 if (path.exists())
1485                     throw new Fault(i18n, "cnfg.wd.cantRemoveWorkDir", path);
1486             }
1487 
1488             try {
1489                 return WorkDirectory.create(path, ts);
1490             }
1491             catch (WorkDirectory.Fault e) {
1492                 throw new Fault(i18n, "cnfg.wd.cantCreateWorkDir",
1493                                 new Object[] { path, e.getMessage() } );
1494             }
1495         }
1496         */
1497 
1498         /*OLD
1499         private WorkDirectory openWorkDirectory(TestSuite ts) throws Fault {
1500             try {
1501                 WorkDirectory wd;
1502                 if (path.exists()) {
1503                     if (WorkDirectory.isWorkDirectory(path)) {
1504                         if (ts == null) {
1505                             wd = WorkDirectory.open(path);
1506                             ts = wd.getTestSuite();
1507                         }
1508                         else
1509                             wd = WorkDirectory.open(path, ts);
1510                     }
1511                     else if (WorkDirectory.isEmptyDirectory(path)) {
1512                         if (ts == null)
1513                             throw new Fault(i18n, "cnfg.wd.cantCreateWorkDir_noTestSuite", path);
1514                         else
1515                             wd = WorkDirectory.create(path, ts);
1516                     }
1517                     else
1518                         throw new Fault(i18n, "cnfg.wd.notWorkDir", path);
1519                 }
1520                 else
1521                     throw new Fault(i18n, "cnfg.wd.cantFindWorkDir", path);
1522 
1523                 if (wd.getTestSuite().getID() != ts.getID())
1524                     throw new Fault(i18n, "cnfg.wd.incompatibleWorkDir", path);
1525 
1526                 return wd;
1527             }
1528             catch (FileNotFoundException e) {
1529                 throw new Fault(i18n, "cnfg.wd.cantFindWorkDir", path);
1530             }
1531             catch (WorkDirectory.Fault e) {
1532                 throw new Fault(i18n, "cnfg.wd.cantOpenWorkDir", e.getMessage());
1533             }
1534         }
1535         */
1536 
1537         private void remove(File path) {
1538             if (path.exists()) {
1539                 if (path.isDirectory()) {
1540                     File[] files = path.listFiles();
1541                     for (int i = 0; i < files.length; i++)
1542                         remove(files[i]);
1543                     // workaround for leftover .nfs* files
1544                     String[] undeletables = path.list();
1545                     if (undeletables != null && undeletables.length > 0) {
1546                         for (int i = 0; i < undeletables.length; i++) {
1547                             String name = undeletables[i];
1548                             if (name.startsWith(".nfs")) {
1549                                 File fOld = new File(path, name);
1550                                 File fNew = new File(path.getParentFile(), name);
1551                                 boolean ok = fOld.renameTo(fNew);
1552                                 // discard ok result
1553                             }
1554                         }
1555                     }
1556                 }
1557                 path.delete();
1558             }
1559         }
1560 
1561         private File path;
1562         private boolean createFlag;
1563         private boolean overwriteFlag;
1564     }
1565 }