comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java @ 3812:f788d2d901d6

merged flys-artifacts/pre2.6-2011-12-05
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:53 +0200
parents 1be3a4636ee3
children 9d5f339d83a3
comparison
equal deleted inserted replaced
3808:5fab0fe3c445 3812:f788d2d901d6
1 package de.intevation.flys.artifacts;
2
3 import de.intevation.artifactdatabase.ArtifactDatabaseImpl;
4 import de.intevation.artifactdatabase.DefaultArtifact;
5
6 import de.intevation.artifactdatabase.data.DefaultStateData;
7 import de.intevation.artifactdatabase.data.StateData;
8
9 import de.intevation.artifactdatabase.state.DefaultFacet;
10 import de.intevation.artifactdatabase.state.DefaultOutput;
11 import de.intevation.artifactdatabase.state.Facet;
12 import de.intevation.artifactdatabase.state.Output;
13 import de.intevation.artifactdatabase.state.State;
14 import de.intevation.artifactdatabase.state.StateEngine;
15
16 import de.intevation.artifactdatabase.transition.TransitionEngine;
17
18 import de.intevation.artifacts.Artifact;
19 import de.intevation.artifacts.ArtifactDatabase;
20 import de.intevation.artifacts.ArtifactDatabaseException;
21 import de.intevation.artifacts.ArtifactFactory;
22 import de.intevation.artifacts.CallContext;
23 import de.intevation.artifacts.CallMeta;
24
25 import de.intevation.artifacts.common.ArtifactNamespaceContext;
26
27 import de.intevation.artifacts.common.utils.XMLUtils;
28
29 import de.intevation.flys.artifacts.cache.CacheFactory;
30
31 import de.intevation.flys.artifacts.context.FLYSContext;
32
33 import de.intevation.flys.artifacts.states.DefaultState;
34 import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
35
36 import de.intevation.flys.utils.FLYSUtils;
37
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.TreeMap;
46
47 import javax.xml.xpath.XPathConstants;
48
49 import net.sf.ehcache.Cache;
50
51 import org.apache.log4j.Logger;
52
53 import org.w3c.dom.Document;
54 import org.w3c.dom.Element;
55 import org.w3c.dom.NodeList;
56
57 /**
58 * The defaul FLYS artifact.
59 *
60 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
61 */
62 public abstract class FLYSArtifact extends DefaultArtifact {
63
64 /** The logger that is used in this artifact. */
65 private static Logger logger = Logger.getLogger(FLYSArtifact.class);
66
67 public static final String COMPUTING_CACHE = "computed.values";
68
69 /** The XPath that points to the input data elements of the FEED document. */
70 public static final String XPATH_FEED_INPUT =
71 "/art:action/art:data/art:input";
72
73 /** The XPath that points to the name of the target state of ADVANCE. */
74 public static final String XPATH_ADVANCE_TARGET =
75 "/art:action/art:target/@art:name";
76
77 public static final String XPATH_MODEL_ARTIFACT =
78 "/art:action/art:template/@uuid";
79
80 public static final String XPATH_FILTER =
81 "/art:action/art:filter/art:out";
82
83 /** The constant string that shows that an operation was successful. */
84 public static final String OPERATION_SUCCESSFUL = "SUCCESS";
85
86 /** The constant string that shows that an operation failed. */
87 public static final String OPERATION_FAILED = "FAILURE";
88
89 /** The identifier of the current state. */
90 protected String currentStateId;
91
92 /** The identifiers of previous states on a stack. */
93 protected List<String> previousStateIds;
94
95 /** The name of the artifact. */
96 protected String name;
97
98 /** The data that have been inserted into this artifact. */
99 protected Map<String, StateData> data;
100
101 /** Mapping of state names to created facets. */
102 protected Map<String, List<Facet>> facets;
103
104 /**
105 * Used to generates "view" on the facets (hides facets not matching the
106 * filter in output of collection); out -&gt; facets.
107 */
108 protected Map<String, List<Facet>> filterFacets;
109
110
111 /**
112 * The default constructor that creates an empty FLYSArtifact.
113 */
114 public FLYSArtifact() {
115 data = new TreeMap<String, StateData>();
116 previousStateIds = new ArrayList<String>();
117 facets = new HashMap<String, List<Facet>>();
118 }
119
120
121 /**
122 * Returns the name of the concrete artifact.
123 *
124 * @return the name of the concrete artifact.
125 */
126 public String getName() {
127 return name;
128 }
129
130
131 /**
132 * Initialize the artifact and insert new data if <code>data</code> contains
133 * information necessary for this artifact.
134 *
135 * @param identifier The UUID.
136 * @param factory The factory that is used to create this artifact.
137 * @param context The CallContext.
138 * @param data Some optional data.
139 */
140 @Override
141 public void setup(
142 String identifier,
143 ArtifactFactory factory,
144 Object context,
145 CallMeta callMeta,
146 Document data)
147 {
148 logger.debug("Setup this artifact with the uuid: " + identifier);
149
150 super.setup(identifier, factory, context, callMeta, data);
151
152 FLYSContext flysContext = FLYSUtils.getFlysContext(context);
153
154 List<State> states = getStates(context);
155
156 String name = getName();
157 logger.debug("Set initial state for artifact '" + name + "'");
158
159 setCurrentState(states.get(0));
160
161 String model = XMLUtils.xpathString(
162 data,
163 XPATH_MODEL_ARTIFACT,
164 ArtifactNamespaceContext.INSTANCE);
165
166 if (model != null && model.length() > 0) {
167 ArtifactDatabase db = (ArtifactDatabase) flysContext.get(
168 ArtifactDatabaseImpl.GLOBAL_CONTEXT_KEY);
169
170 try {
171 initialize(db.getRawArtifact(model), context, callMeta);
172 }
173 catch (ArtifactDatabaseException adbe) {
174 logger.error(adbe, adbe);
175 }
176 }
177
178 filterFacets = buildFilterFacets(data);
179 }
180
181
182 protected List<String> clonePreviousStateIds() {
183 return new ArrayList<String>(previousStateIds);
184 }
185
186 /**
187 * Copies data item from other artifact to this artifact.
188 * @param other Artifact from which to get data.
189 * @param name Name of data.
190 */
191 protected void importData(FLYSArtifact other, final String name) {
192 if (other == null) {
193 logger.error("No other art. to import data " + name + " from.");
194 }
195
196 StateData sd = other.getData(name);
197
198 if (sd == null) {
199 logger.warn("Other artifact has no data " + name + ".");
200 return;
201 }
202
203 this.addData(name, sd);
204 }
205
206 protected Map<String, StateData> cloneData() {
207 Map<String, StateData> copy = new TreeMap<String, StateData>();
208
209 for (Map.Entry<String, StateData> entry: data.entrySet()) {
210 copy.put(entry.getKey(), entry.getValue().deepCopy());
211 }
212
213 return copy;
214 }
215
216 /**
217 * Return a copy of the facet mapping.
218 * @return Mapping of state-ids to facets.
219 */
220 protected Map<String, List<Facet>> cloneFacets() {
221 Map copy = new HashMap<String, List<Facet>>();
222
223 for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
224 List<Facet> facets = entry.getValue();
225 List<Facet> facetCopies = new ArrayList<Facet>(facets.size());
226 for (Facet facet: facets) {
227 facetCopies.add(facet.deepCopy());
228 }
229 copy.put(entry.getKey(), facetCopies);
230 }
231
232 return copy;
233 }
234
235
236 /**
237 * (called from setup).
238 * @param artifact master-artifact (if any, otherwise initialize is not called).
239 */
240 protected void initialize(
241 Artifact artifact,
242 Object context,
243 CallMeta callMeta)
244 {
245 if (!(artifact instanceof FLYSArtifact)) {
246 return;
247 }
248
249 FLYSArtifact flys = (FLYSArtifact)artifact;
250
251 currentStateId = flys.currentStateId;
252 previousStateIds = flys.clonePreviousStateIds();
253 name = flys.name;
254 data = flys.cloneData();
255 facets = flys.cloneFacets();
256 // Do not clone filter facets!
257 }
258
259
260 /**
261 * Builds filter facets from document.
262 * @see filterFacets
263 */
264 protected Map<String, List<Facet>> buildFilterFacets(Document document) {
265
266 NodeList nodes = (NodeList)XMLUtils.xpath(
267 document,
268 XPATH_FILTER,
269 XPathConstants.NODESET,
270 ArtifactNamespaceContext.INSTANCE);
271
272 if (nodes == null || nodes.getLength() == 0) {
273 return null;
274 }
275
276 Map<String, List<Facet>> result = new HashMap<String, List<Facet>>();
277
278 for (int i = 0, N = nodes.getLength(); i < N; ++i) {
279 Element element = (Element)nodes.item(i);
280 String oName = element.getAttribute("name");
281 if (oName.length() == 0) {
282 continue;
283 }
284
285 List<Facet> facets = new ArrayList<Facet>();
286
287 NodeList facetNodes = element.getElementsByTagNameNS(
288 ArtifactNamespaceContext.NAMESPACE_URI,
289 "facet");
290
291 for (int j = 0, M = facetNodes.getLength(); j < M; ++j) {
292 Element facetElement = (Element)facetNodes.item(j);
293
294 String fName = facetElement.getAttribute("name");
295
296 int index;
297 try {
298 index = Integer.parseInt(facetElement.getAttribute("index"));
299 }
300 catch (NumberFormatException nfe) {
301 logger.warn(nfe);
302 index = 0;
303 }
304 facets.add(new DefaultFacet(index, fName, ""));
305 }
306
307 if (!facets.isEmpty()) {
308 result.put(oName, facets);
309 }
310 }
311
312 return result;
313 }
314
315
316 /**
317 * Insert new data included in <code>input</code> into the current state.
318 *
319 * @param target XML document that contains new data.
320 * @param context The CallContext.
321 *
322 * @return a document that contains a SUCCESS or FAILURE message.
323 */
324 @Override
325 public Document feed(Document target, CallContext context) {
326 logger.info("FLYSArtifact.feed()");
327
328 Document doc = XMLUtils.newDocument();
329
330 XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
331 doc,
332 ArtifactNamespaceContext.NAMESPACE_URI,
333 ArtifactNamespaceContext.NAMESPACE_PREFIX);
334
335 Element result = creator.create("result");
336 doc.appendChild(result);
337
338 try {
339 saveData(target, XPATH_FEED_INPUT, context);
340
341 compute(context, ComputeType.FEED, true);
342
343 return describe(target, context);
344 }
345 catch (IllegalArgumentException iae) {
346 // do not store state if validation fails.
347 context.afterCall(CallContext.NOTHING);
348 creator.addAttr(result, "type", OPERATION_FAILED, true);
349
350 result.setTextContent(iae.getMessage());
351 }
352
353 return doc;
354 }
355
356
357 /**
358 * This method handles request for changing the current state of an
359 * artifact. It is possible to step forward or backward.
360 *
361 * @param target The incoming ADVANCE document.
362 * @param context The CallContext.
363 *
364 * @return a document that contains a SUCCESS or FAILURE message.
365 */
366 public Document advance(Document target, CallContext context) {
367 Document doc = XMLUtils.newDocument();
368
369 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
370 doc,
371 ArtifactNamespaceContext.NAMESPACE_URI,
372 ArtifactNamespaceContext.NAMESPACE_PREFIX);
373
374 Element result = ec.create("result");
375
376 String targetState = XMLUtils.xpathString(
377 target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE);
378
379 logger.info("FLYSArtifact.advance() to '" + targetState + "'");
380
381 if (isStateReachable(targetState, context)) {
382 logger.info("Advance: Step forward");
383
384 List<String> prev = getPreviousStateIds();
385 prev.add(getCurrentStateId());
386
387 setCurrentStateId(targetState);
388
389 logger.debug("Compute data for state: " + targetState);
390 compute(context, ComputeType.ADVANCE, true);
391
392 return describe(target, context);
393 }
394 else if (isPreviousState(targetState, context)) {
395 logger.info("Advance: Step back to");
396
397 List<String> prevs = getPreviousStateIds();
398 int targetIdx = prevs.indexOf(targetState);
399 int start = prevs.size() - 1;
400
401 destroyStates(prevs, context);
402
403 for (int i = start; i >= targetIdx; i--) {
404 String prev = prevs.get(i);
405 logger.debug("Remove state id '" + prev + "'");
406
407 prevs.remove(prev);
408 facets.remove(prev);
409 }
410
411 destroyState(getCurrentStateId(), context);
412 setCurrentStateId(targetState);
413
414 return describe(target, context);
415 }
416
417 logger.warn("Advance: Cannot advance to '" + targetState + "'");
418 ec.addAttr(result, "type", OPERATION_FAILED, true);
419
420 doc.appendChild(result);
421
422 return doc;
423 }
424
425
426 /**
427 * Returns the identifier of the current state.
428 *
429 * @return the identifier of the current state.
430 */
431 public String getCurrentStateId() {
432 return currentStateId;
433 }
434
435
436 /**
437 * Sets the identifier of the current state.
438 *
439 * @param id the identifier of a state.
440 */
441 protected void setCurrentStateId(String id) {
442 currentStateId = id;
443 }
444
445
446 /**
447 * Set the current state of this artifact. <b>NOTE</b>We don't store the
448 * State object itself - which is not necessary - but its identifier. So
449 * this method will just call the setCurrentStateId() method with the
450 * identifier of <i>state</i>.
451 *
452 * @param state The new current state.
453 */
454 protected void setCurrentState(State state) {
455 setCurrentStateId(state.getID());
456 }
457
458
459 /**
460 * Returns the current state of the artifact.
461 *
462 * @return the current State of the artifact.
463 */
464 public State getCurrentState(Object context) {
465 return getState(context, getCurrentStateId());
466 }
467
468
469 /**
470 * Get list of existant states for this Artifact.
471 * @param context Contex to get StateEngine from.
472 * @return list of states.
473 */
474 protected List<State> getStates(Object context) {
475 FLYSContext flysContext = FLYSUtils.getFlysContext(context);
476 StateEngine engine = (StateEngine) flysContext.get(
477 FLYSContext.STATE_ENGINE_KEY);
478 return engine.getStates(getName());
479 }
480
481
482 /**
483 * Get state with given ID.
484 * @param context Context to get StateEngine from.
485 * @param stateID ID of state to get.
486 * @return state with given ID.
487 */
488 protected State getState(Object context, String stateID) {
489 FLYSContext flysContext = FLYSUtils.getFlysContext(context);
490 StateEngine engine = (StateEngine) flysContext.get(
491 FLYSContext.STATE_ENGINE_KEY);
492 return engine.getState(stateID);
493 }
494
495
496 /**
497 * Returns the vector of previous state identifiers.
498 *
499 * @return the vector of previous state identifiers.
500 */
501 protected List<String> getPreviousStateIds() {
502 return previousStateIds;
503 }
504
505
506 /**
507 * Get all previous and the current state id.
508 * @return #getPreviousStateIds() + #getCurrentStateId()
509 */
510 public List<String> getStateHistoryIds() {
511 List<String> allIds = getPreviousStateIds();
512 allIds.add(getCurrentStateId());
513 return allIds;
514 }
515
516
517 /**
518 * Adds a new StateData item to the data pool of this artifact.
519 *
520 * @param name the name of the data object.
521 * @param data the data object itself.
522 */
523 protected void addData(String name, StateData data) {
524 this.data.put(name, data);
525 }
526
527
528 protected StateData removeData(String name) {
529 return this.data.remove(name);
530 }
531
532
533 /**
534 * This method returns a specific StateData object that is stored in the
535 * data pool of this artifact.
536 *
537 * @param name The name of the data object.
538 *
539 * @return the StateData object if existing, otherwise null.
540 */
541 public StateData getData(String name) {
542 return data.get(name);
543 }
544
545
546 public String getDataAsString(String name) {
547 StateData data = getData(name);
548 return data != null ? (String) data.getValue() : null;
549 }
550
551
552 /**
553 * Add StateData containing a given string.
554 * @param name Name of the data object.
555 * @param value String to store.
556 */
557 public void addStringData(String name, String value) {
558 addData(name, new DefaultStateData(name, null, null, value));
559 }
560
561
562 public Collection<StateData> getAllData() {
563 return data.values();
564 }
565
566
567 /**
568 * Get facet as stored internally, with equalling name and index than given
569 * facet.
570 * @param facet that defines index and name of facet searched.
571 * @return facet instance or null if not found.
572 */
573 public Facet getNativeFacet(Facet facet) {
574 String name = facet.getName();
575 int index = facet.getIndex();
576
577 for (Map.Entry<String, List<Facet>> facetList: facets.entrySet()) {
578 for (Facet f: facetList.getValue()) {
579 if (f.getIndex() == index && f.getName().equals(name)) {
580 return f;
581 }
582 }
583 }
584
585 logger.warn("Could not find facet: " + name + " at " + index);
586 return null;
587 }
588
589
590 /**
591 * This method stores the data that is contained in the FEED document.
592 *
593 * @param feed The FEED document.
594 * @param xpath The XPath that points to the data nodes.
595 */
596 public void saveData(Document feed, String xpath, CallContext context)
597 throws IllegalArgumentException
598 {
599 if (feed == null || xpath == null || xpath.length() == 0) {
600 throw new IllegalArgumentException("error_feed_no_data");
601 }
602
603 NodeList nodes = (NodeList) XMLUtils.xpath(
604 feed,
605 xpath,
606 XPathConstants.NODESET,
607 ArtifactNamespaceContext.INSTANCE);
608
609 if (nodes == null || nodes.getLength() == 0) {
610 throw new IllegalArgumentException("error_feed_no_data");
611 }
612
613 int count = nodes.getLength();
614 logger.debug("Try to save " + count + " data items.");
615
616 String uri = ArtifactNamespaceContext.NAMESPACE_URI;
617
618 DefaultState current = (DefaultState) getCurrentState(context);
619
620 for (int i = 0; i < count; i++) {
621 Element node = (Element)nodes.item(i);
622
623 String name = node.getAttributeNS(uri, "name");
624 String value = node.getAttributeNS(uri, "value");
625
626 if (name.length() > 0 && value.length() > 0) {
627 logger.debug("Save data item for '" + name + "' : " + value);
628
629 addData(name, current.transform(this, context, name, value));
630 }
631 else if (name.length() > 0 && value.length() == 0) {
632 if (removeData(name) != null) {
633 logger.debug("Removed data '" + name + "' successfully.");
634 }
635 }
636 }
637
638 current.validate(this);
639 }
640
641
642 /**
643 * Determines if the state with the identifier <i>stateId</i> is reachable
644 * from the current state. The determination itself takes place in the
645 * TransitionEngine.
646 *
647 * @param stateId The identifier of a state.
648 * @param context The context object.
649 *
650 * @return true, if the state specified by <i>stateId</i> is reacahble,
651 * otherwise false.
652 */
653 protected boolean isStateReachable(String stateId, Object context) {
654 logger.debug("Determine if the state '" + stateId + "' is reachable.");
655
656 FLYSContext flysContext = FLYSUtils.getFlysContext(context);
657
658 State currentState = getCurrentState(context);
659 StateEngine sEngine = (StateEngine) flysContext.get(
660 FLYSContext.STATE_ENGINE_KEY);
661
662 TransitionEngine tEngine = (TransitionEngine) flysContext.get(
663 FLYSContext.TRANSITION_ENGINE_KEY);
664
665 return tEngine.isStateReachable(this, stateId, currentState, sEngine);
666 }
667
668
669 /**
670 * Determines if the state with the identifier <i>stateId</i> is a previous
671 * state of the current state.
672 *
673 * @param stateId The target state identifier.
674 * @param context The context object.
675 */
676 protected boolean isPreviousState(String stateId, Object context) {
677 logger.debug("Determine if the state '" + stateId + "' is old.");
678
679 List<String> prevs = getPreviousStateIds();
680 if (prevs.contains(stateId)) {
681 return true;
682 }
683
684 return false;
685 }
686
687
688 /**
689 * Computes the hash code of the entered values.
690 *
691 * @return a hash code.
692 */
693 @Override
694 public String hash() {
695 Set<Map.Entry<String, StateData>> entries = data.entrySet();
696
697 long hash = 0L;
698 int shift = 3;
699
700 for (Map.Entry<String, StateData> entry: entries) {
701 String key = entry.getKey();
702 Object value = entry.getValue().getValue();
703
704 hash ^= ((long)key.hashCode() << shift)
705 | ((long)value.hashCode() << (shift + 3));
706 shift += 2;
707 }
708
709 return getCurrentStateId() + hash;
710 }
711
712
713 /**
714 * Return List of outputs, where combinations of outputname and filtername
715 * that match content in filterFacets is left out.
716 * @return filtered Outputlist.
717 */
718 protected List<Output> filterOutputs(List<Output> outs) {
719
720 if (filterFacets == null || filterFacets.isEmpty()) {
721 logger.debug("No filter for Outputs.");
722 return outs;
723 }
724
725 List<Output> filtered = new ArrayList<Output>();
726
727 for (Output out: outs) {
728
729 List<Facet> fFacets = filterFacets.get(out.getName());
730 if (fFacets != null) {
731
732 List<Facet> resultFacets = new ArrayList<Facet>();
733
734 for (Facet facet: out.getFacets()) {
735 for (Facet fFacet: fFacets) {
736 if (facet.getIndex() == fFacet.getIndex()
737 && facet.getName().equals(fFacet.getName())) {
738 resultFacets.add(facet);
739 break;
740 }
741 }
742 }
743
744 if (!resultFacets.isEmpty()) {
745 DefaultOutput nout = new DefaultOutput(
746 out.getName(),
747 out.getDescription(),
748 out.getMimeType(),
749 resultFacets);
750 filtered.add(nout);
751 }
752 }
753 }
754
755 return filtered;
756 }
757
758
759 /**
760 * Get all outputs that the Artifact can do in this state (which includes
761 * all previous states).
762 *
763 * @return list of outputs
764 */
765 public List<Output> getOutputs(Object context) {
766 List<String> stateIds = getPreviousStateIds();
767 List<Output> generated = new ArrayList<Output>();
768
769 for (String stateId: stateIds) {
770 DefaultState state = (DefaultState) getState(context, stateId);
771 generated.addAll(getOutputForState(state));
772 }
773
774 generated.addAll(getCurrentOutputs(context));
775
776 return filterOutputs(generated);
777 }
778
779
780 /**
781 * Get output(s) for current state.
782 * @return list of outputs for current state.
783 */
784 public List<Output> getCurrentOutputs(Object context) {
785 DefaultState cur = (DefaultState) getCurrentState(context);
786
787 try {
788 if (cur.validate(this)) {
789 return getOutputForState(cur);
790 }
791 }
792 catch (IllegalArgumentException iae) { }
793
794 return new ArrayList<Output>();
795 }
796
797
798 /**
799 * Get output(s) for a specific state.
800 * @param state State of interest
801 * @return list of output(s) for given state.
802 */
803 protected List<Output> getOutputForState(DefaultState state) {
804 List<Output> list = state.getOutputs();
805 if (list == null || list.size() == 0) {
806 logger.debug("-> No output modes for this state.");
807 return new ArrayList<Output>();
808 }
809
810 List<Facet> fs = facets.get(state.getID());
811
812 if (fs == null || fs.size() == 0) {
813 logger.debug("No facets found.");
814 return new ArrayList<Output>();
815 }
816
817 return generateOutputs(list, fs);
818 }
819
820
821 /**
822 * Generate a list of outputs with facets from fs if type is found in list
823 * of output.
824 *
825 * @param list List of outputs
826 * @param fs List of facets
827 */
828 protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) {
829 List<Output> generated = new ArrayList<Output>();
830
831 boolean debug = logger.isDebugEnabled();
832
833 for (Output out: list) {
834 Output o = new DefaultOutput(
835 out.getName(),
836 out.getDescription(),
837 out.getMimeType(),
838 out.getType());
839
840 Set<String> outTypes = new HashSet<String>();
841
842 for (Facet f: out.getFacets()) {
843 if (outTypes.add(f.getName()) && debug) {
844 logger.debug("configured facet " + f);
845 }
846 }
847
848 boolean facetAdded = false;
849 for (Facet f: fs) {
850 String type = f.getName();
851
852 if (outTypes.contains(type)) {
853 if (debug) {
854 logger.debug("Add facet " + f);
855 }
856 facetAdded = true;
857 o.addFacet(f);
858 }
859 }
860
861 if (facetAdded) {
862 generated.add(o);
863 }
864 }
865
866 return generated;
867 }
868
869
870 /**
871 * Dispatches the computation request to compute(CallContext context, String
872 * hash) with the current hash value of the artifact which is provided by
873 * hash().
874 *
875 * @param context The CallContext.
876 */
877 public Object compute(
878 CallContext context,
879 ComputeType type,
880 boolean generateFacets
881 ) {
882 return compute(context, hash(), type, generateFacets);
883 }
884
885
886 /**
887 * Dispatches computation requests to the current state which needs to
888 * implement a createComputeCallback(String hash, FLYSArtifact artifact)
889 * method.
890 *
891 * @param context The CallContext.
892 * @param hash The hash value which is used to fetch computed data from
893 * cache.
894 *
895 * @return the computed data.
896 */
897 public Object compute(
898 CallContext context,
899 String hash,
900 ComputeType type,
901 boolean generateFacets
902 ) {
903 DefaultState current = (DefaultState) getCurrentState(context);
904 return compute(context, hash, current, type, generateFacets);
905 }
906
907
908 /**
909 * Like compute, but identify State by it id (string).
910 */
911 public Object compute(
912 CallContext context,
913 String hash,
914 String stateID,
915 ComputeType type,
916 boolean generateFacets
917 ) {
918 DefaultState current = stateID == null
919 ? (DefaultState)getCurrentState(context)
920 : (DefaultState)getState(context, stateID);
921
922 if (hash == null) {
923 hash = hash();
924 }
925
926 return compute(context, hash, current, type, generateFacets);
927 }
928
929
930 /**
931 * Let current state compute and register facets.
932 *
933 * @param key key of state
934 * @param state state
935 * @param type Type of compute
936 * @param generateFacets Whether new facets shall be generated.
937 */
938 public Object compute(
939 CallContext context,
940 String key,
941 DefaultState state,
942 ComputeType type,
943 boolean generateFacets
944 ) {
945 String stateID = state.getID();
946
947 List<Facet> fs = (generateFacets) ? new ArrayList<Facet>() : null;
948
949 try {
950 Cache cache = CacheFactory.getCache(COMPUTING_CACHE);
951
952 Object old = null;
953
954 if (cache != null) {
955 net.sf.ehcache.Element element = cache.get(key);
956 if (element != null) {
957 logger.debug("Got computation result from cache.");
958 old = element.getValue();
959 }
960 }
961
962 Object res;
963 switch (type) {
964 case FEED:
965 res = state.computeFeed(this, key, context, fs, old);
966 break;
967 case ADVANCE:
968 res = state.computeAdvance(this, key, context, fs, old);
969 break;
970 case INIT:
971 res = state.computeInit(this, key, context, context.getMeta(), fs);
972 default:
973 res = null;
974 }
975
976 if (cache != null && old != res && res != null) {
977 logger.debug("Store computation result to cache.");
978 net.sf.ehcache.Element element =
979 new net.sf.ehcache.Element(key, res);
980 cache.put(element);
981 }
982
983 return res;
984 }
985 finally {
986 if (generateFacets) {
987 if (fs.isEmpty()) {
988 facets.remove(stateID);
989 }
990 else {
991 facets.put(stateID, fs);
992 }
993 }
994 }
995 }
996
997
998 /**
999 * Method to dump the artifacts state/data.
1000 */
1001 protected void dumpArtifact() {
1002 if (logger.isDebugEnabled()) {
1003 logger.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
1004
1005 logger.debug("------ DUMP DATA ------");
1006 Collection<StateData> allData = data.values();
1007
1008 for (StateData d: allData) {
1009 String name = d.getName();
1010 String value = (String) d.getValue();
1011
1012 logger.debug("- " + name + ": " + value);
1013 }
1014
1015 logger.debug("------ DUMP PREVIOUS STATES ------");
1016 List<String> stateIds = getPreviousStateIds();
1017
1018 for (String id: stateIds) {
1019 logger.debug("- State: " + id);
1020 }
1021
1022 logger.debug("CURRENT STATE: " + getCurrentStateId());
1023
1024 logger.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++");
1025 }
1026 }
1027
1028
1029 protected void destroyState(String id, Object context) {
1030 State s = getState(context, id);
1031 s.endOfLife(this, context);
1032 }
1033
1034
1035 /**
1036 * Calls endOfLife() for each state in the list <i>ids</i>.
1037 *
1038 * @param ids The State IDs that should be destroyed.
1039 * @param context The FLYSContext.
1040 */
1041 protected void destroyStates(List<String> ids, Object context) {
1042 for (int i = 0, num = ids.size(); i < num; i++) {
1043 destroyState(ids.get(i), context);
1044 }
1045 }
1046
1047
1048 /**
1049 * Destroy the states.
1050 */
1051 @Override
1052 public void endOfLife(Object context) {
1053 logger.info("FLYSArtifact.endOfLife: " + identifier());
1054
1055 List<String> ids = getPreviousStateIds();
1056 ids.add(getCurrentStateId());
1057
1058 destroyStates(ids, context);
1059 }
1060
1061
1062 /**
1063 * Determines Facets initial disposition regarding activity (think of
1064 * selection in Client ThemeList GUI). This will be checked one time
1065 * when the facet enters a collections describe document.
1066 *
1067 * @param facetName name of the facet.
1068 * @param outputName name of the output.
1069 * @param index index of the facet.
1070 *
1071 * @return 1 if wished to be initally active, 0 if not. FLYSArtifact
1072 * defaults to "1".
1073 */
1074 public int getInitialFacetActivity(
1075 String outputName,
1076 String facetName,
1077 int index)
1078 {
1079 return 1;
1080 }
1081 }
1082 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org