comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java @ 2424:092e519ff461

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

http://dive4elements.wald.intevation.org