view flys-artifacts/src/main/java/de/intevation/flys/exports/OutputHelper.java @ 3313:1a9ab048573a

Prevent nasty NPE. flys-artifacts/trunk@5002 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Felix Wolfsteller <felix.wolfsteller@intevation.de>
date Fri, 13 Jul 2012 19:01:03 +0000
parents 02d5731b43a2
children 8e713e9bb4d7
line wrap: on
line source
package de.intevation.flys.exports;

import java.io.IOException;
import java.util.ArrayList;
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.artifactdatabase.Backend;
import de.intevation.artifactdatabase.Backend.PersistentArtifact;
import de.intevation.artifactdatabase.state.ArtifactAndFacet;
import de.intevation.artifacts.Artifact;
import de.intevation.artifacts.ArtifactDatabase;
import de.intevation.artifacts.ArtifactDatabaseException;
import de.intevation.artifacts.CallContext;
import de.intevation.artifacts.CallMeta;
import de.intevation.artifacts.common.ArtifactNamespaceContext;
import de.intevation.artifacts.common.utils.ClientProtocolUtils;
import de.intevation.artifacts.common.utils.XMLUtils;
import de.intevation.flys.artifacts.FLYSArtifact;
import de.intevation.flys.artifacts.context.FLYSContext;
import de.intevation.flys.artifacts.model.ManagedDomFacet;
import de.intevation.flys.artifacts.model.ManagedFacet;
import de.intevation.flys.themes.Theme;
import de.intevation.flys.themes.ThemeFactory;

public class OutputHelper {
    /** The logger used in this class. */
    private static Logger log = Logger.getLogger(OutputHelper.class);

    protected String identifier;

    public OutputHelper(String identifier) {
        this.identifier = identifier;
    }
    /**
     * Creates a 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.
     */
    public void doOut(
        OutGenerator generator,
        String       outName,
        String       facet,
        Document     attributes,
        CallContext  context)
    throws IOException
    {
        boolean debug = log.isDebugEnabled();

        if (debug) {
            log.debug("FLYSArtifactCollection.doOut: " + outName);
        }

        ThemeList themeList = new ThemeList(attributes);

        int size = themeList.size();
        if (debug) {
            log.debug("Output will contain " + size + " elements.");
        }

        List<ArtifactAndFacet> dataProviders =
            doBlackboardPass(themeList, context);

        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();
                String facetName = theme.getName();

                if (debug) {
                    log.debug("Do output for...");
                    log.debug("... artifact: " + art);
                    log.debug("... facet: " + facetName);
                }

                if (outName.equals("export") && !facetName.equals(facet)) {
                    continue;
                }

                // Skip invisible themes.
                if (theme.getVisible() == 0) {
                    continue;
                }

                if (outName.equals("sq_overview")) {
                    generator.doOut(
                        dataProviders.get(i),
                        attributes,
                        theme.getActive() == 1);
                }
                else {
                    generator.doOut(
                            dataProviders.get(i),
                            getFacetThemeFromAttribute(
                                art,
                                outName,
                                facetName,
                                theme.getDescription(),
                                theme.getIndex(),
                                context),
                            theme.getActive() == 1);
                }
            }
        }
        catch (ArtifactDatabaseException ade) {
            log.error(ade, ade);
        }
    }
    /**
     * Returns the attribute that belongs to an artifact and facet 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,
        String      pattern,
        int         index,
        CallContext context)
    throws    ArtifactDatabaseException
    {
        boolean debug = log.isDebugEnabled();

        if (debug) {
            log.debug(
                "FLYSArtifactCollection.getFacetThemeFromAttribute(facet="
                + facet + ", index=" + index + ")");
        }


        ArtifactDatabase db = context.getDatabase();
        CallMeta       meta = context.getMeta();

        FLYSContext flysContext = context instanceof FLYSContext
            ? (FLYSContext) context
            : (FLYSContext) context.globalContext();

        Document attr = db.getCollectionItemAttribute(identifier, uuid, meta);

        if (attr == null) {
            attr = initItemAttribute(uuid, facet, pattern, index, outName, context);

            if (attr == null) {
                return null;
            }
        }

        if (debug) {
            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;
        }

        if (debug) {
            log.debug("Search theme for facet '" + facet + "' in attribute.");
        }

        Map<String, String> vars = new HashMap<String, String>();
        vars.put("facet", facet);
        vars.put("index", String.valueOf(index));

        Node theme = (Node) XMLUtils.xpath(
            tmp,
            "art:themes/theme[@facet=$facet and @index=$index]",
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE,
            vars);

        if (theme == null) {
            log.warn("Could not find the theme in attribute of: " + facet + " " + uuid);

            Theme t = getThemeForFacet(
                uuid, facet, pattern, index, outName, 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);
        }
        catch (ArtifactDatabaseException e) {
            // do nothing
            log.warn("Cannot set attribute of item: " + uuid);
        }
    }

    /**
     * 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);
    }


    /**
     * Show blackboard (context) to each facet and create a list of
     * ArtifactAndFacets on the fly (with the same ordering as the passed
     * ThemeList).
     * @param themeList ThemeList to create a ArtifactAndFacetList along.
     * @param context   The "Blackboard".
     */
    protected List<ArtifactAndFacet> doBlackboardPass(
        ThemeList themeList, CallContext context
    ) {
        ArrayList<ArtifactAndFacet> dataProviders =
            new ArrayList<ArtifactAndFacet>();
        int size = themeList.size();

        try {
            // Collect all ArtifactAndFacets for blackboard pass.
            for (int i = 0; i < size; i++) {
                ManagedFacet theme = themeList.get(i);
                if (theme == null) {
                    log.warn("A ManagedFacet in ThemeList is null.");
                    continue;
                }
                String uuid        = theme.getArtifact();
                Artifact artifact  = getArtifact(uuid, context);
                FLYSArtifact flys  = (FLYSArtifact) artifact;

                ArtifactAndFacet artifactAndFacet = new ArtifactAndFacet(
                    artifact,
                    flys.getNativeFacet(theme));

                // XXX HELP ME PLEASE
                artifactAndFacet.setFacetDescription(theme.getDescription());

                // Show blackboard to facet.
                artifactAndFacet.register(context);

                // Add to themes.
                dataProviders.add(i, artifactAndFacet);
            }
        }
        catch (ArtifactDatabaseException ade) {
            log.error("ArtifactDatabaseException!", ade);
        }

        return dataProviders;
    }
    /**
     * 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;
    }

    /**
     * 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,
        String      pattern,
        int         index,
        String      outName,
        CallContext context)
    {
        boolean debug = log.isDebugEnabled();

        if (debug) {
            log.debug("FLYSArtifactCollection.initItemAttribute");
        }

        Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, 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);

        if (debug) {
            log.debug("initItemAttribute for facet " + facet + ": "
                + XMLUtils.toString(attr));
        }

        return attr;
    }

        /**
     * 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,
        String pattern,
        int    index,
        String outName,
        CallContext context)
    {
        log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);

        FLYSContext flysContext = context instanceof FLYSContext
            ? (FLYSContext) context
            : (FLYSContext) context.globalContext();

        // Push artifact in flysContext.
        ArtifactDatabase db = context.getDatabase();
        try {
            FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid);
            log.debug("Got raw artifact");
            flysContext.put(FLYSContext.ARTIFACT_KEY, artifact);
        }
        catch (ArtifactDatabaseException dbe) {
            log.error("Exception caught when trying to get art.", dbe);
        }

        Theme t = ThemeFactory.getTheme(
                      flysContext,
                      facet,
                      pattern,
                      outName,
                      "default");

        if (t != null) {
            t.setFacet(facet);
            t.setIndex(index);
        }

        return t;
    }

    /**
     * Inner class to structure/order the themes of a chart.
     */
    private static 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 themeList = (NodeList) XMLUtils.xpath(
                output,
                "art:output/art:facet",
                XPathConstants.NODESET,
                ArtifactNamespaceContext.INSTANCE);

            int num = themeList != null ? themeList.getLength() : 0;

            logger.debug("Output has " +  num + " elements.");

            if (num == 0) {
                return;
            }

            String uri = ArtifactNamespaceContext.NAMESPACE_URI;

            for (int i = 0; i < num; i++) {
                Element theme = (Element) themeList.item(i);

                ManagedDomFacet facet = new ManagedDomFacet(theme);
                themes.put(Integer.valueOf(facet.getPosition()-1), facet);
            }
        }

        public ManagedFacet get(int idx) {
            return themes.get(Integer.valueOf(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