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 :

http://dive4elements.wald.intevation.org