view flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java @ 4205:0dd8963cec9c

Set also the width of the GaugeTree when resizing the GaugePanel GWT is no longer able to calculate and set the correct width of the GaugeTree since the GaugeTree is added via a Canvas wrapper. Therefore set the width manually when resizing the GaugeTree.
author Björn Ricks <bjoern.ricks@intevation.de>
date Mon, 22 Oct 2012 15:33:16 +0200
parents 975f608dd254
children b74399bd0960
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 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.DefaultArtifactCollection;
import de.intevation.artifactdatabase.state.Output;
import de.intevation.artifactdatabase.state.Settings;
import de.intevation.artifactdatabase.state.StateEngine;
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.XMLUtils;
import de.intevation.flys.artifacts.FLYSArtifact;
import de.intevation.flys.artifacts.context.FLYSContext;
import de.intevation.flys.exports.OutGenerator;
import de.intevation.flys.exports.OutputHelper;
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);

            if (aUUIDs != null) {
                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);

        if (cAttribute == null) {
            log.warn("mergeAttributes: cAttribute == null");
            return null;
        }

        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 skipped.
     */
    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.isEmpty()) {
            log.debug("No Output Settings check necessary.");
            return modified;
        }


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

            if (outName.equals("sq_overview")) {
                continue;
            }
            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 = FLYSContext.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);
            OutputHelper helper = new OutputHelper(identifier());
            helper.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
    {
        boolean debug = log.isDebugEnabled();

        long reqBegin = System.currentTimeMillis();

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

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

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

        if (debug) {
            log.debug("-> Output name = " + name);
            log.debug("-> Output type = " + type);
            log.debug("-> Output subtype = " + subtype);
        }

        OutGenerator generator = null;
        if (type != null
             && type.length() > 0
             && type.indexOf("chartinfo") > 0)
        {
            generator = FLYSContext.getOutGenerator(context, type, subtype);
        }
        else {
            generator = FLYSContext.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 = null;
        if (output != null) {
            settings = output.getSettings();
        }

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

        try {
            Document attr = getAttribute(context, cAttr, name);
            OutputHelper helper = new OutputHelper(identifier());
            if (name.equals("sq_overview")) {
                helper.doOut(generator, name, subtype, format, context);
            }
            helper.doOut(generator, name, subtype, attr, context);
            generator.generate();
        }
        catch (ArtifactDatabaseException adbe) {
            log.error(adbe, adbe);
        }

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


    /**
     * @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)
    {
        FLYSContext flysContext = FLYSUtils.getFlysContext(context);
        StateEngine engine = (StateEngine) flysContext.get(
            FLYSContext.STATE_ENGINE_KEY);

        if (engine == null) {
            log.error("buildOutAttributes: engine == null");
            return null;
        }

        FLYSArtifact masterArtifact = getMasterArtifact(context);

        if (masterArtifact == null) {
            log.debug("buildOutAttributes: masterArtifact == null");
            return null;
        }

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

        AttributeWriter aWriter = new AttributeWriter(
            db,
            aParser.getCollectionAttribute(),
            aParser.getOuts(),
            aParser.getFacets(),
            oParser.getOuts(),
            oParser.getFacets(),
            engine.getCompatibleFacets(masterArtifact.getStateHistoryIds())
            );
        return aWriter.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 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;
    }


}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org