/*
* 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 may have multiple concurrent values.
* The HTML input form will be a list box that allows multiple values to be
* selected at the same time. The internal representation of the value of this
* parameter will be an integer, with the individual values each associated with
* an increasing power of 2 (the first item will be associated with a value of
* 1, the second with a value of 2, the third with 4, the fourth with 8, etc.).
* Anything that uses this parameter type must maintain that mapping externally
* because the value strings are not included in the encoded version of this
* parameter that is sent between the client and server.
*
*
* @author Neil A. Wilson
*/
public class MultiValuedParameter
extends Parameter
{
// The numeric value associated with this parameter.
private int intValue;
// The number of rows to display at any given time.
private int visibleRows = 10;
// The set of potential values that this parameter may have.
private String[] valueStrings;
/**
* 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 MultiValuedParameter()
{
super();
// Create an empty set of values.
valueStrings = new String[0];
}
/**
* Creates a new multivalued parameter with the specified name and set of
* potential values. The display name will be the same as the name, it will
* not have a description, no values will be selected, and it will not be
* required.
*
* @param name The name to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
*/
public MultiValuedParameter(String name, String[] valueStrings)
{
this(name, name, null, valueStrings, 0, false);
}
/**
* Creates a new multivalued parameter with the specified name, set of
* potential values, and required/optional indicator. The display name will
* be the same as the name, it will not have a description, and there will
* not be any values selected.
*
* @param name The name to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
* @param isRequired Indicates whether this parameter is required to
* have a value.
*/
public MultiValuedParameter(String name, String[] valueStrings,
boolean isRequired)
{
this(name, name, null, valueStrings, 0, isRequired);
}
/**
* Creates a new multivalued parameter with the specified name, display name,
* and set of potential values. It will not have a description, there will
* not be any values selected, and it will not be required.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
*/
public MultiValuedParameter(String name, String displayName,
String[] valueStrings)
{
this(name, displayName, null, valueStrings, 0, false);
}
/**
* Creates a new multivalued parameter with the specified name, display name,
* set of potential values, and required/optional indicator. It will not have
* a description and there will not be any values selected.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
* @param isRequired Indicates whether this parameter is required to
* have a value.
*/
public MultiValuedParameter(String name, String displayName,
String[] valueStrings, boolean isRequired)
{
this(name, displayName, null, valueStrings, 0, isRequired);
}
/**
* Creates a new multivalued parameter with the specified name, display name,
* description, and set of potential values. There will not be any values
* selected and it will not be required.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param description The description to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
*/
public MultiValuedParameter(String name, String displayName,
String description, String[] valueStrings)
{
this(name, displayName, description, valueStrings, 0, false);
}
/**
* Creates a new multivalued parameter with the specified name, display name,
* description, set of potential values, and required/optional indicator.
* There will not be any values selected.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param description The description to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
* @param isRequired Indicates whether this parameter is required to
* have a value.
*/
public MultiValuedParameter(String name, String displayName,
String description, String[] valueStrings,
boolean isRequired)
{
this(name, displayName, description, valueStrings, 0, isRequired);
}
/**
* Creates a new multivalued parameter with the specified name, display name,
* description, set of potential values, and set of selected values. It will
* not be required.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param description The description to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
* @param intValue The numeric representation that indicates which of
* the values are selected.
*/
public MultiValuedParameter(String name, String displayName,
String description, String[] valueStrings,
int intValue)
{
this(name, displayName, description, valueStrings, intValue, false);
}
/**
* Creates a new multivalued parameter with the specified name, display name,
* description, set of potential values, set of selected values, and
* required/optional indicator.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param description The description to use for this parameter.
* @param valueStrings The text associated with the potential values of this
* parameter.
* @param intValue The numeric representation that indicates which of
* the values are selected.
* @param isRequired Indicates whether this parameter is required to
* have a value.
*/
public MultiValuedParameter(String name, String displayName,
String description, String[] valueStrings,
int intValue, boolean isRequired)
{
super(name, displayName, description, isRequired, intValue);
this.valueStrings = valueStrings;
this.intValue = intValue;
}
/**
* Creates a new multivalued parameter with the specified information. This
* version is only intended for internal use, and only for decoding the value
* of the parameter transported between the client and the server. The value
* string provided should be the string representation of the numeric value
* associated with this parameter.
*
* @param name The name to use for this parameter.
* @param displayName The display name to use for this parameter.
* @param description The description to use for this parameter.
* @param valueString The string representation of the numeric value for
* this parameter.
* @param isRequired Indicates whether this parameter is required to
* have a value.
*/
MultiValuedParameter(String name, String displayName, String description,
String valueString, boolean isRequired)
{
super(name, displayName, description, isRequired, null);
int intValue = 0;
try
{
intValue = Integer.parseInt(valueString);
} catch (NumberFormatException nfe) {}
this.intValue = intValue;
this.value = intValue;
this.valueStrings = new String[0];
}
/**
* Retrieves the set of value strings that may be used with this parameter
* (i.e., the potential values).
*
* @return The set of value strings that may be used with this parameter.
*/
public String[] getValueStrings()
{
return valueStrings;
}
/**
* Retrieves the integer value that specifies which of the potential values
* are actually selected.
*
* @return The integer value that specifies which of the potential values are
* actually selected.
*/
public int getIntValue()
{
return intValue;
}
/**
* Indicates whether the option associated with the specified value is
* selected.
*
* @param optionValue The numeric value of the option for which to make the
* determination.
*
* @return <CODE>true</CODE> if the specified value is selected, or
* <CODE>false</CODE> if it is not.
*/
public boolean isSelected(int optionValue)
{
return ((optionValue & intValue) == optionValue);
}
/**
* Specifies the integer value to use for this parameter.
*
* @param intValue The integer value to use for this parameter.
*
* @throws InvalidValueException If there is a problem with the value
* information provided.
*/
public void setValue(int intValue)
throws InvalidValueException
{
setValue(Integer.valueOf(intValue));
}
/**
* Specifies the value to use for this parameter. The value should be a Java
* Integer.
*
* @param value The value to use for this parameter.
*
* @throws InvalidValueException If the value provided is not valid.
*/
@Override()
public void setValue(Object value)
throws InvalidValueException
{
String invalidReason = getInvalidReason(value);
if (invalidReason != null)
{
throw new InvalidValueException(invalidReason);
}
this.value = value;
if (value == null)
{
this.intValue = 0;
}
else if (value instanceof Integer)
{
intValue = ((Integer) value);
}
else if (value instanceof String)
{
intValue = Integer.parseInt((String) value);
}
}
/**
* 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 MultiValuedParameter))
{
MultiValuedParameter mvp = (MultiValuedParameter) parameter;
this.value = mvp.value;
this.intValue = mvp.intValue;
}
}
/**
* Retrieves the maximum number of rows that will be visible when displaying
* the HTML input form.
*
* @return The maximum number of rows that will be visible when displaying
* the HTML input form.
*/
public int getVisibleRows()
{
return visibleRows;
}
/**
* Specifies the maximum number of rows that will be visible when displaying
* the HTML input form.
*
* @param visibleRows The maximum number of rows that will be visible when
* displaying the HTML input form.
*/
public void setVisibleRows(int visibleRows)
{
this.visibleRows = visibleRows;
}
/**
* 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.valueOf(intValue);
}
/**
* 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
{
intValue = Integer.parseInt(valueString);
value = intValue;
}
catch (Exception e)
{
throw new InvalidValueException("Unable to set int value: " + e, e);
}
}
/**
* Retrieves a string that indicates why the provided value is invalid.
*
* @param value The value for which to obtain the invalid reason.
*
* @return The reason that the specified value is invalid, or
* <CODE>null</CODE> if the value is valid.
*/
@Override()
public String getInvalidReason(Object value)
{
if (value == null)
{
if (isRequired)
{
return "No value(s) provided for required parameter";
}
else
{
return null;
}
}
if (value instanceof Integer)
{
int intValue = ((Integer) value);
if (intValue == 0)
{
if (isRequired)
{
return "No value(s) provided for required parameter";
}
else
{
return null;
}
}
if (intValue < 0)
{
return "Integer value may not be negative";
}
if (intValue > getMaxValue())
{
return "Integer value may not be greater than " + getMaxValue() +
" (" + intValue + " was provided)";
}
return null;
}
if (value instanceof String)
{
try
{
int intValue = Integer.parseInt((String) value);
if (intValue == 0)
{
if (isRequired)
{
return "No value(s) provided for required parameter";
}
else
{
return null;
}
}
if (intValue < 0)
{
return "Integer value may not be negative";
}
if (intValue > getMaxValue())
{
return "Integer value may not be greater than " + getMaxValue() +
" (" + intValue + " was provided)";
}
return null;
}
catch (NumberFormatException nfe)
{
return "Value " + String.valueOf(value) +
" cannot be converted to an integer";
}
}
return (value.getClass().getName() + " is not a valid object type for " +
"the set of values for a multivalued parameter");
}
/**
* Indicates whether this parameter has one or more values.
*
* @return <CODE>true</CODE> if this parameter has one or more values, or
* <CODE>false</CODE> if not.
*/
@Override()
public boolean hasValue()
{
return (intValue > 0);
}
/**
* Retrieves a string representation of the value of this parameter.
*
* @return A string representation of the value of this parameter.
*/
@Override()
public String getDisplayValue()
{
if ((valueStrings == null) || (valueStrings.length == 0))
{
return "";
}
else
{
StringBuilder returnBuffer = new StringBuilder();
String eol = "";
for (int i=0; i < valueStrings.length; i++)
{
returnBuffer.append(eol);
if (isSelected(indexToIntValue(i)))
{
returnBuffer.append("X ");
}
else
{
returnBuffer.append(" ");
}
returnBuffer.append(valueStrings[i]);
eol = Constants.EOL;
}
return returnBuffer.toString();
}
}
/**
* Retrieves the value of this parameter in a form that can be displayed in
* an HTML document.
*
* @return The value of this parameter in a form that can be displayed in an
* HTML document.
*/
@Override()
public String getHTMLDisplayValue()
{
if ((valueStrings == null) || (valueStrings.length == 0))
{
return "";
}
else
{
StringBuilder returnBuffer = new StringBuilder();
returnBuffer.append("<TABLE BORDER=\"0\">\n");
returnBuffer.append(" <TR>\n");
returnBuffer.append(" <TD><B>Selected</B></TD>\n");
returnBuffer.append(" <TD><B>Value</B></TD>\n");
returnBuffer.append(" </TR>\n");
for (int i=0; i < valueStrings.length; i++)
{
returnBuffer.append(" <TR>\n");
if (isSelected(indexToIntValue(i)))
{
returnBuffer.append(" <TD>Y</TD>\n");
}
else
{
returnBuffer.append(" <TD>N</TD>\n");
}
returnBuffer.append(" <TD>" + valueStrings[i] + "</TD>\n");
returnBuffer.append(" </TR>\n");
}
returnBuffer.append("</TABLE>\n");
return returnBuffer.toString();
}
}
/**
* Retrieves an HTML string that can be used to specify the value(s) for this
* parameter.
*
* @param prefix The prefix that should be placed in front of the parameter
* name as the name of the form element.
*
* @return An HTML string that can be used to specify the value(s) for this
* parameter.
*/
@Override()
public String getHTMLInputForm(String prefix)
{
String returnStr = "<SELECT MULTIPLE NAME=\"" + prefix + name +
"\" SIZE=\"" +
Math.min(visibleRows, valueStrings.length) + "\">\n";
for (int i=0; i < valueStrings.length; i++)
{
String selectedStr = (isSelected(indexToIntValue(i))) ? "SELECTED " : "";
returnStr += " <OPTION " + selectedStr + "VALUE=\"" + valueStrings[i] +
"\">" + valueStrings[i] + '\n';
}
returnStr += "</SELECT>\n";
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
{
int returnValue = 0;
if (values == null)
{
setValue(0);
return;
}
for (int i=0; i < valueStrings.length; i++)
{
boolean match = false;
for (int j=0; j < values.length; j++)
{
if (values[j].equalsIgnoreCase(valueStrings[i]))
{
match = true;
break;
}
}
if (match)
{
returnValue += indexToIntValue(i);
}
}
setValue(returnValue);
}
/**
* 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 ((valueStrings == null) || (valueStrings.length == 0) || (intValue <= 0))
{
return "";
}
StringBuilder buffer = new StringBuilder();
for (int i=0; i < valueStrings.length; i++)
{
int twoToTheI = 1;
for (int j=0; j < i; j++)
{
twoToTheI *= 2;
}
if ((twoToTheI & intValue) == twoToTheI)
{
buffer.append("<INPUT TYPE=\"HIDDEN\" NAME=\"" + prefix + name +
"\" VALUE=\"" + valueStrings[i] + "\">");
}
}
return buffer.toString();
}
/**
* Creates a new multivalued parameter that is a duplicate of this parameter.
*
* @return A new multivalued parameter that is a duplicate of this parameter.
*/
@Override()
public MultiValuedParameter clone()
{
MultiValuedParameter mvp = new MultiValuedParameter(name, displayName,
description,
valueStrings, intValue,
isRequired);
mvp.visibleRows = visibleRows;
mvp.setSensitive(isSensitive());
return mvp;
}
/**
* Provides a mapping between the position of an element in the set of value
* strings to the numeric value associated with the element in that position.
*
* @param valueIndex The position of the item in the set of value strings.
*
* @return The integer value associated with the item in the specified
* position.
*/
public int indexToIntValue(int valueIndex)
{
if (valueIndex < 0)
{
return 0;
}
int returnValue = 1;
for (int i=0; i < valueIndex; i++)
{
returnValue *= 2;
}
return returnValue;
}
/**
* Retrieves the maximum allowed value for this parameter based on the number
* of items in the set of potential values.
*
* @return The maximum allowed value for this parameter.
*/
public int getMaxValue()
{
return indexToIntValue(valueStrings.length) - 1;
}
}