ingo@634: package de.intevation.gnv.state; ingo@634: sascha@652: import de.intevation.artifactdatabase.XMLUtils; sascha@652: ingo@785: import de.intevation.artifacts.CallContext; ingo@634: import de.intevation.artifacts.CallMeta; ingo@634: ingo@634: import de.intevation.gnv.artifacts.ressource.RessourceFactory; sascha@652: ingo@634: import de.intevation.gnv.geobackend.base.Result; ingo@634: import de.intevation.gnv.geobackend.base.ResultDescriptor; sascha@652: ingo@634: import de.intevation.gnv.state.describedata.ExtendedKeyValueData; ingo@634: import de.intevation.gnv.state.describedata.KeyValueDescibeData; ingo@634: import de.intevation.gnv.state.describedata.NamedArrayList; ingo@634: import de.intevation.gnv.state.describedata.NamedCollection; ingo@634: ingo@785: import de.intevation.gnv.state.exception.StateException; ingo@785: ingo@785: import de.intevation.gnv.utils.InputValidator; ingo@785: ingo@634: import java.util.ArrayList; ingo@634: import java.util.Arrays; ingo@634: import java.util.Collection; ingo@634: import java.util.Iterator; ingo@634: import java.util.List; ingo@785: import java.util.Locale; ingo@1030: import java.util.TreeMap; ingo@634: ingo@634: import org.apache.log4j.Logger; ingo@634: ingo@634: import org.w3c.dom.Document; ingo@634: import org.w3c.dom.Element; ingo@634: import org.w3c.dom.Node; ingo@634: ingo@634: /** ingo@796: * This state handles input of measurements relating to a parameter. The user ingo@796: * interface description created by this class represents a matrix - each ingo@796: * parameter in a single row, each measurement in a column. An invalid ingo@796: * measurement column for a specific parameter is marked as disabled and should ingo@796: * not be selected. sascha@803: * sascha@780: * @author Ingo Weinzierl ingo@634: */ ingo@634: public class MeasurementState ingo@634: extends DefaultState ingo@634: { ingo@634: private static Logger logger = Logger.getLogger(MeasurementState.class); ingo@634: ingo@634: public static final String SQL_KEY_PARAMETERID = "PARAMETERID"; ingo@634: ingo@785: public static final String SEPARATOR = ";"; ingo@785: ingo@634: ingo@796: /** ingo@796: * This class is used to generate the Matrix in MinMaxDateState. ingo@796: * Parameter and Measurements are stored in separate lists and can be ingo@796: * requested via different methods. ingo@796: * ingo@796: * @author Ingo Weinzierl ingo@796: */ ingo@634: private class ParameterMatrix { ingo@634: private final Logger logger = ingo@634: Logger.getLogger(ParameterMatrix.class); ingo@634: ingo@634: private List measurements; ingo@634: private List mDescriptions; ingo@634: private List parameters; ingo@634: private boolean[][] values; ingo@634: ingo@796: /** ingo@796: * Constructs a new matrix. ingo@796: * ingo@796: * @param data A collection containing the measurements. ingo@796: * @param parameter An array of parameters. ingo@796: */ ingo@634: public ParameterMatrix(Collection data, String[] parameter) { ingo@634: measurements = new ArrayList(data.size()); ingo@634: mDescriptions = new ArrayList(data.size()); ingo@634: parameters = new ArrayList(parameter.length); ingo@634: ingo@634: values = new boolean[data.size()][parameter.length]; ingo@634: for (int i = 0; i < data.size(); i++) { ingo@634: Arrays.fill(values[i], false); ingo@634: } ingo@634: ingo@634: initParameters(parameter); ingo@634: initMeasurements(data); ingo@634: } ingo@634: ingo@796: /** ingo@796: * Initialize the measurements used in this matrix. sascha@803: * ingo@796: * @param data The measurements. ingo@796: */ ingo@634: private void initMeasurements(Collection data) { ingo@634: Iterator iter = data.iterator(); ingo@634: while (iter.hasNext()) { ingo@634: ExtendedKeyValueData value = (ExtendedKeyValueData) iter.next(); ingo@634: String key = value.getKey(); ingo@634: String val = value.getValue(); ingo@634: String parameter = value.getParameter(); ingo@634: ingo@634: int i = measurements.indexOf(key); ingo@634: int j = parameters.indexOf(parameter); ingo@634: int tmp = mDescriptions.indexOf(val); ingo@634: ingo@634: if (i < 0) { ingo@634: measurements.add(key); ingo@634: i = measurements.indexOf(key); ingo@1102: ingo@1102: mDescriptions.add(val); ingo@1102: tmp = mDescriptions.indexOf(val); ingo@634: } ingo@634: ingo@634: if (j < 0) { ingo@634: logger.warn("Not a valid parameter: " + parameter); ingo@634: } ingo@634: ingo@634: if (i >= 0 && i < measurements.size() && j >= 0 ingo@634: && j < parameters.size()) ingo@634: { ingo@634: values[i][j] = true; ingo@634: } ingo@634: } ingo@634: } ingo@634: ingo@796: /** ingo@796: * Initialize the parameters used in this matrix. sascha@803: * ingo@796: * @param parameter Parameters. ingo@796: */ ingo@634: private void initParameters(String[] parameter) { ingo@785: for (String param: parameter) { ingo@785: parameters.add(param); ingo@634: } ingo@634: } ingo@634: ingo@796: /** ingo@796: * Returns the number of measurements. sascha@803: * ingo@796: * @return the number of measurements. ingo@796: */ ingo@634: public int measurementSize() { ingo@634: if (measurements != null) ingo@634: return measurements.size(); ingo@634: ingo@634: return 0; ingo@634: } ingo@634: ingo@796: /** ingo@796: * Returns the number of parameters. sascha@803: * ingo@796: * @return number of parameters. ingo@796: */ ingo@634: public int parameterSize() { ingo@634: if (parameters != null) ingo@634: return parameters.size(); ingo@634: ingo@634: return 0; ingo@634: } ingo@634: ingo@796: /** ingo@796: * Returns the measurement at idx. sascha@803: * ingo@796: * @param idx Index. ingo@796: * @return the measurement. ingo@796: */ ingo@634: public String getMeasurement(int idx) { ingo@634: if (idx >= 0 && idx < measurements.size()) ingo@634: return (String) measurements.get(idx); ingo@634: ingo@634: logger.warn("Index is out of bounds: " + idx); ingo@634: return ""; ingo@634: } ingo@634: ingo@796: /** ingo@796: * Returns the parameter at idx. sascha@803: * ingo@796: * @param idx Index ingo@796: * @return the parameter. ingo@796: */ ingo@634: public String getParameter(int idx) { ingo@634: if (idx >= 0 && idx < parameters.size()) { ingo@634: return (String) parameters.get(idx); ingo@634: } ingo@634: ingo@634: logger.warn("Index is out of bounds: " + idx); ingo@634: return ""; ingo@634: } ingo@634: ingo@796: /** ingo@796: * Returns a description text for a specific measurement. ingo@796: * ingo@796: * @param idx Index of a measurement. ingo@796: * @return measurement's description. ingo@796: */ ingo@634: public String getMDescription(int idx) { ingo@634: if (mDescriptions != null) { ingo@634: return (String) mDescriptions.get(idx); ingo@634: } ingo@634: ingo@634: return null; ingo@634: } ingo@634: ingo@796: /** ingo@796: * This method returns true, if a measurement is valid for a specific ingo@796: * parameter - otherwise false. sascha@803: * ingo@796: * @param i Index of a measurement column. ingo@796: * @param j Index of a parameter row. ingo@796: * @return true, if valid, else false. ingo@796: */ ingo@634: public boolean isValid(int i, int j) { ingo@634: if (i < 0 || i > measurements.size() ingo@634: || j < 0 || j > parameters.size()) ingo@634: { ingo@634: logger.warn("Index out of bounds: " + i + "|" + j); ingo@634: return false; ingo@634: } ingo@634: ingo@634: return values[i][j]; ingo@634: } ingo@634: } // End of ParameterMatrix ingo@634: ingo@634: ingo@634: public MeasurementState() { ingo@634: super(); ingo@634: } ingo@634: ingo@634: @Override ingo@634: protected NamedCollection extractKVP( ingo@634: Collection result, ingo@634: String keyid, ingo@634: String valueid ingo@634: ) { ingo@634: NamedCollection kvdd = ingo@634: new NamedArrayList(dataName, result.size()); ingo@634: ingo@634: kvdd.setMultiSelect(true); ingo@634: ingo@634: int keyPos = -1; ingo@634: int valPos = -1; ingo@634: int parPos = -1; ingo@634: ingo@634: for (Result res: result) { ingo@634: if (keyPos < 0 || valPos < 0 || parPos < 0) { ingo@634: ResultDescriptor rd = res.getResultDescriptor(); ingo@634: ingo@634: keyPos = rd.getColumnIndex(keyid); ingo@634: valPos = rd.getColumnIndex(valueid); ingo@634: parPos = rd.getColumnIndex(SQL_KEY_PARAMETERID); ingo@634: } ingo@634: ingo@634: kvdd.add(new ExtendedKeyValueData( ingo@634: res.getString(keyPos), ingo@634: res.getString(valPos), ingo@634: getID(), ingo@634: res.getString(parPos))); ingo@634: } ingo@634: ingo@634: return kvdd; ingo@634: } ingo@634: ingo@634: ingo@796: /** ingo@796: * This method create the user interface description for measurement and ingo@796: * parameters as matrix. A row for each parameter, a column for each ingo@796: * measurement. ingo@796: */ ingo@634: @Override ingo@634: protected void appendToDynamicNode( ingo@634: XMLUtils.ElementCreator artCreator, ingo@634: XMLUtils.ElementCreator creator, ingo@634: Document document, ingo@634: Node dynamicNode, ingo@634: CallMeta callMeta, ingo@634: Object o ingo@634: ) { ingo@634: NamedArrayList all = (NamedArrayList) o; ingo@634: String name = all.getName(); ingo@634: RessourceFactory factory = RessourceFactory.getInstance(); ingo@634: ingo@634: Element matrixNode = creator.create("group"); ingo@634: Element matrixLabel = creator.create("label"); ingo@634: matrixLabel.setTextContent(factory.getRessource( ingo@634: callMeta.getLanguages(), all.getName(), all.getName())); ingo@634: creator.addAttr(matrixNode, "mode", "matrix"); ingo@634: matrixNode.appendChild(matrixLabel); ingo@634: ingo@634: InputData inputParam = inputData.get("parameterid"); ingo@634: ParameterMatrix matrix = new ParameterMatrix(all, inputParam.splitValue()); ingo@634: ingo@634: int measurements = matrix.measurementSize(); ingo@634: int parameters = matrix.parameterSize(); ingo@634: ingo@744: for (int i = 0; i < parameters; i++) { ingo@634: Element select = creator.create("select"); ingo@785: String param = matrix.getParameter(i); ingo@785: creator.addAttr(select, "label", inputParam.getDescription(param)); ingo@650: creator.addAttr(select, "ref", name); ingo@634: ingo@744: for (int j = 0; j < measurements; j++) { ingo@634: Element item = creator.create("item"); ingo@634: Element label = creator.create("label"); ingo@634: Element value = creator.create("value"); ingo@634: ingo@634: creator.addAttr(item, "ref", name); ingo@634: creator.addAttr( ingo@634: item, ingo@634: "parameter", ingo@744: matrix.getMDescription(j)); ingo@634: ingo@744: if (!matrix.isValid(j, i)) { ingo@634: creator.addAttr(item, "disabled", "true"); ingo@634: } ingo@634: else { ingo@634: creator.addAttr(item, "disabled", "false"); ingo@634: } ingo@634: ingo@785: String tmpValue = matrix.getMeasurement(j) + ";" + param; ingo@744: label.setTextContent(matrix.getMDescription(j)); ingo@785: value.setTextContent(tmpValue); ingo@634: ingo@634: item.appendChild(label); ingo@634: item.appendChild(value); ingo@634: select.appendChild(item); ingo@634: } ingo@634: ingo@634: matrixNode.appendChild(select); ingo@634: } ingo@634: ingo@634: dynamicNode.appendChild(matrixNode); ingo@634: } ingo@785: ingo@785: ingo@796: /** ingo@796: * This feed takes some input data storing measurement ids and parameter ids ingo@796: * and put them into ExtendedInputData objects to save the relation between ingo@796: * a measurement and the parameter it belongs to. ingo@796: */ ingo@785: @Override ingo@785: public Document feed( ingo@785: CallContext context, ingo@785: Collection input, ingo@785: String uuid) ingo@785: throws StateException ingo@785: { ingo@785: RessourceFactory resFactory = RessourceFactory.getInstance(); ingo@785: Locale[] serverLocales = resFactory.getLocales(); ingo@785: Locale locale = context.getMeta().getPreferredLocale( ingo@785: serverLocales); ingo@785: ingo@785: if (input == null) { ingo@785: String msg = resFactory.getRessource( ingo@785: locale, ingo@785: EXCEPTION_NO_INPUT, ingo@785: EXCEPTION_NO_INPUT); ingo@785: logger.warn(msg); ingo@785: return feedFailure(msg); ingo@785: } ingo@785: ingo@785: for(InputData item: input) { ingo@785: String name = item.getName(); ingo@785: InputValue inputValue = inputValues.get(name); ingo@785: ingo@785: String[] tupel = extractValuesAndParams(item.getValue()); ingo@785: String type = inputValue.getType(); ingo@785: ingo@785: if (inputValue == null) { ingo@785: String msg = resFactory.getRessource( ingo@785: locale, ingo@785: EXCEPTION_INVALID_INPUT, ingo@785: EXCEPTION_INVALID_INPUT); ingo@785: logger.warn(msg); ingo@785: return feedFailure(msg); ingo@785: } ingo@785: ingo@785: if (!InputValidator.isInputValid(tupel[0], type)) { ingo@785: String msg = resFactory.getRessource( ingo@785: locale, ingo@785: EXCEPTION_INVALID_INPUT, ingo@785: EXCEPTION_INVALID_INPUT); ingo@785: logger.warn(msg); ingo@785: return feedFailure(msg); ingo@785: } ingo@785: ingo@785: if (inputData == null) { ingo@1030: inputData = new TreeMap(); ingo@785: } ingo@785: ingo@785: ExtendedInputData extended = new ExtendedInputData( ingo@785: name, ingo@785: tupel[0], ingo@785: item.getObject(), ingo@785: tupel[1]); ingo@785: ingo@785: if (name.equals(dataName)) { ingo@1038: String[] desc = getDescriptionForInputData( ingo@1038: extended, context, uuid); ingo@785: extended.setDescription(desc); ingo@785: } ingo@785: ingo@785: inputData.put(name, extended); ingo@785: } ingo@785: ingo@785: return feedSuccess(); ingo@785: } ingo@785: ingo@785: ingo@796: /** ingo@796: * Extract parameter ids and measurement ids from DefaultInputData objects ingo@796: * and return an array. In the first position of this array, the measurement ingo@796: * ids are placed, in the second position the parameter ids - all separated ingo@796: * by a character. ingo@796: * ingo@796: * @param tmp String containing measurement ids and parameter ids. ingo@796: * @return An array with separated measurements and parameters. ingo@796: */ ingo@785: protected String[] extractValuesAndParams(String tmp) { ingo@785: String[] array = tmp.split(DefaultInputData.VALUE_SEPARATOR); ingo@785: ingo@785: String[] extracted = new String[2]; ingo@785: for (String item: array) { ingo@785: String[] tupel = item.split(ExtendedInputData.SEPARATOR); ingo@785: ingo@785: if (extracted[0] == null) { ingo@785: extracted[0] = tupel[0]; ingo@785: } ingo@785: else { ingo@785: extracted[0] += ingo@785: DefaultInputData.VALUE_SEPARATOR + tupel[0]; ingo@785: } ingo@785: ingo@785: if (extracted[1] == null) { ingo@785: extracted[1] = tupel[1]; ingo@785: } ingo@785: else { ingo@785: extracted[1] += DefaultInputData.VALUE_SEPARATOR + tupel[1]; ingo@785: } ingo@785: } ingo@785: ingo@785: logger.debug("VALUES RESULT: " + extracted[0]); ingo@785: logger.debug("PARAMS RESULT: " + extracted[1]); ingo@785: ingo@785: return extracted; ingo@785: } ingo@634: } sascha@836: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :