teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
teichmann@5863: * Software engineering by Intevation GmbH
teichmann@5863: *
teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3)
teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the
teichmann@5994: * documentation coming with Dive4Elements River for details.
teichmann@5863: */
teichmann@5863:
teichmann@5831: package org.dive4elements.river.artifacts;
ingo@119:
christian@3883: import java.util.ArrayList;
christian@3883: import java.util.Collection;
christian@3883: import java.util.HashMap;
christian@3883: import java.util.HashSet;
christian@3883: import java.util.LinkedList;
christian@3883: import java.util.List;
christian@3883: import java.util.Map;
christian@3883: import java.util.Set;
christian@3883: import java.util.TreeMap;
christian@3883:
christian@3883: import javax.xml.xpath.XPathConstants;
christian@3883:
christian@3883: import net.sf.ehcache.Cache;
christian@3883:
christian@3883: import org.apache.log4j.Logger;
christian@3883: import org.w3c.dom.Document;
christian@3883: import org.w3c.dom.Element;
christian@3883: import org.w3c.dom.Node;
christian@3883: import org.w3c.dom.NodeList;
christian@3883:
teichmann@5831: import org.dive4elements.artifactdatabase.ArtifactDatabaseImpl;
teichmann@5831: import org.dive4elements.artifactdatabase.DefaultArtifact;
teichmann@5831: import org.dive4elements.artifactdatabase.ProtocolUtils;
teichmann@5831: import org.dive4elements.artifactdatabase.data.DefaultStateData;
teichmann@5831: import org.dive4elements.artifactdatabase.data.StateData;
teichmann@5831: import org.dive4elements.artifactdatabase.state.DefaultFacet;
teichmann@5831: import org.dive4elements.artifactdatabase.state.DefaultOutput;
teichmann@5831: import org.dive4elements.artifactdatabase.state.Facet;
teichmann@5831: import org.dive4elements.artifactdatabase.state.Output;
teichmann@5831: import org.dive4elements.artifactdatabase.state.State;
teichmann@5831: import org.dive4elements.artifactdatabase.state.StateEngine;
rrenkert@7845: import org.dive4elements.artifactdatabase.state.StaticFacet;
teichmann@5831: import org.dive4elements.artifactdatabase.transition.TransitionEngine;
teichmann@5831: import org.dive4elements.artifacts.Artifact;
teichmann@5831: import org.dive4elements.artifacts.ArtifactDatabase;
teichmann@5831: import org.dive4elements.artifacts.ArtifactDatabaseException;
teichmann@5831: import org.dive4elements.artifacts.ArtifactFactory;
teichmann@5831: import org.dive4elements.artifacts.CallContext;
teichmann@5831: import org.dive4elements.artifacts.CallMeta;
teichmann@5831: import org.dive4elements.artifacts.Message;
teichmann@5831: import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils;
teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
teichmann@5831: import org.dive4elements.river.artifacts.cache.CacheFactory;
teichmann@5866: import org.dive4elements.river.artifacts.context.RiverContext;
teichmann@7227: import org.dive4elements.river.artifacts.context.RiverContextFactory;
teichmann@5831: import org.dive4elements.river.artifacts.model.CalculationMessage;
teichmann@5831: import org.dive4elements.river.artifacts.states.DefaultState;
teichmann@5831: import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
teichmann@5865: import org.dive4elements.river.utils.RiverUtils;
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: */
rrenkert@7843: public class D4EArtifact extends DefaultArtifact {
ingo@119:
teichmann@8202: /** The log that is used in this artifact. */
teichmann@5867: private static Logger log = Logger.getLogger(D4EArtifact.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:
aheinecke@5975: /** Path to 'ids' (data) in doc that comes from datacage. */
aheinecke@5975: public static final String XPATH_IDS = "/art:action/art:ids/@value";
aheinecke@5975:
aheinecke@6140: /** Path to 'target_out' (data) in doc that comes from datacage. */
aheinecke@6140: public static final String XPATH_TARGET_OUT = "/art:action/art:target_out/@value";
aheinecke@5975:
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 data that have been inserted into this artifact. */
bjoern@3920: private Map data;
ingo@121:
felix@1771: /** Mapping of state names to created facets. */
bjoern@4497: private 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:
felix@7676: /** To which out this artifacts facets are bound by default. */
aheinecke@6140: private String boundToOut;
aheinecke@5975:
felix@7676:
teichmann@7227: /**
teichmann@7227: * Interface to a global facet filter.
teichmann@7227: */
teichmann@7227: public interface FacetFilter {
teichmann@7227: boolean accept(String outName, String facetName);
teichmann@7227: } // interface FacetFilter
teichmann@7227:
ingo@121:
ingo@121: /**
teichmann@5867: * The default constructor that creates an empty D4EArtifact.
ingo@121: */
teichmann@5867: public D4EArtifact() {
ingo@686: data = new TreeMap();
sascha@661: previousStateIds = new ArrayList();
felix@1771: facets = new HashMap>();
ingo@121: }
ingo@121:
felix@4147:
sascha@3193: /**
sascha@3193: * This method appends the static data - that has already been inserted by
sascha@3193: * the user - to the static node of the DESCRIBE document.
sascha@3193: *
sascha@3193: * @param doc The document.
sascha@3193: * @param ui The root node.
sascha@3193: * @param context The CallContext.
sascha@3193: * @param uuid The identifier of the artifact.
sascha@3193: */
sascha@3193: protected void appendStaticUI(
sascha@3193: Document doc,
sascha@3193: Node ui,
sascha@3193: CallContext context,
sascha@3193: String uuid)
sascha@3193: {
sascha@3193: List stateIds = getPreviousStateIds();
sascha@3193:
teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context);
sascha@3193: StateEngine engine = (StateEngine) flysContext.get(
teichmann@5866: RiverContext.STATE_ENGINE_KEY);
sascha@3193:
sascha@3554: boolean debug = log.isDebugEnabled();
sascha@3554:
sascha@3193: for (String stateId: stateIds) {
sascha@3554: if (debug) {
sascha@3554: log.debug("Append static data for state: " + stateId);
sascha@3554: }
sascha@3193: DefaultState state = (DefaultState) engine.getState(stateId);
sascha@3193:
sascha@3193: ui.appendChild(state.describeStatic(this, doc, ui, context, uuid));
sascha@3193: }
sascha@3193: }
sascha@3193:
ingo@121:
ingo@121: /**
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,
rrenkert@7842: Document data,
rrenkert@7842: List facets)
ingo@119: {
aheinecke@6140: log.debug("Setup this artifact with the uuid: " + identifier);
ingo@119:
rrenkert@7842: super.setup(identifier, factory, context, callMeta, data, facets);
ingo@119:
teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context);
felix@1067:
felix@1067: List states = getStates(context);
ingo@119:
ingo@121: String name = getName();
sascha@3554:
aheinecke@6140: log.debug("setup(): Set initial state for artifact '" + name + "'");
ingo@119:
felix@2169: if (states == null) {
sascha@3554: log.error("No states found from which an initial "
felix@2766: + "state could be picked.");
felix@2169: }
ingo@119: setCurrentState(states.get(0));
ingo@940:
felix@7746: handleInitModel(data, context, callMeta);
felix@7746:
rrenkert@7888: if (!facets.isEmpty()) {
rrenkert@7888: buildStaticFacets(data, facets, callMeta);
rrenkert@7888: }
rrenkert@7845:
felix@7746: filterFacets = buildFilterFacets(data);
felix@7746:
felix@7746: extractOut(data);
felix@7746: }
felix@7746:
rrenkert@7845: protected void buildStaticFacets(
rrenkert@7845: Document data,
rrenkert@7845: List facets,
rrenkert@7845: CallMeta callMeta)
rrenkert@7845: {
rrenkert@7845: List staticFacets = new ArrayList();
rrenkert@7845: String currentState = getCurrentStateId();
rrenkert@7845: for (int i = 0; i < facets.size(); i++) {
rrenkert@7845: try {
rrenkert@7845: StaticFacet facet = (StaticFacet)facets.get(i).newInstance();
rrenkert@7845: facet.setup(this, data, callMeta);
rrenkert@7845: staticFacets.add(facet);
rrenkert@7845: }
rrenkert@7845: catch (InstantiationException ie) {
rrenkert@7845: log.error(ie.getLocalizedMessage(), ie);
rrenkert@7845: }
rrenkert@7845: catch (IllegalAccessException iae) {
rrenkert@7845: log.error(iae.getLocalizedMessage(), iae);
rrenkert@7845: }
rrenkert@7845: }
rrenkert@7845: this.facets.put(currentState, staticFacets);
rrenkert@7845: }
rrenkert@7845:
felix@7746: protected void handleInitModel(Document data, Object context, CallMeta callMeta) {
felix@7746: RiverContext flysContext = RiverUtils.getFlysContext(context);
felix@7746:
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) {
sascha@3554: log.error(adbe, adbe);
ingo@940: }
ingo@940: }
ingo@940: }
ingo@940:
aheinecke@5975: protected void extractOut(Document data) {
aheinecke@6140: String targetOut = XMLUtils.xpathString(data, XPATH_TARGET_OUT,
aheinecke@5975: ArtifactNamespaceContext.INSTANCE);
aheinecke@6140: if (targetOut.isEmpty()) {
aheinecke@6140: targetOut = null;
aheinecke@6140: }
aheinecke@5975:
aheinecke@6140: setBoundToOut(targetOut);
aheinecke@5975: }
aheinecke@5975:
aheinecke@5975: /**
aheinecke@5975: * Return the value of id element in Datacage data document.
aheinecke@5975: * @param data Document as passed by datacage.
aheinecke@5975: * @return the id element value of data document.
aheinecke@5975: */
aheinecke@5975: public static String getDatacageIDValue(Document data) {
aheinecke@5975: String ids = XMLUtils.xpathString(data, XPATH_IDS,
aheinecke@5975: ArtifactNamespaceContext.INSTANCE);
aheinecke@5975:
aheinecke@5975: return ids;
aheinecke@5975: }
aheinecke@5975:
aheinecke@5975:
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: */
teichmann@5867: protected void importData(D4EArtifact other, final String name) {
felix@1895: if (other == null) {
sascha@3554: log.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) {
sascha@3554: log.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:
felix@4206: /** Clone the internal map of map of state-name to state-data. */
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@4147:
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() {
christian@3306: 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: {
teichmann@5867: if (!(artifact instanceof D4EArtifact)) {
sascha@1059: return;
sascha@1059: }
sascha@1059:
teichmann@5867: D4EArtifact flys = (D4EArtifact)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) {
bjoern@4403: if (log.isDebugEnabled()) {
bjoern@4403: log.debug("Building filter factes for artifact " + this.getName());
bjoern@4403: }
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");
bjoern@4403: if (oName == null || oName.isEmpty()) {
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@3554: log.warn(nfe);
sascha@1057: index = 0;
sascha@1057: }
bjoern@4403: if (log.isDebugEnabled()) {
bjoern@4403: log.debug("Creating filter facet " + fName + " with index " + index +
bjoern@4403: " for out " + oName);
bjoern@4403: }
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) {
teichmann@5867: log.debug("D4EArtifact.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 {
sascha@3553: saveData(target, 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:
felix@4147:
sascha@3193: /**
sascha@3193: * This method returns a description of this artifact.
sascha@3193: *
sascha@3193: * @param data Some data.
sascha@3193: * @param context The CallContext.
sascha@3193: *
sascha@3193: * @return the description of this artifact.
sascha@3193: */
christian@3306: @Override
sascha@3193: public Document describe(Document data, CallContext context) {
sascha@3193:
sascha@3554: if (log.isDebugEnabled()) {
sascha@3554: log.debug(
sascha@3554: "Describe: the current state is: " + getCurrentStateId());
sascha@3193: dumpArtifact();
sascha@3193: }
sascha@3193:
teichmann@5866: RiverContext flysContext = RiverUtils.getFlysContext(context);
sascha@3193:
sascha@3193: StateEngine stateEngine = (StateEngine) flysContext.get(
teichmann@5866: RiverContext.STATE_ENGINE_KEY);
sascha@3193:
sascha@3193: TransitionEngine transitionEngine = (TransitionEngine) flysContext.get(
teichmann@5866: RiverContext.TRANSITION_ENGINE_KEY);
sascha@3193:
sascha@3193: List reachable = transitionEngine.getReachableStates(
sascha@3193: this, getCurrentState(context), stateEngine);
sascha@3193:
sascha@3193: Document description = XMLUtils.newDocument();
sascha@3193: XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
sascha@3193: description,
sascha@3193: ArtifactNamespaceContext.NAMESPACE_URI,
sascha@3193: ArtifactNamespaceContext.NAMESPACE_PREFIX);
sascha@3193:
sascha@3193: Element root = ProtocolUtils.createRootNode(creator);
sascha@3193: description.appendChild(root);
sascha@3193:
sascha@3193: State current = getCurrentState(context);
sascha@3193:
sascha@3193: ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
sascha@3193: ProtocolUtils.appendState(creator, root, current);
sascha@3193: ProtocolUtils.appendReachableStates(creator, root, reachable);
sascha@3193:
sascha@3193: appendBackgroundActivity(creator, root, context);
sascha@3193:
sascha@3193: Element ui = ProtocolUtils.createArtNode(
sascha@3193: creator, "ui", null, null);
sascha@3193:
sascha@3193: Element staticUI = ProtocolUtils.createArtNode(
sascha@3193: creator, "static", null, null);
sascha@3193:
sascha@3193: Element outs = ProtocolUtils.createArtNode(
sascha@3193: creator, "outputmodes", null, null);
sascha@3193: appendOutputModes(description, outs, context, identifier());
sascha@3193:
sascha@3193: appendStaticUI(description, staticUI, context, identifier());
sascha@3193:
sascha@3193: Element name = ProtocolUtils.createArtNode(
sascha@3193: creator, "name",
sascha@3193: new String[] { "value" },
sascha@3193: new String[] { getName() });
sascha@3193:
sascha@3193: Element dynamic = current.describe(
sascha@3193: this,
sascha@3193: description,
sascha@3193: root,
sascha@3193: context,
sascha@3193: identifier());
sascha@3193:
sascha@3193: if (dynamic != null) {
sascha@3193: ui.appendChild(dynamic);
sascha@3193: }
sascha@3193:
sascha@3193: ui.appendChild(staticUI);
sascha@3193:
sascha@3193: root.appendChild(name);
sascha@3193: root.appendChild(ui);
sascha@3193: root.appendChild(outs);
sascha@3193:
sascha@3193: return description;
sascha@3193: }
sascha@3193:
sascha@3193: /** Override me! */
sascha@3193:
sascha@3193: protected void appendBackgroundActivity(
sascha@3193: ElementCreator cr,
sascha@3193: Element root,
sascha@3193: CallContext context
sascha@3193: ) {
sascha@3193: LinkedList messages = context.getBackgroundMessages();
sascha@3193:
sascha@3193: if (messages == null) {
sascha@3193: return;
sascha@3193: }
sascha@3193:
sascha@3193: Element inBackground = cr.create("background-processing");
sascha@3193: root.appendChild(inBackground);
sascha@3193:
sascha@3193: cr.addAttr(
sascha@3193: inBackground,
sascha@3193: "value",
sascha@3193: String.valueOf(context.isInBackground()),
sascha@3193: true);
sascha@3193:
felix@4206: CalculationMessage message = (CalculationMessage) messages.getLast();
sascha@3193: cr.addAttr(
sascha@3193: inBackground,
sascha@3193: "steps",
sascha@3193: String.valueOf(message.getSteps()),
sascha@3193: true);
sascha@3193:
sascha@3193: cr.addAttr(
sascha@3193: inBackground,
sascha@3193: "currentStep",
sascha@3193: String.valueOf(message.getCurrentStep()),
sascha@3193: true);
sascha@3193:
sascha@3193: inBackground.setTextContent(message.getMessage());
sascha@3193: }
sascha@3193:
sascha@3193: /**
sascha@3193: * Append output mode nodes to a document.
sascha@3193: */
sascha@3193: protected void appendOutputModes(
sascha@3193: Document doc,
sascha@3193: Element outs,
sascha@3193: CallContext context,
sascha@3193: String uuid)
sascha@3193: {
sascha@3193: List