ingo@119: package de.intevation.flys.artifacts;
ingo@119: 
sascha@735: import java.util.Arrays;
sascha@637: import java.util.ArrayList;
ingo@623: import java.util.Collection;
sascha@650: import java.util.Collections;
ingo@691: import java.util.HashMap;
ingo@937: import java.util.HashSet;
ingo@686: import java.util.TreeMap;
ingo@119: import java.util.List;
ingo@121: import java.util.Map;
ingo@122: import java.util.Set;
ingo@121: 
ingo@121: import javax.xml.xpath.XPathConstants;
ingo@119: 
ingo@379: import gnu.trove.TDoubleArrayList;
ingo@379: 
ingo@686: import net.sf.ehcache.Cache;
ingo@686: 
ingo@119: import org.apache.log4j.Logger;
ingo@119: 
ingo@119: import org.w3c.dom.Document;
ingo@121: import org.w3c.dom.Element;
ingo@121: import org.w3c.dom.NodeList;
ingo@119: 
ingo@940: import de.intevation.artifacts.Artifact;
ingo@940: import de.intevation.artifacts.ArtifactDatabase;
ingo@940: import de.intevation.artifacts.ArtifactDatabaseException;
ingo@119: import de.intevation.artifacts.ArtifactFactory;
ingo@119: import de.intevation.artifacts.CallContext;
ingo@952: import de.intevation.artifacts.CallMeta;
ingo@119: 
ingo@119: import de.intevation.artifacts.common.ArtifactNamespaceContext;
ingo@119: import de.intevation.artifacts.common.utils.XMLUtils;
ingo@119: 
ingo@940: import de.intevation.artifactdatabase.ArtifactDatabaseImpl;
ingo@119: import de.intevation.artifactdatabase.DefaultArtifact;
ingo@121: import de.intevation.artifactdatabase.data.DefaultStateData;
ingo@119: import de.intevation.artifactdatabase.data.StateData;
ingo@937: import de.intevation.artifactdatabase.state.DefaultOutput;
ingo@687: import de.intevation.artifactdatabase.state.Facet;
ingo@937: import de.intevation.artifactdatabase.state.Output;
ingo@119: import de.intevation.artifactdatabase.state.State;
ingo@119: import de.intevation.artifactdatabase.state.StateEngine;
ingo@122: import de.intevation.artifactdatabase.transition.TransitionEngine;
ingo@119: 
sascha@655: import de.intevation.flys.utils.DoubleUtil;
sascha@655: 
ingo@317: import de.intevation.flys.model.Gauge;
ingo@317: import de.intevation.flys.model.River;
ingo@317: 
ingo@119: import de.intevation.flys.artifacts.context.FLYSContext;
sascha@650: 
ingo@686: import de.intevation.flys.artifacts.cache.CacheFactory;
ingo@686: 
ingo@377: import de.intevation.flys.artifacts.model.DischargeTables;
ingo@317: import de.intevation.flys.artifacts.model.RiverFactory;
sascha@650: import de.intevation.flys.artifacts.model.Segment;
sascha@650: 
ingo@322: import de.intevation.flys.artifacts.states.DefaultState;
sascha@697: import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
ingo@628: import de.intevation.flys.artifacts.states.LocationDistanceSelect;
ingo@119: 
ingo@119: 
ingo@119: /**
ingo@119:  * The defaul FLYS artifact.
ingo@119:  *
ingo@119:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@119:  */
ingo@119: public abstract class FLYSArtifact extends DefaultArtifact {
ingo@119: 
ingo@119:     /** The logger that is used in this artifact.*/
ingo@119:     private static Logger logger = Logger.getLogger(FLYSArtifact.class);
ingo@119: 
ingo@119: 
ingo@686:     public static final String COMPUTING_CACHE = "computed.values";
ingo@686: 
ingo@121:     /** The XPath that points to the input data elements of the FEED document.*/
ingo@121:     public static final String XPATH_FEED_INPUT =
ingo@121:         "/art:action/art:data/art:input";
ingo@119: 
ingo@122:     /** The XPath that points to the name of the target state of ADVANCE.*/
ingo@122:     public static final String XPATH_ADVANCE_TARGET =
ingo@122:         "/art:action/art:target/@art:name";
ingo@122: 
ingo@940:     public static final String XPATH_MODEL_ARTIFACT =
ingo@941:         "/art:action/art:template/@uuid";
ingo@940: 
ingo@122:     /** The constant string that shows that an operation was successful.*/
ingo@122:     public static final String OPERATION_SUCCESSFUL = "SUCCESS";
ingo@122: 
ingo@122:     /** The constant string that shows that an operation failed.*/
ingo@122:     public static final String OPERATION_FAILED = "FAILURE";
ingo@122: 
ingo@362:     /** The default number of steps between the start end end of a selected Q
ingo@362:      * range.*/
ingo@362:     public static final int DEFAULT_Q_STEPS = 30;
ingo@362: 
ingo@398:     /** The default step width between the start end end kilometer.*/
ingo@398:     public static final double DEFAULT_KM_STEPS = 0.1;
ingo@398: 
ingo@119: 
ingo@119:     /** The identifier of the current state. */
ingo@119:     protected String currentStateId;
ingo@119: 
ingo@122:     /** The identifiers of previous states on a stack.*/
sascha@661:     protected List<String> previousStateIds;
ingo@122: 
ingo@119:     /** The name of the artifact.*/
ingo@119:     protected String name;
ingo@119: 
ingo@121:     /** The data that have been inserted into this artifact.*/
ingo@121:     protected Map<String, StateData> data;
ingo@121: 
ingo@687:     /** The list of facets supported by this artifact.*/
ingo@691:     protected Map<String, List<Facet>> facets;
ingo@687: 
ingo@121: 
ingo@121:     /**
ingo@121:      * The default constructor that creates an empty FLYSArtifact.
ingo@121:      */
ingo@121:     public FLYSArtifact() {
ingo@686:         data             = new TreeMap<String, StateData>();
sascha@661:         previousStateIds = new ArrayList<String>();
ingo@691:         facets           = new HashMap<String, List<Facet>>();
ingo@121:     }
ingo@121: 
ingo@121: 
ingo@121:     /**
ingo@121:      * Returns the name of the concrete artifact.
ingo@121:      *
ingo@121:      * @return the name of the concrete artifact.
ingo@121:      */
ingo@121:     public abstract String getName();
ingo@121: 
ingo@121: 
ingo@121:     /**
ingo@121:      * Returns the FLYSContext from context object.
ingo@121:      *
ingo@121:      * @param context The CallContext or the FLYSContext.
ingo@121:      *
ingo@121:      * @return the FLYSContext.
ingo@121:      */
sascha@706:     protected static FLYSContext getFlysContext(Object context) {
ingo@121:         return context instanceof FLYSContext
ingo@121:             ? (FLYSContext) context
ingo@121:             : (FLYSContext) ((CallContext) context).globalContext();
ingo@121:     }
ingo@121: 
ingo@119: 
ingo@119:     /**
ingo@119:      * Initialize the artifact and insert new data if <code>data</code> contains
ingo@119:      * information necessary for this artifact.
ingo@119:      *
ingo@119:      * @param identifier The UUID.
ingo@119:      * @param factory The factory that is used to create this artifact.
ingo@119:      * @param context The CallContext.
ingo@119:      * @param data Some optional data.
ingo@119:      */
ingo@119:     @Override
ingo@119:     public void setup(
ingo@119:         String          identifier,
ingo@119:         ArtifactFactory factory,
ingo@119:         Object          context,
ingo@952:         CallMeta        callMeta,
ingo@119:         Document        data)
ingo@119:     {
ingo@119:         logger.debug("Setup this artifact with the uuid: " + identifier);
ingo@119: 
ingo@952:         super.setup(identifier, factory, context, callMeta, data);
ingo@119: 
ingo@119:         FLYSContext flysContext = (FLYSContext) context;
ingo@119:         StateEngine engine      = (StateEngine) flysContext.get(
ingo@119:             FLYSContext.STATE_ENGINE_KEY);
ingo@119: 
ingo@121:         String name = getName();
ingo@121: 
ingo@121:         logger.debug("Set initial state for artifact '" + name + "'");
ingo@119:         List<State> states = engine.getStates(name);
ingo@119: 
ingo@119:         setCurrentState(states.get(0));
ingo@940: 
ingo@940:         String model = XMLUtils.xpathString(
ingo@940:             data,
ingo@940:             XPATH_MODEL_ARTIFACT,
ingo@940:             ArtifactNamespaceContext.INSTANCE);
ingo@940: 
ingo@940:         if (model != null && model.length() > 0) {
ingo@940:             ArtifactDatabase db = (ArtifactDatabase) flysContext.get(
ingo@940:                 ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY);
ingo@940: 
ingo@940:             try {
ingo@952:                 initialize(db.getRawArtifact(model), context, callMeta);
ingo@940:             }
ingo@940:             catch (ArtifactDatabaseException adbe) {
ingo@940:                 logger.error(adbe, adbe);
ingo@940:             }
ingo@940:         }
ingo@940:     }
ingo@940: 
ingo@940: 
ingo@952:     protected void initialize(
ingo@952:         Artifact artifact,
ingo@952:         Object   context,
ingo@952:         CallMeta callMeta)
ingo@952:     {
ingo@940:         // do nothing here
ingo@119:     }
ingo@119: 
ingo@119: 
ingo@119:     /**
ingo@119:      * Insert new data included in <code>input</code> into the current state.
ingo@119:      *
ingo@119:      * @param target XML document that contains new data.
ingo@119:      * @param context The CallContext.
ingo@119:      *
ingo@119:      * @return a document that contains a SUCCESS or FAILURE message.
ingo@119:      */
ingo@119:     @Override
ingo@119:     public Document feed(Document target, CallContext context) {
ingo@123:         logger.info("FLYSArtifact.feed()");
ingo@123: 
ingo@121:         Document doc = XMLUtils.newDocument();
ingo@119: 
ingo@121:         XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
ingo@121:             doc,
ingo@121:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@121:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
ingo@119: 
ingo@121:         Element result = creator.create("result");
ingo@121:         doc.appendChild(result);
ingo@119: 
ingo@121:         try {
ingo@322:             saveData(target, XPATH_FEED_INPUT, context);
ingo@689: 
sascha@705:             compute(context, ComputeType.FEED, true);
ingo@689: 
ingo@123:             return describe(target, context);
ingo@121:         }
ingo@121:         catch (IllegalArgumentException iae) {
sascha@658:             // do not store state if validation fails.
sascha@658:             context.afterCall(CallContext.NOTHING);
ingo@122:             creator.addAttr(result, "type", OPERATION_FAILED, true);
ingo@121: 
ingo@121:             result.setTextContent(iae.getMessage());
ingo@121:         }
ingo@121: 
ingo@121:         return doc;
ingo@119:     }
ingo@119: 
ingo@119: 
ingo@119:     /**
ingo@122:      * This method handles request for changing the current state of an
ingo@122:      * artifact. It is possible to step forward or backward.
ingo@122:      *
ingo@122:      * @param target The incoming ADVANCE document.
ingo@122:      * @param context The CallContext.
ingo@122:      *
ingo@122:      * @return a document that contains a SUCCESS or FAILURE message.
ingo@122:      */
ingo@122:     public Document advance(Document target, CallContext context) {
ingo@122:         Document doc = XMLUtils.newDocument();
ingo@122: 
ingo@122:         XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
ingo@122:             doc,
ingo@122:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@122:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
ingo@122: 
ingo@122:         Element result = ec.create("result");
ingo@122: 
ingo@122:         String targetState = XMLUtils.xpathString(
ingo@122:             target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE);
ingo@122: 
ingo@140:         logger.info("FLYSArtifact.advance() to '" + targetState + "'");
ingo@140: 
ingo@122:         if (isStateReachable(targetState, context)) {
ingo@140:             logger.info("Advance: Step forward");
ingo@122: 
sascha@661:             List<String> prev = getPreviousStateIds();
ingo@122:             prev.add(getCurrentStateId());
ingo@122: 
ingo@122:             setCurrentStateId(targetState);
ingo@122: 
ingo@693:             logger.debug("Compute data for state: " + targetState);
sascha@705:             compute(context, ComputeType.ADVANCE, true);
ingo@693: 
ingo@123:             return describe(target, context);
ingo@122:         }
ingo@140:         else if (isPreviousState(targetState, context)) {
ingo@140:             logger.info("Advance: Step back to");
ingo@122: 
sascha@661:             List<String> prevs = getPreviousStateIds();
ingo@140:             int targetIdx        = prevs.indexOf(targetState);
ingo@140:             int start            = prevs.size() - 1;
ingo@140: 
ingo@140:             for (int i = start; i >= targetIdx; i--) {
ingo@140:                 String prev = prevs.get(i);
ingo@140:                 logger.debug("Remove state id '" + prev + "'");
ingo@140:                 prevs.remove(prev);
ingo@691:                 facets.remove(prev);
ingo@140:             }
ingo@140: 
ingo@140:             setCurrentStateId(targetState);
ingo@140: 
ingo@140:             return describe(target, context);
ingo@140:         }
ingo@122: 
ingo@122:         logger.warn("Advance: Cannot advance to '" + targetState + "'");
ingo@123:         ec.addAttr(result, "type", OPERATION_FAILED, true);
ingo@122: 
ingo@123:         doc.appendChild(result);
ingo@122: 
ingo@122:         return doc;
ingo@122:     }
ingo@122: 
ingo@122: 
ingo@122:     /**
ingo@119:      * Returns the identifier of the current state.
ingo@119:      *
ingo@119:      * @return the identifier of the current state.
ingo@119:      */
sascha@722:     public String getCurrentStateId() {
ingo@119:         return currentStateId;
ingo@119:     }
ingo@119: 
ingo@119: 
ingo@119:     /**
ingo@119:      * Sets the identifier of the current state.
ingo@119:      *
ingo@119:      * @param id the identifier of a state.
ingo@119:      */
ingo@119:     protected void setCurrentStateId(String id) {
ingo@119:         currentStateId = id;
ingo@119:     }
ingo@119: 
ingo@119: 
ingo@119:     /**
ingo@119:      * Set the current state of this artifact. <b>NOTE</b>We don't store the
ingo@119:      * State object itself - which is not necessary - but its identifier. So
ingo@119:      * this method will just call the setCurrentStateId() method with the
ingo@119:      * identifier of <i>state</i>.
ingo@119:      *
ingo@119:      * @param state The new current state.
ingo@119:      */
ingo@119:     protected void setCurrentState(State state) {
ingo@119:         setCurrentStateId(state.getID());
ingo@119:     }
ingo@119: 
ingo@119: 
ingo@119:     /**
ingo@119:      * Returns the current state of the artifact.
ingo@119:      *
ingo@119:      * @return the current State of the artifact.
ingo@119:      */
ingo@119:     protected State getCurrentState(Object context) {
ingo@121:         FLYSContext flysContext = getFlysContext(context);
ingo@119:         StateEngine engine      = (StateEngine) flysContext.get(
ingo@119:             FLYSContext.STATE_ENGINE_KEY);
ingo@119: 
ingo@119:         return engine.getState(getCurrentStateId());
ingo@119:     }
ingo@119: 
sascha@722:     protected State getState(Object context, String stateID) {
sascha@722:         FLYSContext flysContext = getFlysContext(context);
sascha@722:         StateEngine engine      = (StateEngine) flysContext.get(
sascha@722:             FLYSContext.STATE_ENGINE_KEY);
sascha@722:         return engine.getState(stateID);
sascha@722:     }
sascha@722: 
ingo@119: 
ingo@119:     /**
ingo@122:      * Returns the vector of previous state identifiers.
ingo@122:      *
ingo@122:      * @return the vector of previous state identifiers.
ingo@122:      */
sascha@661:     protected List<String> getPreviousStateIds() {
ingo@122:         return previousStateIds;
ingo@122:     }
ingo@122: 
ingo@122: 
ingo@122:     /**
ingo@121:      * Adds a new StateData item to the data pool of this artifact.
ingo@121:      *
ingo@121:      * @param name the name of the data object.
ingo@121:      * @param data the data object itself.
ingo@121:      */
ingo@121:     protected void addData(String name, StateData data) {
ingo@121:         this.data.put(name, data);
ingo@121:     }
ingo@121: 
ingo@121: 
ingo@121:     /**
ingo@122:      * This method returns a specific StateData object that is stored in the
ingo@122:      * data pool of this artifact.
ingo@122:      *
ingo@122:      * @param name The name of the data object.
ingo@122:      *
ingo@122:      * @return the StateData object if existing, otherwise null.
ingo@122:      */
ingo@298:     public StateData getData(String name) {
ingo@122:         return data.get(name);
ingo@122:     }
ingo@122: 
ingo@122: 
ingo@937:     public String getDataAsString(String name) {
ingo@937:         StateData data = getData(name);
ingo@937:         return data != null ? (String) data.getValue() : null;
ingo@937:     }
ingo@937: 
sascha@960:     public Collection<StateData> getAllData() {
sascha@960:         return data.values();
sascha@960:     }
sascha@960: 
ingo@937: 
ingo@696:     public Facet getNativeFacet(Facet facet) {
ingo@696:         String name  = facet.getName();
ingo@696:         int    index = facet.getIndex();
ingo@696: 
ingo@696:         for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
ingo@696:             for (Facet f: entry.getValue()) {
sascha@699:                 if (f.getIndex() == index && f.getName().equals(name)) {
ingo@696:                     return f;
ingo@696:                 }
ingo@696:             }
ingo@696:         }
ingo@696: 
ingo@696:         logger.warn("Could not find facet: " + name + " at " + index);
ingo@696:         return null;
ingo@696:     }
ingo@696: 
ingo@696: 
ingo@122:     /**
ingo@121:      * This method stores the data that is contained in the FEED document.
ingo@119:      *
ingo@119:      * @param feed The FEED document.
ingo@119:      * @param xpath The XPath that points to the data nodes.
ingo@119:      */
ingo@322:     public void saveData(Document feed, String xpath, CallContext context)
ingo@121:     throws IllegalArgumentException
ingo@121:     {
ingo@121:         if (feed == null || xpath == null || xpath.length() == 0) {
ingo@325:             throw new IllegalArgumentException("error_feed_no_data");
ingo@121:         }
ingo@119: 
ingo@121:         NodeList nodes = (NodeList) XMLUtils.xpath(
ingo@121:             feed,
ingo@121:             xpath,
ingo@121:             XPathConstants.NODESET,
ingo@121:             ArtifactNamespaceContext.INSTANCE);
ingo@121: 
ingo@121:         if (nodes == null || nodes.getLength() == 0) {
ingo@325:             throw new IllegalArgumentException("error_feed_no_data");
ingo@121:         }
ingo@121: 
ingo@121:         int count = nodes.getLength();
ingo@121:         logger.debug("Try to save " + count + " data items.");
ingo@121: 
sascha@708:         String uri = ArtifactNamespaceContext.NAMESPACE_URI;
ingo@121: 
sascha@708:         for (int i = 0; i < count; i++) {
sascha@708:             Element node = (Element)nodes.item(i);
ingo@121: 
sascha@708:             String name  = node.getAttributeNS(uri, "name");
sascha@708:             String value = node.getAttributeNS(uri, "value");
sascha@708: 
sascha@708:             if (name.length() > 0 && value.length() > 0) {
ingo@121:                 logger.debug("Save data item for '" + name + "' : " + value);
ingo@121: 
ingo@121:                 addData(name, new DefaultStateData(name, null, null, value));
ingo@121:             }
ingo@121:         }
ingo@322: 
ingo@624:         DefaultState current = (DefaultState) getCurrentState(context);
ingo@624:         current.validate(this, context);
ingo@122:     }
ingo@122: 
ingo@122: 
ingo@122:     /**
ingo@122:      * Determines if the state with the identifier <i>stateId</i> is reachable
ingo@122:      * from the current state. The determination itself takes place in the
ingo@122:      * TransitionEngine.
ingo@122:      *
ingo@122:      * @param stateId The identifier of a state.
ingo@122:      * @param context The context object.
ingo@122:      *
ingo@122:      * @return true, if the state specified by <i>stateId</i> is reacahble,
ingo@122:      * otherwise false.
ingo@122:      */
ingo@122:     protected boolean isStateReachable(String stateId, Object context) {
ingo@122:         logger.debug("Determine if the state '" + stateId + "' is reachable.");
ingo@122: 
ingo@122:         FLYSContext flysContext = getFlysContext(context);
ingo@122: 
ingo@624:         State currentState  = getCurrentState(context);
ingo@122:         StateEngine sEngine = (StateEngine) flysContext.get(
ingo@122:             FLYSContext.STATE_ENGINE_KEY);
ingo@122: 
ingo@122:         TransitionEngine tEngine = (TransitionEngine) flysContext.get(
ingo@122:             FLYSContext.TRANSITION_ENGINE_KEY);
ingo@122: 
ingo@355:         return tEngine.isStateReachable(this, stateId, currentState, sEngine);
ingo@122:     }
ingo@140: 
ingo@140: 
ingo@140:     /**
ingo@140:      * Determines if the state with the identifier <i>stateId</i> is a previous
ingo@140:      * state of the current state.
ingo@140:      *
ingo@140:      * @param stateId The target state identifier.
ingo@140:      * @param context The context object.
ingo@140:      */
ingo@140:     protected boolean isPreviousState(String stateId, Object context) {
ingo@140:         logger.debug("Determine if the state '" + stateId + "' is old.");
ingo@140: 
sascha@661:         List<String> prevs = getPreviousStateIds();
ingo@140:         if (prevs.contains(stateId)) {
ingo@140:             return true;
ingo@140:         }
ingo@140: 
ingo@140:         return false;
ingo@140:     }
ingo@317: 
ingo@317: 
ingo@317:     /**
ingo@317:      * Returns the selected River object based on the 'river' data that might
ingo@317:      * have been inserted by the user.
ingo@317:      *
ingo@317:      * @return the selected River or null if no river has been chosen yet.
ingo@317:      */
ingo@317:     public River getRiver() {
ingo@317:         StateData dRiver = getData("river");
ingo@317: 
ingo@317:         return dRiver != null
ingo@317:             ? RiverFactory.getRiver((String) dRiver.getValue())
ingo@317:             : null;
ingo@317:     }
ingo@317: 
ingo@317: 
ingo@317:     /**
ingo@317:      * Returns the selected distance of points.
ingo@317:      *
ingo@317:      * @return the selected distance or points.
ingo@317:      */
ingo@317:     public double[] getDistance() {
sascha@735:         StateData dMode      = getData("ld_mode");
ingo@383:         StateData dFrom      = getData("ld_from");
ingo@383:         StateData dTo        = getData("ld_to");
ingo@383:         StateData dLocations = getData("ld_locations");
ingo@317: 
sascha@735:         if (dMode != null) {
sascha@735:             String mode = (String)dMode.getValue();
sascha@735:             if ("location".equals(mode)) {
sascha@735:                 double[] locations = getLocations();
sascha@735:                 return new double[] { locations[0], locations[locations.length-1] };
sascha@735:             }
sascha@735:             if (dFrom != null && dTo != null) {
sascha@735:                 return getDistanceByRange(dFrom, dTo);
sascha@735:             }
sascha@735:         }
sascha@735: 
sascha@735:         if (dLocations != null) {
sascha@735:             double[] locations = getLocations();
sascha@735:             return new double[] { locations[0], locations[locations.length-1] };
sascha@735:         }
sascha@735: 
ingo@383:         if (dFrom != null && dTo != null) {
ingo@383:             return getDistanceByRange(dFrom, dTo);
ingo@383:         }
ingo@383: 
ingo@383:         logger.warn("No data found for distance determination!");
ingo@383: 
ingo@383:         return null;
ingo@383:     }
ingo@383: 
ingo@383: 
ingo@383:     /**
ingo@627:      * Determines the selected mode of distance/range input.
ingo@627:      *
ingo@627:      * @return true, if the range mode is selected otherwise false.
ingo@627:      */
ingo@627:     public boolean isRange() {
ingo@627:         StateData mode = getData("ld_mode");
ingo@627: 
ingo@627:         if (mode == null) {
ingo@627:             logger.warn("No mode location/range chosen. Defaults to range.");
ingo@627:             return true;
ingo@627:         }
ingo@627: 
ingo@627:         String value = (String) mode.getValue();
ingo@627: 
ingo@628:         return value.equals("distance");
ingo@627:     }
ingo@627: 
ingo@627: 
ingo@627:     /**
ingo@383:      * Returns the selected locations based on a given array of locations.
ingo@383:      *
ingo@383:      * @param locations The StateData that contains the locations.
ingo@383:      *
ingo@383:      * @return the selected locations.
ingo@383:      */
ingo@383:     public double[] getLocations() {
ingo@383:         StateData dLocations  = getData("ld_locations");
ingo@383:         String    locationStr = dLocations != null
ingo@383:             ? (String) dLocations.getValue()
ingo@383:             : "";
ingo@383: 
ingo@383:         if (locationStr == null || locationStr.length() == 0) {
ingo@383:             logger.warn("No valid location string found!");
ingo@383:             return null;
ingo@383:         }
ingo@383: 
ingo@383:         String[] tmp               = locationStr.split(" ");
ingo@383:         TDoubleArrayList locations = new TDoubleArrayList();
ingo@383: 
ingo@383:         for (String l: tmp) {
ingo@383:             try {
ingo@383:                 locations.add(Double.parseDouble(l));
ingo@383:             }
ingo@383:             catch (NumberFormatException nfe) {
ingo@383:                 logger.warn(nfe, nfe);
ingo@383:             }
ingo@383:         }
ingo@383: 
ingo@383:         locations.sort();
ingo@383: 
ingo@383:         return locations.toNativeArray();
ingo@383:     }
ingo@383: 
ingo@383: 
ingo@383:     /**
ingo@383:      * Returns the selected distance based on a given range (from, to).
ingo@383:      *
ingo@383:      * @param dFrom The StateData that contains the lower value.
ingo@383:      * @param dTo The StateData that contains the upper value.
ingo@383:      *
ingo@383:      * @return the selected distance.
ingo@383:      */
ingo@383:     protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
ingo@317:         double from = Double.parseDouble((String) dFrom.getValue());
ingo@317:         double to   = Double.parseDouble((String) dTo.getValue());
ingo@317: 
ingo@317:         return new double[] { from, to };
ingo@317:     }
ingo@317: 
ingo@317: 
ingo@317:     /**
ingo@362:      * Returns the selected Kms.
ingo@362:      *
ingo@402:      * @param distance An 2dim array with [lower, upper] values.
ingo@402:      *
ingo@362:      * @return the selected Kms.
ingo@362:      */
ingo@402:     public double[] getKms(double[] distance) {
ingo@362:         StateData dStep = getData("ld_step");
ingo@362: 
ingo@362:         if (dStep == null) {
ingo@362:             logger.warn("No step width given. Cannot compute Kms.");
ingo@362:             return null;
ingo@362:         }
ingo@362: 
ingo@402:         double step = Double.parseDouble((String) dStep.getValue());
ingo@362: 
ingo@362:         // transform step from 'm' into 'km'
ingo@362:         step = step / 1000;
ingo@362: 
ingo@398:         if (step == 0d) {
ingo@398:             step = DEFAULT_KM_STEPS;
ingo@398:         }
ingo@398: 
sascha@707:         return DoubleUtil.explode(distance[0], distance[1], step);
ingo@362:     }
ingo@362: 
ingo@362: 
ingo@362:     /**
ingo@402:      * Returns the selected Kms.
ingo@402:      *
ingo@402:      * @return the selected kms.
ingo@402:      */
ingo@402:     public double[] getKms() {
ingo@628:         if (isRange()) {
ingo@628:             double[] distance = getDistance();
ingo@628:             return getKms(distance);
ingo@628: 
ingo@628:         }
ingo@628:         else {
ingo@628:             return LocationDistanceSelect.getLocations(this);
ingo@628:         }
ingo@402:     }
ingo@402: 
sascha@655:     public double [] getFromToStep() {
sascha@655:         if (!isRange()) {
sascha@655:             return null;
sascha@655:         }
sascha@655:         double [] fromTo = getDistance();
sascha@655: 
sascha@655:         if (fromTo == null) {
sascha@655:             return null;
sascha@655:         }
sascha@655: 
sascha@655:         StateData dStep = getData("ld_step");
sascha@655:         if (dStep == null) {
sascha@655:             return null;
sascha@655:         }
sascha@655: 
sascha@655:         double [] result = new double[3];
sascha@655:         result[0] = fromTo[0];
sascha@655:         result[1] = fromTo[1];
sascha@655: 
sascha@655:         try {
sascha@655:             String step = (String)dStep.getValue();
sascha@655:             result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d);
sascha@655:         }
sascha@655:         catch (NumberFormatException nfe) {
sascha@655:             return null;
sascha@655:         }
sascha@655: 
sascha@655:         return result;
sascha@655:     }
sascha@655: 
ingo@402: 
ingo@402:     /**
ingo@317:      * Returns the gauge based on the current distance and river.
ingo@317:      *
ingo@317:      * @return the gauge.
ingo@317:      */
ingo@317:     public Gauge getGauge() {
ingo@317:         River    river = getRiver();
sascha@735: 
sascha@735:         if (river == null) {
sascha@735:             logger.debug("no river found");
sascha@735:             return null;
sascha@735:         }
sascha@735: 
ingo@317:         double[] dist  = getDistance();
ingo@317: 
sascha@735:         if (dist == null) {
sascha@735:             logger.debug("no range found");
sascha@735:             return null;
sascha@735:         }
sascha@735: 
ingo@317:         if (logger.isDebugEnabled()) {
ingo@317:             logger.debug("Determine gauge for:");
ingo@317:             logger.debug("... river: " + river.getName());
ingo@317:             logger.debug("... distance: " + dist[0] + " - " + dist[1]);
ingo@317:         }
ingo@317: 
ingo@320:         Gauge gauge = river.determineGauge(dist[0], dist[1]);
ingo@317: 
ingo@320:         String name = gauge != null ? gauge.getName() : "'n/a";
ingo@320:         logger.debug("Found gauge: " + name);
ingo@320: 
ingo@320:         return gauge;
ingo@317:     }
ingo@362: 
ingo@362: 
ingo@362:     /**
ingo@399:      * Returns the gauges that match the selected kilometer range.
ingo@399:      *
ingo@399:      * @return the gauges based on the selected kilometer range.
ingo@399:      */
ingo@399:     public List<Gauge> getGauges() {
sascha@698: 
sascha@698:         River river = getRiver();
sascha@698:         if (river == null) {
sascha@698:             return null;
sascha@698:         }
sascha@698: 
sascha@698:         double [] dist  = getDistance();
sascha@698:         if (dist == null) {
sascha@698:             return null;
sascha@698:         }
ingo@399: 
ingo@399:         return river.determineGauges(dist[0], dist[1]);
ingo@399:     }
ingo@399: 
ingo@399: 
ingo@399:     /**
ingo@362:      * This method returns the Q values.
ingo@362:      *
ingo@362:      * @return the selected Q values or null, if no Q values are selected.
ingo@362:      */
ingo@362:     public double[] getQs() {
ingo@631:         StateData dMode      = getData("wq_mode");
ingo@631:         StateData dSelection = getData("wq_selection");
ingo@377: 
ingo@377:         String mode = dMode != null ? (String) dMode.getValue() : "";
ingo@631:         String sel  = dSelection != null ? (String)dSelection.getValue() : null;
ingo@377: 
ingo@377:         if (mode.equals("Q")) {
ingo@631:             if (sel != null && sel.equals("single")) {
ingo@379:                 return getSingleWQValues();
ingo@379:             }
ingo@379:             else {
ingo@379:                 return getWQTriple();
ingo@379:             }
ingo@377:         }
ingo@377:         else {
ingo@377:             logger.warn("You try to get Qs, but W has been inserted.");
ingo@377:             return null;
ingo@377:         }
ingo@377:     }
ingo@377: 
ingo@685: 
sascha@455:     public boolean isQ() {
sascha@455:         StateData mode = getData("wq_mode");
sascha@455:         return mode != null && mode.getValue().equals("Q");
sascha@455:     }
sascha@455: 
ingo@377: 
ingo@377:     /**
ingo@685:      * Returns true, if the parameter is set to compute data on a free range.
ingo@685:      * Otherwise it returns false, which tells the calculation that it is bound
ingo@685:      * to a gauge.
ingo@685:      *
ingo@685:      * @return true, if the calculation should compute on a free range otherwise
ingo@685:      * false and the calculation is bound to a gauge.
ingo@685:      */
ingo@685:     public boolean isFreeQ() {
ingo@685:         StateData mode  = getData("wq_free");
ingo@685:         String    value = mode != null ? (String) mode.getValue() : null;
ingo@685: 
sascha@738:         logger.debug("isFreeQ: " + value);
sascha@738: 
sascha@738:         if (value == null) {
ingo@685:             return false;
ingo@685:         }
ingo@685: 
ingo@685:         return Boolean.valueOf(value);
ingo@685:     }
ingo@685: 
ingo@685: 
ingo@685:     /**
ingo@402:      * Returns the Q values based on a specified kilometer range.
ingo@402:      *
ingo@402:      * @param range A 2dim array with lower and upper kilometer range.
ingo@402:      *
ingo@402:      * @return an array of Q values.
ingo@402:      */
ingo@402:     public double[] getQs(double[] range) {
ingo@402:         StateData dMode   = getData("wq_mode");
ingo@402:         StateData dValues = getData("wq_values");
ingo@402: 
ingo@402:         String mode = dMode != null ? (String) dMode.getValue() : "";
ingo@402: 
ingo@402:         if (mode.equals("Q")) {
ingo@402:             return getWQForDist(range);
ingo@402:         }
ingo@402: 
ingo@402:         logger.warn("You try to get Qs, but Ws has been inserted.");
ingo@402:         return null;
ingo@402:     }
ingo@402: 
ingo@402: 
ingo@402:     /**
ingo@402:      * Returns the W values based on a specified kilometer range.
ingo@402:      *
ingo@402:      * @param range A 2dim array with lower and upper kilometer range.
ingo@402:      *
ingo@402:      * @return an array of W values.
ingo@402:      */
ingo@402:     public double[] getWs(double[] range) {
ingo@402:         StateData dMode   = getData("wq_mode");
ingo@402:         StateData dValues = getData("wq_values");
ingo@402: 
ingo@402:         String mode = dMode != null ? (String) dMode.getValue() : "";
ingo@402: 
ingo@402:         if (mode.equals("W")) {
ingo@402:             return getWQForDist(range);
ingo@402:         }
ingo@402: 
ingo@402:         logger.warn("You try to get Ws, but Qs has been inserted.");
ingo@402:         return null;
ingo@402:     }
ingo@402: 
ingo@402: 
ingo@402:     /**
ingo@377:      * This method returns the W values.
ingo@377:      *
ingo@377:      * @return the selected W values or null, if no W values are selected.
ingo@377:      */
ingo@377:     public double[] getWs() {
ingo@379:         StateData dMode   = getData("wq_mode");
ingo@379:         StateData dSingle = getData("wq_single");
ingo@377: 
ingo@377:         String mode = dMode != null ? (String) dMode.getValue() : "";
ingo@377: 
ingo@377:         if (mode.equals("W")) {
ingo@379:             if (dSingle != null) {
ingo@379:                 return getSingleWQValues();
ingo@379:             }
ingo@379:             else {
ingo@379:                 return getWQTriple();
ingo@379:             }
ingo@377:         }
ingo@377:         else {
ingo@377:             logger.warn("You try to get Qs, but W has been inserted.");
ingo@377:             return null;
ingo@377:         }
ingo@377:     }
ingo@377: 
sascha@650:     public List<Segment> getSegments() {
sascha@650:         StateData wqValues = getData("wq_values");
sascha@650:         if (wqValues == null) {
sascha@650:             logger.warn("no wq_values given");
sascha@650:             return Collections.emptyList();
sascha@650:         }
sascha@650:         String input = (String)wqValues.getValue();
sascha@650:         if (input == null || (input = input.trim()).length() == 0) {
sascha@650:             logger.warn("wq_values are empty");
sascha@650:             return Collections.emptyList();
sascha@650:         }
sascha@650:         return Segment.parseSegments(input);
sascha@650:     }
sascha@650: 
ingo@377: 
ingo@377:     /**
ingo@377:      * Returns the Qs for a number of Ws. This method makes use of
ingo@377:      * DischargeTables.getQForW().
ingo@377:      *
ingo@377:      * @param ws An array of W values.
ingo@377:      *
ingo@377:      * @return an array of Q values.
ingo@377:      */
ingo@377:     public double[] getQsForWs(double[] ws) {
sascha@735: 
sascha@735:         boolean debug = logger.isDebugEnabled();
sascha@735: 
sascha@735:         if (debug) {
sascha@735:             logger.debug("FLYSArtifact.getQsForWs");
sascha@735:         }
ingo@377: 
ingo@377:         River r = getRiver();
sascha@735:         if (r == null) {
sascha@735:             logger.warn("no river found");
sascha@735:             return null;
sascha@735: 
sascha@735:         }
sascha@735: 
sascha@735:         double [] range = getDistance();
sascha@735:         if (range == null) {
sascha@735:             logger.warn("no ranges found");
sascha@735:             return null;
sascha@735:         }
sascha@735: 
sascha@735:         if (debug) {
sascha@735:             logger.debug("range: " + Arrays.toString(range));
sascha@735:         }
sascha@735: 
sascha@735:         Gauge g = r.determineGaugeByPosition(range[0]);
sascha@735:         if (g == null) {
sascha@735:             logger.warn("no gauge found for km: " + range[0]);
sascha@735:             return null;
sascha@735:         }
sascha@735: 
sascha@735:         if (debug) {
sascha@735:             logger.debug("convert w->q with gauge '" + g.getName() + "'");
sascha@735:         }
ingo@377: 
ingo@377:         DischargeTables dt = new DischargeTables(r.getName(), g.getName());
ingo@377:         Map<String, double [][]>  tmp = dt.getValues();
ingo@377: 
ingo@377:         double[][] values = tmp.get(g.getName());
ingo@377:         double[]   qs     = new double[ws.length];
ingo@377: 
ingo@377:         for (int i = 0; i < ws.length; i++) {
ingo@377:             qs[i] = dt.getQForW(values, ws[i]);
sascha@735:             if (debug) {
sascha@735:                 logger.debug("w: " + ws[i] + " -> q: " + qs[i]);
sascha@735:             }
ingo@377:         }
ingo@377: 
ingo@377:         return qs;
ingo@377:     }
ingo@377: 
ingo@377:     /**
ingo@402:      * This method returns the given W or Q values for a specific range
ingo@402:      * (inserted in the WQ input panel for discharge longitudinal sections).
ingo@402:      *
ingo@402:      * @param dist A 2dim array with lower und upper kilometer values.
ingo@402:      *
ingo@402:      * @return an array of W or Q values.
ingo@402:      */
ingo@402:     protected double[] getWQForDist(double[] dist) {
ingo@402:         logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
ingo@402:         StateData data = getData("wq_values");
ingo@402: 
ingo@402:         if (data == null) {
ingo@402:             logger.warn("Missing wq values!");
ingo@402:             return null;
ingo@402:         }
ingo@402: 
ingo@402:         String dataString = (String) data.getValue();
ingo@402:         String[]   ranges = dataString.split(":");
ingo@402: 
ingo@402:         for (String range: ranges) {
ingo@402:             String[] parts = range.split(";");
ingo@402: 
ingo@402:             double lower = Double.parseDouble(parts[0]);
ingo@402:             double upper = Double.parseDouble(parts[1]);
ingo@402: 
ingo@402:             if (lower <= dist[0] && upper >= dist[1]) {
ingo@402:                 String[] values = parts[2].split(",");
ingo@402: 
ingo@402:                 int      num = values.length;
ingo@402:                 double[] res = new double[num];
ingo@402: 
ingo@402:                 for (int i = 0; i < num; i++) {
ingo@402:                     try {
ingo@402:                         res[i] = Double.parseDouble(values[i]);
ingo@402:                     }
ingo@402:                     catch (NumberFormatException nfe) {
ingo@402:                         logger.warn(nfe, nfe);
ingo@402:                     }
ingo@402:                 }
ingo@402: 
ingo@402:                 return res;
ingo@402:             }
ingo@402:         }
ingo@402: 
ingo@402:         logger.warn("Specified range for WQ not found!");
ingo@402: 
ingo@402:         return null;
ingo@402:     }
ingo@402: 
ingo@402: 
ingo@402:     /**
ingo@377:      * This method returns an array of inserted WQ triples that consist of from,
ingo@377:      * to and the step width.
ingo@377:      *
ingo@377:      * @return an array of from, to and step width.
ingo@377:      */
ingo@377:     protected double[] getWQTriple() {
ingo@362:         StateData dFrom = getData("wq_from");
ingo@362:         StateData dTo   = getData("wq_to");
ingo@362: 
ingo@362:         if (dFrom == null || dTo == null) {
ingo@379:             logger.warn("Missing start or end value for range.");
ingo@362:             return null;
ingo@362:         }
ingo@362: 
ingo@362:         double from = Double.parseDouble((String) dFrom.getValue());
ingo@362:         double to   = Double.parseDouble((String) dTo.getValue());
ingo@362: 
ingo@362:         StateData dStep = getData("wq_step");
ingo@362: 
ingo@362:         if (dStep == null) {
ingo@362:             logger.warn("No step width given. Cannot compute Qs.");
ingo@362:             return null;
ingo@362:         }
ingo@362: 
ingo@362:         double step  = Double.parseDouble((String) dStep.getValue());
ingo@362: 
ingo@362:         // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
ingo@362:         // width. Maybe, we should round the value to a number of digits.
ingo@362:         if (step == 0d) {
ingo@362:             double diff = to - from;
ingo@362:             step = diff / DEFAULT_Q_STEPS;
ingo@362:         }
ingo@362: 
sascha@707:         return DoubleUtil.explode(from, to, step);
ingo@362:     }
ingo@362: 
ingo@362: 
ingo@362:     /**
ingo@379:      * Returns an array of inserted WQ double values stored as whitespace
ingo@379:      * separated list.
ingo@379:      *
ingo@379:      * @return an array of W or Q values.
ingo@379:      */
ingo@379:     protected double[] getSingleWQValues() {
ingo@379:         StateData dSingle = getData("wq_single");
ingo@379: 
ingo@379:         if (dSingle == null) {
ingo@379:             logger.warn("Cannot determine single WQ values. No data given.");
ingo@379:             return null;
ingo@379:         }
ingo@379: 
ingo@379:         String   tmp       = (String) dSingle.getValue();
ingo@379:         String[] strValues = tmp.split(" ");
ingo@379: 
ingo@379:         TDoubleArrayList values = new TDoubleArrayList();
ingo@379: 
ingo@379:         for (String strValue: strValues) {
ingo@379:             try {
ingo@379:                 values.add(Double.parseDouble(strValue));
ingo@379:             }
ingo@379:             catch (NumberFormatException nfe) {
ingo@379:                 logger.warn(nfe, nfe);
ingo@379:             }
ingo@379:         }
ingo@379: 
ingo@379:         values.sort();
ingo@379: 
ingo@379:         return values.toNativeArray();
ingo@379:     }
ingo@379: 
ingo@379: 
ingo@379:     /**
ingo@686:      * Computes the hash code of the entered values.
ingo@686:      *
ingo@686:      * @return a hash code.
ingo@686:      */
ingo@686:     @Override
ingo@686:     public String hash() {
ingo@686:         Set<Map.Entry<String, StateData>> entries = data.entrySet();
ingo@686: 
sascha@705:         long hash  = 0L;
sascha@705:         int  shift = 3;
ingo@686: 
ingo@686:         for (Map.Entry<String, StateData> entry: entries) {
ingo@686:             String key   = entry.getKey();
ingo@686:             Object value = entry.getValue().getValue();
ingo@686: 
sascha@742:             hash ^= ((long)key.hashCode() << shift)
sascha@722:                  |  ((long)value.hashCode() << (shift + 3));
ingo@686:             shift += 2;
ingo@686:         }
ingo@686: 
ingo@686:         return getCurrentStateId() + hash;
ingo@686:     }
ingo@686: 
ingo@686: 
ingo@944:     public List<Output> getOutputs(CallContext context) {
ingo@965:         List<String> stateIds  = getPreviousStateIds();
ingo@944:         List<Output> generated = new ArrayList<Output>();
ingo@944: 
ingo@944:         FLYSContext flysContext = getFlysContext(context);
ingo@944:         StateEngine engine      = (StateEngine) flysContext.get(
ingo@944:             FLYSContext.STATE_ENGINE_KEY);
ingo@944: 
ingo@944:         for (String stateId: stateIds) {
ingo@944:             DefaultState state = (DefaultState) engine.getState(stateId);
sascha@981:             generated.addAll(getOutputForState(state));
ingo@944:         }
ingo@944: 
ingo@965:         generated.addAll(getCurrentOutputs(context));
ingo@944: 
ingo@944:         return generated;
ingo@944:     }
ingo@944: 
ingo@944: 
ingo@965:     public List<Output> getCurrentOutputs(CallContext context) {
ingo@965:         DefaultState cur = (DefaultState) getCurrentState(context);
ingo@965: 
ingo@965:         try {
ingo@965:             if (cur.validate(this, context)) {
sascha@981:                 return getOutputForState(cur);
ingo@965:             }
ingo@965:         }
ingo@965:         catch (IllegalArgumentException iae) { }
ingo@965: 
ingo@965:         return new ArrayList<Output>();
ingo@965:     }
ingo@965: 
sascha@981:     public List<Output> getCurrentOutputs(Object context) {
sascha@981:         return getOutputForState((DefaultState)getCurrentState(context));
sascha@981:     }
ingo@965: 
sascha@981:     protected List<Output> getOutputForState(DefaultState state) {
ingo@965:         List<Output> list = state.getOutputs();
ingo@965:         if (list == null || list.size() == 0) {
ingo@965:             logger.debug("-> No output modes for this state.");
ingo@965:             return new ArrayList<Output>();
ingo@965:         }
ingo@965: 
ingo@965:         List<Facet>  fs = facets.get(state.getID());
ingo@965:         if (fs == null || fs.size() == 0) {
ingo@965:             logger.debug("No facets found.");
ingo@965:             return new ArrayList<Output>();
ingo@965:         }
ingo@965: 
ingo@965:         return generateOutputs(list, fs);
ingo@965:     }
ingo@965: 
ingo@965: 
ingo@937:     protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) {
ingo@937:         List<Output> generated = new ArrayList<Output>();
ingo@937: 
ingo@937:         boolean debug = logger.isDebugEnabled();
ingo@937: 
ingo@937:         for (Output out: list) {
ingo@937:             Output o = new DefaultOutput(
ingo@937:                 out.getName(),
ingo@937:                 out.getDescription(),
ingo@937:                 out.getMimeType(),
ingo@937:                 out.getType());
ingo@937: 
ingo@937:             Set<String> outTypes = new HashSet<String>();
ingo@937: 
ingo@937:             for (Facet f: out.getFacets()) {
ingo@937:                 if (outTypes.add(f.getName()) && debug) {
ingo@937:                     logger.debug("configured facet " + f);
ingo@937:                 }
ingo@937:             }
ingo@937: 
ingo@937:             boolean facetAdded = false;
ingo@937:             for (Facet f: fs) {
ingo@937:                 String type = f.getName();
ingo@937: 
ingo@937:                 if (outTypes.contains(type)) {
ingo@937:                     if (debug) {
ingo@937:                         logger.debug("Add facet " + f);
ingo@937:                     }
ingo@937:                     facetAdded = true;
ingo@937:                     o.addFacet(f);
ingo@937:                 }
ingo@937:             }
ingo@937: 
ingo@937:             if (facetAdded) {
ingo@937:                 generated.add(o);
ingo@937:             }
ingo@937:         }
ingo@937: 
ingo@937:         return generated;
ingo@937:     }
ingo@937: 
ingo@937: 
ingo@689:     /**
ingo@689:      * Dispatches the computation request to compute(CallContext context, String
ingo@689:      * hash) with the current hash value of the artifact which is provided by
ingo@689:      * hash().
ingo@689:      *
ingo@689:      * @param context The CallContext.
ingo@689:      */
sascha@705:     public Object compute(
sascha@742:         CallContext context,
sascha@742:         ComputeType type,
sascha@705:         boolean     generateFacets
sascha@705:     ) {
sascha@705:         return compute(context, hash(), type, generateFacets);
ingo@689:     }
ingo@689: 
ingo@689: 
ingo@689:     /**
ingo@689:      * Dispatches computation requests to the current state which needs to
ingo@689:      * implement a createComputeCallback(String hash, FLYSArtifact artifact)
ingo@689:      * method.
ingo@689:      *
ingo@689:      * @param context The CallContext.
ingo@689:      * @param hash The hash value which is used to fetch computed data from
ingo@689:      * cache.
ingo@689:      *
ingo@689:      * @return the computed data.
ingo@689:      */
sascha@705:     public Object compute(
sascha@705:         CallContext context,
sascha@705:         String      hash,
sascha@742:         ComputeType type,
sascha@705:         boolean     generateFacets
sascha@705:     ) {
ingo@693:         DefaultState current = (DefaultState) getCurrentState(context);
sascha@722:         return compute(context, hash, current, type, generateFacets);
sascha@722:     }
ingo@689: 
sascha@722:     public Object compute(
sascha@722:         CallContext context,
sascha@722:         String      stateID,
sascha@722:         String      hash,
sascha@742:         ComputeType type,
sascha@722:         boolean     generateFacets
sascha@722:     ) {
sascha@722:         DefaultState current = stateID == null
sascha@722:             ? (DefaultState)getCurrentState(context)
sascha@722:             : (DefaultState)getState(context, stateID);
sascha@722: 
sascha@722:         if (hash == null) {
sascha@722:             hash = hash();
sascha@722:         }
ingo@689: 
sascha@705:         return compute(context, hash, current, type, generateFacets);
ingo@689:     }
ingo@689: 
ingo@690:     public Object compute(
sascha@697:         CallContext   context,
sascha@697:         String        key,
sascha@697:         DefaultState  state,
sascha@705:         ComputeType   type,
sascha@705:         boolean       generateFacets
sascha@697:     ) {
sascha@697:         String stateID = state.getID();
ingo@691: 
sascha@705:         List<Facet> fs = generateFacets ? new ArrayList<Facet>() : null;
sascha@697: 
sascha@697:         try {
sascha@697:             Cache cache = CacheFactory.getCache(COMPUTING_CACHE);
sascha@697: 
sascha@697:             Object old = null;
sascha@697: 
sascha@697:             if (cache != null) {
sascha@697:                 net.sf.ehcache.Element element = cache.get(key);
sascha@697:                 if (element != null) {
sascha@697:                     logger.debug("Got computation result from cache.");
sascha@697:                     old = element.getValue();
sascha@697:                 }
sascha@697:             }
sascha@697: 
sascha@697:             Object res;
ingo@690:             switch (type) {
ingo@690:                 case FEED:
sascha@697:                     res = state.computeFeed(this, key, context, fs, old);
sascha@697:                     break;
sascha@697:                 case ADVANCE:
sascha@700:                     res = state.computeAdvance(this, key, context, fs, old);
sascha@697:                     break;
ingo@942:                 case INIT:
ingo@958:                     res = state.computeInit(this, key, context, context.getMeta(), fs);
sascha@697:                 default:
sascha@697:                     res = null;
sascha@697:             }
ingo@690: 
sascha@697:             if (cache != null && old != res && res != null) {
sascha@697:                 logger.debug("Store computation result to cache.");
sascha@697:                 net.sf.ehcache.Element element =
sascha@697:                     new net.sf.ehcache.Element(key, res);
sascha@697:                 cache.put(element);
sascha@697:             }
sascha@697: 
sascha@697:             return res;
sascha@697:         }
sascha@697:         finally {
sascha@705:             if (generateFacets) {
sascha@705:                 if (fs.isEmpty()) {
sascha@705:                     facets.remove(stateID);
sascha@705:                 }
sascha@705:                 else {
sascha@705:                     facets.put(stateID, fs);
sascha@705:                 }
ingo@690:             }
ingo@686:         }
ingo@686:     }
ingo@686: 
ingo@686: 
ingo@623:     /**
ingo@623:      * Method to dump the artifacts state/data.
ingo@623:      */
ingo@623:     protected void dumpArtifact() {
ingo@623:         if (logger.isDebugEnabled()) {
ingo@623:             logger.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
ingo@623: 
ingo@623:             logger.debug("------ DUMP DATA ------");
ingo@623:             Collection<StateData> allData = data.values();
ingo@623: 
ingo@623:             for (StateData d: allData) {
ingo@623:                 String name  = d.getName();
ingo@623:                 String value = (String) d.getValue();
ingo@623: 
ingo@623:                 logger.debug("- " + name + ": " + value);
ingo@623:             }
ingo@623: 
ingo@623:             logger.debug("------ DUMP PREVIOUS STATES ------");
sascha@661:             List<String> stateIds = getPreviousStateIds();
ingo@623: 
ingo@623:             for (String id: stateIds) {
ingo@623:                 logger.debug("- State: " + id);
ingo@623:             }
ingo@623: 
ingo@623:             logger.debug("CURRENT STATE: " + getCurrentStateId());
ingo@623: 
ingo@623:             logger.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++");
ingo@623:         }
ingo@623:     }
ingo@119: }
ingo@119: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :