view flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 3275:3570abdf7daa

SQOverview: Made km separating of dates epsilon tolerant and avoid empty date lists. flys-artifacts/trunk@4919 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 10 Jul 2012 17:04:04 +0000
parents 68320805566a
children 4fc442f1b4f6
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.artifactdatabase.state.ArtifactAndFacet;
import de.intevation.artifactdatabase.state.Output;
import de.intevation.artifactdatabase.state.Settings;
import de.intevation.artifactdatabase.state.StateEngine;

import de.intevation.flys.artifacts.context.FLYSContext;
import de.intevation.flys.artifacts.FLYSArtifact;
import de.intevation.flys.artifacts.model.ManagedFacet;
import de.intevation.flys.artifacts.model.ManagedDomFacet;
import de.intevation.flys.exports.OutGenerator;
import de.intevation.flys.themes.Theme;
import de.intevation.flys.themes.ThemeFactory;

import de.intevation.flys.utils.FLYSUtils;

/**
 * @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_ARTIFACT_STATE_DATA =
        "/art:result/art:ui/art:static/art:state/art:data";

    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";

    /** Xpath to master artifacts uuid. */
    public static final String XPATH_MASTER_UUID =
        "/art:artifact-collection/art:artifact/@art:uuid";

    public static final String XPATH_LOADED_RECOMMENDATIONS =
        "/art:attribute/art:loaded-recommendations";


    /**
     * Return description Document for this collection.
     */
    @Override
    public Document describe(CallContext context) {
        log.debug("FLYSArtifactCollection.describe: " + identifier);

        CollectionDescriptionHelper helper = new CollectionDescriptionHelper(
            getName(), identifier(), getCreationTime(), getTTL(),
            context);

        ArtifactDatabase db = context.getDatabase();

        Document        oldAttrs = getAttribute();
        AttributeParser parser   = new AttributeParser(oldAttrs);

        try {
            String[]            aUUIDs  = getArtifactUUIDs(context);

            oldAttrs = removeAttributes(oldAttrs, context);
            parser   = new AttributeParser(oldAttrs);

            CollectionAttribute newAttr = mergeAttributes(
                db, context, parser, aUUIDs);

            if (checkOutputSettings(newAttr, context)) {
                saveCollectionAttribute(db, context, newAttr);
            }

            helper.setAttribute(newAttr);

            // Make it an empty array if null.
            if (aUUIDs == null) {
                aUUIDs = new String[] {};
            }

            for (String uuid: aUUIDs) {
                helper.addArtifact(uuid);
            }
        }
        catch (ArtifactDatabaseException ade) {
            log.error("Error while merging attribute documents.", ade);

            helper.setAttribute(parser.getCollectionAttribute());
        }

        return helper.toXML();
    }


    /**
     * Merge the current art:outputs nodes with the the outputs provided by the
     * artifacts in the Collection.
     *
     * @param uuids Artifact uuids.
     */
    protected CollectionAttribute mergeAttributes(
        ArtifactDatabase db,
        CallContext      context,
        AttributeParser  oldParser,
        String[]         uuids
    ) {
        CollectionAttribute cAttribute =
            buildOutAttributes(db, context, oldParser, uuids);

        cAttribute.setLoadedRecommendations(
            getLoadedRecommendations(oldParser.getAttributeDocument()));

        saveCollectionAttribute(db, context, cAttribute);

        return cAttribute;
    }


    protected Document removeAttributes(Document attrs, CallContext context) {
        Node outs = (Node) XMLUtils.xpath(
            attrs,
            "/art:attribute/art:outputs",
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE);

        NodeList nodes = (NodeList) XMLUtils.xpath(
            attrs,
            "/art:attribute/art:outputs/art:output",
            XPathConstants.NODESET,
            ArtifactNamespaceContext.INSTANCE);

        if (nodes != null) {
            for (int i = 0; i < nodes.getLength(); i++) {
                Element e = (Element)nodes.item(i);
                if(!outputExists(e.getAttribute("name"), context)) {
                    outs.removeChild(e);
                }
            }
        }
        return attrs;
    }


    /**
     * True if current MasterArtifact has given output.
     * @param name Name of the output of interest.
     * @param context current context
     * @return true if current master artifact has given output.
     */
    protected boolean outputExists(String name, CallContext context) {
        FLYSArtifact master = getMasterArtifact(context);
        List<Output> outList = master.getOutputs(context);

        for (Output o : outList) {
            if (name.equals(o.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param db The ArtifactDatabase which is required to save the attribute
     * into.
     * @param attribute The CollectionAttribute that should be stored in the
     * database.
     *
     * @return true, if the transaction was successful, otherwise false.
     */
    protected boolean saveCollectionAttribute(
        ArtifactDatabase    db,
        CallContext         context,
        CollectionAttribute attribute
    ) {
        log.info("Save new CollectionAttribute into database.");

        Document doc = attribute.toXML();

        try {
            // Save the merged document into database.
            db.setCollectionAttribute(identifier(), context.getMeta(), doc);

            log.info("Saving CollectionAttribute was successful.");

            return true;
        }
        catch (ArtifactDatabaseException adb) {
            log.error(adb, adb);
        }

        return false;
    }


    /**
     * Merge the recommendations which have already been loaded from the old
     * attribute document into the new attribute document. This is necessary,
     * because mergeAttributes() only merges the art:outputs nodes - all
     * other nodes are skiped.
     */
    protected Node getLoadedRecommendations(Document oldAttrs) {
        Element loadedRecoms = (Element) XMLUtils.xpath(
            oldAttrs,
            XPATH_LOADED_RECOMMENDATIONS,
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE);

        return loadedRecoms;
    }


    /**
     * Evaluates the Output settings. If an Output has no Settings set, the
     * relevant OutGenerator is used to initialize a default Settings object.
     *
     * @param attribute The CollectionAttribute.
     * @param cc The CallContext.
     *
     * @return true, if the CollectionAttribute was modified, otherwise false.
     */
    protected boolean checkOutputSettings(
        CollectionAttribute attribute,
        CallContext         cc
    ) {
        boolean modified = false;

        Map<String, Output> outputMap = attribute != null
            ? attribute.getOutputs()
            : null;

        if (outputMap == null || outputMap.size() == 0) {
            log.debug("No Output Settings check necessary.");
            return modified;
        }

        Set<Map.Entry<String, Output>> entries = outputMap.entrySet();

        for (Map.Entry<String, Output> entry: entries) {
            String outName = entry.getKey();
            Output output  = entry.getValue();

            Settings settings = output.getSettings();

            if (settings == null) {
                log.debug("No Settings set for Output '" + outName + "'.");
                output.setSettings(
                    createInitialOutputSettings(cc, attribute, outName));

                modified = true;
            }
        }

        return modified;
    }


    /**
     * This method uses the the OutGenerator for the specified Output
     * <i>out</i> to create an initial Settings object.
     *
     * @param cc The CallContext object.
     * @param attr The CollectionAttribute.
     * @param out The name of the output.
     *
     * @return a default Settings object for the specified Output.
     */
    protected Settings createInitialOutputSettings(
        CallContext         cc,
        CollectionAttribute attr,
        String              out
    ) {
        OutGenerator outGen = getOutGenerator(cc, out, null);

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

        // XXX NOTE: the outGen is not able to process its generate() operation,
        // because it has no OutputStream set!
        outGen.init(XMLUtils.newDocument(), null, cc);
        prepareMasterArtifact(outGen, cc);

        try {
            Document outAttr = getAttribute(cc, attr, out);
            doOut(outGen, out, out, outAttr, cc);
        }
        catch (ArtifactDatabaseException adbe) {
            log.error(adbe, adbe);
        }
        catch (IOException ioe) {
            log.error(ioe, ioe);
        }

        return outGen.getSettings();
    }


    @Override
    public void out(
        String       type,
        Document     format,
        OutputStream out,
        CallContext  context)
    throws IOException
    {
        long reqBegin = System.currentTimeMillis();

        log.info("FLYSArtifactCollection.out");

        String name = XMLUtils.xpathString(
            format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);

        String subtype = XMLUtils.xpathString(
            format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);

        log.info("-> Output name = " + name);
        log.info("-> Output type = " + type);
        log.info("-> Output subtype = " + subtype);

        OutGenerator generator = null;
        if (type != null
             && type.length() > 0
             && type.indexOf("chartinfo") > 0)
        {
            generator = getOutGenerator(context, type, subtype);
        }
        else {
            generator = getOutGenerator(context, name, subtype);
        }

        if (generator == null) {
            log.error("There is no generator specified for output: " + name);
            // TODO Throw an exception.

            return;
        }

        Document        oldAttrs  = getAttribute();
        AttributeParser parser    = new AttributeParser(oldAttrs);
        CollectionAttribute cAttr = parser.getCollectionAttribute();

        Output   output   = cAttr.getOutput(name);
        Settings settings = output.getSettings();

        generator.init(format, out, context);
        generator.setSettings(settings);
        prepareMasterArtifact(generator, context);

        try {
            Document attr = getAttribute(context, cAttr, name);
            doOut(generator, name, subtype, attr, context);
            generator.generate();
        }
        catch (ArtifactDatabaseException adbe) {
            log.error(adbe, adbe);
        }

        long duration = System.currentTimeMillis() -reqBegin;
        log.info("Processing out(" + name + ") took " + duration + " ms.");
    }


    /**
     * Sets the master Artifact at the given <i>generator</i>.
     *
     * @param generator The generator that gets a master Artifact.
     * @param cc The CallContext.
     */
    protected void prepareMasterArtifact(OutGenerator generator, CallContext cc
    ) {
        // Get master artifact.
        FLYSArtifact master = getMasterArtifact(cc);
        if (master != null) {
            log.debug("Set master Artifact to uuid: " + master.identifier());
            generator.setMasterArtifact(master);
        }
        else {
            log.warn("Could not set master artifact.");
        }
    }


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

                generator.doOut(
                    dataProviders.get(i),
                    getFacetThemeFromAttribute(
                        art,
                        outName,
                        facetName,
                        theme.getDescription(),
                        theme.getIndex(),
                        context),
                    theme.getActive() == 1);
            }
        }
        catch (ArtifactDatabaseException ade) {
            log.error(ade, ade);
        }
    }


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


    /**
     * @return masterartifact or null if exception/not found.
     */
    protected FLYSArtifact getMasterArtifact(CallContext context)
    {
        try {
            ArtifactDatabase db = context.getDatabase();
            CallMeta callMeta   = context.getMeta();
            Document document   = db.getCollectionsMasterArtifact(
                identifier(), callMeta);

            String masterUUID   = XMLUtils.xpathString(
                document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE);
            FLYSArtifact masterArtifact =
                (FLYSArtifact) getArtifact(masterUUID, context);
            return masterArtifact;
        }
        catch (ArtifactDatabaseException ade) {
            log.error(ade, ade);
        }
        return null;
    }


    /**
     * Return merged output document.
     * @param uuids List of artifact uuids.
     */
    protected CollectionAttribute buildOutAttributes(
        ArtifactDatabase db,
        CallContext      context,
        AttributeParser  aParser,
        String[]         uuids)
    {
        Document doc = XMLUtils.newDocument();

        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
        StateEngine engine = (StateEngine) flysContext.get(
        FLYSContext.STATE_ENGINE_KEY);

        FLYSArtifact masterArtifact = getMasterArtifact(context);

        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
            doc,
            ArtifactNamespaceContext.NAMESPACE_URI,
            ArtifactNamespaceContext.NAMESPACE_PREFIX);

        OutputParser oParser = new OutputParser(db, context);

        if (uuids != null) {
            for (String uuid: uuids) {
                try {
                    oParser.parse(uuid);
                }
                catch (ArtifactDatabaseException ade) {
                    log.warn(ade, ade);
                }
            }
        }

        aParser.parse();

        return new AttributeWriter(
            db,
            aParser.getCollectionAttribute(),
            aParser.getOuts(),
            aParser.getFacets(),
            oParser.getOuts(),
            oParser.getFacets(),
            engine.getCompatibleFacets(masterArtifact.getStateHistoryIds())
            ).write();
    }


    /**
     * Returns the "attribute" (part of description document) for a specific
     * output type.
     *
     * @param context The CallContext object.
     * @param cAttr The CollectionAttribute.
     * @param output The name of the desired output type.
     *
     * @return the attribute for the desired output type.
     */
    protected Document getAttribute(
        CallContext         context,
        CollectionAttribute cAttr,
        String              output)
    throws    ArtifactDatabaseException
    {
        Document attr = cAttr.toXML();

        Map<String, String> vars = new HashMap<String, String>();
        vars.put("output", output);

        Node out = (Node) XMLUtils.xpath(
            attr,
            "art:attribute/art:outputs/art:output[@name=$output]",
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE,
            vars);


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


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


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


    /**
     * 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 Instance of an OutGenerator for specified <i>type</i>.
     */
    protected OutGenerator getOutGenerator(
        CallContext context,
        String      name,
        String      type)
    {
        log.debug("Search OutGenerator for Output '" + name + "'");

        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;
    }


    /**
     * 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