/* * Created on Dec 15, 2005 * * Copyright 2005-2010 Ignis Software Tools Ltd. All rights reserved. */ package jsystem.framework.scenario; import java.lang.reflect.Method; import java.util.Comparator; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import org.springframework.util.ObjectUtils; import jsystem.extensions.report.html.HtmlCodeWriter; import jsystem.framework.FrameworkOptions; import jsystem.framework.JSystemProperties; import jsystem.framework.common.CommonResources; import jsystem.utils.StringUtils; import jsystem.utils.beans.BeanUtils; /** * POJO which models a test/scenario parameter. Parameters are created by the * RunnerTest object when test class is loaded. * * in TAS 4.9 the dirty flag was added, the purpose of this flag is to help the * system avoid unneeded updates of scenario/tests. when parameters are fetched * from the JTest object, the dirtyFlag is reset. When ever an update is made to * a parameter, if the new value of the parameter is different from the current * value, the dirty flag is raised. When saving parameters the system ignores * Parameters which are not dirty. * * @author guy.arieli, golan.derazon */ public class Parameter { private static Logger log = Logger.getLogger(Parameter.class.getName()); public enum ParameterType { STRING, INT, LONG, BOOLEAN, FLOAT, DOUBLE, SHORT, ENUM, FILE, DATE( "Date/Time"), REFERENCE, STRING_ARRAY, USER_DEFINED, JSYSTEM_INTERNAL_FLAG; private String description; private ParameterType() { description = toString().toLowerCase(); } private ParameterType(String description) { this.description = description; } /** * used for the parameters panel * * @return */ public String getDescription() { return description; } } private boolean dirty = false; /** * Added to support parameters changing in sub-scenarios. Only a parameter * that has a raised save flag will be saved. */ private boolean shouldBeSaved = false; private ParameterType type = ParameterType.STRING; private String originalDescription = null; private String description = null; private String name = null; private String section = null;// "General"; protected Object value = null; private Object defaultValue = null; private boolean asOptions = false; private Method setMethod = null; Method getMethod = null; private Object[] options; private boolean visible = true; private boolean editable = true; private boolean badRefernceParameter = false; private Class<?> paramClass = null; private ParameterProvider provider; /** * added for support for ENUMS with customizable toString method */ private HashMap<String, String> enumStringsAndNames; public String getDescription() { return description; } private boolean isMandatory = false; public void setDescription(String description) { StringBuilder stringBuilder = new StringBuilder(isMandatory ? "Mandatory" : ""); if (!StringUtils.isEmpty(description)) { stringBuilder.append(isMandatory ? " : " : "").append(description); this.originalDescription = description; initSection(); } this.description = stringBuilder.toString(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public ParameterType getType() { return type; } public void setType(ParameterType type) { this.type = type; } public Object getValue() { if (value == null) { return null; } // The param class can be null in primitive parameters. Fixes issue #239 if (null == paramClass && ParametersManager.isReferenceValue(value)) { return value; } // Added as fix for issue #214 if (paramClass != null && !paramClass.isArray() && ParametersManager.isReferenceValue(value)) { return value; } if (getProvider() != null) { if (value instanceof String) { try { return provider.getFromString((String) value); } catch (Exception e) { log.log(Level.WARNING, "Fail to convert to object: " + value, e); } } } return BeanUtils.getMatchingTypeObject(value, type); } /** * sets the value for a parameter * * @param inValue */ public void setValue(Object inValue) throws NumberFormatException { if (isAsOptions() && "false".equals( JSystemProperties.getInstance().getPreference(FrameworkOptions.ADD_DEFAULTS_CURRENT_TO_PARAM)) && !isValueInOptions(inValue) && getOptions() != null && getOptions().length > 0) { inValue = getOptions()[0]; } inValue = normalizeInValue(inValue); // if it is a reference value than it won't be checked for format // compatibility if (!ParametersManager.isReferenceValue(inValue)) { inValue = BeanUtils.getMatchingTypeObject(inValue, type); } // We are ready to check if the value changed if (!type.equals(ParameterType.USER_DEFINED)) { if (ObjectUtils.nullSafeEquals(inValue, value)) { return; } } else { // Comparing parameter from type user defined is a little bit more // complicated. if (isUserDefinedEquals(inValue)) { return; } } // The value was changed. value = inValue; setDirty(); } /** * Checks if parameter from type user defined was changed. <br> * if current and new parameter are null will return true <br> * if one of the parameters is null and the other one is not will return * false<br> * * @param inValue * new value from type parameter provider * @return true, and only if the new value is different from the current * one. */ private boolean isUserDefinedEquals(final Object inValue) { if (!type.equals(ParameterType.USER_DEFINED)) { log.warning("Parameter is not from type user defined"); return false; } if (value == null && inValue == null) { return true; } if ((value == null && inValue != null) || (value != null && inValue == null)) { return false; } if (inValue instanceof String && value instanceof String) { // We need the following regular expression for cleaning the time // stamp from the string presentation of objects. // [A-Z][a-z]{2}\s[A-Z][a-z]{2}\s\d{2}\s\d{2}:\d{2}:\d{2}\s[A-Z]{3}\s\d{4} final String cleanTimestampRegex = "[A-Z][a-z]{2}\\s[A-Z][a-z]{2}\\s\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\s[A-Z]{3}\\s\\d{4}"; if (value.toString().replaceFirst(cleanTimestampRegex, "") .equals(inValue.toString().replaceFirst(cleanTimestampRegex, ""))) { // The parameter is from type parameter provider and the value // was not changed. return true; } } return false; } /** * Normalize the parameter we want to set * * @param inValue * @return normalize object. */ private Object normalizeInValue(final Object inValue) { Object normalizeValue = inValue; switch (type) { case DATE: case FILE: if (StringUtils.isEmpty(inValue + "")) { normalizeValue = null; } break; case STRING_ARRAY: if (StringUtils.isEmpty(inValue + "")) { normalizeValue = null; } else if ((inValue instanceof String[])) { if (!isAsOptions()) { normalizeValue = StringUtils.objectArrayToString(CommonResources.DELIMITER, (Object[]) inValue); } } break; case STRING: if (inValue != null) { if (defaultValue == null && inValue.toString().isEmpty()) { normalizeValue = null; } } break; case USER_DEFINED: if (StringUtils.isEmpty(inValue + "")) { normalizeValue = null; } break; case BOOLEAN: if (null == inValue) { normalizeValue = "false"; } break; default: break; } return normalizeValue; } public Object getDefaultValue() { return defaultValue; } public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; } public boolean isAsOptions() { return asOptions; } public void setAsOptions(boolean asOptions) { this.asOptions = asOptions; } public Object[] getOptions() { // if parameter not set to disable then perform the option add if (!"false".equals( JSystemProperties.getInstance().getPreference(FrameworkOptions.ADD_DEFAULTS_CURRENT_TO_PARAM))) { /** * if the defaultValue is not part of the options add it to the * options. */ if (!isDefaultValueInOptions()) { addValueToOptions(defaultValue); } /** * if the value is not part of the options add it to the options. */ if (!isCurrentValueInOptions()) { addValueToOptions(value); } } return options; } private void addValueToOptions(Object value) { if (value == null) { return; } Object[] toAdd = null; if (value.getClass().isArray()) { toAdd = (Object[]) value; } else { toAdd = new Object[] { value }; } Object[] newOptions = new Object[options.length + toAdd.length]; System.arraycopy(toAdd, 0, newOptions, 0, toAdd.length); System.arraycopy(options, 0, newOptions, toAdd.length, options.length); options = newOptions; } /** * Check if the default value is part of the options * * @return true if null or found in options */ private boolean isDefaultValueInOptions() { return isValueInOptions(defaultValue); } /** * Check if the default value is part of the options * * @return true if null or found in options */ private boolean isCurrentValueInOptions() { return isValueInOptions(value); } /** * Check if the _value is part of the options. If _value is array an array * verifies that all array elements are in options. */ private boolean isValueInOptions(Object _value) { if (options == null || _value == null) { return true; } Object[] objArr = null; if (_value.getClass().isArray()) { objArr = (Object[]) _value; } else { objArr = new Object[] { _value }; } for (Object val : objArr) { boolean found = false; for (Object op : options) { if (val.equals(op)) { found = true; } } if (!found) { return false; } } return true; } public void setOptions(Object o) { if (o instanceof Object[]) { this.options = (Object[]) o; } else if (o instanceof int[]) { int[] d = (int[]) o; Integer[] dd = new Integer[d.length]; for (int i = 0; i < d.length; i++) { dd[i] = Integer.valueOf(d[i]); } options = dd; } else if (o instanceof long[]) { long[] d = (long[]) o; Long[] dd = new Long[d.length]; for (int i = 0; i < d.length; i++) { dd[i] = Long.valueOf(d[i]); } options = dd; } else if (o instanceof float[]) { float[] d = (float[]) o; Float[] dd = new Float[d.length]; for (int i = 0; i < d.length; i++) { dd[i] = Float.valueOf(d[i]); } options = dd; } else if (o instanceof double[]) { double[] d = (double[]) o; Double[] dd = new Double[d.length]; for (int i = 0; i < d.length; i++) { dd[i] = Double.valueOf(d[i]); } options = dd; } else if (o instanceof short[]) { short[] d = (short[]) o; Short[] dd = new Short[d.length]; for (int i = 0; i < d.length; i++) { dd[i] = Short.valueOf(d[i]); } options = dd; } } public String getParamTypeString() { return type.getDescription(); } public Method getSetMethod() { return setMethod; } public Method getGetMethod() { return getMethod; } public void setSetMethod(Method setMethod) { this.setMethod = setMethod; } public void setGetMethod(Method getMethod) { this.getMethod = getMethod; } public String getSection() { if (StringUtils.isEmpty(section)) { return "General"; } return section; } public void setSection(String section) { this.section = section; } public void initSection() { // TODO: do we need to add getMethod here ? if (setMethod == null) { return; } String section = HtmlCodeWriter.getInstance().getMethodAnnotation(setMethod.getDeclaringClass().getName(), setMethod.getName(), "section"); setSection(section); } @Override public String toString() { return "Parametr Name : " + name + "\nParametr section : " + section + "\nParametr value : " + value + "\nClass: " + paramClass; } /** * Check if the value was changed from the default value * * @return the change value status */ public boolean isChanged() { if (defaultValue == null && value == null) { // both null = no change return false; } else if (defaultValue == null && value.toString().isEmpty()) { return false; } else { if (defaultValue != null && value != null) { return !defaultValue.toString().equals(value.toString()); } else { // one is null and the other is not = changed return true; } } } /** * Compartor for comparing between Parameters, created for Arrays.sort. */ public static Comparator<Parameter> ParameterNameComparator = new Comparator<Parameter>() { @Override public int compare(Parameter parameter, Parameter anotherParameter) { String paramName1 = parameter.getName(); String paramName2 = anotherParameter.getName(); return paramName1.compareTo(paramName2); } }; public boolean isEditable() { return editable; } public void setEditable(boolean editable) { this.editable = editable; } public boolean isVisible() { return visible; } public void setVisible(boolean visible) { this.visible = visible; } /** * @return True if parameter value was changed and not saved to FileSystem * yet */ public boolean isDirty() { return dirty; } public void resetDirty() { dirty = false; } /** * Signal a parameter was changed and save is needed to FileSystem */ public void setDirty() { dirty = true; } public Parameter cloneParameter() { Parameter param; try { param = (Parameter) getClass().newInstance(); } catch (Exception e) { param = new Parameter(); } param.dirty = dirty; param.type = type; param.originalDescription = originalDescription; param.description = description; param.isMandatory = isMandatory; param.name = name; param.section = section; param.value = value; param.defaultValue = defaultValue; param.asOptions = asOptions; param.setMethod = setMethod; param.getMethod = getMethod; param.provider = provider; param.paramClass = paramClass; if (options != null) { param.options = new Object[options.length]; System.arraycopy(options, 0, param.options, 0, options.length); } param.visible = visible; param.editable = editable; return param; } public String getStringValue() { if (getValue() == null) { return null; } return getProvider() != null ? getProvider().getAsString(getValue()) : getValue().toString(); } /** * checks if the parameter value is a reference * * @return */ public boolean isReferenceParam() { return ParametersManager.isReferenceValue(value); } /** * if True then the reference threw an exception */ public boolean isBadRefernceParameter() { return badRefernceParameter; } public void setBadRefernceParameter(boolean badRefernceParameter) { this.badRefernceParameter = badRefernceParameter; } /** * convert enum String to enum Name (using the HashMap) (Used only for enum * type) * * @return the enum matching name */ public String getEnumValueAsName() { if (getType().equals(ParameterType.ENUM) && enumStringsAndNames != null) { String tmp = enumStringsAndNames.get(value); // replace toString // value with name if (tmp != null) { return tmp; } } return value.toString(); } public void setEnumStringsAndNames(HashMap<String, String> enumStringsAndNames) { this.enumStringsAndNames = enumStringsAndNames; } public ParameterProvider getProvider() { return provider; } public void setProvider(ParameterProvider provider) { this.provider = provider; } public Class<?> getParamClass() { return paramClass; } public void setParamClass(Class<?> paramClass) { this.paramClass = paramClass; } public boolean isMandatory() { return isMandatory; } public void setMandatory(boolean isMandatory) { this.isMandatory = isMandatory; } /** * Check if this parameter needs saving * * @return True if this parameter should be saved */ public boolean shouldBeSaved() { return shouldBeSaved; } /** * Signal this parameter should not be saved */ public void signalNotToSave() { this.shouldBeSaved = false; } /** * Raise the save flag to save parameter on next save event */ public void signalToSave() { this.shouldBeSaved = true; } }