/* * $Id$ * * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.javatest; import java.io.*; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.text.MessageFormat; import java.util.Properties; import java.util.ResourceBundle; import com.sun.interview.Interview; import com.sun.interview.Question; import com.sun.interview.WizPrint; import com.sun.javatest.util.BackupPolicy; import com.sun.javatest.util.I18NResourceBundle; /** * This class provides a utility for command-line editing of configuration (.jti) files. * It is intended to be invoked from the command line, as in:
* java com.sun.javatest.EditJIT options... ** For details of the options, use the
-help
option.
*/
public class EditJTI
{
/**
* This exception is used to indicate a problem with the command line arguments.
*/
public static class BadArgs extends Exception
{
/**
* Create a BadArgs exception.
* @param i18n A resource bundle in which to find the detail message.
* @param s The key for the detail message.
*/
BadArgs(ResourceBundle i18n, String s) {
super(i18n.getString(s));
}
/**
* Create a BadArgs exception.
* @param i18n A resource bundle in which to find the detail message.
* @param s The key for the detail message.
* @param o An argument to be formatted with the detail message by
* {@link java.text.MessageFormat#format}
*/ BadArgs(ResourceBundle i18n, String s, Object o) {
super(MessageFormat.format(i18n.getString(s), new Object[] {o}));
}
/**
* Create a BadArgs exception.
* @param i18n A resource bundle in which to find the detail message.
* @param s The key for the detail message.
* @param o An array of arguments to be formatted with the detail message by
* {@link java.text.MessageFormat#format}
*/
BadArgs(ResourceBundle i18n, String s, Object[] o) {
super(MessageFormat.format(i18n.getString(s), o));
}
}
/**
* This exception is used to report problems that arise when using this API.
*/
public static class Fault extends Exception
{
Fault(I18NResourceBundle i18n, String s) {
super(i18n.getString(s));
}
Fault(I18NResourceBundle i18n, String s, Object o) {
super(i18n.getString(s, o));
}
Fault(I18NResourceBundle i18n, String s, Object[] o) {
super(i18n.getString(s, o));
}
}
/**
* Command line entry point. Run with -help
to get
* brief command line help. Warning: this method uses System.exit
* and so does not return if called directly.
* @param args Comamnd line arguments.
*/
public static void main(String[] args) {
try {
EditJTI e = new EditJTI();
boolean ok = e.run(args);
System.exit(ok ? 0 : 1);
}
catch (BadArgs e) {
System.err.println(e.getMessage());
usage(System.err);
System.exit(2);
}
catch (Fault e) {
System.err.println(e.getMessage());
System.exit(3);
}
}
/**
* Print out brief command line help.
* @param out the stream to which to write the command line help.
*/
public static void usage(PrintStream out) {
String prog = System.getProperty("program", "java " + EditJTI.class.getName());
out.println(i18n.getString("editJTI.usage.title"));
out.print(" ");
out.print(prog);
out.println(i18n.getString("editJTI.usage.summary"));
out.println(i18n.getString("editJTI.usage.options"));
out.println(i18n.getString("editJTI.usage.help1"));
out.println(i18n.getString("editJTI.usage.help2"));
out.println(i18n.getString("editJTI.usage.help3"));
out.println(i18n.getString("editJTI.usage.classpath1"));
out.println(i18n.getString("editJTI.usage.classpath2"));
out.println(i18n.getString("editJTI.usage.log1"));
out.println(i18n.getString("editJTI.usage.log2"));
out.println(i18n.getString("editJTI.usage.outfile1"));
out.println(i18n.getString("editJTI.usage.outfile2"));
out.println(i18n.getString("editJTI.usage.path1"));
out.println(i18n.getString("editJTI.usage.path2"));
out.println(i18n.getString("editJTI.usage.preview1"));
out.println(i18n.getString("editJTI.usage.preview2"));
out.println(i18n.getString("editJTI.usage.ts1"));
out.println(i18n.getString("editJTI.usage.ts2"));
out.println(i18n.getString("editJTI.usage.verbose1"));
out.println(i18n.getString("editJTI.usage.verbose2"));
out.println("");
out.println(i18n.getString("editJTI.usage.edit"));
out.println(i18n.getString("editJTI.usage.set"));
out.println(i18n.getString("editJTI.usage.search"));
out.println("");
}
/**
* Run the utility, without exiting. Any messages are written to
* the standard output stream.
* @param args command line args
* @return true if the resulting configuration is valid (complete),
* and false otherwise.
* @throws EditJTI.BadArgs if there is an error analysing the args
* @throws EditJTI.Fault if there is an error executing the args
*/
public boolean run(String[] args) throws BadArgs, Fault {
PrintWriter out = new PrintWriter(System.out);
try {
return run(args, out);
}
finally {
out.flush();
}
}
/**
* Run the utility, without exiting, writing any messages to a specified stream.
* @param args command line args
* @param out the stream to which to write any messages
* @return true if the resulting configuration is valid (complete),
* and false otherwise.
* @throws EditJTI.BadArgs if there is an error analysing the args
* @throws EditJTI.Fault if there is an error executing the args
*/
public boolean run(String[] args, PrintWriter out) throws BadArgs, Fault {
File inFile = null;
File outFile = null;
File logFile = null;
File classPath = null;
File testSuitePath = null;
File workDirPath = null;
String[] editCmds = null;
boolean helpFlag = false;
boolean previewFlag = false;
boolean showPathFlag = false;
boolean verboseFlag = false;
for (int i = 0; i < args.length; i++) {
if ((args[i].equals("-o") || args[i].equals("-out"))
&& i + 1 < args.length) {
checkUnset(outFile, args[i]);
outFile = new File(args[++i]);
}
else if ((args[i].equals("-i") || args[i].equals("-in"))
&& i + 1 < args.length) {
checkUnset(inFile, args[i]);
inFile = new File(args[++i]);
}
else if ((args[i].equals("-l") || args[i].equals("-log"))
&& i + 1 < args.length) {
checkUnset(logFile, args[i]);
logFile = new File(args[++i]);
}
else if (args[i].equals("-n") || args[i].equals("-preview"))
previewFlag = true;
else if (args[i].equals("-p") || args[i].equals("-path"))
showPathFlag = true;
else if (args[i].equals("-v") || args[i].equals("-verbose") )
verboseFlag = true;
else if ((args[i].equals("-cp") || args[i].equals("-classpath")) && i + 1 < args.length) {
checkUnset(classPath, args[i]);
classPath = new File(args[++i]);
}
else if ((args[i].equals("-ts") || args[i].equals("-testsuite")) && i + 1 < args.length) {
checkUnset(testSuitePath, args[i]);
testSuitePath = new File(args[++i]);
}
else if ((args[i].equals("-wd") || args[i].equals("-workdir")) && i + 1 < args.length) {
checkUnset(testSuitePath, args[i]);
workDirPath = new File(args[++i]);
}
else if (args[i].equals("-help") || args[i].equals("-usage") || args[i].equals("/?") )
helpFlag = true;
else if (args[i].startsWith("-"))
throw new BadArgs(i18n, "editJTI.badOption", args[i]);
else if (i <= args.length - 1) {
if (inFile == null) {
editCmds = new String[args.length - 1 - i];
System.arraycopy(args, i, editCmds, 0, editCmds.length);
inFile = new File(args[args.length - 1]);
}
else {
editCmds = new String[args.length - i];
System.arraycopy(args, i, editCmds, 0, editCmds.length);
}
i = args.length - 1;
}
else
throw new BadArgs(i18n, "editJTI.badOption", args[i]);
}
if (args.length == 0 || helpFlag) {
usage(System.out);
if (inFile == null)
return true;
}
if (classPath != null && testSuitePath != null)
throw new BadArgs(i18n, "editJTI.cantHaveClassPathAndTestSuite");
if (inFile == null)
throw new BadArgs(i18n, "editJTI.noInterview");
// if (editCmds.length == 0 && outFile == null && logFile == null && !showPathFlag)
// throw new BadArgs(...no.actions....);
verbose = verboseFlag;
this.out = out;
try {
/* the following looks nice and simple, but breaks compatibility
with 3.1.4, because InterviewParameters.open will try and open
the wd in the .jti file if not given explicitly -- and previously,
this was not required/done. So, only use the simple code if wd
is set, and use the old 3.1.4 code if wd is not set.
*/
/* See comment above
if (workDirPath != null || testSuitePath != null) {
interview = InterviewParameters.open(testSuitePath, workDirPath, inFile);
}
*/
if (workDirPath != null)
interview = InterviewParameters.open(testSuitePath, workDirPath, inFile);
else if (testSuitePath != null) {
// only open the test suite, not the work dir
TestSuite ts;
try {
ts = TestSuite.open(testSuitePath);
}
catch (FileNotFoundException e) {
throw new Fault(i18n, "editJTI.cantFindTestSuite", testSuitePath);
}
catch (TestSuite.NotTestSuiteFault e) {
throw new Fault(i18n, "editJTI.notATestSuite", testSuitePath);
}
catch (TestSuite.Fault e) {
throw new Fault(i18n, "editJTI.cantOpenTestSuite",
new Object[] { testSuitePath, e });
}
load(inFile, ts);
}
// End of patches for 3.1.4 compatibility
else if (classPath != null) {
URLClassLoader loader = new URLClassLoader(new URL[] { classPath.toURL() });
load(inFile, loader);
}
else
load(inFile);
}
catch (Interview.Fault e) {
throw new Fault(i18n, "editJTI.cantOpenFile",
new Object[] { inFile.getPath(), e.getMessage() });
}
catch (FileNotFoundException e) {
throw new Fault(i18n, "editJTI.cantFindFile", inFile.getPath());
}
catch (IOException e) {
throw new Fault(i18n, "editJTI.cantOpenFile",
new Object[] { inFile.getPath(), e });
}
catch (IllegalStateException e) {
// only occurs if keywords are being used in the config, and the
// test suite is not available. user needs to specify -wd or -ts
if (verbose)
e.printStackTrace();
throw new Fault(i18n, "editJTI.badState", e.getMessage());
}
if (NUM_BACKUPS > 0)
interview.setBackupPolicy(BackupPolicy.simpleBackups(NUM_BACKUPS));
if (editCmds != null)
edit(editCmds);
if (showPathFlag)
showPath();
try {
if (logFile != null) {
if (previewFlag) {
String msg = i18n.getString("editJTI.wouldWriteLog", logFile);
out.println(msg);
}
else
writeLog(logFile);
}
}
catch (IOException e) {
throw new Fault(i18n, "editJTI.cantWriteLog",
new Object[] { logFile.getPath(), e });
}
try {
if (previewFlag) {
String msg;
if (interview.isEdited())
msg = i18n.getString("editJTI.wouldSaveEdited",
(outFile != null ? outFile : inFile));
else if (outFile != null)
msg = i18n.getString("editJTI.wouldSaveNotEdited", outFile);
else
msg = i18n.getString("editJTI.wouldNotSave");
out.println(msg);
}
else {
if (outFile != null)
save(outFile);
else if (interview.isEdited())
save(inFile);
}
}
catch (Interview.Fault e) {
throw new Fault(i18n, "editJTI.cantOpenFile",
new Object[] {
(outFile == null || outFile.getPath() == null ?
"??": outFile.getPath()), e });
}
catch (IOException e) {
File f = (outFile == null ? interview.getFile() : outFile);
throw new Fault(i18n, "editJTI.cantSaveFile",
new Object[] { f.getPath(), e });
}
return (interview.isFinishable());
}
/**
* Load a configuration file to be edited.
* @param inFile the file to be loaded
* @throws IOException if there is a problem reading the file
* @throws Interview.Fault if there is a problem loading the interview data from the file
*/
public void load(File inFile) throws IOException, Interview.Fault {
// this opens the interview via the work directory and test suite;
// the test suite implicitly knows its classpath via the .jtt file
interview = InterviewParameters.open(inFile);
interview.setEdited(false);
}
/**
* Load a configuration file to be edited.
* @param inFile the file to be loaded
* @param ts the test suite for which the interview is to be loaded
* @throws IOException if there is a problem reading the file
* @throws Interview.Fault if there is a problem loading the interview data from the file
* @throws EditJTI.Fault if there is a problem creating the interview for the testsuite
*/
public void load(File inFile, TestSuite ts)
throws IOException, Interview.Fault, Fault
{
// this opens the interview via the work directory and test suite;
// the test suite implicitly knows its classpath via the .jtt file
try {
interview = ts.createInterview();
}
catch (TestSuite.Fault e) {
throw new Fault(i18n, "editJTI.cantCreateInterviewForTestSuite",
new Object[] { ts.getPath(), e.getMessage() });
}
interview.load(inFile);
interview.setEdited(false);
}
/**
* Load a configuration file to be edited, using a specified class loader
* to load the interview class.
* @param inFile the file to be loaded
* @param loader the class loader to be used to load the interview class
* @throws IOException if there is a problem reading the file
* @throws Interview.Fault if there is a problem loading the interview data from the file
* @throws EditJTI.Fault if there is a problem creating the interview for the testsuite
*/
public void load(File inFile, URLClassLoader loader)
throws IOException, Interview.Fault, Fault
{
InputStream in = new BufferedInputStream(new FileInputStream(inFile));
Properties p = new Properties();
p.load(in);
in.close();
String interviewClassName = (String) (p.get("INTERVIEW"));
try {
Class interviewClass = loader.loadClass(interviewClassName);
interview = (InterviewParameters)(interviewClass.newInstance());
}
catch (ClassCastException e) {
throw new Fault(i18n, "editJTI.invalidInterview", inFile);
}
catch (ClassNotFoundException e) {
throw new Fault(i18n, "editJTI.cantFindClass",
new Object[] { interviewClassName, inFile });
}
catch (InstantiationException e) {
throw new Fault(i18n, "editJTI.cantInstantiateClass",
new Object[] { interviewClassName, inFile });
}
catch (IllegalAccessException e) {
throw new Fault(i18n, "editJTI.cantAccessClass",
new Object[] { interviewClassName, inFile });
}
finally {
try { if (in != null) in.close(); } catch (IOException e) {}
}
interview.load(inFile);
interview.setEdited(false);
}
/**
* Save the edited configuration in a specified file.
* @param file The file in which to save the configuration
* @throws IOException if there is a problem while writing the file
* @throws Interview.Fault if there is a problem while saving the interview data
*/
public void save(File file) throws IOException, Interview.Fault {
interview.save(file);
}
/**
* Show the current question path for the configuration.
*/
public void showPath() {
Question[] path = interview.getPath();
int indent = 0;
for (int i = 0; i < path.length; i++)
indent = Math.max(indent, path[i].getTag().length());
indent = Math.min(indent, MAX_INDENT);
for (int i = 0; i < path.length; i++) {
Question q = path[i];
String tag = q.getTag();
String value = q.getStringValue();
out.print(tag);
int l = tag.length();
if (l > MAX_INDENT && value != null && value.length() > 0) {
out.println();
l = 0;
}
for (int x = l; x < indent; x++)
out.print(' ');
out.print(' ');
out.println(value == null ? "" : value);
}
}
/**
* Write a log of the questions that determine the current configuration.
* @param logFile the file to which to write the log
* @throws IOException if there is a problem while writing the log file
*/
public void writeLog(File logFile) throws IOException {
WizPrint wp = new WizPrint(interview);
wp.setShowResponses(true);
wp.setShowResponseTypes(false);
wp.setShowTags(true);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile), StandardCharsets.UTF_8));
wp.write(out);
}
/**
* Apply a series of edits to the current configuration.
* @param cmds the editing commands to be applied
* @throws EditJTI.Fault if there is a problem while applying the edit commands.
* @see #edit(String)
*/
public void edit(String[] cmds) throws Fault {
for (int i = 0; i < cmds.length; i++) {
edit(cmds[i]);
}
}
/**
* Apply an edit to the current configuration.
* @param cmd the editing command to be applied
* Currently, two forms of command are supported: