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