Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 365:c13ca9d632d6
Introduced GNU Trove to store WQKms values in a more performant data structure.
flys-artifacts/trunk@1773 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Ingo Weinzierl <ingo.weinzierl@intevation.de> |
---|---|
date | Fri, 29 Apr 2011 08:28:44 +0000 |
parents | 8830eecad69e |
children | 4a8d934e745f |
line wrap: on
line source
package de.intevation.flys.collections; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; 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); OutGenerator generator = getOutGenerator(context, name, type); if (generator == null) { log.error("There is no generator specified for output: " + type); // TODO throw an exception. return; } generator.init(format, out, context); try { doOut(generator, name, 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, 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) { 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(); generator.doOut( getArtifact(art, context), 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(type); 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 Map<Integer, ManagedFacet> themes; public ThemeList(Document output) { themes = new HashMap<Integer, 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 { Integer pos = new Integer(position); Integer act = new Integer(active); themes.put(pos, new ManagedFacet(name, null, uuid, pos, act)); } catch (NumberFormatException nfe) { // do nothing } } public ManagedFacet get(int idx) { return themes.get(new Integer(idx)); } public int size() { return themes.size(); } } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :