comparison flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 430:7ab81ff32111 2.3

merged flys-artifacts/2.3
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:10 +0200
parents 046bd86ae41d
children 9c565eb46f06
comparison
equal deleted inserted replaced
290:a6f56ed9238b 430:7ab81ff32111
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.Date;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Vector;
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
34 import de.intevation.flys.artifacts.context.FLYSContext;
35 import de.intevation.flys.artifacts.model.ManagedFacet;
36 import de.intevation.flys.exports.OutGenerator;
37 import de.intevation.flys.themes.Theme;
38 import de.intevation.flys.themes.ThemeFactory;
39
40
41 /**
42 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
43 */
44 public class FLYSArtifactCollection extends DefaultArtifactCollection {
45
46 /** The logger used in this class.*/
47 private static Logger log = Logger.getLogger(FLYSArtifactCollection.class);
48
49
50 /** Constant XPath that points to the outputmodes of an artifact.*/
51 public static final String XPATH_ARTIFACT_OUTPUTMODES =
52 "/art:result/art:outputmodes";
53
54 public static final String XPATH_COLLECTION_ITEMS =
55 "/art:result/art:artifact-collection/art:collection-item";
56
57 public static final String XPATH_OUT_NAME = "/art:action/@art:name";
58
59 public static final String XPATH_OUT_TYPE = "/art:action/@art:type";
60
61
62
63 @Override
64 public Document describe(CallContext context) {
65 log.debug("FLYSArtifactCollection.describe: " + identifier);
66
67 Document doc = XMLUtils.newDocument();
68
69 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
70 doc,
71 ArtifactNamespaceContext.NAMESPACE_URI,
72 ArtifactNamespaceContext.NAMESPACE_PREFIX);
73
74 Date creationTime = getCreationTime();
75 String creation = creationTime != null
76 ? Long.toString(creationTime.getTime())
77 : "";
78
79 Element collection = ec.create("artifact-collection");
80 Element artifacts = ec.create("artifacts");
81 Element attributes = ec.create("attribute");
82
83 ec.addAttr(collection, "name", getName(), true);
84 ec.addAttr(collection, "uuid", identifier(), true);
85 ec.addAttr(collection, "creation", creation, true);
86
87 collection.appendChild(artifacts);
88 collection.appendChild(attributes);
89 doc.appendChild(collection);
90
91 ArtifactDatabase db = context.getDatabase();
92
93 try {
94 String[] artifactUUIDs = getArtifactUUIDs(context);
95
96 Document oldAttrs = getAttribute();
97 Document attrs = buildAttributes(
98 db, context,
99 oldAttrs,
100 artifactUUIDs);
101
102 db.setCollectionAttribute(identifier(), context.getMeta(), attrs);
103
104 Node child = attrs.getFirstChild();
105 attributes.appendChild(doc.importNode(child, true));
106
107 for (String uuid: artifactUUIDs) {
108 try {
109 artifacts.appendChild(
110 buildArtifactNode(db, uuid, context, ec));
111 }
112 catch (ArtifactDatabaseException dbe) {
113 log.warn(dbe, dbe);
114 }
115 }
116 }
117 catch (ArtifactDatabaseException ade) {
118 log.error(ade, ade);
119 }
120
121 return doc;
122 }
123
124
125 @Override
126 public void out(Document format, OutputStream out, CallContext context)
127 throws IOException
128 {
129 log.info("FLYSArtifactCollection.out");
130
131 String name = XMLUtils.xpathString(
132 format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);
133
134 String type = XMLUtils.xpathString(
135 format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);
136
137 log.debug("Output name = " + name);
138 log.debug("Output type = " + type);
139
140 OutGenerator generator = getOutGenerator(context, name, type);
141 if (generator == null) {
142 log.error("There is no generator specified for output: " + name);
143 // TODO throw an exception.
144
145 return;
146 }
147
148 generator.init(format, out, context);
149
150 // TODO Determine the correct master artifact here!
151
152 try {
153 doOut(generator, name, type, getAttribute(context, name), context);
154 }
155 catch (ArtifactDatabaseException adbe) {
156 log.error(adbe, adbe);
157 }
158 }
159
160
161 /**
162 * This method creates the concrete output.
163 *
164 * @param generator The OutGenerator that creates the output.
165 * @param outputName The name of the requested output.
166 * @param attributes The collection's attributes for this concrete output
167 * type.
168 * @param context The context object.
169 */
170 protected void doOut(
171 OutGenerator generator,
172 String outName,
173 String facet,
174 Document attributes,
175 CallContext context)
176 throws IOException
177 {
178 log.debug("FLYSArtifactCollection.doOut: " + outName);
179
180 ThemeList themeList = new ThemeList(attributes);
181
182 int size = themeList.size();
183 log.debug("Output will contain " + size + " elements.");
184
185 try {
186 for (int i = 0; i < size; i++) {
187 ManagedFacet theme = themeList.get(i);
188
189 if (theme == null) {
190 log.debug("Theme is empty - no output is generated.");
191 continue;
192 }
193
194 String art = theme.getArtifact();
195
196 if (log.isDebugEnabled()) {
197 log.debug("Do output for...");
198 log.debug("... artifact: " + art);
199 log.debug("... facet: " + theme.getName());
200 }
201
202 String facetName = theme.getName();
203
204 if (outName.equals("export") && !facetName.equals(facet)) {
205 continue;
206 }
207
208 // TODO Remove the following two lines of code! The master
209 // artifact has to be determined correctly after
210 // OutGenerator.init is called!
211 Artifact artifact = getArtifact(art, context);
212 generator.setMasterArtifact(artifact);
213
214 generator.doOut(
215 artifact,
216 facetName,
217 getFacetThemeFromAttribute(
218 art,
219 outName,
220 facetName,
221 context));
222 }
223 }
224 catch (ArtifactDatabaseException ade) {
225 log.error(ade, ade);
226 }
227
228 generator.generate();
229 }
230
231
232 protected Document buildAttributes(
233 ArtifactDatabase db,
234 CallContext context,
235 Document oldAttr,
236 String[] items)
237 {
238 Document doc = XMLUtils.newDocument();
239
240 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
241 doc,
242 ArtifactNamespaceContext.NAMESPACE_URI,
243 ArtifactNamespaceContext.NAMESPACE_PREFIX);
244
245 AttributeParser aParser = new AttributeParser();
246 OutputParser oParser = new OutputParser(db, context.getMeta());
247
248 for (String uuid: items) {
249 try {
250 oParser.parse(uuid);
251 }
252 catch (ArtifactDatabaseException ade) {
253 log.warn(ade, ade);
254 }
255 }
256
257 aParser.parse(oldAttr);
258
259 return new AttributeWriter(aParser.getOuts(), oParser.getOuts()).write();
260 }
261
262
263 /**
264 * Returns the attribute for a specific output type.
265 *
266 * @param output The name of the desired output type.
267 *
268 * @return the attribute for the desired output type.
269 */
270 protected Document getAttribute(CallContext context, String output)
271 throws ArtifactDatabaseException
272 {
273 Document attr = buildAttributes(
274 context.getDatabase(),
275 context,
276 getAttribute(),
277 getArtifactUUIDs(context));
278
279 Node out = (Node) XMLUtils.xpath(
280 attr,
281 "art:outputs/art:output[@name='" + output + "']",
282 XPathConstants.NODE,
283 ArtifactNamespaceContext.INSTANCE);
284
285
286 if (out != null) {
287 Document o = XMLUtils.newDocument();
288
289 o.appendChild(o.importNode(out, true));
290
291 return o;
292 }
293
294 return null;
295 }
296
297
298 /**
299 * This method returns the list of artifact UUIDs that this collections
300 * contains.
301 *
302 * @param context The CallContext that is necessary to get information about
303 * the ArtifactDatabase.
304 *
305 * @return a list of uuids.
306 */
307 protected String[] getArtifactUUIDs(CallContext context)
308 throws ArtifactDatabaseException
309 {
310 log.debug("FLYSArtifactCollection.getArtifactUUIDs");
311
312 ArtifactDatabase db = context.getDatabase();
313 CallMeta meta = context.getMeta();
314
315 Document itemList = db.listCollectionArtifacts(identifier(), meta);
316 NodeList items = (NodeList) XMLUtils.xpath(
317 itemList,
318 XPATH_COLLECTION_ITEMS,
319 XPathConstants.NODESET,
320 ArtifactNamespaceContext.INSTANCE);
321
322 if (items == null || items.getLength() == 0) {
323 log.debug("No artifacts found in this collection.");
324 return null;
325 }
326
327 int num = items.getLength();
328
329 List<String> uuids = new ArrayList<String>(num);
330
331 for (int i = 0; i < num; i++) {
332 String uuid = XMLUtils.xpathString(
333 items.item(i),
334 "@art:uuid",
335 ArtifactNamespaceContext.INSTANCE);
336
337 if (uuid != null && uuid.trim().length() != 0) {
338 uuids.add(uuid);
339 }
340 }
341
342 return (String[]) uuids.toArray(new String[uuids.size()]);
343 }
344
345
346 /**
347 * Returns a concrete Artifact of this collection specified by its uuid.
348 *
349 * @param uuid The Artifact's uuid.
350 * @param context The CallContext.
351 *
352 * @return an Artifact.
353 */
354 protected Artifact getArtifact(String uuid, CallContext context)
355 throws ArtifactDatabaseException
356 {
357 log.debug("FLYSArtifactCollection.getArtifact");
358
359 Backend backend = Backend.getInstance();
360 PersistentArtifact persistent = backend.getArtifact(uuid);
361
362 return persistent != null ? persistent.getArtifact() : null;
363 }
364
365
366 /**
367 * Returns the attribute that belongs to an artifact stored in this
368 * collection.
369 *
370 * @param uuid The Artifact's uuid.
371 * @param outname The name of the requested output.
372 * @param facet The name of the requested facet.
373 * @param context The CallContext.
374 *
375 * @return an attribute in form of a document.
376 */
377 protected Document getFacetThemeFromAttribute(
378 String uuid,
379 String outName,
380 String facet,
381 CallContext context)
382 throws ArtifactDatabaseException
383 {
384 log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute");
385
386 ArtifactDatabase db = context.getDatabase();
387 CallMeta meta = context.getMeta();
388
389 FLYSContext flysContext = context instanceof FLYSContext
390 ? (FLYSContext) context
391 : (FLYSContext) context.globalContext();
392
393 Map<String, String> mappings = (Map<String, String>)
394 flysContext.get(FLYSContext.THEME_MAPPING);
395
396 String themeName = mappings.get(facet);
397
398 Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta);
399
400 if (attr == null) {
401 attr = initItemAttribute(uuid, facet, context);
402
403 if (attr == null) {
404 return null;
405 }
406 }
407
408 log.debug("Search attribute of collection item: " + uuid);
409
410 Node tmp = (Node) XMLUtils.xpath(
411 attr,
412 "/art:attribute",
413 XPathConstants.NODE,
414 ArtifactNamespaceContext.INSTANCE);
415
416 if (tmp == null) {
417 log.warn("No attribute found. Operation failed.");
418 return null;
419 }
420
421 log.debug("Search theme '" + themeName + "' in attribute.");
422
423 Node theme = (Node) XMLUtils.xpath(
424 tmp,
425 "art:themes/theme[@name='" + themeName + "']",
426 XPathConstants.NODE,
427 ArtifactNamespaceContext.INSTANCE);
428
429 if (theme == null) {
430 log.warn("Could not find the theme in attribute of: " + uuid);
431
432 Theme t = getThemeForFacet(uuid, facet, context);
433
434 if (t == null) {
435 log.warn("No theme found for facet: " + facet);
436 return null;
437 }
438
439 addThemeToAttribute(uuid, attr, t, context);
440 theme = t.toXML().getFirstChild();
441 }
442
443 Document doc = XMLUtils.newDocument();
444 doc.appendChild(doc.importNode(theme, true));
445
446 return doc;
447 }
448
449
450 /**
451 * Adds the theme of a facet to a CollectionItem's attribute.
452 *
453 * @param uuid The uuid of the artifact.
454 * @param attr The current attribute of an artifact.
455 * @param t The theme to add.
456 * @param context The CallContext.
457 */
458 protected void addThemeToAttribute(
459 String uuid,
460 Document attr,
461 Theme t,
462 CallContext context)
463 {
464 log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid);
465
466 if (t == null) {
467 log.warn("Theme is empty - cancel adding it to attribute!");
468 return;
469 }
470
471 XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
472 attr,
473 ArtifactNamespaceContext.NAMESPACE_URI,
474 ArtifactNamespaceContext.NAMESPACE_PREFIX);
475
476 Node tmp = (Node) XMLUtils.xpath(
477 attr,
478 "/art:attribute",
479 XPathConstants.NODE,
480 ArtifactNamespaceContext.INSTANCE);
481
482 if (tmp == null) {
483 tmp = ec.create("attribute");
484 attr.appendChild(tmp);
485 }
486
487 Node themes = (Node) XMLUtils.xpath(
488 tmp,
489 "art:themes",
490 XPathConstants.NODE,
491 ArtifactNamespaceContext.INSTANCE);
492
493 if (themes == null) {
494 themes = ec.create("themes");
495 tmp.appendChild(themes);
496 }
497
498 themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true));
499
500 try {
501 setCollectionItemAttribute(uuid, attr, context);
502
503 log.debug("Successfully added theme to item attribute.");
504 }
505 catch (ArtifactDatabaseException e) {
506 // do nothing
507 log.warn("Cannot set attribute of item: " + uuid);
508 }
509 }
510
511
512 /**
513 * Initializes the attribute of an collection item with the theme of a
514 * specific facet.
515 *
516 * @param uuid The uuid of an artifact.
517 * @param facet The name of a facet.
518 * @param context The CallContext.
519 *
520 * @param the new attribute.
521 */
522 protected Document initItemAttribute(
523 String uuid,
524 String facet,
525 CallContext context)
526 {
527 log.info("FLYSArtifactCollection.initItemAttribute");
528
529 Theme t = getThemeForFacet(uuid, facet, context);
530
531 if (t == null) {
532 log.info("Could not find theme for facet. Cancel initialization.");
533 return null;
534 }
535
536 Document attr = XMLUtils.newDocument();
537
538 addThemeToAttribute(uuid, attr, t, context);
539
540 return attr;
541 }
542
543
544 /**
545 * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new
546 * value <i>attr</i>.
547 *
548 * @param uuid The uuid of the CollectionItem.
549 * @param attr The new attribute for the CollectionItem.
550 * @param context The CallContext.
551 */
552 public void setCollectionItemAttribute(
553 String uuid,
554 Document attr,
555 CallContext context)
556 throws ArtifactDatabaseException
557 {
558 Document doc = ClientProtocolUtils.newSetItemAttributeDocument(
559 uuid,
560 attr);
561
562 if (doc == null) {
563 log.warn("Cannot set item attribute: No attribute found.");
564 return;
565 }
566
567 ArtifactDatabase db = context.getDatabase();
568 CallMeta meta = context.getMeta();
569
570 db.setCollectionItemAttribute(identifier(), uuid, doc, meta);
571 }
572
573
574 /**
575 * Returns the theme of a specific facet.
576 *
577 * @param uuid The uuid of an artifact.
578 * @param facet The name of the facet.
579 * @param context The CallContext object.
580 *
581 * @return the desired theme.
582 */
583 protected Theme getThemeForFacet(
584 String uuid,
585 String facet,
586 CallContext context)
587 {
588 log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
589
590 FLYSContext flysContext = context instanceof FLYSContext
591 ? (FLYSContext) context
592 : (FLYSContext) context.globalContext();
593
594 return ThemeFactory.getTheme(flysContext, facet);
595 }
596
597
598 /**
599 * Returns the OutGenerator for a specified <i>type</i>.
600 *
601 * @param name The name of the output type.
602 * @param type Defines the type of the desired OutGenerator.
603 *
604 * @return The OutGenerator specified by <i>type</i>.
605 */
606 protected OutGenerator getOutGenerator(
607 CallContext context,
608 String name,
609 String type)
610 {
611 FLYSContext flysContext = context instanceof FLYSContext
612 ? (FLYSContext) context
613 : (FLYSContext) context.globalContext();
614
615 Map<String, Class> generators = (Map<String, Class>)
616 flysContext.get(FLYSContext.OUTGENERATORS_KEY);
617
618 if (generators == null) {
619 log.error("No output generators found in the running application!");
620 return null;
621 }
622
623 Class clazz = generators.get(name);
624
625 try {
626 return clazz != null ? (OutGenerator) clazz.newInstance() : null;
627 }
628 catch (InstantiationException ie) {
629 log.error(ie, ie);
630 }
631 catch (IllegalAccessException iae) {
632 log.error(iae, iae);
633 }
634
635 return null;
636 }
637
638
639 protected Element buildArtifactNode(
640 ArtifactDatabase database,
641 String uuid,
642 CallContext context,
643 XMLUtils.ElementCreator ec)
644 throws ArtifactDatabaseException
645 {
646 log.debug("Append artifact '" + uuid + "' to collection description");
647
648 // TODO
649 String hash = "MYHASH";
650
651 Element ci = ec.create("artifact");
652 ec.addAttr(ci, "uuid", uuid, true);
653 ec.addAttr(ci, "hash", hash, true);
654
655 // XXX I am not sure if it works well every time with an empty document
656 // in the describe operation of an artifact.
657 Document description = database.describe(uuid, null, context.getMeta());
658
659 Node outputModes = (Node) XMLUtils.xpath(
660 description,
661 XPATH_ARTIFACT_OUTPUTMODES,
662 XPathConstants.NODE,
663 ArtifactNamespaceContext.INSTANCE);
664
665 if (outputModes != null) {
666 Document doc = ci.getOwnerDocument();
667 ci.appendChild(doc.importNode(outputModes, true));
668 }
669
670 return ci;
671 }
672
673
674 /**
675 * Inner class to structure/order the themes of a chart.
676 */
677 private class ThemeList {
678 private Logger logger = Logger.getLogger(ThemeList.class);
679 protected Vector<ManagedFacet> themes;
680
681 public ThemeList(Document output) {
682 themes = new Vector<ManagedFacet>();
683 parse(output);
684 }
685
686 protected void parse(Document output) {
687 NodeList themes = (NodeList) XMLUtils.xpath(
688 output,
689 "art:output/art:theme",
690 XPathConstants.NODESET,
691 ArtifactNamespaceContext.INSTANCE);
692
693 int num = themes != null ? themes.getLength() : 0;
694
695 logger.debug("Output has " + num + " elements.");
696
697 if (num == 0) {
698 return;
699 }
700
701 for (int i = 0; i < num; i++) {
702 Node theme = themes.item(i);
703
704 String name = XMLUtils.xpathString(
705 theme, "@art:facet", ArtifactNamespaceContext.INSTANCE);
706
707 String uuid = XMLUtils.xpathString(
708 theme, "@art:artifact", ArtifactNamespaceContext.INSTANCE);
709
710 String pos = XMLUtils.xpathString(
711 theme, "@art:pos", ArtifactNamespaceContext.INSTANCE);
712
713 String active = XMLUtils.xpathString(
714 theme, "@art:active", ArtifactNamespaceContext.INSTANCE);
715
716 addTheme(uuid, name, pos, active);
717 }
718 }
719
720 protected void addTheme(
721 String uuid,
722 String name,
723 String position,
724 String active)
725 {
726 if (logger.isDebugEnabled()) {
727 logger.debug("Add theme: ");
728 logger.debug(".. name: " + name);
729 logger.debug(".. uuid: " + uuid);
730 logger.debug(".. position: " + position);
731 logger.debug(".. active: " + active);
732 }
733
734 try {
735 int pos = Integer.parseInt(position);
736 int act = Integer.parseInt(active);
737
738 themes.add(pos-1, new ManagedFacet(name, null, uuid, pos, act));
739 }
740 catch (NumberFormatException nfe) {
741 logger.warn(nfe, nfe);
742 }
743 }
744
745 public ManagedFacet get(int idx) {
746 return themes.get(idx);
747 }
748
749 public int size() {
750 return themes.size();
751 }
752 }
753 }
754 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org