diff gnv-artifacts/src/main/java/de/intevation/gnv/state/StateBase.java @ 1119:7c4f81f74c47

merged gnv-artifacts
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:00 +0200
parents dec4257ad570
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/StateBase.java	Fri Sep 28 12:14:00 2012 +0200
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2010 by Intevation GmbH
+ *
+ * This program is free software under the LGPL (>=v2.1)
+ * Read the file LGPL.txt coming with the software for details
+ * or visit http://www.gnu.org/licenses/ if it does not exist.
+ */
+
+package de.intevation.gnv.state;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.xml.xpath.XPathConstants;
+
+import net.sf.ehcache.Cache;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+import de.intevation.gnv.artifacts.cache.CacheFactory;
+import de.intevation.gnv.artifacts.ressource.RessourceFactory;
+import de.intevation.gnv.geobackend.base.Result;
+import de.intevation.gnv.geobackend.base.query.QueryExecutor;
+import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory;
+import de.intevation.gnv.geobackend.base.query.exception.QueryException;
+import de.intevation.gnv.geobackend.util.DateUtils;
+import de.intevation.gnv.state.describedata.DefaultKeyValueDescribeData;
+import de.intevation.gnv.state.describedata.KeyValueDescibeData;
+import de.intevation.gnv.state.describedata.MinMaxDescribeData;
+import de.intevation.gnv.state.describedata.NamedArrayList;
+import de.intevation.gnv.state.describedata.NamedCollection;
+import de.intevation.gnv.state.describedata.SingleValueDescribeData;
+import de.intevation.gnv.state.exception.StateException;
+import de.intevation.gnv.utils.ArtifactXMLUtilities;
+import de.intevation.gnv.utils.InputValidator;
+
+/**
+ * This is the major implementation of <code>State</code>. Nearly every other
+ * state is derived by this class.
+ *
+ * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a>
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
+ */
+public abstract class StateBase implements State {
+
+    /**
+     * The UID of this Class
+     */
+    private static final long serialVersionUID = 2411169179001645426L;
+
+    /**
+     * the logger, used to log exceptions and additonaly information
+     */
+    private static Logger log = Logger.getLogger(StateBase.class);
+
+    protected final static String MINVALUEFIELDNAME = "minvalue";
+
+    protected final static String MAXVALUEFIELDNAME = "maxvalue";
+
+    private final static String NODATASELECTIONKEY = "n/n";
+
+    public final static String DESCRIBEDATAKEY = "_DESCRIBEDATA";
+
+    public final static String XPATH_STATIC_UI  = "art:static";
+
+    public final static String XPATH_DYNAMIC_UI = "art:dynamic";
+
+    public static final String EXCEPTION_NO_INPUT      = "no.input.data";
+
+    public static final String EXCEPTION_INVALID_INPUT =
+        "input.is.not.valid";
+
+    public final static String HASH_ID_SEPARATOR = "#";
+
+    /** input value names which should not be rendered from State itself */
+    public final static String[] BLACKLIST = {"sourceid", "fisname"};
+
+    private String id = null;
+
+    protected String hash;
+
+    private String description = null;
+
+    protected String dataName = null;
+
+    protected String preSettingsName = null;
+
+    protected boolean dataMultiSelect = false;
+
+    protected boolean dataNoSelect = false;
+
+    protected String queryID = null;
+
+    protected Collection<String> inputValueNames = null;
+
+    protected Map<String, InputValue> inputValues = null;
+
+    protected State parent = null;
+
+    protected Map<String, InputData> inputData = null;
+
+    protected Map<String, InputData> preSettings = null;
+
+
+    /**
+     * The source date format as string.
+     */
+    public static String srcDateFormat = "yyyy.MM.dd hh:mm:ss";
+
+
+    /**
+     * The source date format used to read string represented strings.
+     */
+    public static DateFormat srcFormat;
+
+
+    static {
+        srcFormat = new SimpleDateFormat(srcDateFormat);
+    }
+
+
+    /**
+     * Constructor
+     */
+    public StateBase() {
+        super();
+    }
+
+
+    public String getID() {
+        return this.id;
+    }
+
+
+    public String getDescription() {
+        return this.description;
+    }
+
+
+    public Collection<InputValue> getRequiredInputValues() {
+        return this.inputValues.values();
+    }
+
+
+    public void reset(String uuid) {
+        inputData.remove(dataName);
+    }
+
+
+    public void setup(Node configuration) {
+        this.id = ((Element)configuration).getAttribute("id");
+        this.description = ((Element)configuration).getAttribute("description");
+
+        log.info("State-ID = " + this.id);
+
+        NodeList inputValuesNodes = Config.getNodeSetXPath(configuration,
+                "inputvalues/inputvalue");
+        this.inputValues = new HashMap<String, InputValue>(inputValuesNodes
+                .getLength());
+        this.inputValueNames = new ArrayList<String>(inputValuesNodes
+                .getLength());
+        for (int i = 0; i < inputValuesNodes.getLength(); i++) {
+            Element inputValueNode = (Element)inputValuesNodes.item(i);
+            String usedinQueryValue = inputValueNode.getAttribute("usedinquery");
+            int usedinQuery = 1;
+            if (usedinQueryValue != null) {
+                try {
+                    usedinQuery = Integer.parseInt(usedinQueryValue);
+                } catch (NumberFormatException e) {
+                    log
+                            .warn("Used in Query Value cannot be transformed into a Number");
+                }
+            }
+            InputValue inputValue = new DefaultInputValue(inputValueNode.getAttribute("name"),
+                                                          inputValueNode.getAttribute("type"),
+                                                          Boolean.parseBoolean(inputValueNode.
+                                                          getAttribute("multiselect")), usedinQuery);
+            this.inputValues.put(inputValue.getName(), inputValue);
+            this.inputValueNames.add(inputValue.getName());
+        }
+
+        this.queryID = Config.getStringXPath(configuration, "queryID");
+        log.info("QueryID ==> " + this.queryID);
+
+        this.dataName = Config.getStringXPath(configuration, "dataname");
+
+        String dataMultiSelectValue = Config.getStringXPath(configuration,
+                                                           "data-multiselect");
+        if (dataMultiSelectValue != null) {
+            this.dataMultiSelect = Boolean.parseBoolean(dataMultiSelectValue);
+        }
+
+        String dataNoSelectValue =Config.getStringXPath(configuration,
+                                                        "data-noselect");
+        if (dataNoSelectValue != null) {
+            this. dataNoSelect = Boolean.parseBoolean(dataNoSelectValue);
+        }
+
+        this.preSettingsName = Config.getStringXPath(configuration, "presettings-name");
+
+    }
+
+
+    public State getParent() {
+        return this.parent;
+    }
+
+
+    public void setParent(State state) {
+        this.parent = state;
+    }
+
+
+    public Document feed(
+        CallContext           context,
+        Collection<InputData> inputData,
+        String                uuid)
+    throws StateException
+    {
+        RessourceFactory resFactory = RessourceFactory.getInstance();
+        Locale[] serverLocales      = resFactory.getLocales();
+        Locale locale               = context.getMeta().getPreferredLocale(
+            serverLocales);
+
+        if (inputData != null) {
+            Iterator<InputData> it = inputData.iterator();
+            InputValidator iv = new InputValidator();
+            while (it.hasNext()) {
+                InputData tmpItem = it.next();
+                InputValue inputValue = this.inputValues.get(tmpItem.getName());
+                if (inputValue != null) {
+                    if (this.inputData == null) {
+                        this.inputData = new TreeMap<String, InputData>();
+                    }
+
+                    boolean valid = InputValidator.isInputValid(tmpItem.getValue(),
+                            inputValue.getType());
+                    if (valid) {
+
+                        if (tmpItem.getName().equals(this.dataName)){
+                            String[] desc = getDescriptionForInputData(
+                                tmpItem, context, uuid);
+                            tmpItem.setDescription(desc);
+                        }
+                        this.inputData.put(tmpItem.getName(), tmpItem);
+                    } else {
+                        String msg = resFactory.getRessource(
+                            locale,
+                            EXCEPTION_INVALID_INPUT,
+                            EXCEPTION_INVALID_INPUT);
+                        log.warn(msg);
+                        return feedFailure(msg);
+                    }
+
+                } else {
+                    String msg = resFactory.getRessource(
+                        locale,
+                        EXCEPTION_INVALID_INPUT,
+                        EXCEPTION_INVALID_INPUT);
+                    log.warn(msg);
+                    return feedFailure(msg);
+
+                }
+            }
+
+            return feedSuccess();
+        } else {
+            String msg = resFactory.getRessource(
+                locale,
+                EXCEPTION_NO_INPUT,
+                EXCEPTION_NO_INPUT);
+            log.warn(msg);
+            return feedFailure(msg);
+        }
+    }
+
+
+    protected Document feedSuccess() {
+        return ArtifactXMLUtilities.createSuccessReport(
+            "Initialize success", XMLUtils.newDocument());
+    }
+
+
+    protected Document feedFailure(String msg) {
+        return ArtifactXMLUtilities.createInputExceptionReport(
+            msg, XMLUtils.newDocument());
+    }
+
+
+    protected String[] getDescriptionForInputData(
+        InputData data, CallContext context, String uuid)
+    {
+        // there is only one element in the list, so take the first
+        Object obj = getDescibeData(uuid).get(0);
+        List descs = new ArrayList();
+
+        if (obj instanceof NamedArrayList) {
+            NamedArrayList list = (NamedArrayList) obj;
+            List       selected = Arrays.asList(data.splitValue());
+            int            size = list.size();
+
+            for (int i = 0; i < size; i++) {
+                KeyValueDescibeData kv = (KeyValueDescibeData) list.get(i);
+
+                // values are concatinated in InputData, so one InputData object can
+                // contain many input
+                String key = kv.getKey();
+                int idx = selected.indexOf(key);
+                if (idx >= 0) {
+                    descs.add(kv.getValue());
+
+                    // XXX Workarround: I just wanted to remove the element at
+                    // 'idx' from selected, but for any reason this is not
+                    // possible (throws an exception) (iw)
+                    List tmp = new ArrayList();
+                    for (int j = 0; j < selected.size(); j++) {
+                        if (j != idx)
+                            tmp.add(selected.get(j));
+                    }
+
+                    selected = tmp;
+                }
+            }
+        }
+
+        return (String[]) descs.toArray(new String[descs.size()]);
+    }
+
+
+    public void putInputData(Collection<InputData> inputData, String uuid)
+    throws StateException {
+        if (inputData != null) {
+            Iterator<InputData> it = inputData.iterator();
+            InputValidator iv = new InputValidator();
+            while (it.hasNext()) {
+                InputData tmpItem = it.next();
+                InputValue inputValue = this.inputValues.get(tmpItem.getName());
+                if (inputValue != null) {
+                    if (this.inputData == null) {
+                        this.inputData = new TreeMap<String, InputData>();
+                    }
+
+                    boolean valid = InputValidator.isInputValid(tmpItem.getValue(),
+                            inputValue.getType());
+                    if (valid) {
+                        if (tmpItem.getName().equals(MINVALUEFIELDNAME)){
+                            String minValue = tmpItem.getValue();
+                            String maxValue = this.getInputValue4ID(inputData, MAXVALUEFIELDNAME);
+                            valid = InputValidator.isInputValid(maxValue,inputValue.getType());
+                            if (!valid){
+                                String errMsg = "Wrong input for " + tmpItem.getValue()
+                                                + " is not an " + inputValue.getType()
+                                                + " Value.";
+                                log.warn(errMsg);
+                                throw new StateException(errMsg);
+                            }
+
+                            valid = InputValidator.isInputValid(minValue,
+                                    maxValue,
+                                    inputValue.getType());
+                            if (!valid){
+                                String errMsg = "MaxValue-Input is less than MinValue-Input ";
+                                log.warn(errMsg);
+                                throw new StateException(errMsg);
+                            }
+                        }else if (tmpItem.getName().equals(MAXVALUEFIELDNAME)){
+                            String minValue = this.getInputValue4ID(inputData, MINVALUEFIELDNAME);
+                            String maxValue = tmpItem.getValue();
+                            valid = InputValidator.isInputValid(minValue,inputValue.getType());
+                            if (!valid){
+                                String errMsg = "Wrong input for " + tmpItem.getValue()
+                                                + " is not an " + inputValue.getType()
+                                                + " Value.";
+                                log.warn(errMsg);
+                                throw new StateException(errMsg);
+                            }
+
+                            valid = InputValidator.isInputValid(minValue,
+                                                    maxValue,
+                                                    inputValue.getType());
+                            if (!valid){
+                                String errMsg = "MaxValue-Input is less than MinValue-Input ";
+                                log.warn(errMsg);
+                                throw new StateException(errMsg);
+                            }
+                        }
+                        this.inputData.put(tmpItem.getName(), tmpItem);
+                    } else {
+                        String errMsg = "Wrong input for " + tmpItem.getValue()
+                                        + " is not an " + inputValue.getType()
+                                        + " Value.";
+                        log.warn(errMsg);
+                        throw new StateException(errMsg);
+                    }
+
+                } else {
+                    String errMsg = "No Inputvalue given for Inputdata "
+                                    + tmpItem.getName();
+                    log.warn(errMsg + "Value will be ignored");
+
+                }
+            }
+        } else {
+            log.warn("No Inputdata given");
+        }
+
+        setHash(uuid);
+    }
+
+
+    public void setPreSettings(Map<String, InputData> preSettings) {
+        this.preSettings = preSettings;
+    }
+
+
+    public Map<String, InputData> getPreSettings() {
+        return this.preSettings;
+    }
+
+
+    protected String getInputValue4ID(Collection<InputData> inputData, String inputName){
+        Iterator<InputData> it = inputData.iterator();
+        while (it.hasNext()) {
+            InputData tmpItem = it.next();
+            if (tmpItem.getName().equals(inputName)){
+                return tmpItem.getValue();
+            }
+        }
+        return null;
+    }
+
+
+    public void advance(String uuid, CallContext context)
+    throws StateException
+    {
+    }
+
+
+    public void initialize(String uuid, CallContext context)
+    throws StateException
+    {
+    }
+
+
+    protected String[] generateFilterValuesFromInputData() {
+        List<String> list = new ArrayList<String>();
+        Iterator<String> it = this.inputValueNames.iterator();
+        while (it.hasNext()) {
+            String value = it.next();
+            InputData data = this.inputData.get(value);
+            if (data != null
+                && this.inputValues.containsKey(data.getName())) {
+
+                int size = this.inputValues.get(data.getName())
+                        .usedInQueries();
+                String type = this.inputValues.get(data.getName())
+                        .getType();
+                String requestValue = data.getValue();
+                if (type.equalsIgnoreCase("string")) {
+                    requestValue = this
+                            .prepareInputData4DBQuery(requestValue);
+                } else if (type.equalsIgnoreCase("date")) {
+                    requestValue = this
+                            .prepareInputData4DateDBQuery(requestValue);
+                } else if (type.equalsIgnoreCase("coordinate") || (type.equalsIgnoreCase("geometry") && requestValue.toLowerCase().startsWith("point"))){
+                    requestValue = this
+                    .prepareInputData4RegionDBQuery(requestValue);
+                }
+                for (int j = 0; j < size; j++) {
+                    list.add(requestValue);
+                }
+            }
+        }
+        String[] filterValues = list.toArray(new String[list.size()]);
+        return filterValues;
+    }
+
+
+    protected String prepareInputData4RegionDBQuery(String value){
+        return value;
+    }
+
+    private String prepareInputData4DateDBQuery(String value) {
+        if (value != null) {
+            String[] values = value.split(",");
+            String newValue = "";
+            for (int i = 0; i < values.length; i++) {
+                if (newValue.length() > 0) {
+                    newValue = newValue + " , ";
+                }
+                // TODO JUST HACK FIND A BETTER RESOLUTION
+                newValue = newValue + "to_date('" + values[i].trim()
+                           + "', 'YYYY.MM.DD HH24:MI:SS')";
+            }
+            return newValue;
+        }
+
+        return value;
+    }
+
+    private String prepareInputData4DBQuery(String value) {
+        if (value != null) {
+            String[] values = value.split(",");
+            String newValue = "";
+            for (int i = 0; i < values.length; i++) {
+                if (newValue.length() > 0) {
+                    newValue = newValue + " , ";
+                }
+                newValue = newValue + "'" + values[i].trim() + "'";
+            }
+            return newValue;
+        }
+
+        return value;
+
+    }
+
+
+    protected List<Object> purifyResult(Collection<Result> result, String uuid) {
+        List<Object> describeData = new ArrayList<Object>();
+
+        NamedCollection<KeyValueDescibeData> keyValueDescibeData =
+            extractKVP(result, "KEY", "VALUE");
+
+        describeData.add(keyValueDescibeData);
+
+        return describeData;
+    }
+
+
+    protected NamedCollection<KeyValueDescibeData> extractKVP(Collection<Result> result,
+                                                              String keyid,
+                                                              String valueid) {
+        Iterator<Result> rit = result.iterator();
+        int dataSize = (this.dataNoSelect ? result.size()+1 : result.size());
+
+        NamedCollection<KeyValueDescibeData> keyValueDescibeData = new NamedArrayList<KeyValueDescibeData>(
+                this.dataName, dataSize);
+        keyValueDescibeData.setMultiSelect(this.dataMultiSelect);
+
+        if (this.dataNoSelect){
+            keyValueDescibeData.add(new DefaultKeyValueDescribeData(
+                NODATASELECTIONKEY,
+                "No Selection",
+                getID()
+            ));
+        }
+
+        boolean initialized = false;
+        int     keyPos      = 0;
+        int     valuePos    = 1;
+        String  previousKey = null;
+        InputData preSettingsData =
+            (this.preSettings != null && this.preSettingsName != null)
+                ? this.preSettings.get(this.preSettingsName)
+                : null;
+        boolean filterWithPresettings = preSettingsData != null;
+
+        List<String> preSettingValues = null;
+        if(filterWithPresettings){
+            preSettingValues = Arrays.asList(preSettingsData.splitValue());
+        }
+        while (rit.hasNext()) {
+            Result resultValue = rit.next();
+            if (!initialized){
+                keyPos = resultValue.getResultDescriptor().getColumnIndex(keyid);
+                valuePos = resultValue.getResultDescriptor().getColumnIndex(valueid);
+                if (valuePos < 0){
+                    valuePos = 1;
+                }
+                initialized = true;
+            }
+            String tmpKey = resultValue.getString(keyPos);
+
+            // TODO: FIXME: We have to do that because the arcsde does not
+            //       support a distinct Query on Layers.
+            if (previousKey == null || !tmpKey.equals(previousKey)){
+                previousKey = tmpKey;
+                if (!filterWithPresettings || preSettingValues.contains(tmpKey)){
+                    keyValueDescibeData.add(
+                            new DefaultKeyValueDescribeData(
+                                                tmpKey,
+                                                resultValue.getString(valuePos),
+                                                getID())
+                                );
+                }
+            }
+        }
+        return keyValueDescibeData;
+    }
+
+
+    public static boolean inBlackList(String key) {
+        int length = BLACKLIST.length;
+        for (int i = 0; i < length; i++) {
+            if (BLACKLIST[i].equals(key)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    public void describe(
+        Document    document,
+        Node        rootNode,
+        CallContext context,
+        String      uuid)
+    {
+        XMLUtils.ElementCreator xCreator = new XMLUtils.ElementCreator(
+            document,
+            XMLUtils.XFORM_URL,
+            XMLUtils.XFORM_PREFIX
+        );
+
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            document,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX
+        );
+
+        // append dynamic node
+        Node dynamic = (Node) XMLUtils.xpath(
+            rootNode,
+            XPATH_DYNAMIC_UI,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE
+        );
+
+        describeDynamic(
+            creator, xCreator, document, dynamic, context, uuid);
+
+        // append static nodes
+        Node staticNode = (Node) XMLUtils.xpath(
+            rootNode,
+            XPATH_STATIC_UI,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE
+        );
+
+        State parent = getParent();
+        if (parent != null && parent instanceof StateBase) {
+            ((StateBase) parent).describeStatic(
+                creator,xCreator, document, staticNode, context,uuid);
+        }
+    }
+
+
+    protected void describeDynamic(
+        XMLUtils.ElementCreator artCreator,
+        XMLUtils.ElementCreator creator,
+        Document                document,
+        Node                    dynamic,
+        CallContext             context,
+        String                  uuid)
+    {
+        CallMeta callMeta = context.getMeta();
+
+        if (dataName == null)
+            return;
+
+        List<Object> descibeData = getDescibeData(uuid);
+        if (descibeData != null) {
+            Iterator<Object> it = descibeData.iterator();
+
+            while (it.hasNext()) {
+                Object o = it.next();
+                if ((!it.hasNext() && dataName != null)) {
+                    appendToDynamicNode(
+                        artCreator, creator, document, dynamic, callMeta, o);
+                }
+            }
+        }
+    }
+
+
+    protected void describeStatic(
+        XMLUtils.ElementCreator artCreator,
+        XMLUtils.ElementCreator creator,
+        Document                document,
+        Node                    staticNode,
+        CallContext             context,
+        String                  uuid)
+    {
+        State parent = getParent();
+        if (parent instanceof StateBase) {
+            ((StateBase) parent).describeStatic(
+                artCreator, creator, document, staticNode, context, uuid);
+        }
+
+        CallMeta callMeta = context.getMeta();
+        appendToStaticNode(artCreator, creator, document, staticNode, callMeta);
+    }
+
+
+    protected void appendToStaticNode(
+        XMLUtils.ElementCreator artCreator,
+        XMLUtils.ElementCreator creator,
+        Document                document,
+        Node                    staticNode,
+        CallMeta                callMeta
+    ) {
+        InputData  data = dataName!= null ? inputData.get(dataName) : null;
+
+        if (data == null) {
+            return;
+        }
+
+        Element selectNode = creator.create("select1");
+        creator.addAttr(selectNode, "ref", dataName);
+
+        Element lableNode = creator.create("label");
+        lableNode.setTextContent(RessourceFactory.getInstance()
+                .getRessource(callMeta.getLanguages(), dataName, dataName));
+        Element choiceNode = creator.create("choices");
+
+        artCreator.addAttr(
+            selectNode, "state", getID(), true
+        );
+
+        String[] descriptions = data.getDescription();
+        int              size = descriptions.length;
+
+        for (int i = 0; i < size; i++) {
+            Element itemNode = creator.create("item");
+            String value = data.getValue();
+            String desc  = descriptions[i];
+            desc = desc == null ? value : desc;
+
+            creator.addAttr(itemNode, "selected", "true");
+
+            Element choiceLableNode = creator.create("label");
+            choiceLableNode.setTextContent(desc);
+            itemNode.appendChild(choiceLableNode);
+
+            Element choiceValueNode = creator.create("value");
+            choiceValueNode.setTextContent(value);
+            itemNode.appendChild(choiceValueNode);
+            choiceNode.appendChild(itemNode);
+        }
+
+        selectNode.appendChild(lableNode);
+        selectNode.appendChild(choiceNode);
+
+        staticNode.appendChild(selectNode);
+    }
+
+
+    protected void appendToDynamicNode(
+        XMLUtils.ElementCreator artCreator,
+        XMLUtils.ElementCreator creator,
+        Document                document,
+        Node                    dynamicNode,
+        CallMeta                callMeta,
+        Object                  o
+    ) {
+        if (o instanceof Collection<?>) {
+            String name = null;
+            boolean multiselect = false;
+            if (o instanceof NamedCollection<?>) {
+                NamedCollection<?> nc = ((NamedCollection<?>) o);
+                name = nc.getName();
+                multiselect = nc.isMultiSelect();
+            } else {
+                Object[] names = this.inputValueNames.toArray();
+                name = names[names.length - 1].toString();
+            }
+
+            Element selectNode = creator.create(multiselect?"select":"select1");
+            creator.addAttr(selectNode, "ref", name);
+
+            Element lableNode = creator.create("label");
+            lableNode.setTextContent(RessourceFactory.getInstance()
+                    .getRessource(callMeta.getLanguages(), name, name));
+            Element choiceNode = creator.create("choices");
+
+            Collection<KeyValueDescibeData> values = (Collection<KeyValueDescibeData>) o;
+            Iterator<KeyValueDescibeData> resultIt = values.iterator();
+            while (resultIt.hasNext()) {
+                KeyValueDescibeData result = resultIt.next();
+                Element itemNode = creator.create("item");
+
+                if (result.isSelected()) {
+                    itemNode.setAttribute("selected", "true");
+                }
+
+                Element choiceLableNode = creator.create("label");
+                choiceLableNode.setTextContent(result.getValue());
+                itemNode.appendChild(choiceLableNode);
+
+                Element choicValueNode = creator.create("value");
+                choicValueNode.setTextContent("" + result.getKey());
+                itemNode.appendChild(choicValueNode);
+                choiceNode.appendChild(itemNode);
+            }
+            selectNode.appendChild(lableNode);
+            selectNode.appendChild(choiceNode);
+
+            dynamicNode.appendChild(selectNode);
+        }
+        else if (o instanceof MinMaxDescribeData) {
+            appendMinMaxDescribeData(
+                artCreator,
+                creator,
+                document,
+                dynamicNode,
+                callMeta,
+                o);
+        }
+        else if (o instanceof SingleValueDescribeData) {
+            appendSingleValueDescribeData(
+                artCreator,
+                creator,
+                document,
+                dynamicNode,
+                callMeta,
+                o);
+        }
+    }
+
+
+    protected void appendMinMaxDescribeData(
+        XMLUtils.ElementCreator artCreator,
+        XMLUtils.ElementCreator creator,
+        Document                document,
+        Node                    node,
+        CallMeta                callMeta,
+        Object                  o
+    ) {
+        MinMaxDescribeData minMaxDescibeData = (MinMaxDescribeData) o;
+        Object min = minMaxDescibeData.getMinValue();
+        Object max = minMaxDescibeData.getMaxValue();
+        if (min instanceof GregorianCalendar) {
+            Date d = ((GregorianCalendar) min).getTime();
+            min = DateUtils.getPatternedDateAmer(d);
+        }
+
+        if (max instanceof GregorianCalendar) {
+            Date d = ((GregorianCalendar) max).getTime();
+            max = DateUtils.getPatternedDateAmer(d);
+        }
+
+        Element groupNode = creator.create("group");
+        artCreator.addAttr(groupNode, "state", minMaxDescibeData.getState(), true);
+
+        creator.addAttr(groupNode, "ref", minMaxDescibeData.getName());
+        Element groupNodeLableNode = creator.create("label");
+        groupNodeLableNode.setTextContent(RessourceFactory
+                .getInstance().getRessource(
+                        callMeta.getLanguages(),
+                        minMaxDescibeData.getName(),
+                        minMaxDescibeData.getName()));
+        groupNode.appendChild(groupNodeLableNode);
+
+        Element inputMinNode = creator.create("input");
+        creator.addAttr(inputMinNode, "ref", MINVALUEFIELDNAME);
+        Element inputMinLableNode = creator.create("label");
+        inputMinLableNode.setTextContent(RessourceFactory
+                .getInstance().getRessource(
+                        callMeta.getLanguages(), MINVALUEFIELDNAME,
+                        MINVALUEFIELDNAME));
+        inputMinNode.appendChild(inputMinLableNode);
+
+        Element inputMinValueNode = creator.create("value");
+        inputMinValueNode.setTextContent(min.toString());
+        inputMinNode.appendChild(inputMinValueNode);
+
+        Element inputMaxNode = creator.create("input");
+        creator.addAttr(inputMaxNode, "ref", MAXVALUEFIELDNAME);
+        Element inputMaxLableNode = creator.create("label");
+        inputMaxLableNode.setTextContent(RessourceFactory
+                .getInstance().getRessource(
+                        callMeta.getLanguages(), MAXVALUEFIELDNAME,
+                        MAXVALUEFIELDNAME));
+        inputMaxNode.appendChild(inputMaxLableNode);
+
+        Element inputMaxValueNode = creator.create("value");
+        inputMaxValueNode.setTextContent(max.toString());
+        inputMaxNode.appendChild(inputMaxValueNode);
+
+        groupNode.appendChild(inputMinNode);
+        groupNode.appendChild(inputMaxNode);
+
+        node.appendChild(groupNode);
+    }
+
+
+    protected void appendSingleValueDescribeData(
+        XMLUtils.ElementCreator artCreator,
+        XMLUtils.ElementCreator creator,
+        Document                document,
+        Node                    node,
+        CallMeta                callMeta,
+        Object                  o
+    ) {
+        SingleValueDescribeData svdb = (SingleValueDescribeData) o;
+
+        Element groupNode = creator.create("group");
+        artCreator.addAttr(groupNode, "state", svdb.getState(), true);
+        creator.addAttr(groupNode, "ref",  svdb.getName());
+
+        Element groupNodeLableNode = creator.create("label");
+        groupNodeLableNode.setTextContent(RessourceFactory
+                .getInstance().getRessource(
+                        callMeta.getLanguages(),
+                        svdb.getName(),
+                        svdb.getName()));
+        groupNode.appendChild(groupNodeLableNode);
+
+        Element inputNode = creator.create("input");
+        creator.addAttr(inputNode, "ref", svdb.getName());
+
+        Element inputLableNode = creator.create("label");
+        inputLableNode.setTextContent("");
+        inputNode.appendChild(inputLableNode);
+
+        Element inputValueNode = creator.create("value");
+        inputValueNode.setTextContent(svdb.getValue());
+        inputNode.appendChild(inputValueNode);
+
+        groupNode.appendChild(inputNode);
+
+        node.appendChild(groupNode);
+    }
+
+
+    protected void setHash(String uuid) {
+        String newHash = uuid + HASH_ID_SEPARATOR + id + HASH_ID_SEPARATOR;
+        Set    keys    = inputData.keySet();
+
+        int nhash = 0;
+        int shift = 0;
+
+        for (Object o: keys) {
+            nhash ^= inputData.get(o).hashCode() << shift;
+            shift += 2;
+        }
+
+        log.info("### OLD HASH: " + hash);
+        log.info("### NEW HASH: " + (newHash + nhash));
+
+        this.hash = newHash + nhash;
+    }
+
+
+    protected String getHash() {
+        return this.hash;
+    }
+
+
+    public List<Object> getDescibeData(String uuid) {
+        CacheFactory factory = CacheFactory.getInstance();
+        if (factory.isInitialized()) {
+            // we use a cache
+            Cache cache = factory.getCache();
+            String key  = getHash();
+
+            log.debug("Using cache - key: " + key);
+
+            net.sf.ehcache.Element value = cache.get(key);
+            if (value != null) {
+                // element already in cache, so return it.
+                log.debug("Found element in cache.");
+                return (List<Object>) (value.getObjectValue());
+            }
+            else {
+                // element is not in cache yet, so we need to fetch data from
+                // database and put it into cache right now
+                log.debug("Element not in cache, we need to ask the database");
+                try {
+                    String[] filterValues = generateFilterValuesFromInputData();
+                    List<Object> data     = queryDatabase(filterValues, uuid);
+
+                    cache.put(new net.sf.ehcache.Element(key, data));
+
+                    return data;
+                }
+                catch (QueryException qe) {
+                    log.error(qe, qe);
+                }
+            }
+        }
+        else {
+            // we don't use a cache, so we have to query the database every
+            // single time
+            log.debug("Not using cache.");
+            String[] filterValues     = generateFilterValuesFromInputData();
+            Collection<Result> result = null;
+            try {
+                return queryDatabase(filterValues, uuid);
+            }
+            catch (RuntimeException e) {
+                log.error(e, e);
+            }
+            catch (QueryException e) {
+                log.error(e, e);
+            }
+        }
+
+        return null;
+    }
+
+
+    protected List<Object> queryDatabase(String[] filterValues, String uuid)
+    throws QueryException {
+        Collection<Result> result = null;
+
+        if (queryID != null) {
+            QueryExecutor queryExecutor =
+                QueryExecutorFactory.getInstance().getQueryExecutor();
+
+            result = queryExecutor.executeQuery(queryID, filterValues);
+        }
+        return purifyResult(result, uuid);
+    }
+
+
+
+    public Map<String, InputData> inputData() {
+        return inputData;
+    }
+
+
+    public Collection<InputData> getInputData() throws StateException {
+        return this.inputData != null ? this.inputData.values() : null;
+    }
+
+
+    public InputData getInputDataByName(String name) {
+        State state = this;
+
+        while (state != null) {
+            InputData data = state.inputData().get(name);
+            if (data != null) {
+                return data;
+            }
+
+            state = state.getParent();
+        }
+
+        return null;
+    }
+
+
+    public void endOfLife(Object globalContext) {
+        log.debug("end of life of the current state.");
+
+        CacheFactory factory = CacheFactory.getInstance();
+        if (factory.isInitialized()) {
+            Cache cache = factory.getCache();
+            String key  = getHash();
+
+            if (key != null && cache.remove(key)) {
+                log.info("Removed element from cache - key: " + key);
+            }
+        }
+    }
+
+
+    public void cleanup(Object context) {
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org