comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java @ 3814:8083f6384023

merged flys-artifacts/pre2.6-2012-01-04
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:56 +0200
parents f73036b991e2
children ebc2aa64c1be
comparison
equal deleted inserted replaced
1491:2a00f4849738 3814:8083f6384023
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 * This method returns the value of a StateData object stored in the data
554 * pool of this Artifact as Integer.
555 *
556 * @param name The name of the StateData object.
557 *
558 * @return an Integer representing the value of the data object or null if
559 * no object was found for <i>name</i>.
560 *
561 * @throws NumberFormatException if the value of the data object could not
562 * be transformed into an Integer.
563 */
564 public Integer getDataAsInteger(String name)
565 throws NumberFormatException
566 {
567 String value = getDataAsString(name);
568
569 if (value != null && value.length() > 0) {
570 return Integer.parseInt(value);
571 }
572
573 return null;
574 }
575
576
577 /**
578 * This method returns the value of a StateData object stored in the data
579 * pool of this Artifact is Boolean using Boolean.valueOf().
580 *
581 * @param name The name of the StateData object.
582 *
583 * @return a Boolean representing the value of the data object or null if no
584 * such object is existing.
585 */
586 public Boolean getDataAsBoolean(String name) {
587 String value = getDataAsString(name);
588
589 if (value == null || value.length() == 0) {
590 return null;
591 }
592
593 return Boolean.valueOf(value);
594 }
595
596
597 /**
598 * Add StateData containing a given string.
599 * @param name Name of the data object.
600 * @param value String to store.
601 */
602 public void addStringData(String name, String value) {
603 addData(name, new DefaultStateData(name, null, null, value));
604 }
605
606
607 public Collection<StateData> getAllData() {
608 return data.values();
609 }
610
611
612 /**
613 * Get facet as stored internally, with equalling name and index than given
614 * facet.
615 * @param facet that defines index and name of facet searched.
616 * @return facet instance or null if not found.
617 */
618 public Facet getNativeFacet(Facet facet) {
619 String name = facet.getName();
620 int index = facet.getIndex();
621
622 for (Map.Entry<String, List<Facet>> facetList: facets.entrySet()) {
623 for (Facet f: facetList.getValue()) {
624 if (f.getIndex() == index && f.getName().equals(name)) {
625 return f;
626 }
627 }
628 }
629
630 logger.warn("Could not find facet: " + name + " at " + index);
631 return null;
632 }
633
634
635 /**
636 * This method stores the data that is contained in the FEED document.
637 *
638 * @param feed The FEED document.
639 * @param xpath The XPath that points to the data nodes.
640 */
641 public void saveData(Document feed, String xpath, CallContext context)
642 throws IllegalArgumentException
643 {
644 if (feed == null || xpath == null || xpath.length() == 0) {
645 throw new IllegalArgumentException("error_feed_no_data");
646 }
647
648 NodeList nodes = (NodeList) XMLUtils.xpath(
649 feed,
650 xpath,
651 XPathConstants.NODESET,
652 ArtifactNamespaceContext.INSTANCE);
653
654 if (nodes == null || nodes.getLength() == 0) {
655 throw new IllegalArgumentException("error_feed_no_data");
656 }
657
658 int count = nodes.getLength();
659 logger.debug("Try to save " + count + " data items.");
660
661 String uri = ArtifactNamespaceContext.NAMESPACE_URI;
662
663 DefaultState current = (DefaultState) getCurrentState(context);
664
665 for (int i = 0; i < count; i++) {
666 Element node = (Element)nodes.item(i);
667
668 String name = node.getAttributeNS(uri, "name");
669 String value = node.getAttributeNS(uri, "value");
670
671 if (name.length() > 0 && value.length() > 0) {
672 logger.debug("Save data item for '" + name + "' : " + value);
673
674 addData(name, current.transform(this, context, name, value));
675 }
676 else if (name.length() > 0 && value.length() == 0) {
677 if (removeData(name) != null) {
678 logger.debug("Removed data '" + name + "' successfully.");
679 }
680 }
681 }
682
683 current.validate(this);
684 }
685
686
687 /**
688 * Determines if the state with the identifier <i>stateId</i> is reachable
689 * from the current state. The determination itself takes place in the
690 * TransitionEngine.
691 *
692 * @param stateId The identifier of a state.
693 * @param context The context object.
694 *
695 * @return true, if the state specified by <i>stateId</i> is reacahble,
696 * otherwise false.
697 */
698 protected boolean isStateReachable(String stateId, Object context) {
699 logger.debug("Determine if the state '" + stateId + "' is reachable.");
700
701 FLYSContext flysContext = FLYSUtils.getFlysContext(context);
702
703 State currentState = getCurrentState(context);
704 StateEngine sEngine = (StateEngine) flysContext.get(
705 FLYSContext.STATE_ENGINE_KEY);
706
707 TransitionEngine tEngine = (TransitionEngine) flysContext.get(
708 FLYSContext.TRANSITION_ENGINE_KEY);
709
710 return tEngine.isStateReachable(this, stateId, currentState, sEngine);
711 }
712
713
714 /**
715 * Determines if the state with the identifier <i>stateId</i> is a previous
716 * state of the current state.
717 *
718 * @param stateId The target state identifier.
719 * @param context The context object.
720 */
721 protected boolean isPreviousState(String stateId, Object context) {
722 logger.debug("Determine if the state '" + stateId + "' is old.");
723
724 List<String> prevs = getPreviousStateIds();
725 if (prevs.contains(stateId)) {
726 return true;
727 }
728
729 return false;
730 }
731
732
733 /**
734 * Computes the hash code of the entered values.
735 *
736 * @return a hash code.
737 */
738 @Override
739 public String hash() {
740 Set<Map.Entry<String, StateData>> entries = data.entrySet();
741
742 long hash = 0L;
743 int shift = 3;
744
745 for (Map.Entry<String, StateData> entry: entries) {
746 String key = entry.getKey();
747 Object value = entry.getValue().getValue();
748
749 hash ^= ((long)key.hashCode() << shift)
750 | ((long)value.hashCode() << (shift + 3));
751 shift += 2;
752 }
753
754 return getCurrentStateId() + hash;
755 }
756
757
758 /**
759 * Return List of outputs, where combinations of outputname and filtername
760 * that match content in filterFacets is left out.
761 * @return filtered Outputlist.
762 */
763 protected List<Output> filterOutputs(List<Output> outs) {
764
765 if (filterFacets == null || filterFacets.isEmpty()) {
766 logger.debug("No filter for Outputs.");
767 return outs;
768 }
769
770 List<Output> filtered = new ArrayList<Output>();
771
772 for (Output out: outs) {
773
774 List<Facet> fFacets = filterFacets.get(out.getName());
775 if (fFacets != null) {
776
777 List<Facet> resultFacets = new ArrayList<Facet>();
778
779 for (Facet facet: out.getFacets()) {
780 for (Facet fFacet: fFacets) {
781 if (facet.getIndex() == fFacet.getIndex()
782 && facet.getName().equals(fFacet.getName())) {
783 resultFacets.add(facet);
784 break;
785 }
786 }
787 }
788
789 if (!resultFacets.isEmpty()) {
790 DefaultOutput nout = new DefaultOutput(
791 out.getName(),
792 out.getDescription(),
793 out.getMimeType(),
794 resultFacets);
795 filtered.add(nout);
796 }
797 }
798 }
799
800 return filtered;
801 }
802
803
804 /**
805 * Get all outputs that the Artifact can do in this state (which includes
806 * all previous states).
807 *
808 * @return list of outputs
809 */
810 public List<Output> getOutputs(Object context) {
811 List<String> stateIds = getPreviousStateIds();
812 List<Output> generated = new ArrayList<Output>();
813
814 for (String stateId: stateIds) {
815 DefaultState state = (DefaultState) getState(context, stateId);
816 generated.addAll(getOutputForState(state));
817 }
818
819 generated.addAll(getCurrentOutputs(context));
820
821 return filterOutputs(generated);
822 }
823
824
825 /**
826 * Get output(s) for current state.
827 * @return list of outputs for current state.
828 */
829 public List<Output> getCurrentOutputs(Object context) {
830 DefaultState cur = (DefaultState) getCurrentState(context);
831
832 try {
833 if (cur.validate(this)) {
834 return getOutputForState(cur);
835 }
836 }
837 catch (IllegalArgumentException iae) { }
838
839 return new ArrayList<Output>();
840 }
841
842
843 /**
844 * Get output(s) for a specific state.
845 * @param state State of interest
846 * @return list of output(s) for given state.
847 */
848 protected List<Output> getOutputForState(DefaultState state) {
849 List<Output> list = state.getOutputs();
850 if (list == null || list.size() == 0) {
851 logger.debug("-> No output modes for this state.");
852 return new ArrayList<Output>();
853 }
854
855 List<Facet> fs = facets.get(state.getID());
856
857 if (fs == null || fs.size() == 0) {
858 logger.debug("No facets found.");
859 return new ArrayList<Output>();
860 }
861
862 return generateOutputs(list, fs);
863 }
864
865
866 /**
867 * Generate a list of outputs with facets from fs if type is found in list
868 * of output.
869 *
870 * @param list List of outputs
871 * @param fs List of facets
872 */
873 protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) {
874 List<Output> generated = new ArrayList<Output>();
875
876 boolean debug = logger.isDebugEnabled();
877
878 for (Output out: list) {
879 Output o = new DefaultOutput(
880 out.getName(),
881 out.getDescription(),
882 out.getMimeType(),
883 out.getType());
884
885 Set<String> outTypes = new HashSet<String>();
886
887 for (Facet f: out.getFacets()) {
888 if (outTypes.add(f.getName()) && debug) {
889 logger.debug("configured facet " + f);
890 }
891 }
892
893 boolean facetAdded = false;
894 for (Facet f: fs) {
895 String type = f.getName();
896
897 if (outTypes.contains(type)) {
898 if (debug) {
899 logger.debug("Add facet " + f);
900 }
901 facetAdded = true;
902 o.addFacet(f);
903 }
904 }
905
906 if (facetAdded) {
907 generated.add(o);
908 }
909 }
910
911 return generated;
912 }
913
914
915 /**
916 * Dispatches the computation request to compute(CallContext context, String
917 * hash) with the current hash value of the artifact which is provided by
918 * hash().
919 *
920 * @param context The CallContext.
921 */
922 public Object compute(
923 CallContext context,
924 ComputeType type,
925 boolean generateFacets
926 ) {
927 return compute(context, hash(), type, generateFacets);
928 }
929
930
931 /**
932 * Dispatches computation requests to the current state which needs to
933 * implement a createComputeCallback(String hash, FLYSArtifact artifact)
934 * method.
935 *
936 * @param context The CallContext.
937 * @param hash The hash value which is used to fetch computed data from
938 * cache.
939 *
940 * @return the computed data.
941 */
942 public Object compute(
943 CallContext context,
944 String hash,
945 ComputeType type,
946 boolean generateFacets
947 ) {
948 DefaultState current = (DefaultState) getCurrentState(context);
949 return compute(context, hash, current, type, generateFacets);
950 }
951
952
953 /**
954 * Like compute, but identify State by it id (string).
955 */
956 public Object compute(
957 CallContext context,
958 String hash,
959 String stateID,
960 ComputeType type,
961 boolean generateFacets
962 ) {
963 DefaultState current = stateID == null
964 ? (DefaultState)getCurrentState(context)
965 : (DefaultState)getState(context, stateID);
966
967 if (hash == null) {
968 hash = hash();
969 }
970
971 return compute(context, hash, current, type, generateFacets);
972 }
973
974
975 /**
976 * Let current state compute and register facets.
977 *
978 * @param key key of state
979 * @param state state
980 * @param type Type of compute
981 * @param generateFacets Whether new facets shall be generated.
982 */
983 public Object compute(
984 CallContext context,
985 String key,
986 DefaultState state,
987 ComputeType type,
988 boolean generateFacets
989 ) {
990 String stateID = state.getID();
991
992 List<Facet> fs = (generateFacets) ? new ArrayList<Facet>() : null;
993
994 try {
995 Cache cache = CacheFactory.getCache(COMPUTING_CACHE);
996
997 Object old = null;
998
999 if (cache != null) {
1000 net.sf.ehcache.Element element = cache.get(key);
1001 if (element != null) {
1002 logger.debug("Got computation result from cache.");
1003 old = element.getValue();
1004 }
1005 }
1006
1007 Object res;
1008 switch (type) {
1009 case FEED:
1010 res = state.computeFeed(this, key, context, fs, old);
1011 break;
1012 case ADVANCE:
1013 res = state.computeAdvance(this, key, context, fs, old);
1014 break;
1015 case INIT:
1016 res = state.computeInit(this, key, context, context.getMeta(), fs);
1017 default:
1018 res = null;
1019 }
1020
1021 if (cache != null && old != res && res != null) {
1022 logger.debug("Store computation result to cache.");
1023 net.sf.ehcache.Element element =
1024 new net.sf.ehcache.Element(key, res);
1025 cache.put(element);
1026 }
1027
1028 return res;
1029 }
1030 finally {
1031 if (generateFacets) {
1032 if (fs.isEmpty()) {
1033 facets.remove(stateID);
1034 }
1035 else {
1036 facets.put(stateID, fs);
1037 }
1038 }
1039 }
1040 }
1041
1042
1043 /**
1044 * Method to dump the artifacts state/data.
1045 */
1046 protected void dumpArtifact() {
1047 if (logger.isDebugEnabled()) {
1048 logger.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
1049
1050 logger.debug("------ DUMP DATA ------");
1051 Collection<StateData> allData = data.values();
1052
1053 for (StateData d: allData) {
1054 String name = d.getName();
1055 String value = (String) d.getValue();
1056
1057 logger.debug("- " + name + ": " + value);
1058 }
1059
1060 logger.debug("------ DUMP PREVIOUS STATES ------");
1061 List<String> stateIds = getPreviousStateIds();
1062
1063 for (String id: stateIds) {
1064 logger.debug("- State: " + id);
1065 }
1066
1067 logger.debug("CURRENT STATE: " + getCurrentStateId());
1068
1069 logger.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++");
1070 }
1071 }
1072
1073
1074 protected void destroyState(String id, Object context) {
1075 State s = getState(context, id);
1076 s.endOfLife(this, context);
1077 }
1078
1079
1080 /**
1081 * Calls endOfLife() for each state in the list <i>ids</i>.
1082 *
1083 * @param ids The State IDs that should be destroyed.
1084 * @param context The FLYSContext.
1085 */
1086 protected void destroyStates(List<String> ids, Object context) {
1087 for (int i = 0, num = ids.size(); i < num; i++) {
1088 destroyState(ids.get(i), context);
1089 }
1090 }
1091
1092
1093 /**
1094 * Destroy the states.
1095 */
1096 @Override
1097 public void endOfLife(Object context) {
1098 logger.info("FLYSArtifact.endOfLife: " + identifier());
1099
1100 List<String> ids = getPreviousStateIds();
1101 ids.add(getCurrentStateId());
1102
1103 destroyStates(ids, context);
1104 }
1105
1106
1107 /**
1108 * Determines Facets initial disposition regarding activity (think of
1109 * selection in Client ThemeList GUI). This will be checked one time
1110 * when the facet enters a collections describe document.
1111 *
1112 * @param facetName name of the facet.
1113 * @param outputName name of the output.
1114 * @param index index of the facet.
1115 *
1116 * @return 1 if wished to be initally active, 0 if not. FLYSArtifact
1117 * defaults to "1".
1118 */
1119 public int getInitialFacetActivity(
1120 String outputName,
1121 String facetName,
1122 int index)
1123 {
1124 return 1;
1125 }
1126 }
1127 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org