/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.parameter; import com.slamd.common.Constants; /** * This class defines a parameter that consists of two parts -- an integer value * and a string value. The string value will be presented in a drop-down list * form that allows the user to choose an appropriate set of units to use for * the integer value. * * * @author Neil A. Wilson */ public class IntegerWithUnitParameter extends Parameter { /** * The number of columns to display by default if no other value is specified. */ private static final int DEFAULT_VISIBLE_COLUMNS = 10; // Indicates whether a lower bound should be enforced on this parameter private boolean hasLowerBound; // Indicates whether an upper bound should be enforced on this parameter private boolean hasUpperBound; // The value associated with this parameter as a Java integer private int intValue; // The lower bound that will be enforced if hasLowerBound is true private int lowerBound; // The upper bound that will be enforced if hasUpperBound is true private int upperBound; // The number of columns to display in the HTML input form. private int visibleColumns = DEFAULT_VISIBLE_COLUMNS; // The choice that is currently selected. private String selectedChoice; // The set of choices that are available for use with this parameter. private String[] choices; /** * Creates a new instance of the Parameter to be used when decoding values * transported over the network, and should not be used by jobs to create * parameters. If any initialization is needed that will not be covered by * calls to the <CODE>setName</CODE>, <CODE>setDisplayName</CODE>, * <CODE>setDescription</CODE>, or <CODE>setValueFromString</CODE>, then it * should be done here. */ public IntegerWithUnitParameter() { super(); // Indicate that there are no bounds hasLowerBound = false; hasUpperBound = false; } /** * Creates a new integer with unit parameter with the specified name, value * required/optional indicator, and set of choices. The display name will be * the same as the name and it will not have a description. Upper and lower * bounds will not be enforced. * * @param name The name to use for this parameter. * @param value The value to use for this parameter. * @param choices The set of choices for this parameter. */ public IntegerWithUnitParameter(String name, int value, String[] choices) { this(name, name, null, value, choices, choices[0]); } /** * Creates a new integer with unit parameter with the specified name, * display name, value, required/optional indicator, and set of choices. * It will not have a description, and upper and lower bounds will not be * enforced. * * @param name The name to use for this parameter. * @param displayName The name to use for this parameter when it is * displayed to the end user. * @param value The value to use for this parameter. * @param choices The set of choices for this parameter. */ public IntegerWithUnitParameter(String name, String displayName, int value, String[] choices) { this(name, displayName, null, value, choices, choices[0]); } /** * Creates a new integer with unit parameter with the specified name, display * name, description, value, required/optional indicator, and choices. Upper * and lower bounds will not be enforced. * * @param name The name to use for this parameter. * @param displayName The name to use for this parameter when it is * displayed to the end user. * @param description The description to use for this parameter. * @param value The value to use for this parameter. * @param choices The set of choices for this parameter. */ public IntegerWithUnitParameter(String name, String displayName, String description, int value, String[] choices) { this(name, displayName, description, value, choices, choices[0]); } /** * Creates a new integer with unit parameter with the specified name, display * name, description, value, required/optional indicator, and choices. Upper * and lower bounds will not be enforced. This is a version of the * constructor intended for internal use only to allow for either null or * integer values. * * @param name The name to use for this parameter. * @param displayName The name to use for this parameter when it is * displayed to the end user. * @param description The description to use for this parameter. * @param value The value to use for this parameter. * @param choices The set of choices for this parameter. * @param selectedChoice The choice that should be selected by default. */ private IntegerWithUnitParameter(String name, String displayName, String description, int value, String[] choices, String selectedChoice) { super(name, displayName, description, true, value + " " + selectedChoice); this.intValue = value; this.choices = choices; this.selectedChoice = selectedChoice; hasLowerBound = false; lowerBound = 0; hasUpperBound = false; upperBound = 0; } /** * Creates a new integer with unit parameter with the specified name, display * name, description, value, required/optional indicator, and choices as well * as information pertaining to upper and lower bounds. * * @param name The name to use for this parameter. * @param displayName The name to use for this parameter when it is * displayed to the end user. * @param description The description to use for this parameter. * @param value The value to use for this parameter. * @param hasLowerBound Indicates whether the lower bound will be enforced * for this parameter. * @param lowerBound The lower bound to use for this parameter (will * only be effective if hasLowerBound is true). * @param hasUpperBound Indicates whether the upper bound will be enforced * for this parameter. * @param upperBound The upper bound to use for this parameter (will * only be effective if hasUpperBound is true). * @param choices The set of choices for this parameter. * @param selectedChoice The choice that should be selected by default. */ public IntegerWithUnitParameter(String name, String displayName, String description, int value, boolean hasLowerBound, int lowerBound, boolean hasUpperBound, int upperBound, String[] choices, String selectedChoice) { this(name, displayName, description, value, choices, selectedChoice); this.hasLowerBound = hasLowerBound; this.lowerBound = lowerBound; this.hasUpperBound = hasUpperBound; this.upperBound = upperBound; } /** * Retrieves the value associated with this parameter as a Java integer. Note * that you should check to make sure that this parameter actually has a value * before calling this method, because it is possible to have an integer * parameter whose value is not required. If this parameter does not have a * value, then the value returned from this function is unreliable. * * @return The value associated with this parameter as a Java integer. */ public int getIntValue() { return intValue; } /** * Retrieves the value that has been selected from the drop-down list. * * @return The value that has been selected from the drop-down list. */ public String getSelectedChoice() { return selectedChoice; } /** * Specifies the choice that has been selected from the drop-down list. * * @param selectedChoice The choice that has been selected from the * drop-down list. * * @throws InvalidValueException If the specified choice is not a valid * option. */ public void setSelectedChoice(String selectedChoice) throws InvalidValueException { if (choices == null) { this.selectedChoice = selectedChoice; return; } for (int i=0; i < choices.length; i++) { if (selectedChoice.equals(choices[i])) { this.selectedChoice = selectedChoice; return; } } throw new InvalidValueException('"' + selectedChoice + "\" is not a valid choice"); } /** * Retrieves the value associated with this parameter. If it does not have a * value, then <CODE>null</CODE> will be returned. If it does have a value, * then that value will be returned as a Java Integer object. * * @return The value associated with this parameter. */ @Override() public Object getValue() { return value; } /** * Specifies the value to use for this parameter. * * @param value The value to use for this parameter. * @param selectedChoice The selected choice indicating the unit for the * value. * * @throws InvalidValueException If the specified value does not meet the * upper or lower bounds requirements (if they * are to be enforced). */ public void setValue(int value, String selectedChoice) throws InvalidValueException { if (hasUpperBound && (value > upperBound)) { throw new InvalidValueException("Specified value of " + value + " is above upper bound of " + upperBound); } else if (hasLowerBound && (value < lowerBound)) { throw new InvalidValueException("Specified value of " + value + " is below lower bound of " + lowerBound); } else if (choices != null) { boolean choiceOK = false; for (int i=0; i < choices.length; i++) { if (selectedChoice.equals(choices[i])) { choiceOK = true; } } if (! choiceOK) { throw new InvalidValueException("Specified selected choice of " + selectedChoice + " is not a valid option."); } } this.intValue = value; this.selectedChoice = selectedChoice; this.value = intValue + " " + selectedChoice; } /** * Specifies the value to use for this parameter. * * @param value The value to use for this parameter. * * @throws InvalidValueException If the specified value cannot be interpreted * as a Java integer, or if it does not meet * the upper or lower bounds requirements (if * they are to be enforced). */ @Override() public void setValue(Object value) throws InvalidValueException { String invalidReason = getInvalidReason(value); if (invalidReason != null) { throw new InvalidValueException(invalidReason); } if (! (value instanceof String)) { throw new InvalidValueException("Only string values consisting of an " + "integer followed by a space and the " + "selected choice are acceptable."); } try { String stringValue = ((String) value).trim(); int spacePos = stringValue.indexOf(' '); int intValue = Integer.parseInt(stringValue.substring(0, spacePos)); String choice = stringValue.substring(spacePos+1).trim(); setValue(intValue, choice); } catch (InvalidValueException ive) { throw ive; } catch (Exception e) { throw new InvalidValueException("Only string values consisting of an " + "integer followed by a space and the " + "selected choice are acceptable.", e); } } /** * Sets the value for this parameter from the information in the provided * parameter. Note that the provided parameter must be of the same type * as this parameter or no action will be taken. * * @param parameter The parameter from which to take the value for this * parameter. */ @Override() public void setValueFrom(Parameter parameter) { if ((parameter != null) && (parameter instanceof IntegerWithUnitParameter)) { IntegerWithUnitParameter iwp = (IntegerWithUnitParameter) parameter; this.value = iwp.value; this.intValue = iwp.intValue; this.hasLowerBound = iwp.hasLowerBound; this.lowerBound = iwp.lowerBound; this.hasUpperBound = iwp.hasUpperBound; this.upperBound = iwp.upperBound; this.choices = iwp.choices; this.selectedChoice = iwp.selectedChoice; } } /** * Specifies the value to use for this parameter from the provided String. * Note that no validation is performed with this method. * * @param valueString The string representation of the value to use for this * parameter. * * @throws InvalidValueException If the provided value cannot be used to * provide a value for this parameter. */ @Override() public void setValueFromString(String valueString) throws InvalidValueException { try { int spacePos = valueString.indexOf(' '); int intValue = Integer.parseInt(valueString.substring(0, spacePos)); String choice = valueString.substring(spacePos+1).trim(); setValue(intValue, choice); } catch (InvalidValueException ive) { throw ive; } catch (Exception e) { e.printStackTrace(); throw new InvalidValueException("Unable to set value: " + e, e); } } /** * Retrieves a string representation of the value of this parameter. * * @return A string representation of the value of this parameter. */ @Override() public String getValueString() { return (String) value; } /** * Retrieves the lower bound that will be used if a lower bound is to be * enforced. If the lower bound is not to be enforced, then the value * returned from this method will be meaningless. * * @return The lower bound that will be used if a lower bound is to be * enforced. */ public int getLowerBound() { return lowerBound; } /** * Specifies the value to use as the lower bound, and indicates that a lower * bound is to be enforced for this parameter. * * @param lowerBound The value to use as the lower bound. */ public void setLowerBound(int lowerBound) { this.lowerBound = lowerBound; hasLowerBound = true; } /** * Specifies that a lower bound should not be enforced for this parameter. */ public void unsetLowerBound() { hasLowerBound = false; } /** * Indicates whether a lower bound will be enforced for this parameter. * * @return <CODE>true</CODE> if a lower bound will be enforced, or * <CODE>false</CODE> if not. */ public boolean hasLowerBound() { return hasLowerBound; } /** * Retrieves the upper bound that will be used if an upper bound is to be * enforced. If the upper bound is not to be enforced, then the value * returned from this method will be meaningless. * * @return The lower bound that will be used if a lower bound is to be * enforced. */ public int getUpperBound() { return upperBound; } /** * Specifies the value to use as the upper bound, and indicates that an upper * bound is to be enforced for this parameter. * * @param upperBound The value to use as the upper bound. */ public void setUpperBound(int upperBound) { this.upperBound = upperBound; hasUpperBound = true; } /** * Specifies that an upper bound should not be enforced for this parameter. */ public void unsetUpperBound() { hasUpperBound = false; } /** * Indicates whether an upper bound will be enforced for this parameter. * * @return <CODE>true</CODE> if an upper bound will be enforced, or * <CODE>false</CODE> if not. */ public boolean hasUpperBound() { return hasUpperBound; } /** * Retrieves the list of choices available for the unit drop-down list. * * @return The list of choices available for the unit drop-down list. */ public String[] getChoices() { return choices; } /** * Specifies the list of choices available for the unit drop-down list. If * the currently selected choice is not in the new list, then the selected * choice will be changed so that it is the first value of the new list. * * @param choices The list of choices available for the unit drop-down list. */ public void setChoices(String[] choices) { this.choices = choices; for (int i=0; i < choices.length; i++) { if (choices[i].equals(selectedChoice)) { return; } } selectedChoice = choices[0]; } /** * Retrieves the reason that the specified value is not valid. * * @param value The value for which to make the determination. * * @return The reason that the specified value is not valid, or * <CODE>null</CODE> if it is valid. */ @Override() public String getInvalidReason(Object value) { if (value == null) { return "No value specified for required parameter"; } if (! (value instanceof String)) { return "Value must be a string with an integer followed by the " + "selected choice"; } String stringValue = (String) value; int spacePos = stringValue.indexOf(' '); if (spacePos <= 0) { return "Value must be a string with an integer followed by the " + "selected choice"; } try { int intValue = Integer.parseInt(stringValue.substring(0, spacePos)); if (hasUpperBound && (intValue > upperBound)) { return "Integer portion must be below upper bound of " + upperBound; } else if (hasLowerBound && (intValue < lowerBound)) { return "Integer portion must be above lower bound of " + lowerBound; } } catch (NumberFormatException nfe) { return "Value must be a string with an integer followed by the " + "selected choice"; } String selectedChoice = stringValue.substring(spacePos+1); if (choices == null) { return null; } for (int i=0; i < choices.length; i++) { if (selectedChoice.equals(choices[i])) { return null; } } return "Selected choice \"" + selectedChoice + "\" is not a valid option."; } /** * Retrieves a String that can be used when displaying the value of this * parameter to the end user. * * @return A String that can be used when displaying the value of this * parameter to the end user. */ @Override() public String getDisplayValue() { return intValue + " " + selectedChoice; } /** * Retrieves a String that can be used when displaying the value of this * parameter to the end user in the context of an HTML page. * * @return A String that can be used when displaying the value of this * parameter to the end user in the context of an HTML page. */ @Override() public String getHTMLDisplayValue() { return getDisplayValue(); } /** * Retrieves the number of columns that should be visible in the HTML input * form. * * @return The number of columns that should be visible in the HTML input * form. */ public int getVisibleColumns() { return visibleColumns; } /** * Specifies the number of columns that should be visible in the HTML input * form. * * @param visibleColumns The number of columns that should be visible in the * HTML input form. */ public void setVisibleColumns(int visibleColumns) { this.visibleColumns = visibleColumns; } /** * Retrieves a string of text that can be used to request a value for this * parameter using an HTML form. Note that this should just be for the input * field itself and should not use the display name or have any special marker * to indicate whether the value is required or not, as those are to be added * by whatever is generating the HTML page. * * @param prefix The prefix that should be placed in front of the parameter * name as the name of the form element. * * @return A string of text that can be used to request a value for this * parameter using an HTML form. */ @Override() public String getHTMLInputForm(String prefix) { String returnStr = "<INPUT TYPE=\"TEXT\" NAME=\"" + prefix + name + "\" VALUE=\"" + intValue + "\" SIZE=\"" + visibleColumns + "\">" + Constants.EOL; returnStr += "<SELECT NAME=\"" + prefix + name + "\">" + Constants.EOL; for (int i=0; i < choices.length; i++) { if (selectedChoice.equals(choices[i])) { returnStr += " <OPTION SELECTED VALUE=\"" + choices[i] + "\">" + choices[i] + Constants.EOL; } else { returnStr += " <OPTION VALUE=\"" + choices[i] + "\">" + choices[i] + Constants.EOL; } } returnStr += "</SELECT>"; return returnStr; } /** * Specifies the value of this parameter based on the provided text that would * be returned from posting an HTML form. * * @param values The set of values for this parameter contained in the * servlet request. * * @throws InvalidValueException If the specified value is not acceptable * for this parameter. */ @Override() public void htmlInputFormToValue(String[] values) throws InvalidValueException { if (values.length != 2) { throw new InvalidValueException("Both an integer and a string portion " + "are required for the " + displayName + " parameter."); } try { int intValue = Integer.parseInt(values[0]); if (hasUpperBound && (intValue > upperBound)) { throw new InvalidValueException("Integer value must be less than " + "upper bound of " + upperBound + " for parameter " + displayName); } if (hasLowerBound && (intValue < lowerBound)) { throw new InvalidValueException("Integer value must be greater than " + "lower bound of " + lowerBound + " for parameter " + displayName); } String selectedChoice = values[1]; boolean found = false; for (int i=0; i < choices.length; i++) { if (selectedChoice.equals(choices[i])) { found = true; break; } } if (! found) { throw new InvalidValueException("String value \"" + selectedChoice + "\" is not a valid option for " + "parameter " + displayName); } this.intValue = intValue; this.selectedChoice = selectedChoice; this.value = intValue + " " + selectedChoice; } catch (NumberFormatException nfe) { try { int intValue = Integer.parseInt(values[1]); if (hasUpperBound && (intValue > upperBound)) { throw new InvalidValueException("Integer value must be less than " + "upper bound of " + upperBound + " for parameter " + displayName, nfe); } if (hasLowerBound && (intValue < lowerBound)) { throw new InvalidValueException("Integer value must be greater " + "than lower bound of " + lowerBound + " for parameter " + displayName, nfe); } String selectedChoice = values[0]; boolean found = false; for (int i=0; i < choices.length; i++) { if (selectedChoice.equals(choices[i])) { found = true; break; } } if (! found) { throw new InvalidValueException("String value \"" + selectedChoice + "\" is not a valid option for " + "parameter " + displayName); } this.intValue = intValue; this.selectedChoice = selectedChoice; this.value = intValue + " " + selectedChoice; } catch (NumberFormatException nfe2) { throw new InvalidValueException("Unable to parse either \"" + values[0] + "\" or \"" + values[1] + "\" as an integer for parameter " + displayName, nfe2); } } } /** * Retrieves a string representation of the content that should be included in * an HTML form in which this parameter should be provided as a hidden * element. * * @param prefix The prefix to use for the parameter name. * * @return A string representation of this parameter as a hidden element in * an HTML form. */ @Override() public String generateHidden(String prefix) { if (value == null) { return ""; } return "<INPUT TYPE=\"HIDDEN\" NAME=\"" + prefix + name + "\" VALUE=\"" + intValue + "\"><INPUT TYPE=\"HIDDEN\" NAME=\"" + prefix + name + "\" VALUE=\"" + selectedChoice + "\">"; } /** * Creates a clone of this parameter. * * @return A clone of this parameter. */ @Override() public IntegerWithUnitParameter clone() { IntegerWithUnitParameter iwp = new IntegerWithUnitParameter(name, displayName, description, intValue, hasLowerBound, lowerBound, hasUpperBound, upperBound, choices, selectedChoice); iwp.visibleColumns = visibleColumns; iwp.setSensitive(isSensitive()); return iwp; } }