ingo@126: package de.intevation.flys.artifacts.states;
ingo@126: 
ingo@669: import java.text.NumberFormat;
ingo@669: import java.util.Locale;
ingo@126: import java.util.Map;
sascha@697: import java.util.List;
ingo@126: 
ingo@126: import org.apache.log4j.Logger;
ingo@126: 
ingo@126: import org.w3c.dom.Document;
ingo@126: import org.w3c.dom.Element;
ingo@126: import org.w3c.dom.Node;
ingo@126: 
ingo@306: import de.intevation.artifacts.Artifact;
ingo@142: import de.intevation.artifacts.ArtifactNamespaceContext;
ingo@126: import de.intevation.artifacts.CallContext;
ingo@142: import de.intevation.artifacts.CallMeta;
ingo@126: 
ingo@126: import de.intevation.artifacts.common.utils.XMLUtils;
ingo@1159: import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
ingo@126: 
ingo@126: import de.intevation.artifactdatabase.ProtocolUtils;
sascha@697: 
ingo@126: import de.intevation.artifactdatabase.data.StateData;
sascha@697: 
ingo@126: import de.intevation.artifactdatabase.state.AbstractState;
sascha@697: import de.intevation.artifactdatabase.state.Facet;
ingo@126: 
ingo@624: import de.intevation.flys.artifacts.FLYSArtifact;
sascha@697: 
ingo@126: import de.intevation.flys.artifacts.resources.Resources;
ingo@126: 
ingo@126: 
ingo@126: /**
ingo@126:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@126:  */
ingo@126: public abstract class DefaultState extends AbstractState {
ingo@126: 
felix@1029:     /** The logger that is used in this class. */
ingo@126:     private static Logger logger = Logger.getLogger(DefaultState.class);
ingo@126: 
ingo@2015: 
ingo@2015:     /** Determines, if the DESCRIBE document should contain default values or
ingo@2015:      * not. */
ingo@2015:     public static final boolean USE_DEFAULTS =
ingo@2015:         Boolean.getBoolean("flys.use.default.values");
ingo@2015: 
felix@1768:     /** The three possible compute types. */
sascha@697:     public static enum ComputeType {
ingo@942:         FEED, ADVANCE, INIT
sascha@697:     }
ingo@126: 
felix@1768: 
ingo@624:     protected StateData getData(FLYSArtifact artifact,  String name) {
ingo@624:         return artifact.getData(name);
ingo@624:     }
ingo@624: 
ingo@624: 
felix@1136:     /**
felix@1136:      * Append to a node and return xml description relevant for gui.
felix@1136:      */
ingo@134:     public Element describeStatic(
ingo@624:         Artifact    artifact,
ingo@134:         Document    document,
ingo@134:         Node        root,
ingo@134:         CallContext context,
ingo@134:         String      uuid)
ingo@134:     {
ingo@1159:         ElementCreator creator = new ElementCreator(
ingo@134:             document,
ingo@134:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@134:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
ingo@134: 
ingo@142:         CallMeta meta = context.getMeta();
ingo@142: 
bjoern@4155:         String helpText = getHelpText() != null ?
bjoern@4155:             Resources.getMsg(meta, getHelpText(), getHelpText())
bjoern@4155:             : null;
ingo@2661: 
ingo@142:         String label = Resources.getMsg(meta, getID(), getID());
ingo@136:         Element ui   = ProtocolUtils.createArtNode(
ingo@134:             creator, "state",
ingo@2661:             new String[] { "name", "uiprovider", "label", "helpText"},
ingo@2661:             new String[] { getID(), getUIProvider(), label, helpText });
ingo@134: 
ingo@134:         Map<String, StateData> theData = getData();
ingo@134:         if (theData == null) {
ingo@134:             return ui;
ingo@134:         }
ingo@134: 
sascha@3732:         FLYSArtifact flys = (FLYSArtifact)artifact;
ingo@134: 
sascha@3732:         for (String name: theData.keySet()) {
ingo@1180:             appendStaticData(flys, context, creator, ui, name);
ingo@134:         }
ingo@134: 
ingo@134:         return ui;
ingo@134:     }
ingo@134: 
ingo@134: 
ingo@1159:     protected void appendStaticData(
ingo@1159:         FLYSArtifact   flys,
ingo@1180:         CallContext    context,
ingo@1180:         ElementCreator cr,
ingo@1159:         Element        ui,
ingo@1159:         String         name
ingo@1159:     ) {
ingo@1159:         StateData data  = getData(flys, name);
ingo@1159:         String    value = (data != null) ? (String) data.getValue() : null;
ingo@1159: 
ingo@1159:         if (value == null) {
ingo@1159:             return;
ingo@1159:         }
ingo@1159: 
ingo@2205:         String type = data.getType();
ingo@1159: 
sascha@3732:         if (logger.isDebugEnabled()) {
sascha@3732:             logger.debug(
sascha@3732:                 "Append element " + type + "'" +
sascha@3732:                 name + "' (" + value + ")");
sascha@3732:         }
ingo@2205: 
ingo@2205:         Element e = createStaticData(flys, cr, context, name, value, type);
ingo@1180: 
ingo@1180:         ui.appendChild(e);
ingo@1180: 
ingo@1180:     }
ingo@1180: 
ingo@1180: 
ingo@1180:     /**
ingo@1180:      * Creates a <i>data</i> element used in the static part of the DESCRIBE
ingo@1180:      * document.
ingo@1180:      *
ingo@1180:      * @param creator The ElementCreator that is used to build new Elements.
felix@3284:      * @param cc The CallContext object used for nested i18n retrieval.
ingo@1180:      * @param name The name of the data item.
ingo@1180:      * @param value The value as string.
ingo@1180:      *
ingo@1180:      * @return an Element.
ingo@1180:      */
ingo@1180:     protected Element createStaticData(
ingo@1743:         FLYSArtifact   flys,
ingo@1180:         ElementCreator creator,
ingo@1180:         CallContext    cc,
ingo@1180:         String         name,
ingo@1180:         String         value,
ingo@1180:         String         type
ingo@1180:     ) {
ingo@1159:         Element dataElement = creator.create("data");
ingo@1159:         creator.addAttr(dataElement, "name", name, true);
ingo@1180:         creator.addAttr(dataElement, "type", type, true);
ingo@1159: 
ingo@1159:         Element itemElement = creator.create("item");
ingo@1159:         creator.addAttr(itemElement, "value", value, true);
ingo@1159: 
ingo@2205:         creator.addAttr(
ingo@2205:             itemElement,
ingo@2205:             "label",
ingo@2205:             getLabelFor(cc, name, value, type),
ingo@2205:             true);
ingo@2205: 
ingo@2205:         dataElement.appendChild(itemElement);
ingo@2205: 
ingo@2205:         return dataElement;
ingo@2205:     }
ingo@2205: 
ingo@2205: 
ingo@2205:     /**
ingo@2205:      * @param cc
ingo@2205:      * @param name
ingo@2205:      * @param value
ingo@2205:      * @param type
ingo@2205:      *
ingo@2205:      * @return
ingo@2205:      */
ingo@2205:     protected String getLabelFor(
ingo@2205:         CallContext cc,
ingo@2205:         String      name,
ingo@2205:         String      value,
ingo@2205:         String      type
ingo@2205:     ) {
ingo@2205:         CallMeta meta = cc.getMeta();
ingo@2205: 
ingo@1159:         try {
ingo@1159:             // XXX A better way to format the output would be to use the
ingo@1159:             // 'type' value of the data objects.
sascha@3732:             double doubleVal = Double.parseDouble(value);
ingo@1159:             Locale         l = Resources.getLocale(meta);
ingo@1159:             NumberFormat  nf = NumberFormat.getInstance(l);
ingo@1159: 
ingo@2205:             return nf.format(doubleVal);
ingo@1159:         }
ingo@1159:         catch (NumberFormatException nfe) {
ingo@2205:             return Resources.getMsg(meta, value, value);
ingo@1159:         }
ingo@1159:     }
ingo@1159: 
ingo@1159: 
ingo@3407:     /**
ingo@3407:      * This method returns the default value and description for <i>data</i>.
ingo@3407:      * Note, that this method returns the defaults only if <i>USE_DEFAULTS</i>
sascha@3414:      * is set; otherwise, null is returned.
ingo@3407:      * @param context The CallContext used for i18n.
ingo@3407:      * @param data The data objects that the defaults are for.
ingo@3407:      * @return a String[] with [default value, default label].
ingo@3407:      */
ingo@3407:     protected String[] getDefaultsFor(CallContext context, StateData data) {
ingo@3407:         if (USE_DEFAULTS) {
ingo@3407:             String defValue = (String) data.getValue();
ingo@3407:             String defDesc  = null;
ingo@3407: 
ingo@3407:             if (defValue != null && defValue.length() > 0) {
ingo@3407:                 defDesc = Resources.getMsg(
ingo@3407:                     context.getMeta(),
ingo@3407:                     defValue,
ingo@3407:                     defValue);
ingo@3407:             }
ingo@3407: 
ingo@3407:             if (defValue != null && defDesc != null) {
ingo@3407:                 return new String[] { defValue, defDesc };
ingo@3407:             }
ingo@3407:         }
ingo@3407: 
ingo@3407:         return null;
ingo@3407:     }
ingo@3407: 
ingo@3407: 
ingo@126:     public Element describe(
ingo@306:         Artifact    artifact,
ingo@126:         Document    document,
ingo@126:         Node        root,
ingo@126:         CallContext context,
ingo@126:         String      uuid)
ingo@126:     {
ingo@1159:         ElementCreator creator = new ElementCreator(
ingo@126:             document,
ingo@126:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@126:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
ingo@126: 
ingo@2661:         String helpText = Resources.getMsg(
ingo@2661:             context.getMeta(), getHelpText(), getHelpText());
ingo@2661: 
ingo@135:         Element ui        = null;
ingo@135:         String uiprovider = getUIProvider();
ingo@135:         if (uiprovider != null) {
felix@1136:             ui = ProtocolUtils.createArtNode(
ingo@135:                 creator, "dynamic",
ingo@2661:                 new String[] { "uiprovider", "helpText" },
ingo@2661:                 new String[] { uiprovider, helpText });
ingo@135:         }
ingo@135:         else {
ingo@2661:             ui = ProtocolUtils.createArtNode(
ingo@2661:                 creator, "dynamic",
ingo@2661:                 new String[] { "helpText" },
ingo@2661:                 new String[] { helpText });
ingo@135:         }
ingo@126: 
ingo@126:         Map<String, StateData> theData = getData();
ingo@126:         if (theData == null) {
ingo@126:             return ui;
ingo@126:         }
ingo@126: 
sascha@3732:         FLYSArtifact flys = (FLYSArtifact)artifact;
ingo@126: 
sascha@3732:         for (String name: theData.keySet()) {
ingo@624:             StateData data = getData(flys, name);
ingo@624: 
sascha@3732:             if (data == null) {
sascha@3732:                 data = getData(name);
sascha@3732:             }
ingo@624: 
ingo@313:             Element select = createData(creator, artifact, data, context);
ingo@126: 
ingo@3407:             String[] defaults = getDefaultsFor(context, data);
ingo@3407:             if (defaults != null && defaults.length > 1) {
ingo@3407:                 creator.addAttr(select, "defaultValue", defaults[0], true);
ingo@3407:                 creator.addAttr(select, "defaultLabel", defaults[1], true);
ingo@630:             }
ingo@630: 
ingo@2189:             appendItems(artifact, creator, name, context, select);
ingo@126:             ui.appendChild(select);
ingo@126:         }
ingo@126: 
ingo@126:         return ui;
ingo@126:     }
ingo@126: 
ingo@126: 
ingo@126:     /**
ingo@2189:      * @param artifact
ingo@2189:      * @param creator
ingo@2189:      * @param name
ingo@2189:      * @param context
ingo@2189:      * @param select
ingo@2189:      */
ingo@2189:     protected void appendItems(
ingo@2189:         Artifact       artifact,
ingo@2189:         ElementCreator creator,
ingo@2189:         String         name,
ingo@2189:         CallContext    context,
ingo@2189:         Element        select
ingo@2189:     ) {
ingo@2189:         Element choices = ProtocolUtils.createArtNode(
ingo@2189:             creator, "choices", null, null);
ingo@2189: 
ingo@2189:         select.appendChild(choices);
ingo@2189: 
ingo@2189:         Element[] items = createItems(creator, artifact, name, context);
ingo@2189:         if (items != null) {
ingo@2189:             for (Element item: items) {
ingo@2189:                 choices.appendChild(item);
ingo@2189:             }
ingo@2189:         }
ingo@2189:     }
ingo@2189: 
ingo@2189: 
ingo@2189:     /**
ingo@126:      * This method creates the root node that contains the list of selectable
ingo@126:      * items.
ingo@126:      *
ingo@126:      * @param cr The ElementCreator.
ingo@126:      *
ingo@126:      * @return the root node of the item list.
ingo@126:      */
ingo@126:     protected Element createData(
ingo@1159:         ElementCreator cr,
ingo@313:         Artifact    artifact,
ingo@126:         StateData   data,
ingo@126:         CallContext context)
ingo@126:     {
ingo@126:         Element select = ProtocolUtils.createArtNode(
ingo@126:             cr, "select", null, null);
ingo@129:         cr.addAttr(select, "name", data.getName(), true);
ingo@126: 
ingo@126:         Element label = ProtocolUtils.createArtNode(
ingo@126:             cr, "label", null, null);
ingo@126: 
ingo@129:         select.appendChild(label);
ingo@129: 
ingo@126:         label.setTextContent(Resources.getMsg(
ingo@126:             context.getMeta(),
ingo@126:             getID(),
ingo@126:             getID()));
ingo@126: 
ingo@126:         return select;
ingo@126:     }
ingo@126: 
ingo@126: 
ingo@126:     /**
ingo@126:      * This method creates a list of items. These items represent the amount of
ingo@126:      * input data that is possible for this state.
ingo@126:      *
ingo@126:      * @param cr The ElementCreator.
ingo@126:      * @param name The name of the amount of data.
ingo@126:      *
ingo@126:      * @return a list of items.
ingo@126:      */
sascha@660:     protected Element[] createItems(
ingo@1159:         ElementCreator cr,
ingo@313:         Artifact    artifact,
ingo@126:         String      name,
sascha@660:         CallContext context
sascha@660:     ) {
sascha@660:         return null;
sascha@660:     }
ingo@135: 
ingo@135: 
ingo@322:     /**
ingo@1180:      * This method is used to create an <i>item</i> Element that contains two
ingo@1180:      * further elements <i>label</i> and <i>value</i>. The label and value
ingo@1180:      * elements both have text nodes.
ingo@1180:      *
ingo@1180:      * @param cr The ElementCreator used to build new Elements.
ingo@1180:      * @param obj This implementation awaits a String array with [0] = label and
ingo@1180:      * [1] = value.
ingo@1180:      *
ingo@1180:      * @return an Element.
ingo@1180:      */
ingo@1180:     protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
ingo@1180:         Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
ingo@1180:         Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
ingo@1180:         Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
ingo@1180: 
ingo@1180:         String[] arr = (String[]) obj;
ingo@1180: 
ingo@1180:         label.setTextContent(arr[0]);
ingo@1180:         value.setTextContent(arr[1]);
ingo@1180: 
ingo@1180:         item.appendChild(label);
ingo@1180:         item.appendChild(value);
ingo@1180: 
ingo@1180:         return item;
ingo@1180:     }
ingo@1180: 
ingo@1180: 
ingo@1180:     /**
ingo@1176:      * This method transform a given value into a StateData object.
ingo@1176:      *
ingo@1176:      * @param flys The FLYSArtifact.
ingo@1176:      * @param name The name of the data object.
ingo@1176:      * @param val The value of the data object.
ingo@1176:      *
ingo@1176:      * @return a StateData object with <i>name</i> and <i>val</i>ue.
ingo@1176:      */
ingo@1176:     public StateData transform(
ingo@1176:         FLYSArtifact flys,
ingo@1176:         CallContext  cc,
ingo@2205:         StateData    stateData,
ingo@1176:         String       name,
ingo@1176:         String       val
ingo@1176:     ) {
sascha@3732:         if (logger.isDebugEnabled()) {
sascha@3732:             logger.debug("Transform data ('" + name + "','" + val + "')");
sascha@3732:         }
ingo@2205: 
ingo@2205:         stateData.setValue(val);
ingo@2205: 
ingo@2205:         return stateData;
ingo@1176:     }
ingo@1176: 
ingo@1176: 
ingo@1176:     /**
ingo@322:      * This method validates the inserted data and returns true, if everything
ingo@322:      * was correct, otherwise an exception is thrown.
ingo@322:      *
ingo@322:      * @param artifact A reference to the owner artifact.
ingo@322:      *
ingo@322:      * @return true, if everything was fine.
ingo@322:      */
sascha@1050:     public boolean validate(Artifact artifact)
ingo@322:     throws IllegalArgumentException
ingo@322:     {
ingo@322:         return true;
ingo@322:     }
ingo@322: 
ingo@322: 
felix@1691:     /**
felix@1691:      * Returns which UIProvider shall be used to aid user input.
felix@1691:      */
ingo@135:     protected String getUIProvider() {
ingo@135:         return null;
ingo@135:     }
ingo@687: 
felix@1136: 
sascha@697:     public Object computeAdvance(
sascha@697:         FLYSArtifact artifact,
sascha@697:         String       hash,
sascha@742:         CallContext  context,
sascha@742:         List<Facet>  facets,
sascha@697:         Object       old
sascha@697:     ) {
sascha@697:         return null;
sascha@697:     }
ingo@687: 
felix@1136: 
sascha@697:     public Object computeFeed(
sascha@697:         FLYSArtifact artifact,
sascha@697:         String       hash,
sascha@697:         CallContext  context,
sascha@742:         List<Facet>  facets,
sascha@697:         Object       old
sascha@697:     ) {
ingo@687:         return null;
ingo@687:     }
ingo@941: 
felix@1136: 
ingo@941:     public Object computeInit(
ingo@941:         FLYSArtifact artifact,
ingo@941:         String       hash,
ingo@958:         Object       context,
ingo@941:         CallMeta     meta,
ingo@941:         List<Facet>  facets)
ingo@941:     {
ingo@941:         return null;
ingo@941:     }
ingo@126: }
ingo@126: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :