Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 3818:dc18457b1cef
merged flys-artifacts/pre2.7-2012-03-16
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:59 +0200 |
parents | 804088c2e23a |
children | 3aec5a42696a |
comparison
equal
deleted
inserted
replaced
2456:60ab1054069d | 3818:dc18457b1cef |
---|---|
1 package de.intevation.flys.collections; | |
2 | |
3 import java.io.IOException; | |
4 import java.io.OutputStream; | |
5 import java.util.ArrayList; | |
6 import java.util.HashMap; | |
7 import java.util.List; | |
8 import java.util.Map; | |
9 import java.util.Set; | |
10 | |
11 import javax.xml.xpath.XPathConstants; | |
12 | |
13 import org.apache.log4j.Logger; | |
14 | |
15 import org.w3c.dom.Document; | |
16 import org.w3c.dom.Element; | |
17 import org.w3c.dom.Node; | |
18 import org.w3c.dom.NodeList; | |
19 | |
20 import de.intevation.artifacts.Artifact; | |
21 import de.intevation.artifacts.ArtifactDatabase; | |
22 import de.intevation.artifacts.ArtifactDatabaseException; | |
23 import de.intevation.artifacts.ArtifactNamespaceContext; | |
24 import de.intevation.artifacts.CallContext; | |
25 import de.intevation.artifacts.CallMeta; | |
26 | |
27 import de.intevation.artifacts.common.utils.ClientProtocolUtils; | |
28 import de.intevation.artifacts.common.utils.XMLUtils; | |
29 | |
30 import de.intevation.artifactdatabase.Backend; | |
31 import de.intevation.artifactdatabase.Backend.PersistentArtifact; | |
32 import de.intevation.artifactdatabase.DefaultArtifactCollection; | |
33 import de.intevation.artifactdatabase.state.ArtifactAndFacet; | |
34 import de.intevation.artifactdatabase.state.Output; | |
35 import de.intevation.artifactdatabase.state.Settings; | |
36 import de.intevation.artifactdatabase.state.StateEngine; | |
37 | |
38 import de.intevation.flys.artifacts.context.FLYSContext; | |
39 import de.intevation.flys.artifacts.FLYSArtifact; | |
40 import de.intevation.flys.artifacts.model.ManagedFacet; | |
41 import de.intevation.flys.artifacts.model.ManagedDomFacet; | |
42 import de.intevation.flys.exports.OutGenerator; | |
43 import de.intevation.flys.themes.Theme; | |
44 import de.intevation.flys.themes.ThemeFactory; | |
45 | |
46 import de.intevation.flys.utils.FLYSUtils; | |
47 | |
48 /** | |
49 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
50 */ | |
51 public class FLYSArtifactCollection extends DefaultArtifactCollection { | |
52 /** The logger used in this class. */ | |
53 private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); | |
54 | |
55 /** Constant XPath that points to the outputmodes of an artifact. */ | |
56 public static final String XPATH_ARTIFACT_OUTPUTMODES = | |
57 "/art:result/art:outputmodes"; | |
58 | |
59 public static final String XPATH_ARTIFACT_STATE_DATA = | |
60 "/art:result/art:ui/art:static/art:state/art:data"; | |
61 | |
62 public static final String XPATH_COLLECTION_ITEMS = | |
63 "/art:result/art:artifact-collection/art:collection-item"; | |
64 | |
65 public static final String XPATH_OUT_NAME = "/art:action/@art:name"; | |
66 | |
67 public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; | |
68 | |
69 /** Xpath to master artifacts uuid. */ | |
70 public static final String XPATH_MASTER_UUID = | |
71 "/art:artifact-collection/art:artifact/@art:uuid"; | |
72 | |
73 public static final String XPATH_LOADED_RECOMMENDATIONS = | |
74 "/art:attribute/art:loaded-recommendations"; | |
75 | |
76 | |
77 /** | |
78 * Return description Document for this collection. | |
79 */ | |
80 @Override | |
81 public Document describe(CallContext context) { | |
82 log.debug("FLYSArtifactCollection.describe: " + identifier); | |
83 | |
84 CollectionDescriptionHelper helper = new CollectionDescriptionHelper( | |
85 getName(), identifier(), getCreationTime(), getTTL(), | |
86 context); | |
87 | |
88 ArtifactDatabase db = context.getDatabase(); | |
89 | |
90 Document oldAttrs = getAttribute(); | |
91 AttributeParser parser = new AttributeParser(oldAttrs); | |
92 | |
93 try { | |
94 String[] aUUIDs = getArtifactUUIDs(context); | |
95 | |
96 oldAttrs = removeAttributes(oldAttrs, context); | |
97 parser = new AttributeParser(oldAttrs); | |
98 | |
99 CollectionAttribute newAttr = mergeAttributes( | |
100 db, context, parser, aUUIDs); | |
101 | |
102 if (checkOutputSettings(newAttr, context)) { | |
103 saveCollectionAttribute(db, context, newAttr); | |
104 } | |
105 | |
106 helper.setAttribute(newAttr); | |
107 | |
108 // Make it an empty array if null. | |
109 if (aUUIDs == null) { | |
110 aUUIDs = new String[] {}; | |
111 } | |
112 | |
113 for (String uuid: aUUIDs) { | |
114 helper.addArtifact(uuid); | |
115 } | |
116 } | |
117 catch (ArtifactDatabaseException ade) { | |
118 log.error("Error while merging attribute documents.", ade); | |
119 | |
120 helper.setAttribute(parser.getCollectionAttribute()); | |
121 } | |
122 | |
123 return helper.toXML(); | |
124 } | |
125 | |
126 | |
127 /** | |
128 * Merge the current art:outputs nodes with the the outputs provided by the | |
129 * artifacts in the Collection. | |
130 * | |
131 * @param uuids Artifact uuids. | |
132 */ | |
133 protected CollectionAttribute mergeAttributes( | |
134 ArtifactDatabase db, | |
135 CallContext context, | |
136 AttributeParser oldParser, | |
137 String[] uuids | |
138 ) { | |
139 CollectionAttribute cAttribute = | |
140 buildOutAttributes(db, context, oldParser, uuids); | |
141 | |
142 cAttribute.setLoadedRecommendations( | |
143 getLoadedRecommendations(oldParser.getAttributeDocument())); | |
144 | |
145 saveCollectionAttribute(db, context, cAttribute); | |
146 | |
147 return cAttribute; | |
148 } | |
149 | |
150 | |
151 protected Document removeAttributes(Document attrs, CallContext context) { | |
152 FLYSArtifact master = getMasterArtifact(context); | |
153 List<Output> outList = master.getOutputs(context); | |
154 | |
155 Document doc = XMLUtils.newDocument(); | |
156 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
157 doc, | |
158 ArtifactNamespaceContext.NAMESPACE_URI, | |
159 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
160 | |
161 Element root = ec.create("attribute"); | |
162 doc.appendChild(root); | |
163 | |
164 if (outList.size() > 0) { | |
165 Element outs = ec.create("outputs"); | |
166 root.appendChild(outs); | |
167 | |
168 for (Output o : outList) { | |
169 Node n = (Node) XMLUtils.xpath( | |
170 attrs, | |
171 "/art:attribute/art:outputs/art:output[@name='" + o.getName() + "']", | |
172 XPathConstants.NODE, | |
173 ArtifactNamespaceContext.INSTANCE); | |
174 if (n != null) { | |
175 Node clone = doc.importNode(n, true); | |
176 outs.appendChild(clone); | |
177 } | |
178 } | |
179 } | |
180 | |
181 return doc; | |
182 } | |
183 | |
184 /** | |
185 * @param db The ArtifactDatabase which is required to save the attribute | |
186 * into. | |
187 * @param attribute The CollectionAttribute that should be stored in the | |
188 * database. | |
189 * | |
190 * @return true, if the transaction was successful, otherwise false. | |
191 */ | |
192 protected boolean saveCollectionAttribute( | |
193 ArtifactDatabase db, | |
194 CallContext context, | |
195 CollectionAttribute attribute | |
196 ) { | |
197 log.info("Save new CollectionAttribute into database."); | |
198 | |
199 Document doc = attribute.toXML(); | |
200 | |
201 try { | |
202 // Save the merged document into database. | |
203 db.setCollectionAttribute(identifier(), context.getMeta(), doc); | |
204 | |
205 log.info("Saving CollectionAttribute was successful."); | |
206 | |
207 return true; | |
208 } | |
209 catch (ArtifactDatabaseException adb) { | |
210 log.error(adb, adb); | |
211 } | |
212 | |
213 return false; | |
214 } | |
215 | |
216 | |
217 /** | |
218 * Merge the recommendations which have already been loaded from the old | |
219 * attribute document into the new attribute document. This is necessary, | |
220 * because mergeAttributes() only merges the art:outputs nodes - all | |
221 * other nodes are skiped. | |
222 */ | |
223 protected Node getLoadedRecommendations(Document oldAttrs) { | |
224 Element loadedRecoms = (Element) XMLUtils.xpath( | |
225 oldAttrs, | |
226 XPATH_LOADED_RECOMMENDATIONS, | |
227 XPathConstants.NODE, | |
228 ArtifactNamespaceContext.INSTANCE); | |
229 | |
230 return loadedRecoms; | |
231 } | |
232 | |
233 | |
234 /** | |
235 * Evaluates the Output settings. If an Output has no Settings set, the | |
236 * relevant OutGenerator is used to initialize a default Settings object. | |
237 * | |
238 * @param attribute The CollectionAttribute. | |
239 * @param cc The CallContext. | |
240 * | |
241 * @return true, if the CollectionAttribute was modified, otherwise false. | |
242 */ | |
243 protected boolean checkOutputSettings( | |
244 CollectionAttribute attribute, | |
245 CallContext cc | |
246 ) { | |
247 boolean modified = false; | |
248 | |
249 Map<String, Output> outputMap = attribute != null | |
250 ? attribute.getOutputs() | |
251 : null; | |
252 | |
253 if (outputMap == null || outputMap.size() == 0) { | |
254 log.debug("No Output Settings check necessary."); | |
255 return modified; | |
256 } | |
257 | |
258 Set<Map.Entry<String, Output>> entries = outputMap.entrySet(); | |
259 | |
260 for (Map.Entry<String, Output> entry: entries) { | |
261 String outName = entry.getKey(); | |
262 Output output = entry.getValue(); | |
263 | |
264 Settings settings = output.getSettings(); | |
265 | |
266 if (settings == null) { | |
267 log.debug("No Settings set for Output '" + outName + "'."); | |
268 output.setSettings( | |
269 createInitialOutputSettings(cc, attribute, outName)); | |
270 | |
271 modified = true; | |
272 } | |
273 } | |
274 | |
275 return modified; | |
276 } | |
277 | |
278 | |
279 /** | |
280 * This method uses the the OutGenerator for the specified Output | |
281 * <i>out</i> to create an initial Settings object. | |
282 * | |
283 * @param cc The CallContext object. | |
284 * @param attr The CollectionAttribute. | |
285 * @param out The name of the output. | |
286 * | |
287 * @return a default Settings object for the specified Output. | |
288 */ | |
289 protected Settings createInitialOutputSettings( | |
290 CallContext cc, | |
291 CollectionAttribute attr, | |
292 String out | |
293 ) { | |
294 OutGenerator outGen = getOutGenerator(cc, out, null); | |
295 | |
296 if (outGen == null) { | |
297 return null; | |
298 } | |
299 | |
300 // XXX NOTE: the outGen is not able to process its generate() operation, | |
301 // because it has no OutputStream set! | |
302 outGen.init(XMLUtils.newDocument(), null, cc); | |
303 prepareMasterArtifact(outGen, cc); | |
304 | |
305 try { | |
306 Document outAttr = getAttribute(cc, attr, out); | |
307 doOut(outGen, out, out, outAttr, cc); | |
308 } | |
309 catch (ArtifactDatabaseException adbe) { | |
310 log.error(adbe, adbe); | |
311 } | |
312 catch (IOException ioe) { | |
313 log.error(ioe, ioe); | |
314 } | |
315 | |
316 return outGen.getSettings(); | |
317 } | |
318 | |
319 | |
320 @Override | |
321 public void out( | |
322 String type, | |
323 Document format, | |
324 OutputStream out, | |
325 CallContext context) | |
326 throws IOException | |
327 { | |
328 long reqBegin = System.currentTimeMillis(); | |
329 | |
330 log.info("FLYSArtifactCollection.out"); | |
331 | |
332 String name = XMLUtils.xpathString( | |
333 format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE); | |
334 | |
335 String subtype = XMLUtils.xpathString( | |
336 format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE); | |
337 | |
338 log.info("-> Output name = " + name); | |
339 log.info("-> Output type = " + type); | |
340 log.info("-> Output subtype = " + subtype); | |
341 | |
342 OutGenerator generator = null; | |
343 if (type != null | |
344 && type.length() > 0 | |
345 && type.indexOf("chartinfo") > 0) | |
346 { | |
347 generator = getOutGenerator(context, type, subtype); | |
348 } | |
349 else { | |
350 generator = getOutGenerator(context, name, subtype); | |
351 } | |
352 | |
353 if (generator == null) { | |
354 log.error("There is no generator specified for output: " + name); | |
355 // TODO Throw an exception. | |
356 | |
357 return; | |
358 } | |
359 | |
360 Document oldAttrs = getAttribute(); | |
361 AttributeParser parser = new AttributeParser(oldAttrs); | |
362 CollectionAttribute cAttr = parser.getCollectionAttribute(); | |
363 | |
364 Output output = cAttr.getOutput(name); | |
365 Settings settings = output.getSettings(); | |
366 | |
367 generator.init(format, out, context); | |
368 generator.setSettings(settings); | |
369 prepareMasterArtifact(generator, context); | |
370 | |
371 try { | |
372 Document attr = getAttribute(context, cAttr, name); | |
373 doOut(generator, name, subtype, attr, context); | |
374 generator.generate(); | |
375 } | |
376 catch (ArtifactDatabaseException adbe) { | |
377 log.error(adbe, adbe); | |
378 } | |
379 | |
380 long duration = System.currentTimeMillis() -reqBegin; | |
381 log.info("Processing out(" + name + ") took " + duration + " ms."); | |
382 } | |
383 | |
384 | |
385 /** | |
386 * Sets the master Artifact at the given <i>generator</i>. | |
387 * | |
388 * @param generator The generator that gets a master Artifact. | |
389 * @param cc The CallContext. | |
390 */ | |
391 protected void prepareMasterArtifact(OutGenerator generator, CallContext cc | |
392 ) { | |
393 // Get master artifact. | |
394 FLYSArtifact master = getMasterArtifact(cc); | |
395 if (master != null) { | |
396 log.debug("Set master Artifact to uuid: " + master.identifier()); | |
397 generator.setMasterArtifact(master); | |
398 } | |
399 else { | |
400 log.warn("Could not set master artifact."); | |
401 } | |
402 } | |
403 | |
404 | |
405 /** | |
406 * Creates the concrete output. | |
407 * | |
408 * @param generator The OutGenerator that creates the output. | |
409 * @param outputName The name of the requested output. | |
410 * @param attributes The collection's attributes for this concrete output | |
411 * type. | |
412 * @param context The context object. | |
413 */ | |
414 protected void doOut( | |
415 OutGenerator generator, | |
416 String outName, | |
417 String facet, | |
418 Document attributes, | |
419 CallContext context) | |
420 throws IOException | |
421 { | |
422 log.info("FLYSArtifactCollection.doOut: " + outName); | |
423 | |
424 ThemeList themeList = new ThemeList(attributes); | |
425 | |
426 int size = themeList.size(); | |
427 log.debug("Output will contain " + size + " elements."); | |
428 | |
429 List<ArtifactAndFacet> dataProviders = | |
430 doBlackboardPass(themeList, context); | |
431 | |
432 try { | |
433 for (int i = 0; i < size; i++) { | |
434 ManagedFacet theme = themeList.get(i); | |
435 | |
436 if (theme == null) { | |
437 log.debug("Theme is empty - no output is generated."); | |
438 continue; | |
439 } | |
440 | |
441 String art = theme.getArtifact(); | |
442 String facetName = theme.getName(); | |
443 | |
444 if (log.isDebugEnabled()) { | |
445 log.debug("Do output for..."); | |
446 log.debug("... artifact: " + art); | |
447 log.debug("... facet: " + facetName); | |
448 } | |
449 | |
450 if (outName.equals("export") && !facetName.equals(facet)) { | |
451 continue; | |
452 } | |
453 | |
454 // Skip invisible themes. | |
455 if (theme.getVisible() == 0) { | |
456 continue; | |
457 } | |
458 | |
459 generator.doOut( | |
460 dataProviders.get(i), | |
461 getFacetThemeFromAttribute( | |
462 art, | |
463 outName, | |
464 facetName, | |
465 theme.getDescription(), | |
466 theme.getIndex(), | |
467 context), | |
468 theme.getActive() == 1); | |
469 } | |
470 } | |
471 catch (ArtifactDatabaseException ade) { | |
472 log.error(ade, ade); | |
473 } | |
474 } | |
475 | |
476 | |
477 /** | |
478 * Show blackboard (context) to each facet and create a list of | |
479 * ArtifactAndFacets on the fly (with the same ordering as the passed | |
480 * ThemeList). | |
481 * @param themeList ThemeList to create a ArtifactAndFacetList along. | |
482 * @param context The "Blackboard". | |
483 */ | |
484 protected List<ArtifactAndFacet> doBlackboardPass( | |
485 ThemeList themeList, CallContext context | |
486 ) { | |
487 ArrayList<ArtifactAndFacet> dataProviders = | |
488 new ArrayList<ArtifactAndFacet>(); | |
489 int size = themeList.size(); | |
490 | |
491 try { | |
492 // Collect all ArtifactAndFacets for blackboard pass. | |
493 int pivot = 0; | |
494 for (int i = 0; i < size; i++) { | |
495 ManagedFacet theme = themeList.get(i); | |
496 if (theme == null) { | |
497 log.warn("A ManagedFacet in ThemeList is null."); | |
498 continue; | |
499 } | |
500 String uuid = theme.getArtifact(); | |
501 Artifact artifact = getArtifact(uuid, context); | |
502 FLYSArtifact flys = (FLYSArtifact) artifact; | |
503 | |
504 ArtifactAndFacet artifactAndFacet = new ArtifactAndFacet( | |
505 artifact, | |
506 flys.getNativeFacet(theme)); | |
507 | |
508 // XXX HELP ME PLEASE | |
509 artifactAndFacet.setFacetDescription(theme.getDescription()); | |
510 | |
511 // Show blackboard to facet. | |
512 artifactAndFacet.register(context); | |
513 | |
514 // Add to themes. | |
515 dataProviders.add(pivot, artifactAndFacet); | |
516 pivot += 1; | |
517 } | |
518 } | |
519 catch (ArtifactDatabaseException ade) { | |
520 log.error("ArtifactDatabaseException!", ade); | |
521 } | |
522 | |
523 return dataProviders; | |
524 } | |
525 | |
526 | |
527 /** | |
528 * @return masterartifact or null if exception/not found. | |
529 */ | |
530 protected FLYSArtifact getMasterArtifact(CallContext context) | |
531 { | |
532 try { | |
533 ArtifactDatabase db = context.getDatabase(); | |
534 CallMeta callMeta = context.getMeta(); | |
535 Document document = db.getCollectionsMasterArtifact( | |
536 identifier(), callMeta); | |
537 | |
538 String masterUUID = XMLUtils.xpathString( | |
539 document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE); | |
540 FLYSArtifact masterArtifact = | |
541 (FLYSArtifact) getArtifact(masterUUID, context); | |
542 return masterArtifact; | |
543 } | |
544 catch (ArtifactDatabaseException ade) { | |
545 log.error(ade, ade); | |
546 } | |
547 return null; | |
548 } | |
549 | |
550 | |
551 /** | |
552 * Return merged output document. | |
553 * @param uuids List of artifact uuids. | |
554 */ | |
555 protected CollectionAttribute buildOutAttributes( | |
556 ArtifactDatabase db, | |
557 CallContext context, | |
558 AttributeParser aParser, | |
559 String[] uuids) | |
560 { | |
561 Document doc = XMLUtils.newDocument(); | |
562 | |
563 FLYSContext flysContext = FLYSUtils.getFlysContext(context); | |
564 StateEngine engine = (StateEngine) flysContext.get( | |
565 FLYSContext.STATE_ENGINE_KEY); | |
566 | |
567 FLYSArtifact masterArtifact = getMasterArtifact(context); | |
568 | |
569 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
570 doc, | |
571 ArtifactNamespaceContext.NAMESPACE_URI, | |
572 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
573 | |
574 OutputParser oParser = new OutputParser(db, context); | |
575 | |
576 if (uuids != null) { | |
577 for (String uuid: uuids) { | |
578 try { | |
579 oParser.parse(uuid); | |
580 } | |
581 catch (ArtifactDatabaseException ade) { | |
582 log.warn(ade, ade); | |
583 } | |
584 } | |
585 } | |
586 | |
587 aParser.parse(); | |
588 | |
589 return new AttributeWriter( | |
590 db, | |
591 aParser.getCollectionAttribute(), | |
592 aParser.getOuts(), | |
593 aParser.getFacets(), | |
594 oParser.getOuts(), | |
595 oParser.getFacets(), | |
596 engine.getCompatibleFacets(masterArtifact.getStateHistoryIds()) | |
597 ).write(); | |
598 } | |
599 | |
600 | |
601 /** | |
602 * Returns the "attribute" (part of description document) for a specific | |
603 * output type. | |
604 * | |
605 * @param context The CallContext object. | |
606 * @param cAttr The CollectionAttribute. | |
607 * @param output The name of the desired output type. | |
608 * | |
609 * @return the attribute for the desired output type. | |
610 */ | |
611 protected Document getAttribute( | |
612 CallContext context, | |
613 CollectionAttribute cAttr, | |
614 String output) | |
615 throws ArtifactDatabaseException | |
616 { | |
617 Document attr = cAttr.toXML(); | |
618 | |
619 Node out = (Node) XMLUtils.xpath( | |
620 attr, | |
621 "art:attribute/art:outputs/art:output[@name='" + output + "']", | |
622 XPathConstants.NODE, | |
623 ArtifactNamespaceContext.INSTANCE); | |
624 | |
625 | |
626 if (out != null) { | |
627 Document o = XMLUtils.newDocument(); | |
628 | |
629 o.appendChild(o.importNode(out, true)); | |
630 | |
631 return o; | |
632 } | |
633 | |
634 return null; | |
635 } | |
636 | |
637 | |
638 /** | |
639 * This method returns the list of artifact UUIDs that this collections | |
640 * contains. | |
641 * | |
642 * @param context The CallContext that is necessary to get information about | |
643 * the ArtifactDatabase. | |
644 * | |
645 * @return a list of uuids. | |
646 */ | |
647 protected String[] getArtifactUUIDs(CallContext context) | |
648 throws ArtifactDatabaseException | |
649 { | |
650 log.debug("FLYSArtifactCollection.getArtifactUUIDs"); | |
651 | |
652 ArtifactDatabase db = context.getDatabase(); | |
653 CallMeta meta = context.getMeta(); | |
654 | |
655 Document itemList = db.listCollectionArtifacts(identifier(), meta); | |
656 NodeList items = (NodeList) XMLUtils.xpath( | |
657 itemList, | |
658 XPATH_COLLECTION_ITEMS, | |
659 XPathConstants.NODESET, | |
660 ArtifactNamespaceContext.INSTANCE); | |
661 | |
662 if (items == null || items.getLength() == 0) { | |
663 log.debug("No artifacts found in this collection."); | |
664 return null; | |
665 } | |
666 | |
667 int num = items.getLength(); | |
668 | |
669 List<String> uuids = new ArrayList<String>(num); | |
670 | |
671 for (int i = 0; i < num; i++) { | |
672 String uuid = XMLUtils.xpathString( | |
673 items.item(i), | |
674 "@art:uuid", | |
675 ArtifactNamespaceContext.INSTANCE); | |
676 | |
677 if (uuid != null && uuid.trim().length() != 0) { | |
678 uuids.add(uuid); | |
679 } | |
680 } | |
681 | |
682 return (String[]) uuids.toArray(new String[uuids.size()]); | |
683 } | |
684 | |
685 | |
686 /** | |
687 * Returns a concrete Artifact of this collection specified by its uuid. | |
688 * | |
689 * @param uuid The Artifact's uuid. | |
690 * @param context The CallContext. | |
691 * | |
692 * @return an Artifact. | |
693 */ | |
694 protected Artifact getArtifact(String uuid, CallContext context) | |
695 throws ArtifactDatabaseException | |
696 { | |
697 log.debug("FLYSArtifactCollection.getArtifact"); | |
698 | |
699 Backend backend = Backend.getInstance(); | |
700 PersistentArtifact persistent = backend.getArtifact(uuid); | |
701 | |
702 return persistent != null ? persistent.getArtifact() : null; | |
703 } | |
704 | |
705 | |
706 /** | |
707 * Returns the attribute that belongs to an artifact and facet stored in | |
708 * this collection. | |
709 * | |
710 * @param uuid The Artifact's uuid. | |
711 * @param outname The name of the requested output. | |
712 * @param facet The name of the requested facet. | |
713 * @param context The CallContext. | |
714 * | |
715 * @return an attribute in form of a document. | |
716 */ | |
717 protected Document getFacetThemeFromAttribute( | |
718 String uuid, | |
719 String outName, | |
720 String facet, | |
721 String pattern, | |
722 int index, | |
723 CallContext context) | |
724 throws ArtifactDatabaseException | |
725 { | |
726 log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute"); | |
727 | |
728 ArtifactDatabase db = context.getDatabase(); | |
729 CallMeta meta = context.getMeta(); | |
730 | |
731 FLYSContext flysContext = context instanceof FLYSContext | |
732 ? (FLYSContext) context | |
733 : (FLYSContext) context.globalContext(); | |
734 | |
735 Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta); | |
736 | |
737 if (attr == null) { | |
738 attr = initItemAttribute(uuid, facet, pattern, index, outName, context); | |
739 | |
740 if (attr == null) { | |
741 return null; | |
742 } | |
743 } | |
744 | |
745 log.debug("Search attribute of collection item: " + uuid); | |
746 | |
747 Node tmp = (Node) XMLUtils.xpath( | |
748 attr, | |
749 "/art:attribute", | |
750 XPathConstants.NODE, | |
751 ArtifactNamespaceContext.INSTANCE); | |
752 | |
753 if (tmp == null) { | |
754 log.warn("No attribute found. Operation failed."); | |
755 return null; | |
756 } | |
757 | |
758 log.debug("Search theme for facet '" + facet + "' in attribute."); | |
759 | |
760 Node theme = (Node) XMLUtils.xpath( | |
761 tmp, | |
762 "art:themes/theme[@facet='" + facet + | |
763 "' and @index='" + String.valueOf(index) + "']", | |
764 XPathConstants.NODE, | |
765 ArtifactNamespaceContext.INSTANCE); | |
766 | |
767 if (theme == null) { | |
768 log.warn("Could not find the theme in attribute of: " + uuid); | |
769 | |
770 Theme t = getThemeForFacet( | |
771 uuid, facet, pattern, index, outName, context); | |
772 | |
773 if (t == null) { | |
774 log.warn("No theme found for facet: " + facet); | |
775 return null; | |
776 } | |
777 | |
778 addThemeToAttribute(uuid, attr, t, context); | |
779 theme = t.toXML().getFirstChild(); | |
780 } | |
781 | |
782 Document doc = XMLUtils.newDocument(); | |
783 doc.appendChild(doc.importNode(theme, true)); | |
784 | |
785 return doc; | |
786 } | |
787 | |
788 | |
789 /** | |
790 * Adds the theme of a facet to a CollectionItem's attribute. | |
791 * | |
792 * @param uuid The uuid of the artifact. | |
793 * @param attr The current attribute of an artifact. | |
794 * @param t The theme to add. | |
795 * @param context The CallContext. | |
796 */ | |
797 protected void addThemeToAttribute( | |
798 String uuid, | |
799 Document attr, | |
800 Theme t, | |
801 CallContext context) | |
802 { | |
803 log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid); | |
804 | |
805 if (t == null) { | |
806 log.warn("Theme is empty - cancel adding it to attribute!"); | |
807 return; | |
808 } | |
809 | |
810 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( | |
811 attr, | |
812 ArtifactNamespaceContext.NAMESPACE_URI, | |
813 ArtifactNamespaceContext.NAMESPACE_PREFIX); | |
814 | |
815 Node tmp = (Node) XMLUtils.xpath( | |
816 attr, | |
817 "/art:attribute", | |
818 XPathConstants.NODE, | |
819 ArtifactNamespaceContext.INSTANCE); | |
820 | |
821 if (tmp == null) { | |
822 tmp = ec.create("attribute"); | |
823 attr.appendChild(tmp); | |
824 } | |
825 | |
826 Node themes = (Node) XMLUtils.xpath( | |
827 tmp, | |
828 "art:themes", | |
829 XPathConstants.NODE, | |
830 ArtifactNamespaceContext.INSTANCE); | |
831 | |
832 if (themes == null) { | |
833 themes = ec.create("themes"); | |
834 tmp.appendChild(themes); | |
835 } | |
836 | |
837 themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true)); | |
838 | |
839 try { | |
840 setCollectionItemAttribute(uuid, attr, context); | |
841 | |
842 log.debug("Successfully added theme to item attribute."); | |
843 } | |
844 catch (ArtifactDatabaseException e) { | |
845 // do nothing | |
846 log.warn("Cannot set attribute of item: " + uuid); | |
847 } | |
848 } | |
849 | |
850 | |
851 /** | |
852 * Initializes the attribute of an collection item with the theme of a | |
853 * specific facet. | |
854 * | |
855 * @param uuid The uuid of an artifact. | |
856 * @param facet The name of a facet. | |
857 * @param context The CallContext. | |
858 * | |
859 * @param the new attribute. | |
860 */ | |
861 protected Document initItemAttribute( | |
862 String uuid, | |
863 String facet, | |
864 String pattern, | |
865 int index, | |
866 String outName, | |
867 CallContext context) | |
868 { | |
869 log.info("FLYSArtifactCollection.initItemAttribute"); | |
870 | |
871 Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, context); | |
872 | |
873 if (t == null) { | |
874 log.info("Could not find theme for facet. Cancel initialization."); | |
875 return null; | |
876 } | |
877 | |
878 Document attr = XMLUtils.newDocument(); | |
879 | |
880 addThemeToAttribute(uuid, attr, t, context); | |
881 | |
882 return attr; | |
883 } | |
884 | |
885 | |
886 /** | |
887 * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new | |
888 * value <i>attr</i>. | |
889 * | |
890 * @param uuid The uuid of the CollectionItem. | |
891 * @param attr The new attribute for the CollectionItem. | |
892 * @param context The CallContext. | |
893 */ | |
894 public void setCollectionItemAttribute( | |
895 String uuid, | |
896 Document attr, | |
897 CallContext context) | |
898 throws ArtifactDatabaseException | |
899 { | |
900 Document doc = ClientProtocolUtils.newSetItemAttributeDocument( | |
901 uuid, | |
902 attr); | |
903 | |
904 if (doc == null) { | |
905 log.warn("Cannot set item attribute: No attribute found."); | |
906 return; | |
907 } | |
908 | |
909 ArtifactDatabase db = context.getDatabase(); | |
910 CallMeta meta = context.getMeta(); | |
911 | |
912 db.setCollectionItemAttribute(identifier(), uuid, doc, meta); | |
913 } | |
914 | |
915 | |
916 /** | |
917 * Returns the theme of a specific facet. | |
918 * | |
919 * @param uuid The uuid of an artifact. | |
920 * @param facet The name of the facet. | |
921 * @param context The CallContext object. | |
922 * | |
923 * @return the desired theme. | |
924 */ | |
925 protected Theme getThemeForFacet( | |
926 String uuid, | |
927 String facet, | |
928 String pattern, | |
929 int index, | |
930 String outName, | |
931 CallContext context) | |
932 { | |
933 log.info("FLYSArtifactCollection.getThemeForFacet: " + facet); | |
934 | |
935 FLYSContext flysContext = context instanceof FLYSContext | |
936 ? (FLYSContext) context | |
937 : (FLYSContext) context.globalContext(); | |
938 | |
939 // Push artifact in flysContext. | |
940 ArtifactDatabase db = context.getDatabase(); | |
941 try { | |
942 FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid); | |
943 log.debug("Got raw artifact"); | |
944 flysContext.put(flysContext.ARTIFACT_KEY, artifact); | |
945 } | |
946 catch (ArtifactDatabaseException dbe) { | |
947 log.error("Exception caught when trying to get art.", dbe); | |
948 } | |
949 | |
950 Theme t = ThemeFactory.getTheme(flysContext, facet, pattern, outName); | |
951 | |
952 if (t != null) { | |
953 t.setFacet(facet); | |
954 t.setIndex(index); | |
955 } | |
956 | |
957 return t; | |
958 } | |
959 | |
960 | |
961 /** | |
962 * Returns the OutGenerator for a specified <i>type</i>. | |
963 * | |
964 * @param name The name of the output type. | |
965 * @param type Defines the type of the desired OutGenerator. | |
966 * | |
967 * @return Instance of an OutGenerator for specified <i>type</i>. | |
968 */ | |
969 protected OutGenerator getOutGenerator( | |
970 CallContext context, | |
971 String name, | |
972 String type) | |
973 { | |
974 log.debug("Search OutGenerator for Output '" + name + "'"); | |
975 | |
976 FLYSContext flysContext = context instanceof FLYSContext | |
977 ? (FLYSContext) context | |
978 : (FLYSContext) context.globalContext(); | |
979 | |
980 Map<String, Class> generators = (Map<String, Class>) | |
981 flysContext.get(FLYSContext.OUTGENERATORS_KEY); | |
982 | |
983 if (generators == null) { | |
984 log.error("No output generators found in the running application!"); | |
985 return null; | |
986 } | |
987 | |
988 Class clazz = generators.get(name); | |
989 | |
990 try { | |
991 return clazz != null ? (OutGenerator) clazz.newInstance() : null; | |
992 } | |
993 catch (InstantiationException ie) { | |
994 log.error(ie, ie); | |
995 } | |
996 catch (IllegalAccessException iae) { | |
997 log.error(iae, iae); | |
998 } | |
999 | |
1000 return null; | |
1001 } | |
1002 | |
1003 | |
1004 /** | |
1005 * Inner class to structure/order the themes of a chart. | |
1006 */ | |
1007 private static class ThemeList { | |
1008 private Logger logger = Logger.getLogger(ThemeList.class); | |
1009 protected Map<Integer, ManagedFacet> themes; | |
1010 | |
1011 public ThemeList(Document output) { | |
1012 themes = new HashMap<Integer, ManagedFacet>(); | |
1013 parse(output); | |
1014 } | |
1015 | |
1016 protected void parse(Document output) { | |
1017 NodeList themeList = (NodeList) XMLUtils.xpath( | |
1018 output, | |
1019 "art:output/art:facet", | |
1020 XPathConstants.NODESET, | |
1021 ArtifactNamespaceContext.INSTANCE); | |
1022 | |
1023 int num = themeList != null ? themeList.getLength() : 0; | |
1024 | |
1025 logger.debug("Output has " + num + " elements."); | |
1026 | |
1027 if (num == 0) { | |
1028 return; | |
1029 } | |
1030 | |
1031 String uri = ArtifactNamespaceContext.NAMESPACE_URI; | |
1032 | |
1033 for (int i = 0; i < num; i++) { | |
1034 Element theme = (Element) themeList.item(i); | |
1035 | |
1036 ManagedDomFacet facet = new ManagedDomFacet(theme); | |
1037 themes.put(Integer.valueOf(facet.getPosition()-1), facet); | |
1038 } | |
1039 } | |
1040 | |
1041 public ManagedFacet get(int idx) { | |
1042 return themes.get(Integer.valueOf(idx)); | |
1043 } | |
1044 | |
1045 public int size() { | |
1046 return themes.size(); | |
1047 } | |
1048 } | |
1049 } | |
1050 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |