/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. This program 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 for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.util.config;
import java.io.EOFException;
import java.io.IOException;
import java.util.List;
import java.util.Set;
public class InteractiveResponseBuilder implements ResponseBuilder {
private final InteractiveResponseBuilder_IOHandler inout;
public InteractiveResponseBuilder(InteractiveResponseBuilder_IOHandler io)
{
this.inout = io;
}
public String handleInput(String s) throws IOException, EOFException{
return inout.handleInput(s);
}
public String handleHiddenInput(String s) throws IOException, EOFException{
return inout.handleHiddenInput(s);
}
public ConfigResponse processConfigSchema(ConfigSchema schema)
throws EncodingException, IOException, InvalidOptionException,
EarlyExitException
{
return this.processConfigSchema(schema, new ConfigResponse());
}
/**
* Process a configuration schema.
* @param schema The schema to process.
* @return The filled-out ConfigResponse.
*/
public ConfigResponse processConfigSchema(ConfigSchema schema,
ConfigResponse defaults)
throws IOException, InvalidOptionException, EarlyExitException
{
List options = schema.getOptions();
int i, counter, nOptions = options.size();
ConfigResponse res;
Set defaultKeys;
defaultKeys = defaults.getKeys();
res = new ConfigResponse(schema);
i = 0;
counter = 100;
while(i < nOptions){
ConfigOption opt = (ConfigOption)options.get(i);
String val, inputStr, def;
boolean isSecret;
def = defaultKeys.contains(opt.getName()) ?
defaults.getValue(opt.getName()) : opt.getDefault();
/* If the previous option used is now bogus, use the options
real default value */
if(def != null){
try {
opt.checkOptionIsValid(def);
} catch(InvalidOptionValueException exc){
def = opt.getDefault();
}
}
isSecret = false;
if(opt instanceof StringConfigOption){
if (((StringConfigOption)opt).isHidden()) {
try {
res.setValue(opt.getName(), opt.getDefault());
} catch(InvalidOptionException exc){
// should never happen
sendToErrStream("Error setting hidden value: " + exc);
throw new IllegalStateException("Error setting hidden "
+" value, cannot "
+ "continue: " + exc);
} catch(InvalidOptionValueException exc){
// should never happen
sendToErrStream("Error setting hidden value: " + exc);
throw new IllegalStateException("Error setting hidden "
+" value, cannot "
+ "continue: " + exc);
}
i++;
continue;
}
isSecret = ((StringConfigOption)opt).isSecret();
}
if(isSecret){
inputStr = getInputString(opt, def == null ? null : "*hidden*");
val = handleHiddenInput(inputStr + ": ");
} else {
inputStr = getInputString(opt, def);
val = handleInput(inputStr + ": ");
}
// Normalize backend input results
if(val == null || "".equals(val)){
if (def == null) {
if (opt.isOptional())
i++;
// If there is no default, ask the question again
/*
* Added by Jim 04/07/2009
* Put an upper limit on the amount of times
* we allow for looping here.
* TCSRV-233
*/
if (--counter <= 0) {
throw new IOException("Prevented runaway input looping.");
}
continue;
}
else {
val = def;
}
} else {
/* If they entered an actual value, and it was a secret
input, ask for it again, just to make sure */
if (isSecret) {
String verifyVal;
verifyVal = handleHiddenInput("(again): ");
if(!verifyVal.equals(val)){
sendToErrStream("Values do not match");
continue;
}
}
val = val.trim();
if(opt instanceof EnumerationConfigOption){
int index = -1;
List values;
try {
index = Integer.parseInt(val) - 1;
} catch(NumberFormatException exc){
sendToErrStream("Value must be an integer");
continue;
}
values = ((EnumerationConfigOption) opt).getValues();
if(index < 0 || index >= values.size()){
sendToErrStream("Value not in range");
continue;
}
val = values.get(index).toString();
} else if(opt instanceof InstallConfigOption) {
int index = -1;
try {
index = Integer.parseInt(val) - 1;
} catch(NumberFormatException exc){
sendToErrStream("Value must be an integer");
continue;
}
List values = ((InstallConfigOption) opt).getValues();
if(index < 0 || index >= values.size()){
sendToErrStream("Value not in range");
continue;
}
val = ((ConfigOptionDisplay) values.get(index)).getName();
}
if (val.equals(opt.getConfirm())) {
// sendToErrStream(val + " compare with " + opt.getConfirm() + " "
// + val.equals(opt.getConfirm()));
// Double check with user
YesNoConfigOption confirmOpt =
new YesNoConfigOption("confirmation",
"Are you sure (" + val + ")?",
YesNoConfigOption.YES);
String confirm =
handleInput(getInputString(confirmOpt,
confirmOpt.getDefault()) +
": ");
if (!"1".equals(confirm))
continue;
}
}
counter = 100;
try {
res.setValue(opt.getName(), val);
} catch (EarlyExitException e) {
throw e;
} catch(InvalidOptionException exc){
// Give the user a chance to reenter something valid
sendToErrStream("Invalid option, '" + opt.getName() + "'");
continue;
} catch(InvalidOptionValueException exc){
sendToErrStream(exc.getMessage());
continue;
}
i++;
}
return res;
}
public void sendToErrStream ( String msg ) {
inout.errOutput(msg);
}
/**
* Assemble the string that will ask the user for an option.
* @param opt The ConfigOption to generate an input string for.
* @return The String to use when asking the user for the
* value of an option.
*/
private String getInputString ( ConfigOption opt, String defaultValue ) {
StringBuilder result = new StringBuilder();
String desc;
if(inout.isDeveloper()) {
result.append("(").append(opt.getName()).append(") ");
}
desc = opt.getDescription();
// Treat these special, because we want to display the list
// of valid options to the user.
if ( opt instanceof EnumerationConfigOption ) {
result.append("Choices:");
List enumValues = ((EnumerationConfigOption) opt).getValues();
String enumValue;
int defaultIndex = -1;
for ( int i=0; i<enumValues.size(); i++ ) {
enumValue = enumValues.get(i).toString();
result.append("\n\t").append(i+1).append(": ").append(enumValue);
if ( enumValue.equals(defaultValue) ) defaultIndex = i;
}
if ( defaultIndex != -1 ) {
result.append("\n").append(desc).append(" [default '" ).append(defaultIndex+1).append("']");
} else {
result.append("\n").append(desc);
}
} else if ( opt instanceof InstallConfigOption ) {
result.append("Choices:");
List displayValues = ((InstallConfigOption) opt).getValues();
int defaultIndex = -1;
for ( int x = 0; x < displayValues.size(); x++ ) {
String name = ((ConfigOptionDisplay) displayValues.get(x)).getName();
String description = ((ConfigOptionDisplay) displayValues.get(x)).getDescription();
String note = ((ConfigOptionDisplay) displayValues.get(x)).getNote();
result.append("\n\t").append(x + 1).append(": ").append(name);
if ( name.equals(defaultValue) ) {
defaultIndex = x;
}
// ...display the description (the formatting is determined when the value is set)
// and should be a concern here...
if ( description != null && description.length() > 0) {
result.append(description);
}
// ...display the note (again, the formatting is determined when the value is set)...
if ( note != null && note.length() > 0) {
result.append(note);
}
}
if ( defaultIndex != -1 ) {
result.append("\n").append(desc).append(" [default '").append(defaultIndex + 1).append("']");
} else {
result.append("\n").append(desc);
}
} else {
result.append(desc);
if( defaultValue != null) {
result.append(" [default '").append(defaultValue).append("']");
}
}
return result.toString();
}
}