/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2008-2009 Sun Microsystems, Inc.
*/
package com.sun.opends.sdk.tools;
import static com.sun.opends.sdk.messages.Messages.*;
import static com.sun.opends.sdk.tools.Utils.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.opends.sdk.LocalizableMessage;
/**
* This class provides an abstract base class which can be used as the
* basis of a console-based application.
*/
abstract class ConsoleApplication
{
private static final class NullOutputStream extends OutputStream
{
/**
* The singleton instance for this class.
*/
private static final NullOutputStream instance = new NullOutputStream();
/**
* The singleton print stream tied to the null output stream.
*/
private static final PrintStream printStream = new PrintStream(
instance);
/**
* Retrieves a print stream using this null output stream.
*
* @return A print stream using this null output stream.
*/
static PrintStream printStream()
{
return printStream;
}
/**
* Creates a new instance of this null output stream.
*/
private NullOutputStream()
{
// No implementation is required.
}
/**
* Closes the output stream. This has no effect.
*/
public void close()
{
// No implementation is required.
}
/**
* Flushes the output stream. This has no effect.
*/
public void flush()
{
// No implementation is required.
}
/**
* Writes the provided data to this output stream. This has no
* effect.
*
* @param b
* The byte array containing the data to be written.
*/
public void write(byte[] b)
{
// No implementation is required.
}
/**
* Writes the provided data to this output stream. This has no
* effect.
*
* @param b
* The byte array containing the data to be written.
* @param off
* The offset at which the real data begins.
* @param len
* The number of bytes to be written.
*/
public void write(byte[] b, int off, int len)
{
// No implementation is required.
}
/**
* Writes the provided byte to this output stream. This has no
* effect.
*
* @param b
* The byte to be written.
*/
public void write(int b)
{
// No implementation is required.
}
}
/**
* A null reader.
*/
private static final class NullReader extends Reader
{
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException
{
// Do nothing.
}
/**
* {@inheritDoc}
*/
@Override
public int read(char[] cbuf, int off, int len) throws IOException
{
return -1;
}
}
// The error stream which this application should use.
private final PrintStream err;
// The input stream reader which this application should use.
private final BufferedReader reader;
private final InputStream in;
// The output stream which this application should use.
private final PrintStream out;
/**
* Creates a new console application instance.
*
* @param in
* The application input stream.
* @param out
* The application output stream.
* @param err
* The application error stream.
*/
ConsoleApplication(InputStream in, OutputStream out, OutputStream err)
{
this.in = in;
if (in != null)
{
this.reader = new BufferedReader(new InputStreamReader(in));
}
else
{
this.reader = new BufferedReader(new NullReader());
}
if (out != null)
{
this.out = new PrintStream(out);
}
else
{
this.out = NullOutputStream.printStream();
}
if (err != null)
{
this.err = new PrintStream(err);
}
else
{
this.err = NullOutputStream.printStream();
}
}
/**
* Gets the application error stream.
*
* @return Returns the application error stream.
*/
final PrintStream getErrorStream()
{
return err;
}
/**
* Gets the application input stream reader.
*
* @return Returns the application input stream.
*/
final BufferedReader getInputReader()
{
return reader;
}
/**
* Gets the application input stream.
*
* @return Returns the application input stream.
*/
final InputStream getInputStream()
{
return in;
}
/**
* Gets the application output stream.
*
* @return Returns the application output stream.
*/
final PrintStream getOutputStream()
{
return out;
}
/**
* Indicates whether or not the user has requested advanced mode.
*
* @return Returns true if the user has requested
* advanced mode.
*/
abstract boolean isAdvancedMode();
/**
* Indicates whether or not the user has requested interactive
* behavior.
*
* @return Returns true if the user has requested
* interactive behavior.
*/
abstract boolean isInteractive();
/**
* Indicates whether or not this console application is running in its
* menu-driven mode. This can be used to dictate whether output should
* go to the error stream or not. In addition, it may also dictate
* whether or not sub-menus should display a cancel option as well as
* a quit option.
*
* @return Returns true if this console application is
* running in its menu-driven mode.
*/
abstract boolean isMenuDrivenMode();
/**
* Indicates whether or not the user has requested quiet output.
*
* @return Returns true if the user has requested quiet
* output.
*/
abstract boolean isQuiet();
/**
* Indicates whether or not the user has requested script-friendly
* output.
*
* @return Returns true if the user has requested
* script-friendly output.
*/
abstract boolean isScriptFriendly();
/**
* Indicates whether or not the user has requested verbose output.
*
* @return Returns true if the user has requested verbose
* output.
*/
abstract boolean isVerbose();
/**
* Interactively prompts the user to press return to continue. This
* method should be called in situations where a user needs to be
* given a chance to read some documentation before continuing
* (continuing may cause the documentation to be scrolled out of
* view).
*/
final void pressReturnToContinue()
{
LocalizableMessage msg = INFO_MENU_PROMPT_RETURN_TO_CONTINUE.get();
try
{
readLineOfInput(msg);
}
catch (CLIException e)
{
// Ignore the exception - applications don't care.
}
}
/**
* Displays a blank line to the error stream.
*/
final void println()
{
err.println();
}
/**
* Displays a message to the error stream.
*
* @param msg
* The message.
*/
final void println(LocalizableMessage msg)
{
err.println(wrapText(msg, MAX_LINE_WIDTH));
}
/**
* Displays a message to the error stream.
*
* @param msg
* The message.
*/
final void print(LocalizableMessage msg)
{
err.print(wrapText(msg, MAX_LINE_WIDTH));
}
/**
* Displays a blank line to the output stream if we are not in quiet
* mode.
*/
final void printlnProgress()
{
if (!isQuiet())
{
out.println();
}
}
/**
* Displays a message to the output stream if we are not in quiet
* mode.
*
* @param msg
* The message.
*/
final void printProgress(LocalizableMessage msg)
{
if (!isQuiet())
{
out.print(msg);
}
}
/**
* Displays a message to the error stream indented by the specified
* number of columns.
*
* @param msg
* The message.
* @param indent
* The number of columns to indent.
*/
final void println(LocalizableMessage msg, int indent)
{
err.println(wrapText(msg, MAX_LINE_WIDTH, indent));
}
/**
* Displays a message to the error stream if verbose mode is enabled.
*
* @param msg
* The verbose message.
*/
final void printVerboseMessage(LocalizableMessage msg)
{
if (isVerbose() || isInteractive())
{
err.println(wrapText(msg, MAX_LINE_WIDTH));
}
}
/**
* Interactively retrieves a line of input from the console.
*
* @param prompt
* The prompt.
* @return Returns the line of input, or null if the end
* of input has been reached.
* @throws CLIException
* If the line of input could not be retrieved for some
* reason.
*/
final String readLineOfInput(LocalizableMessage prompt) throws CLIException
{
if (prompt != null)
{
err.print(wrapText(prompt, MAX_LINE_WIDTH));
err.print(" ");
}
try
{
String s = reader.readLine();
if (s == null)
{
throw CLIException.adaptInputException(new EOFException(
"End of input"));
}
else
{
return s;
}
}
catch (IOException e)
{
throw CLIException.adaptInputException(e);
}
}
/**
* Commodity method that interactively prompts (on error output) the
* user to provide a string value. Any non-empty string will be
* allowed (the empty string will indicate that the default should be
* used, if there is one).
*
* @param prompt
* The prompt to present to the user.
* @param defaultValue
* The default value to assume if the user presses ENTER
* without typing anything, or null if there
* should not be a default and the user must explicitly
* provide a value.
* @throws CLIException
* If the line of input could not be retrieved for some
* reason.
* @return The string value read from the user.
*/
final String readInput(LocalizableMessage prompt, String defaultValue)
throws CLIException
{
while (true)
{
if (defaultValue != null)
{
prompt = INFO_PROMPT_SINGLE_DEFAULT.get(prompt.toString(),
defaultValue);
}
String response = readLineOfInput(prompt);
if ("".equals(response))
{
if (defaultValue == null)
{
print(INFO_ERROR_EMPTY_RESPONSE.get());
}
else
{
return defaultValue;
}
}
else
{
return response;
}
}
}
/**
* Commodity method that interactively prompts (on error output) the
* user to provide a string value. Any non-empty string will be
* allowed (the empty string will indicate that the default should be
* used, if there is one). If an error occurs a message will be logged
* to the provided logger.
*
* @param prompt
* The prompt to present to the user.
* @param defaultValue
* The default value to assume if the user presses ENTER
* without typing anything, or null if there
* should not be a default and the user must explicitly
* provide a value.
* @param logger
* the Logger to be used to log the error message.
* @return The string value read from the user.
*/
final String readInput(LocalizableMessage prompt, String defaultValue,
Logger logger)
{
String s = defaultValue;
try
{
s = readInput(prompt, defaultValue);
}
catch (CLIException ce)
{
logger.log(Level.WARNING, "Error reading input: " + ce, ce);
}
return s;
}
}