ingo@1115: /*
ingo@1115: * Copyright (c) 2010 by Intevation GmbH
ingo@1115: *
ingo@1115: * This program is free software under the LGPL (>=v2.1)
ingo@1115: * Read the file LGPL.txt coming with the software for details
ingo@1115: * or visit http://www.gnu.org/licenses/ if it does not exist.
ingo@1115: */
ingo@1115:
tim@335: package de.intevation.gnv.state;
tim@335:
ingo@839: import java.text.DateFormat;
ingo@839: import java.text.SimpleDateFormat;
ingo@839:
tim@335: import java.util.ArrayList;
tim@598: import java.util.Arrays;
tim@335: import java.util.Collection;
tim@335: import java.util.Date;
tim@335: import java.util.GregorianCalendar;
tim@335: import java.util.HashMap;
tim@335: import java.util.Iterator;
tim@335: import java.util.List;
ingo@725: import java.util.Locale;
tim@335: import java.util.Map;
ingo@1030: import java.util.Set;
ingo@1030: import java.util.TreeMap;
tim@335:
ingo@488: import javax.xml.xpath.XPathConstants;
ingo@488:
ingo@607: import net.sf.ehcache.Cache;
ingo@607:
tim@335: import org.apache.log4j.Logger;
tim@335: import org.w3c.dom.Document;
tim@335: import org.w3c.dom.Element;
tim@335: import org.w3c.dom.Node;
tim@335: import org.w3c.dom.NodeList;
tim@335:
sascha@1117: import de.intevation.artifacts.common.utils.Config;
sascha@1117: import de.intevation.artifacts.common.utils.XMLUtils;
tim@823: import de.intevation.artifacts.ArtifactNamespaceContext;
tim@823: import de.intevation.artifacts.CallContext;
tim@823: import de.intevation.artifacts.CallMeta;
tim@823: import de.intevation.gnv.artifacts.cache.CacheFactory;
tim@823: import de.intevation.gnv.artifacts.ressource.RessourceFactory;
tim@823: import de.intevation.gnv.geobackend.base.Result;
tim@823: import de.intevation.gnv.geobackend.base.query.QueryExecutor;
tim@823: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
tim@823: import de.intevation.gnv.geobackend.base.query.exception.QueryException;
tim@823: import de.intevation.gnv.geobackend.util.DateUtils;
tim@823: import de.intevation.gnv.state.describedata.DefaultKeyValueDescribeData;
tim@823: import de.intevation.gnv.state.describedata.KeyValueDescibeData;
tim@823: import de.intevation.gnv.state.describedata.MinMaxDescribeData;
tim@823: import de.intevation.gnv.state.describedata.NamedArrayList;
tim@823: import de.intevation.gnv.state.describedata.NamedCollection;
tim@823: import de.intevation.gnv.state.describedata.SingleValueDescribeData;
tim@823: import de.intevation.gnv.state.exception.StateException;
tim@823: import de.intevation.gnv.utils.ArtifactXMLUtilities;
tim@823: import de.intevation.gnv.utils.InputValidator;
tim@823:
tim@335: /**
ingo@796: * This is the major implementation of State
. Nearly every other
ingo@796: * state is derived by this class.
ingo@796: *
sascha@780: * @author Tim Englich
sascha@780: * @author Ingo Weinzierl
sascha@780: * @author Sascha L. Teichmann
tim@335: */
tim@335: public abstract class StateBase implements State {
tim@335:
tim@335: /**
tim@335: * The UID of this Class
tim@335: */
tim@335: private static final long serialVersionUID = 2411169179001645426L;
tim@335:
tim@335: /**
tim@335: * the logger, used to log exceptions and additonaly information
tim@335: */
ingo@630: private static Logger log = Logger.getLogger(StateBase.class);
ingo@605:
ingo@607: protected final static String MINVALUEFIELDNAME = "minvalue";
ingo@815:
ingo@607: protected final static String MAXVALUEFIELDNAME = "maxvalue";
ingo@605:
tim@335: private final static String NODATASELECTIONKEY = "n/n";
ingo@605:
ingo@473: public final static String DESCRIBEDATAKEY = "_DESCRIBEDATA";
tim@335:
ingo@488: public final static String XPATH_STATIC_UI = "art:static";
ingo@815:
ingo@488: public final static String XPATH_DYNAMIC_UI = "art:dynamic";
ingo@488:
ingo@725: public static final String EXCEPTION_NO_INPUT = "no.input.data";
ingo@815:
ingo@725: public static final String EXCEPTION_INVALID_INPUT =
ingo@725: "input.is.not.valid";
ingo@870:
tim@845: public final static String HASH_ID_SEPARATOR = "#";
ingo@725:
ingo@605: /** input value names which should not be rendered from State itself */
ingo@605: public final static String[] BLACKLIST = {"sourceid", "fisname"};
ingo@605:
tim@335: private String id = null;
tim@335:
ingo@609: protected String hash;
ingo@609:
tim@335: private String description = null;
tim@335:
tim@335: protected String dataName = null;
ingo@605:
tim@598: protected String preSettingsName = null;
tim@335:
tim@335: protected boolean dataMultiSelect = false;
ingo@605:
tim@335: protected boolean dataNoSelect = false;
tim@335:
tim@335: protected String queryID = null;
tim@335:
tim@335: protected Collection inputValueNames = null;
tim@335:
ingo@607: protected Map inputValues = null;
tim@335:
ingo@607: protected State parent = null;
tim@335:
tim@335: protected Map inputData = null;
ingo@605:
ingo@607: protected Map preSettings = null;
tim@335:
ingo@839:
ingo@839: /**
ingo@839: * The source date format as string.
ingo@839: */
ingo@839: public static String srcDateFormat = "yyyy.MM.dd hh:mm:ss";
ingo@839:
ingo@839:
ingo@839: /**
ingo@839: * The source date format used to read string represented strings.
ingo@839: */
ingo@839: public static DateFormat srcFormat;
ingo@839:
ingo@839:
ingo@839: static {
ingo@839: srcFormat = new SimpleDateFormat(srcDateFormat);
ingo@839: }
ingo@839:
ingo@839:
tim@335: /**
tim@335: * Constructor
tim@335: */
tim@335: public StateBase() {
tim@335: super();
tim@335: }
tim@335:
ingo@815:
tim@335: public String getID() {
tim@335: return this.id;
tim@335: }
tim@335:
ingo@815:
tim@335: public String getDescription() {
tim@335: return this.description;
tim@335: }
tim@335:
tim@335:
tim@335: public Collection getRequiredInputValues() {
tim@335: return this.inputValues.values();
tim@335: }
tim@335:
ingo@470:
ingo@470: public void reset(String uuid) {
ingo@614: inputData.remove(dataName);
ingo@470: }
ingo@470:
ingo@473:
tim@335: public void setup(Node configuration) {
tim@335: this.id = ((Element)configuration).getAttribute("id");
tim@335: this.description = ((Element)configuration).getAttribute("description");
tim@335:
tim@335: log.info("State-ID = " + this.id);
sascha@778:
tim@335: NodeList inputValuesNodes = Config.getNodeSetXPath(configuration,
tim@335: "inputvalues/inputvalue");
tim@335: this.inputValues = new HashMap(inputValuesNodes
tim@335: .getLength());
tim@335: this.inputValueNames = new ArrayList(inputValuesNodes
tim@335: .getLength());
tim@335: for (int i = 0; i < inputValuesNodes.getLength(); i++) {
tim@335: Element inputValueNode = (Element)inputValuesNodes.item(i);
tim@335: String usedinQueryValue = inputValueNode.getAttribute("usedinquery");
tim@335: int usedinQuery = 1;
tim@335: if (usedinQueryValue != null) {
tim@335: try {
tim@335: usedinQuery = Integer.parseInt(usedinQueryValue);
tim@335: } catch (NumberFormatException e) {
tim@335: log
tim@335: .warn("Used in Query Value cannot be transformed into a Number");
tim@335: }
tim@335: }
sascha@778: InputValue inputValue = new DefaultInputValue(inputValueNode.getAttribute("name"),
sascha@778: inputValueNode.getAttribute("type"),
tim@335: Boolean.parseBoolean(inputValueNode.
tim@335: getAttribute("multiselect")), usedinQuery);
tim@335: this.inputValues.put(inputValue.getName(), inputValue);
tim@335: this.inputValueNames.add(inputValue.getName());
tim@335: }
tim@335:
tim@335: this.queryID = Config.getStringXPath(configuration, "queryID");
tim@335: log.info("QueryID ==> " + this.queryID);
tim@335:
tim@335: this.dataName = Config.getStringXPath(configuration, "dataname");
tim@335:
tim@335: String dataMultiSelectValue = Config.getStringXPath(configuration,
tim@335: "data-multiselect");
tim@335: if (dataMultiSelectValue != null) {
tim@335: this.dataMultiSelect = Boolean.parseBoolean(dataMultiSelectValue);
tim@335: }
sascha@778:
tim@335: String dataNoSelectValue =Config.getStringXPath(configuration,
tim@335: "data-noselect");
tim@335: if (dataNoSelectValue != null) {
tim@335: this. dataNoSelect = Boolean.parseBoolean(dataNoSelectValue);
tim@335: }
sascha@778:
sascha@778: this.preSettingsName = Config.getStringXPath(configuration, "presettings-name");
sascha@778:
tim@335: }
tim@335:
ingo@815:
tim@335: public State getParent() {
tim@335: return this.parent;
tim@335: }
tim@335:
ingo@815:
tim@335: public void setParent(State state) {
tim@335: this.parent = state;
tim@335: }
tim@335:
ingo@607:
ingo@725: public Document feed(
ingo@725: CallContext context,
ingo@725: Collection inputData,
ingo@725: String uuid)
ingo@607: throws StateException
ingo@607: {
ingo@725: RessourceFactory resFactory = RessourceFactory.getInstance();
ingo@725: Locale[] serverLocales = resFactory.getLocales();
ingo@725: Locale locale = context.getMeta().getPreferredLocale(
ingo@725: serverLocales);
ingo@725:
tim@335: if (inputData != null) {
tim@335: Iterator it = inputData.iterator();
tim@335: InputValidator iv = new InputValidator();
tim@335: while (it.hasNext()) {
tim@335: InputData tmpItem = it.next();
tim@335: InputValue inputValue = this.inputValues.get(tmpItem.getName());
tim@335: if (inputValue != null) {
tim@335: if (this.inputData == null) {
ingo@1030: this.inputData = new TreeMap();
tim@335: }
ingo@607:
ingo@796: boolean valid = InputValidator.isInputValid(tmpItem.getValue(),
tim@335: inputValue.getType());
tim@335: if (valid) {
sascha@778:
tim@763: if (tmpItem.getName().equals(this.dataName)){
ingo@1038: String[] desc = getDescriptionForInputData(
ingo@1038: tmpItem, context, uuid);
tim@763: tmpItem.setDescription(desc);
tim@763: }
tim@335: this.inputData.put(tmpItem.getName(), tmpItem);
tim@335: } else {
ingo@725: String msg = resFactory.getRessource(
ingo@725: locale,
ingo@725: EXCEPTION_INVALID_INPUT,
ingo@725: EXCEPTION_INVALID_INPUT);
ingo@725: log.warn(msg);
ingo@725: return feedFailure(msg);
tim@335: }
tim@335:
tim@335: } else {
ingo@725: String msg = resFactory.getRessource(
ingo@725: locale,
ingo@725: EXCEPTION_INVALID_INPUT,
ingo@725: EXCEPTION_INVALID_INPUT);
ingo@725: log.warn(msg);
ingo@725: return feedFailure(msg);
tim@335:
tim@335: }
tim@335: }
ingo@725:
ingo@725: return feedSuccess();
tim@335: } else {
ingo@725: String msg = resFactory.getRessource(
ingo@725: locale,
ingo@725: EXCEPTION_NO_INPUT,
ingo@725: EXCEPTION_NO_INPUT);
ingo@725: log.warn(msg);
ingo@725: return feedFailure(msg);
tim@335: }
tim@335: }
tim@598:
ingo@608:
ingo@725: protected Document feedSuccess() {
ingo@725: return ArtifactXMLUtilities.createSuccessReport(
ingo@725: "Initialize success", XMLUtils.newDocument());
ingo@725: }
ingo@725:
ingo@725:
ingo@725: protected Document feedFailure(String msg) {
ingo@725: return ArtifactXMLUtilities.createInputExceptionReport(
ingo@725: msg, XMLUtils.newDocument());
ingo@725: }
ingo@725:
ingo@725:
ingo@1038: protected String[] getDescriptionForInputData(
ingo@1038: InputData data, CallContext context, String uuid)
ingo@1038: {
ingo@608: // there is only one element in the list, so take the first
ingo@608: Object obj = getDescibeData(uuid).get(0);
ingo@615: List descs = new ArrayList();
ingo@608:
ingo@608: if (obj instanceof NamedArrayList) {
ingo@608: NamedArrayList list = (NamedArrayList) obj;
ingo@634: List selected = Arrays.asList(data.splitValue());
ingo@608: int size = list.size();
ingo@608:
ingo@608: for (int i = 0; i < size; i++) {
ingo@608: KeyValueDescibeData kv = (KeyValueDescibeData) list.get(i);
ingo@608:
ingo@615: // values are concatinated in InputData, so one InputData object can
ingo@615: // contain many input
ingo@634: String key = kv.getKey();
ingo@634: int idx = selected.indexOf(key);
ingo@634: if (idx >= 0) {
ingo@634: descs.add(kv.getValue());
ingo@634:
ingo@634: // XXX Workarround: I just wanted to remove the element at
ingo@634: // 'idx' from selected, but for any reason this is not
ingo@634: // possible (throws an exception) (iw)
ingo@634: List tmp = new ArrayList();
ingo@634: for (int j = 0; j < selected.size(); j++) {
ingo@634: if (j != idx)
ingo@634: tmp.add(selected.get(j));
ingo@615: }
ingo@634:
ingo@634: selected = tmp;
ingo@608: }
ingo@608: }
ingo@608: }
ingo@608:
ingo@615: return (String[]) descs.toArray(new String[descs.size()]);
ingo@608: }
ingo@608:
ingo@608:
ingo@607: public void putInputData(Collection inputData, String uuid)
ingo@607: throws StateException {
ingo@607: if (inputData != null) {
ingo@607: Iterator it = inputData.iterator();
ingo@607: InputValidator iv = new InputValidator();
ingo@607: while (it.hasNext()) {
ingo@607: InputData tmpItem = it.next();
ingo@607: InputValue inputValue = this.inputValues.get(tmpItem.getName());
ingo@607: if (inputValue != null) {
ingo@607: if (this.inputData == null) {
ingo@1030: this.inputData = new TreeMap();
ingo@607: }
ingo@607:
ingo@796: boolean valid = InputValidator.isInputValid(tmpItem.getValue(),
ingo@607: inputValue.getType());
ingo@607: if (valid) {
ingo@607: if (tmpItem.getName().equals(MINVALUEFIELDNAME)){
ingo@607: String minValue = tmpItem.getValue();
ingo@607: String maxValue = this.getInputValue4ID(inputData, MAXVALUEFIELDNAME);
ingo@796: valid = InputValidator.isInputValid(maxValue,inputValue.getType());
ingo@607: if (!valid){
ingo@607: String errMsg = "Wrong input for " + tmpItem.getValue()
ingo@607: + " is not an " + inputValue.getType()
ingo@607: + " Value.";
ingo@607: log.warn(errMsg);
ingo@607: throw new StateException(errMsg);
ingo@607: }
ingo@607:
ingo@796: valid = InputValidator.isInputValid(minValue,
ingo@607: maxValue,
ingo@607: inputValue.getType());
ingo@607: if (!valid){
ingo@607: String errMsg = "MaxValue-Input is less than MinValue-Input ";
ingo@607: log.warn(errMsg);
ingo@607: throw new StateException(errMsg);
ingo@607: }
ingo@607: }else if (tmpItem.getName().equals(MAXVALUEFIELDNAME)){
ingo@607: String minValue = this.getInputValue4ID(inputData, MINVALUEFIELDNAME);
ingo@607: String maxValue = tmpItem.getValue();
ingo@796: valid = InputValidator.isInputValid(minValue,inputValue.getType());
ingo@607: if (!valid){
ingo@607: String errMsg = "Wrong input for " + tmpItem.getValue()
ingo@607: + " is not an " + inputValue.getType()
ingo@607: + " Value.";
ingo@607: log.warn(errMsg);
ingo@607: throw new StateException(errMsg);
ingo@607: }
ingo@607:
ingo@796: valid = InputValidator.isInputValid(minValue,
ingo@607: maxValue,
ingo@607: inputValue.getType());
ingo@607: if (!valid){
ingo@607: String errMsg = "MaxValue-Input is less than MinValue-Input ";
ingo@607: log.warn(errMsg);
ingo@607: throw new StateException(errMsg);
ingo@607: }
ingo@607: }
ingo@607: this.inputData.put(tmpItem.getName(), tmpItem);
ingo@607: } else {
ingo@607: String errMsg = "Wrong input for " + tmpItem.getValue()
ingo@607: + " is not an " + inputValue.getType()
ingo@607: + " Value.";
ingo@607: log.warn(errMsg);
ingo@607: throw new StateException(errMsg);
ingo@607: }
ingo@607:
ingo@607: } else {
ingo@607: String errMsg = "No Inputvalue given for Inputdata "
ingo@607: + tmpItem.getName();
ingo@607: log.warn(errMsg + "Value will be ignored");
ingo@607:
ingo@607: }
ingo@607: }
ingo@607: } else {
ingo@607: log.warn("No Inputdata given");
ingo@607: }
ingo@607:
ingo@609: setHash(uuid);
ingo@607: }
ingo@607:
ingo@607:
tim@598: public void setPreSettings(Map preSettings) {
tim@598: this.preSettings = preSettings;
tim@598: }
sascha@778:
sascha@778:
tim@612: public Map getPreSettings() {
tim@612: return this.preSettings;
tim@612: }
tim@598:
ingo@815:
ingo@610: protected String getInputValue4ID(Collection inputData, String inputName){
tim@335: Iterator it = inputData.iterator();
tim@335: while (it.hasNext()) {
tim@335: InputData tmpItem = it.next();
tim@335: if (tmpItem.getName().equals(inputName)){
tim@335: return tmpItem.getValue();
tim@335: }
tim@335: }
tim@335: return null;
tim@335: }
tim@335:
tim@335:
ingo@493: public void advance(String uuid, CallContext context)
ingo@493: throws StateException
ingo@493: {
tim@335: }
ingo@607:
ingo@815:
ingo@493: public void initialize(String uuid, CallContext context)
ingo@493: throws StateException
ingo@493: {
tim@335: }
tim@335:
ingo@815:
tim@335: protected String[] generateFilterValuesFromInputData() {
tim@335: List list = new ArrayList();
tim@335: Iterator it = this.inputValueNames.iterator();
tim@335: while (it.hasNext()) {
tim@335: String value = it.next();
tim@335: InputData data = this.inputData.get(value);
tim@335: if (data != null
tim@335: && this.inputValues.containsKey(data.getName())) {
ingo@1030:
tim@335: int size = this.inputValues.get(data.getName())
tim@335: .usedInQueries();
tim@335: String type = this.inputValues.get(data.getName())
tim@335: .getType();
tim@335: String requestValue = data.getValue();
tim@335: if (type.equalsIgnoreCase("string")) {
tim@335: requestValue = this
tim@335: .prepareInputData4DBQuery(requestValue);
tim@335: } else if (type.equalsIgnoreCase("date")) {
tim@335: requestValue = this
tim@335: .prepareInputData4DateDBQuery(requestValue);
tim@745: } else if (type.equalsIgnoreCase("coordinate") || (type.equalsIgnoreCase("geometry") && requestValue.toLowerCase().startsWith("point"))){
tim@335: requestValue = this
tim@335: .prepareInputData4RegionDBQuery(requestValue);
tim@335: }
tim@335: for (int j = 0; j < size; j++) {
tim@335: list.add(requestValue);
tim@335: }
tim@335: }
tim@335: }
ingo@428: String[] filterValues = list.toArray(new String[list.size()]);
tim@335: return filterValues;
tim@335: }
sascha@778:
ingo@815:
tim@335: protected String prepareInputData4RegionDBQuery(String value){
tim@335: return value;
tim@335: }
tim@335:
tim@335: private String prepareInputData4DateDBQuery(String value) {
tim@335: if (value != null) {
tim@335: String[] values = value.split(",");
tim@335: String newValue = "";
tim@335: for (int i = 0; i < values.length; i++) {
tim@335: if (newValue.length() > 0) {
tim@335: newValue = newValue + " , ";
tim@335: }
tim@335: // TODO JUST HACK FIND A BETTER RESOLUTION
tim@335: newValue = newValue + "to_date('" + values[i].trim()
tim@335: + "', 'YYYY.MM.DD HH24:MI:SS')";
tim@335: }
tim@335: return newValue;
tim@335: }
tim@335:
tim@335: return value;
tim@335: }
tim@335:
tim@335: private String prepareInputData4DBQuery(String value) {
tim@335: if (value != null) {
tim@335: String[] values = value.split(",");
tim@335: String newValue = "";
tim@335: for (int i = 0; i < values.length; i++) {
tim@335: if (newValue.length() > 0) {
tim@335: newValue = newValue + " , ";
tim@335: }
tim@335: newValue = newValue + "'" + values[i].trim() + "'";
tim@335: }
tim@335: return newValue;
tim@335: }
tim@335:
tim@335: return value;
tim@335:
tim@335: }
tim@335:
ingo@815:
ingo@607: protected List