/* * 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; } }