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