ingo@119: package de.intevation.flys.artifacts;
ingo@119:
sascha@1055: import de.intevation.artifactdatabase.ArtifactDatabaseImpl;
sascha@1055: import de.intevation.artifactdatabase.DefaultArtifact;
ingo@121:
felix@1724: import de.intevation.artifactdatabase.data.DefaultStateData;
sascha@1055: import de.intevation.artifactdatabase.data.StateData;
ingo@379:
sascha@1057: import de.intevation.artifactdatabase.state.DefaultFacet;
sascha@1055: import de.intevation.artifactdatabase.state.DefaultOutput;
sascha@1055: import de.intevation.artifactdatabase.state.Facet;
sascha@1055: import de.intevation.artifactdatabase.state.Output;
sascha@1055: import de.intevation.artifactdatabase.state.State;
sascha@1055: import de.intevation.artifactdatabase.state.StateEngine;
ingo@686:
sascha@1055: import de.intevation.artifactdatabase.transition.TransitionEngine;
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;
sascha@1055:
ingo@119: import de.intevation.artifacts.common.utils.XMLUtils;
ingo@119:
sascha@1055: import de.intevation.flys.artifacts.cache.CacheFactory;
ingo@317:
ingo@119: import de.intevation.flys.artifacts.context.FLYSContext;
sascha@650:
felix@1777: import de.intevation.flys.artifacts.states.DefaultState;
sascha@1055: import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
sascha@650:
felix@1777: import de.intevation.flys.utils.FLYSUtils;
ingo@119:
sascha@1055: import java.util.ArrayList;
sascha@1055: import java.util.Collection;
sascha@1055: import java.util.HashMap;
sascha@1055: import java.util.HashSet;
sascha@1055: import java.util.List;
sascha@1055: import java.util.Map;
sascha@1055: import java.util.Set;
sascha@1055: import java.util.TreeMap;
sascha@1055:
sascha@1055: import javax.xml.xpath.XPathConstants;
sascha@1055:
sascha@1055: import net.sf.ehcache.Cache;
sascha@1055:
sascha@1055: import org.apache.log4j.Logger;
sascha@1055:
sascha@1055: import org.w3c.dom.Document;
sascha@1055: import org.w3c.dom.Element;
sascha@1055: import org.w3c.dom.NodeList;
ingo@119:
ingo@119: /**
felix@2169: * The default FLYS artifact with convenience added.
felix@2169: * (Subclass to get fully functional artifacts).
ingo@119: *
ingo@119: * @author Ingo Weinzierl
ingo@119: */
ingo@119: public abstract class FLYSArtifact extends DefaultArtifact {
ingo@119:
felix@1704: /** The logger that is used in this artifact. */
ingo@119: private static Logger logger = Logger.getLogger(FLYSArtifact.class);
ingo@119:
ingo@686: public static final String COMPUTING_CACHE = "computed.values";
ingo@686:
felix@1704: /** 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:
felix@1704: /** 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:
sascha@1057: public static final String XPATH_FILTER =
sascha@1057: "/art:action/art:filter/art:out";
sascha@1057:
felix@1704: /** The constant string that shows that an operation was successful. */
ingo@122: public static final String OPERATION_SUCCESSFUL = "SUCCESS";
ingo@122:
felix@1704: /** The constant string that shows that an operation failed. */
ingo@122: public static final String OPERATION_FAILED = "FAILURE";
ingo@122:
ingo@119: /** The identifier of the current state. */
ingo@119: protected String currentStateId;
ingo@119:
felix@1704: /** The identifiers of previous states on a stack. */
sascha@661: protected List previousStateIds;
ingo@122:
felix@1704: /** The name of the artifact. */
ingo@119: protected String name;
ingo@119:
felix@1704: /** The data that have been inserted into this artifact. */
ingo@121: protected Map data;
ingo@121:
felix@1771: /** Mapping of state names to created facets. */
felix@1771: protected Map> facets;
ingo@687:
felix@1704: /**
felix@1704: * Used to generates "view" on the facets (hides facets not matching the
felix@1704: * filter in output of collection); out -> facets.
felix@1704: */
sascha@1056: protected Map> filterFacets;
sascha@1056:
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();
sascha@661: previousStateIds = new ArrayList();
felix@1771: facets = new HashMap>();
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: */
sascha@1059: public String getName() {
sascha@1059: return name;
sascha@1059: }
ingo@121:
ingo@119:
ingo@119: /**
ingo@119: * Initialize the artifact and insert new data if data
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:
felix@1777: FLYSContext flysContext = FLYSUtils.getFlysContext(context);
felix@1067:
felix@1067: List states = getStates(context);
ingo@119:
ingo@121: String name = getName();
ingo@121: logger.debug("Set initial state for artifact '" + name + "'");
ingo@119:
felix@2169: if (states == null) {
felix@2766: logger.error("No states found from which an initial "
felix@2766: + "state could be picked.");
felix@2169: }
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: }
sascha@1057:
sascha@1057: filterFacets = buildFilterFacets(data);
ingo@940: }
ingo@940:
felix@1763:
felix@2141: /** Get copy of previous state ids as Strings in list. */
sascha@1059: protected List clonePreviousStateIds() {
sascha@1059: return new ArrayList(previousStateIds);
sascha@1059: }
sascha@1059:
felix@2141:
felix@1895: /**
felix@1895: * Copies data item from other artifact to this artifact.
felix@2111: *
felix@1895: * @param other Artifact from which to get data.
felix@1895: * @param name Name of data.
felix@1895: */
felix@1895: protected void importData(FLYSArtifact other, final String name) {
felix@1895: if (other == null) {
felix@1895: logger.error("No other art. to import data " + name + " from.");
felix@2111: return;
felix@1895: }
felix@1895:
felix@1895: StateData sd = other.getData(name);
felix@1895:
felix@1895: if (sd == null) {
felix@1895: logger.warn("Other artifact has no data " + name + ".");
felix@1895: return;
felix@1895: }
felix@1895:
felix@1895: this.addData(name, sd);
felix@1895: }
felix@1763:
felix@2169:
sascha@1059: protected Map cloneData() {
sascha@1059: Map copy = new TreeMap();
sascha@1059:
sascha@1059: for (Map.Entry entry: data.entrySet()) {
sascha@1059: copy.put(entry.getKey(), entry.getValue().deepCopy());
sascha@1059: }
sascha@1059:
sascha@1059: return copy;
sascha@1059: }
sascha@1059:
felix@1763: /**
felix@1763: * Return a copy of the facet mapping.
felix@1771: * @return Mapping of state-ids to facets.
felix@1763: */
felix@1771: protected Map> cloneFacets() {
felix@1771: Map copy = new HashMap>();
sascha@1059:
felix@1771: for (Map.Entry> entry: facets.entrySet()) {
felix@1771: List facets = entry.getValue();
felix@1771: List facetCopies = new ArrayList(facets.size());
felix@1771: for (Facet facet: facets) {
felix@1771: facetCopies.add(facet.deepCopy());
felix@1771: }
felix@1771: copy.put(entry.getKey(), facetCopies);
sascha@1059: }
sascha@1059:
sascha@1059: return copy;
sascha@1059: }
ingo@940:
felix@1763:
felix@1765: /**
felix@1765: * (called from setup).
felix@1771: * @param artifact master-artifact (if any, otherwise initialize is not called).
felix@1765: */
ingo@952: protected void initialize(
ingo@952: Artifact artifact,
ingo@952: Object context,
ingo@952: CallMeta callMeta)
ingo@952: {
sascha@1059: if (!(artifact instanceof FLYSArtifact)) {
sascha@1059: return;
sascha@1059: }
sascha@1059:
sascha@1059: FLYSArtifact flys = (FLYSArtifact)artifact;
sascha@1059:
sascha@1059: currentStateId = flys.currentStateId;
sascha@1059: previousStateIds = flys.clonePreviousStateIds();
sascha@1059: name = flys.name;
sascha@1059: data = flys.cloneData();
sascha@1059: facets = flys.cloneFacets();
sascha@1059: // Do not clone filter facets!
ingo@2093:
ingo@2606: ArrayList stateIds = (ArrayList) getPreviousStateIds();
ingo@2606: ArrayList toInitialize = (ArrayList) stateIds.clone();
ingo@2093:
ingo@2606: toInitialize.add(getCurrentStateId());
ingo@2606:
ingo@2606: for (String stateId: toInitialize) {
ingo@2093: State state = getState(context, stateId);
ingo@2093:
ingo@2093: if (state != null) {
ingo@2095: state.initialize(artifact, this, context, callMeta);
ingo@2093: }
ingo@2093: }
ingo@119: }
ingo@119:
felix@1704:
felix@1704: /**
felix@1704: * Builds filter facets from document.
felix@1704: * @see filterFacets
felix@1704: */
sascha@1057: protected Map> buildFilterFacets(Document document) {
sascha@1057:
sascha@1057: NodeList nodes = (NodeList)XMLUtils.xpath(
sascha@1057: document,
sascha@1057: XPATH_FILTER,
sascha@1057: XPathConstants.NODESET,
sascha@1057: ArtifactNamespaceContext.INSTANCE);
sascha@1057:
sascha@1057: if (nodes == null || nodes.getLength() == 0) {
sascha@1057: return null;
sascha@1057: }
sascha@1057:
sascha@1057: Map> result = new HashMap>();
sascha@1057:
sascha@1057: for (int i = 0, N = nodes.getLength(); i < N; ++i) {
sascha@1057: Element element = (Element)nodes.item(i);
sascha@1057: String oName = element.getAttribute("name");
sascha@1057: if (oName.length() == 0) {
sascha@1057: continue;
sascha@1057: }
sascha@1057:
sascha@1057: List facets = new ArrayList();
sascha@1057:
sascha@1057: NodeList facetNodes = element.getElementsByTagNameNS(
sascha@1057: ArtifactNamespaceContext.NAMESPACE_URI,
sascha@1057: "facet");
sascha@1057:
sascha@1057: for (int j = 0, M = facetNodes.getLength(); j < M; ++j) {
sascha@1057: Element facetElement = (Element)facetNodes.item(j);
sascha@1057:
sascha@1057: String fName = facetElement.getAttribute("name");
sascha@1057:
sascha@1057: int index;
sascha@1057: try {
sascha@1057: index = Integer.parseInt(facetElement.getAttribute("index"));
sascha@1057: }
sascha@1057: catch (NumberFormatException nfe) {
sascha@1057: logger.warn(nfe);
sascha@1057: index = 0;
sascha@1057: }
sascha@1057: facets.add(new DefaultFacet(index, fName, ""));
sascha@1057: }
sascha@1057:
sascha@1057: if (!facets.isEmpty()) {
sascha@1057: result.put(oName, facets);
sascha@1057: }
sascha@1057: }
sascha@1057:
sascha@1057: return result;
sascha@1057: }
sascha@1057:
ingo@119:
ingo@119: /**
ingo@119: * Insert new data included in input
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@2606: String currentStateId = getCurrentStateId();
ingo@2606: 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@2606: if (!currentStateId.equals(targetState)
ingo@2606: && isStateReachable(targetState, context))
ingo@2606: {
ingo@140: logger.info("Advance: Step forward");
ingo@122:
sascha@661: List prev = getPreviousStateIds();
ingo@2606: prev.add(currentStateId);
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:
ingo@1086: List prevs = getPreviousStateIds();
ingo@140: int targetIdx = prevs.indexOf(targetState);
ingo@140: int start = prevs.size() - 1;
ingo@140:
ingo@1086: destroyStates(prevs, context);
ingo@1084:
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@1084:
ingo@140: prevs.remove(prev);
ingo@691: facets.remove(prev);
ingo@140: }
ingo@140:
ingo@1094: destroyState(getCurrentStateId(), context);
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. NOTEWe 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 state.
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@1654: public State getCurrentState(Object context) {
felix@1067: return getState(context, getCurrentStateId());
felix@1067: }
felix@1067:
felix@1067:
felix@1067: /**
felix@1067: * Get list of existant states for this Artifact.
felix@1067: * @param context Contex to get StateEngine from.
felix@1067: * @return list of states.
felix@1067: */
felix@1067: protected List getStates(Object context) {
felix@1777: FLYSContext flysContext = FLYSUtils.getFlysContext(context);
ingo@119: StateEngine engine = (StateEngine) flysContext.get(
ingo@119: FLYSContext.STATE_ENGINE_KEY);
felix@1067: return engine.getStates(getName());
ingo@119: }
ingo@119:
felix@1067:
felix@1067: /**
felix@1067: * Get state with given ID.
felix@1067: * @param context Context to get StateEngine from.
felix@1067: * @param stateID ID of state to get.
felix@1067: * @return state with given ID.
felix@1067: */
sascha@722: protected State getState(Object context, String stateID) {
felix@1777: FLYSContext flysContext = FLYSUtils.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 getPreviousStateIds() {
ingo@122: return previousStateIds;
ingo@122: }
ingo@122:
ingo@122:
ingo@122: /**
felix@1783: * Get all previous and the current state id.
felix@1783: * @return #getPreviousStateIds() + #getCurrentStateId()
felix@1783: */
felix@1783: public List getStateHistoryIds() {
ingo@2606: ArrayList prevIds = (ArrayList) getPreviousStateIds();
ingo@2606: ArrayList allIds = (ArrayList) prevIds.clone();
ingo@2606:
felix@1783: allIds.add(getCurrentStateId());
felix@1783: return allIds;
felix@1783: }
felix@1783:
felix@1783:
felix@1783: /**
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@1656: protected StateData removeData(String name) {
ingo@1656: return this.data.remove(name);
ingo@1656: }
ingo@1656:
ingo@1656:
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:
felix@2169: /** Return named data item, null if not found. */
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:
felix@1763:
felix@1763: /**
ingo@2013: * This method returns the value of a StateData object stored in the data
ingo@2013: * pool of this Artifact as Integer.
ingo@2013: *
ingo@2013: * @param name The name of the StateData object.
ingo@2013: *
ingo@2013: * @return an Integer representing the value of the data object or null if
ingo@2013: * no object was found for name.
ingo@2013: *
ingo@2013: * @throws NumberFormatException if the value of the data object could not
ingo@2013: * be transformed into an Integer.
ingo@2013: */
ingo@2013: public Integer getDataAsInteger(String name)
ingo@2013: throws NumberFormatException
ingo@2013: {
ingo@2013: String value = getDataAsString(name);
ingo@2013:
ingo@2013: if (value != null && value.length() > 0) {
ingo@2013: return Integer.parseInt(value);
ingo@2013: }
ingo@2013:
ingo@2013: return null;
ingo@2013: }
ingo@2013:
ingo@2013:
ingo@2013: /**
ingo@2038: * This method returns the value of a StateData object stored in the data
ingo@2701: * pool of this Artifact as Double.
ingo@2701: *
ingo@2701: * @param name The name of the StateData object.
ingo@2701: *
ingo@2701: * @return an Double representing the value of the data object or null if
ingo@2701: * no object was found for name.
ingo@2701: *
ingo@2701: * @throws NumberFormatException if the value of the data object could not
ingo@2701: * be transformed into a Double.
ingo@2701: */
ingo@2701: public Double getDataAsDouble(String name)
ingo@2701: throws NumberFormatException
ingo@2701: {
ingo@2701: String value = getDataAsString(name);
ingo@2701:
ingo@2701: if (value != null && value.length() > 0) {
ingo@2701: return Double.parseDouble(value);
ingo@2701: }
ingo@2701:
ingo@2701: return null;
ingo@2701: }
ingo@2701:
ingo@2701:
ingo@2701: /**
ingo@2701: * This method returns the value of a StateData object stored in the data
ingo@2128: * pool of this Artifact as Long.
ingo@2128: *
ingo@2128: * @param name The name of the StateData object.
ingo@2128: *
ingo@2128: * @return a Long representing the value of the data object or null if
ingo@2128: * no object was found for name.
ingo@2128: *
ingo@2128: * @throws NumberFormatException if the value of the data object could not
ingo@2128: * be transformed into a Long.
ingo@2128: */
ingo@2128: public Long getDataAsLong(String name)
ingo@2128: throws NumberFormatException
ingo@2128: {
ingo@2128: String value = getDataAsString(name);
ingo@2128:
ingo@2128: if (value != null && value.length() > 0) {
ingo@2128: return Long.parseLong(value);
ingo@2128: }
ingo@2128:
ingo@2128: return null;
ingo@2128: }
ingo@2128:
ingo@2128:
ingo@2128: /**
ingo@2128: * This method returns the value of a StateData object stored in the data
ingo@2038: * pool of this Artifact is Boolean using Boolean.valueOf().
ingo@2038: *
ingo@2038: * @param name The name of the StateData object.
ingo@2038: *
ingo@2038: * @return a Boolean representing the value of the data object or null if no
ingo@2038: * such object is existing.
ingo@2038: */
ingo@2038: public Boolean getDataAsBoolean(String name) {
ingo@2038: String value = getDataAsString(name);
ingo@2038:
ingo@2038: if (value == null || value.length() == 0) {
ingo@2038: return null;
ingo@2038: }
ingo@2038:
ingo@2038: return Boolean.valueOf(value);
ingo@2038: }
ingo@2038:
ingo@2038:
ingo@2038: /**
felix@1763: * Add StateData containing a given string.
felix@1763: * @param name Name of the data object.
felix@1763: * @param value String to store.
felix@1763: */
felix@1724: public void addStringData(String name, String value) {
felix@1724: addData(name, new DefaultStateData(name, null, null, value));
felix@1724: }
felix@1724:
felix@1763:
sascha@960: public Collection getAllData() {
sascha@960: return data.values();
sascha@960: }
sascha@960:
ingo@937:
ingo@2095: public List getFacets() {
ingo@2095: List all = new ArrayList();
ingo@2095:
ingo@2095: Set>> entries = facets.entrySet();
ingo@2095: for (Map.Entry> entry: entries) {
ingo@2095: List fs = entry.getValue();
ingo@2095: for (Facet f: fs) {
ingo@2095: all.add(f);
ingo@2095: }
ingo@2095: }
ingo@2095:
ingo@2095: return all;
ingo@2095: }
ingo@2095:
ingo@2095:
felix@1771: /**
felix@1771: * Get facet as stored internally, with equalling name and index than given
felix@1771: * facet.
felix@1771: * @param facet that defines index and name of facet searched.
felix@1771: * @return facet instance or null if not found.
felix@1771: */
ingo@696: public Facet getNativeFacet(Facet facet) {
ingo@696: String name = facet.getName();
ingo@696: int index = facet.getIndex();
ingo@696:
felix@1771: for (Map.Entry> facetList: facets.entrySet()) {
felix@1771: for (Facet f: facetList.getValue()) {
felix@1771: if (f.getIndex() == index && f.getName().equals(name)) {
felix@1771: return f;
felix@1771: }
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:
ingo@1176: DefaultState current = (DefaultState) getCurrentState(context);
ingo@1176:
ingo@2205: FLYSContext flysContext = FLYSUtils.getFlysContext(context);
ingo@2205: StateEngine engine = (StateEngine) flysContext.get(
ingo@2205: FLYSContext.STATE_ENGINE_KEY);
ingo@2205:
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@2205: StateData model = engine.getStateData(getName(), name);
ingo@2205:
ingo@2205: StateData sd = model != null
ingo@2205: ? model.deepCopy()
ingo@2205: : new DefaultStateData(name, null, null, value);
ingo@2205:
ingo@2205: addData(
ingo@2205: name, current.transform(this, context, sd, name, value));
ingo@121: }
ingo@1656: else if (name.length() > 0 && value.length() == 0) {
ingo@1656: if (removeData(name) != null) {
ingo@1656: logger.debug("Removed data '" + name + "' successfully.");
ingo@1656: }
ingo@1656: }
ingo@121: }
ingo@322:
sascha@1050: current.validate(this);
ingo@122: }
ingo@122:
ingo@122:
ingo@122: /**
ingo@122: * Determines if the state with the identifier stateId 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 stateId 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:
felix@1777: FLYSContext flysContext = FLYSUtils.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 stateId 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 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@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> entries = data.entrySet();
ingo@686:
sascha@705: long hash = 0L;
sascha@705: int shift = 3;
ingo@686:
ingo@686: for (Map.Entry 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:
felix@1628:
felix@1628: /**
felix@1763: * Return List of outputs, where combinations of outputname and filtername
felix@1763: * that match content in filterFacets is left out.
felix@1763: * @return filtered Outputlist.
felix@1628: */
sascha@1056: protected List