Mercurial > dive4elements > river
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java Fri Sep 28 12:14:10 2012 +0200 @@ -0,0 +1,754 @@ +package de.intevation.flys.collections; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.ClientProtocolUtils; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.Backend; +import de.intevation.artifactdatabase.Backend.PersistentArtifact; +import de.intevation.artifactdatabase.DefaultArtifactCollection; + +import de.intevation.flys.artifacts.context.FLYSContext; +import de.intevation.flys.artifacts.model.ManagedFacet; +import de.intevation.flys.exports.OutGenerator; +import de.intevation.flys.themes.Theme; +import de.intevation.flys.themes.ThemeFactory; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class FLYSArtifactCollection extends DefaultArtifactCollection { + + /** The logger used in this class.*/ + private static Logger log = Logger.getLogger(FLYSArtifactCollection.class); + + + /** Constant XPath that points to the outputmodes of an artifact.*/ + public static final String XPATH_ARTIFACT_OUTPUTMODES = + "/art:result/art:outputmodes"; + + public static final String XPATH_COLLECTION_ITEMS = + "/art:result/art:artifact-collection/art:collection-item"; + + public static final String XPATH_OUT_NAME = "/art:action/@art:name"; + + public static final String XPATH_OUT_TYPE = "/art:action/@art:type"; + + + + @Override + public Document describe(CallContext context) { + log.debug("FLYSArtifactCollection.describe: " + identifier); + + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Date creationTime = getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + Element collection = ec.create("artifact-collection"); + Element artifacts = ec.create("artifacts"); + Element attributes = ec.create("attribute"); + + ec.addAttr(collection, "name", getName(), true); + ec.addAttr(collection, "uuid", identifier(), true); + ec.addAttr(collection, "creation", creation, true); + + collection.appendChild(artifacts); + collection.appendChild(attributes); + doc.appendChild(collection); + + ArtifactDatabase db = context.getDatabase(); + + try { + String[] artifactUUIDs = getArtifactUUIDs(context); + + Document oldAttrs = getAttribute(); + Document attrs = buildAttributes( + db, context, + oldAttrs, + artifactUUIDs); + + db.setCollectionAttribute(identifier(), context.getMeta(), attrs); + + Node child = attrs.getFirstChild(); + attributes.appendChild(doc.importNode(child, true)); + + for (String uuid: artifactUUIDs) { + try { + artifacts.appendChild( + buildArtifactNode(db, uuid, context, ec)); + } + catch (ArtifactDatabaseException dbe) { + log.warn(dbe, dbe); + } + } + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + } + + return doc; + } + + + @Override + public void out(Document format, OutputStream out, CallContext context) + throws IOException + { + log.info("FLYSArtifactCollection.out"); + + String name = XMLUtils.xpathString( + format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE); + + String type = XMLUtils.xpathString( + format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE); + + log.debug("Output name = " + name); + log.debug("Output type = " + type); + + OutGenerator generator = getOutGenerator(context, name, type); + if (generator == null) { + log.error("There is no generator specified for output: " + name); + // TODO throw an exception. + + return; + } + + generator.init(format, out, context); + + // TODO Determine the correct master artifact here! + + try { + doOut(generator, name, type, getAttribute(context, name), context); + } + catch (ArtifactDatabaseException adbe) { + log.error(adbe, adbe); + } + } + + + /** + * This method creates the concrete output. + * + * @param generator The OutGenerator that creates the output. + * @param outputName The name of the requested output. + * @param attributes The collection's attributes for this concrete output + * type. + * @param context The context object. + */ + protected void doOut( + OutGenerator generator, + String outName, + String facet, + Document attributes, + CallContext context) + throws IOException + { + log.debug("FLYSArtifactCollection.doOut: " + outName); + + ThemeList themeList = new ThemeList(attributes); + + int size = themeList.size(); + log.debug("Output will contain " + size + " elements."); + + try { + for (int i = 0; i < size; i++) { + ManagedFacet theme = themeList.get(i); + + if (theme == null) { + log.debug("Theme is empty - no output is generated."); + continue; + } + + String art = theme.getArtifact(); + + if (log.isDebugEnabled()) { + log.debug("Do output for..."); + log.debug("... artifact: " + art); + log.debug("... facet: " + theme.getName()); + } + + String facetName = theme.getName(); + + if (outName.equals("export") && !facetName.equals(facet)) { + continue; + } + + // TODO Remove the following two lines of code! The master + // artifact has to be determined correctly after + // OutGenerator.init is called! + Artifact artifact = getArtifact(art, context); + generator.setMasterArtifact(artifact); + + generator.doOut( + artifact, + facetName, + getFacetThemeFromAttribute( + art, + outName, + facetName, + context)); + } + } + catch (ArtifactDatabaseException ade) { + log.error(ade, ade); + } + + generator.generate(); + } + + + protected Document buildAttributes( + ArtifactDatabase db, + CallContext context, + Document oldAttr, + String[] items) + { + Document doc = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + AttributeParser aParser = new AttributeParser(); + OutputParser oParser = new OutputParser(db, context.getMeta()); + + for (String uuid: items) { + try { + oParser.parse(uuid); + } + catch (ArtifactDatabaseException ade) { + log.warn(ade, ade); + } + } + + aParser.parse(oldAttr); + + return new AttributeWriter(aParser.getOuts(), oParser.getOuts()).write(); + } + + + /** + * Returns the attribute for a specific output type. + * + * @param output The name of the desired output type. + * + * @return the attribute for the desired output type. + */ + protected Document getAttribute(CallContext context, String output) + throws ArtifactDatabaseException + { + Document attr = buildAttributes( + context.getDatabase(), + context, + getAttribute(), + getArtifactUUIDs(context)); + + Node out = (Node) XMLUtils.xpath( + attr, + "art:outputs/art:output[@name='" + output + "']", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + + if (out != null) { + Document o = XMLUtils.newDocument(); + + o.appendChild(o.importNode(out, true)); + + return o; + } + + return null; + } + + + /** + * This method returns the list of artifact UUIDs that this collections + * contains. + * + * @param context The CallContext that is necessary to get information about + * the ArtifactDatabase. + * + * @return a list of uuids. + */ + protected String[] getArtifactUUIDs(CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getArtifactUUIDs"); + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + Document itemList = db.listCollectionArtifacts(identifier(), meta); + NodeList items = (NodeList) XMLUtils.xpath( + itemList, + XPATH_COLLECTION_ITEMS, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (items == null || items.getLength() == 0) { + log.debug("No artifacts found in this collection."); + return null; + } + + int num = items.getLength(); + + List<String> uuids = new ArrayList<String>(num); + + for (int i = 0; i < num; i++) { + String uuid = XMLUtils.xpathString( + items.item(i), + "@art:uuid", + ArtifactNamespaceContext.INSTANCE); + + if (uuid != null && uuid.trim().length() != 0) { + uuids.add(uuid); + } + } + + return (String[]) uuids.toArray(new String[uuids.size()]); + } + + + /** + * Returns a concrete Artifact of this collection specified by its uuid. + * + * @param uuid The Artifact's uuid. + * @param context The CallContext. + * + * @return an Artifact. + */ + protected Artifact getArtifact(String uuid, CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getArtifact"); + + Backend backend = Backend.getInstance(); + PersistentArtifact persistent = backend.getArtifact(uuid); + + return persistent != null ? persistent.getArtifact() : null; + } + + + /** + * Returns the attribute that belongs to an artifact stored in this + * collection. + * + * @param uuid The Artifact's uuid. + * @param outname The name of the requested output. + * @param facet The name of the requested facet. + * @param context The CallContext. + * + * @return an attribute in form of a document. + */ + protected Document getFacetThemeFromAttribute( + String uuid, + String outName, + String facet, + CallContext context) + throws ArtifactDatabaseException + { + log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute"); + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + Map<String, String> mappings = (Map<String, String>) + flysContext.get(FLYSContext.THEME_MAPPING); + + String themeName = mappings.get(facet); + + Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta); + + if (attr == null) { + attr = initItemAttribute(uuid, facet, context); + + if (attr == null) { + return null; + } + } + + log.debug("Search attribute of collection item: " + uuid); + + Node tmp = (Node) XMLUtils.xpath( + attr, + "/art:attribute", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (tmp == null) { + log.warn("No attribute found. Operation failed."); + return null; + } + + log.debug("Search theme '" + themeName + "' in attribute."); + + Node theme = (Node) XMLUtils.xpath( + tmp, + "art:themes/theme[@name='" + themeName + "']", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (theme == null) { + log.warn("Could not find the theme in attribute of: " + uuid); + + Theme t = getThemeForFacet(uuid, facet, context); + + if (t == null) { + log.warn("No theme found for facet: " + facet); + return null; + } + + addThemeToAttribute(uuid, attr, t, context); + theme = t.toXML().getFirstChild(); + } + + Document doc = XMLUtils.newDocument(); + doc.appendChild(doc.importNode(theme, true)); + + return doc; + } + + + /** + * Adds the theme of a facet to a CollectionItem's attribute. + * + * @param uuid The uuid of the artifact. + * @param attr The current attribute of an artifact. + * @param t The theme to add. + * @param context The CallContext. + */ + protected void addThemeToAttribute( + String uuid, + Document attr, + Theme t, + CallContext context) + { + log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid); + + if (t == null) { + log.warn("Theme is empty - cancel adding it to attribute!"); + return; + } + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + attr, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Node tmp = (Node) XMLUtils.xpath( + attr, + "/art:attribute", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (tmp == null) { + tmp = ec.create("attribute"); + attr.appendChild(tmp); + } + + Node themes = (Node) XMLUtils.xpath( + tmp, + "art:themes", + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (themes == null) { + themes = ec.create("themes"); + tmp.appendChild(themes); + } + + themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true)); + + try { + setCollectionItemAttribute(uuid, attr, context); + + log.debug("Successfully added theme to item attribute."); + } + catch (ArtifactDatabaseException e) { + // do nothing + log.warn("Cannot set attribute of item: " + uuid); + } + } + + + /** + * Initializes the attribute of an collection item with the theme of a + * specific facet. + * + * @param uuid The uuid of an artifact. + * @param facet The name of a facet. + * @param context The CallContext. + * + * @param the new attribute. + */ + protected Document initItemAttribute( + String uuid, + String facet, + CallContext context) + { + log.info("FLYSArtifactCollection.initItemAttribute"); + + Theme t = getThemeForFacet(uuid, facet, context); + + if (t == null) { + log.info("Could not find theme for facet. Cancel initialization."); + return null; + } + + Document attr = XMLUtils.newDocument(); + + addThemeToAttribute(uuid, attr, t, context); + + return attr; + } + + + /** + * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new + * value <i>attr</i>. + * + * @param uuid The uuid of the CollectionItem. + * @param attr The new attribute for the CollectionItem. + * @param context The CallContext. + */ + public void setCollectionItemAttribute( + String uuid, + Document attr, + CallContext context) + throws ArtifactDatabaseException + { + Document doc = ClientProtocolUtils.newSetItemAttributeDocument( + uuid, + attr); + + if (doc == null) { + log.warn("Cannot set item attribute: No attribute found."); + return; + } + + ArtifactDatabase db = context.getDatabase(); + CallMeta meta = context.getMeta(); + + db.setCollectionItemAttribute(identifier(), uuid, doc, meta); + } + + + /** + * Returns the theme of a specific facet. + * + * @param uuid The uuid of an artifact. + * @param facet The name of the facet. + * @param context The CallContext object. + * + * @return the desired theme. + */ + protected Theme getThemeForFacet( + String uuid, + String facet, + CallContext context) + { + log.info("FLYSArtifactCollection.getThemeForFacet: " + facet); + + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + return ThemeFactory.getTheme(flysContext, facet); + } + + + /** + * Returns the OutGenerator for a specified <i>type</i>. + * + * @param name The name of the output type. + * @param type Defines the type of the desired OutGenerator. + * + * @return The OutGenerator specified by <i>type</i>. + */ + protected OutGenerator getOutGenerator( + CallContext context, + String name, + String type) + { + FLYSContext flysContext = context instanceof FLYSContext + ? (FLYSContext) context + : (FLYSContext) context.globalContext(); + + Map<String, Class> generators = (Map<String, Class>) + flysContext.get(FLYSContext.OUTGENERATORS_KEY); + + if (generators == null) { + log.error("No output generators found in the running application!"); + return null; + } + + Class clazz = generators.get(name); + + try { + return clazz != null ? (OutGenerator) clazz.newInstance() : null; + } + catch (InstantiationException ie) { + log.error(ie, ie); + } + catch (IllegalAccessException iae) { + log.error(iae, iae); + } + + return null; + } + + + protected Element buildArtifactNode( + ArtifactDatabase database, + String uuid, + CallContext context, + XMLUtils.ElementCreator ec) + throws ArtifactDatabaseException + { + log.debug("Append artifact '" + uuid + "' to collection description"); + + // TODO + String hash = "MYHASH"; + + Element ci = ec.create("artifact"); + ec.addAttr(ci, "uuid", uuid, true); + ec.addAttr(ci, "hash", hash, true); + + // XXX I am not sure if it works well every time with an empty document + // in the describe operation of an artifact. + Document description = database.describe(uuid, null, context.getMeta()); + + Node outputModes = (Node) XMLUtils.xpath( + description, + XPATH_ARTIFACT_OUTPUTMODES, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (outputModes != null) { + Document doc = ci.getOwnerDocument(); + ci.appendChild(doc.importNode(outputModes, true)); + } + + return ci; + } + + + /** + * Inner class to structure/order the themes of a chart. + */ + private class ThemeList { + private Logger logger = Logger.getLogger(ThemeList.class); + protected Vector<ManagedFacet> themes; + + public ThemeList(Document output) { + themes = new Vector<ManagedFacet>(); + parse(output); + } + + protected void parse(Document output) { + NodeList themes = (NodeList) XMLUtils.xpath( + output, + "art:output/art:theme", + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + int num = themes != null ? themes.getLength() : 0; + + logger.debug("Output has " + num + " elements."); + + if (num == 0) { + return; + } + + for (int i = 0; i < num; i++) { + Node theme = themes.item(i); + + String name = XMLUtils.xpathString( + theme, "@art:facet", ArtifactNamespaceContext.INSTANCE); + + String uuid = XMLUtils.xpathString( + theme, "@art:artifact", ArtifactNamespaceContext.INSTANCE); + + String pos = XMLUtils.xpathString( + theme, "@art:pos", ArtifactNamespaceContext.INSTANCE); + + String active = XMLUtils.xpathString( + theme, "@art:active", ArtifactNamespaceContext.INSTANCE); + + addTheme(uuid, name, pos, active); + } + } + + protected void addTheme( + String uuid, + String name, + String position, + String active) + { + if (logger.isDebugEnabled()) { + logger.debug("Add theme: "); + logger.debug(".. name: " + name); + logger.debug(".. uuid: " + uuid); + logger.debug(".. position: " + position); + logger.debug(".. active: " + active); + } + + try { + int pos = Integer.parseInt(position); + int act = Integer.parseInt(active); + + themes.add(pos-1, new ManagedFacet(name, null, uuid, pos, act)); + } + catch (NumberFormatException nfe) { + logger.warn(nfe, nfe); + } + } + + public ManagedFacet get(int idx) { + return themes.get(idx); + } + + public int size() { + return themes.size(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :