view flys-client/src/main/java/de/intevation/flys/client/server/CollectionHelper.java @ 1381:4459901e4ec0

Updated the theme editor window layout. flys-client/trunk@3135 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Raimund Renkert <raimund.renkert@intevation.de>
date Tue, 01 Nov 2011 16:07:39 +0000
parents ab8eb2f544f2
children f6fbfdc813f0
line wrap: on
line source
package de.intevation.flys.client.server;

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.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.apache.log4j.Logger;

import de.intevation.artifacts.common.ArtifactNamespaceContext;
import de.intevation.artifacts.common.utils.ClientProtocolUtils;
import de.intevation.artifacts.common.utils.XMLUtils;
import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;

import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
import de.intevation.artifacts.httpclient.http.HttpClient;
import de.intevation.artifacts.httpclient.http.HttpClientImpl;
import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;

import de.intevation.flys.client.shared.exceptions.ServerException;
import de.intevation.flys.client.shared.model.Artifact;
import de.intevation.flys.client.shared.model.AttributedTheme;
import de.intevation.flys.client.shared.model.ChartMode;
import de.intevation.flys.client.shared.model.Collection;
import de.intevation.flys.client.shared.model.CollectionItem;
import de.intevation.flys.client.shared.model.DefaultCollection;
import de.intevation.flys.client.shared.model.DefaultCollectionItem;
import de.intevation.flys.client.shared.model.DefaultFacet;
import de.intevation.flys.client.shared.model.ExportMode;
import de.intevation.flys.client.shared.model.Facet;
import de.intevation.flys.client.shared.model.MapMode;
import de.intevation.flys.client.shared.model.OutputMode;
import de.intevation.flys.client.shared.model.ReportMode;
import de.intevation.flys.client.shared.model.Recommendation;
import de.intevation.flys.client.shared.model.Theme;
import de.intevation.flys.client.shared.model.ThemeList;


/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class CollectionHelper {

    private static final Logger logger =
        Logger.getLogger(CollectionHelper.class);


    public static final String ERROR_ADD_ARTIFACT = "error_add_artifact";

    public static final String ERROR_REMOVE_ARTIFACT = "error_remove_artifact";

    public static final String XPATH_FACETS = "art:facets/art:facet";

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


    public static Document createAttribute(Collection collection) {
        logger.debug("CollectionHelper.createAttribute");

        Document doc = XMLUtils.newDocument();

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

        Element attr = cr.create("attribute");

        doc.appendChild(attr);

        Map<String, OutputMode> tmpOuts = collection.getOutputModes();

        Element outs = createOutputElements(cr, collection, tmpOuts);
        Element recs = createRecommendationsElements(cr, collection);

        if (outs != null) {
            attr.appendChild(outs);
        }

        if (recs != null) {
            attr.appendChild(recs);
        }

        return doc;
    }


    /**
     * Creates a whole block with art:output nodes.
     *
     * @param cr The ElementCreator used to create new elements.
     * @param c  The collection.
     * @param modes The OutputModes that should be included.
     *
     * @return an element with output modes.
     */
    protected static Element createOutputElements(
        ElementCreator          cr,
        Collection              c,
        Map<String, OutputMode> mmodes)
    {
        logger.debug("CollectionHelper.createOutputElements");

        java.util.Collection<OutputMode> modes = mmodes != null
            ? mmodes.values()
            : null;

        if (modes == null || modes.size() == 0) {
            logger.debug("Collection has no modes: " + c.identifier());
            return null;
        }

        Element outs = cr.create("outputs");

        for (OutputMode mode: modes) {
            Element out = createOutputElement(cr, c, mode);

            if (out != null) {
                outs.appendChild(out);
            }
        }

        return outs;
    }


    /**
     * Create a node art:output that further consist of art:theme nodes.
     *
     * @param cr The ElementCreator used to create new elements.
     * @param c  The collection.
     * @param mode The OutputMode.
     *
     * @return an element that represents an output mode with its themes.
     */
    protected static Element createOutputElement(
        ElementCreator cr,
        Collection     collection,
        OutputMode     mode)
    {
        logger.debug("CollectionHelper.createOutputElement");

        Element out = cr.create("output");
        cr.addAttr(out, "name", mode.getName(), false);

        ThemeList themeList = collection.getThemeList(mode.getName());
        List<Theme> themes  = themeList != null ? themeList.getThemes() : null;

        if (themes == null || themes.size() == 0) {
            logger.debug("No themes for output mode: " + mode.getName());
            return null;
        }

        for (Theme theme: themes) {
            Element t = createThemeElement(cr, collection, theme);

            if (t != null) {
                out.appendChild(t);
            }
        }

        return out;
    }


    /**
     * Creates a theme node art:theme that represents a curve in a chart or map.
     *
     * @param cr The ElementCreator used to create new elements.
     * @param collection The collection.
     * @param theme The theme whose attributes should be written to an element.
     *
     * @return an element that contains the informtion of the given theme.
     */
    protected static Element createThemeElement(
        ElementCreator cr,
        Collection     collection,
        Theme          theme)
    {
        if (theme == null) {
            return null;
        }

        Element t = cr.create("facet");

        if (theme instanceof AttributedTheme) {
            AttributedTheme at = (AttributedTheme) theme;
            Set<String>   keys = at.getKeys();

            for (String key: keys) {
                cr.addAttr(t, key, at.getAttr(key), true);
            }
        }
        else {
            cr.addAttr(t, "active", Integer.toString(theme.getActive()), true);
            cr.addAttr(t, "artifact", theme.getArtifact(), true);
            cr.addAttr(t, "facet", theme.getFacet(), true);
            cr.addAttr(t, "pos", Integer.toString(theme.getPosition()), true);
            cr.addAttr(t, "index", Integer.toString(theme.getIndex()), true);
            cr.addAttr(t, "description", theme.getDescription(), true);
            cr.addAttr(t, "visible", Integer.toString(theme.getVisible()), true);
        }

        return t;
    }


    /**
     * Creates a whole block with art:loaded-recommendations nodes.
     *
     * @param cr The ElementCreator used to create new elements.
     * @param c  The collection.
     *
     * @return an element with loaded recommendations.
     */
    protected static Element createRecommendationsElements(
        ElementCreator cr,
        Collection     c)
    {
        logger.debug("CollectionHelper.createRecommendationsElements");

        List<Recommendation> rs = c.getRecommendations();

        if (rs == null || rs.size() == 0) {
            logger.debug("Collection did not load recommendations: " +
                c.identifier());
            return null;
        }

        Element loaded = cr.create("loaded-recommendations");

        for (Recommendation r: rs) {
            Element recommendation = createRecommendationElement(cr, c, r);

            if (recommendation != null) {
                loaded.appendChild(recommendation);
            }
        }

        return loaded;
    }


    /**
     * Create a node art:recommended.
     *
     * @param cr The ElementCreator used to create new elements.
     * @param c  The collection.
     * @param r  The Recommendation.
     *
     * @return an element that represents an output mode with its themes.
     */
    protected static Element createRecommendationElement(
        ElementCreator cr,
        Collection     c,
        Recommendation r)
    {
        logger.debug("CollectionHelper.createRecommendationElement");

        Element recommendation = cr.create("recommendation");
        cr.addAttr(recommendation, "factory", r.getFactory(), true);
        cr.addAttr(recommendation, "ids", r.getIDs(), true);

        return recommendation;
    }


    /**
     * Take the DESCRIBE document of the Collections describe()
     * operation and extracts the information about the collection itself and
     * the collection items.
     *
     * @param description The DESCRIBE document of the Collections describe()
     * operation.
     *
     * @return a Collection with CollectionItems.
     */
    public static Collection parseCollection(Document description) {
        logger.debug("AddArtifactServiceImpl.parseCollection");

        if (description == null) {
            logger.warn("The DESCRIBE of the Collection is null!");
            return null;
        }

        String uuid = XMLUtils.xpathString(
            description,
            "art:artifact-collection/@art:uuid",
            ArtifactNamespaceContext.INSTANCE);

        String ttlStr = XMLUtils.xpathString(
            description,
            "art:artifact-collection/@art:ttl",
            ArtifactNamespaceContext.INSTANCE);

        String name = XMLUtils.xpathString(
            description,
            "art:artifact-collection/@art:name",
            ArtifactNamespaceContext.INSTANCE);

        if (uuid.length() == 0) {
            logger.warn("Found an invalid (zero length uuid) Collection!");
            return null;
        }

        if (ttlStr.length() == 0) {
            logger.warn("Found an invalid Collectioni (zero length ttl)!");
            return null;
        }


        long ttl = -1;
        try {
            ttl = Long.valueOf(ttlStr);
        }
        catch (NumberFormatException nfe) {
            // do nothing
        }

        Map<String, ThemeList> themeList = parseThemeLists(description);
        List<Recommendation> recommended = parseRecommendations(description);

        name = (name != null && name.length() > 0) ? name : uuid;

        Collection c = !themeList.isEmpty()
            ? new DefaultCollection(uuid, ttl, name, recommended, themeList)
            : new DefaultCollection(uuid, ttl, name, recommended);

        NodeList items = (NodeList) XMLUtils.xpath(
            description,
            "art:artifact-collection/art:artifacts/art:artifact",
            XPathConstants.NODESET,
            ArtifactNamespaceContext.INSTANCE);

        if (items == null || items.getLength() == 0) {
            logger.debug("No collection item found for this collection.");

            return c;
        }

        int size = items.getLength();

        for (int i = 0; i < size; i++) {
            CollectionItem item = parseCollectionItem(
                (Element)items.item(i),
                i == 0);

            if (item != null) {
                c.addItem(item);
            }
        }

        logger.debug(
            "Found " + c.getItemLength() + " collection items " +
            "for the Collection '" + c.identifier() + "'.");

        return c;
    }


    protected static Map<String, ThemeList> parseThemeLists(Document desc) {
        logger.debug("DescribeCollectionServiceImpl.parseThemeLists");

        NodeList lists = (NodeList) XMLUtils.xpath(
            desc,
            "/art:artifact-collection/art:attribute/art:outputs/art:output",
            XPathConstants.NODESET,
            ArtifactNamespaceContext.INSTANCE);

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

        Map<String, ThemeList> themeList = new HashMap<String, ThemeList>(num);

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        for (int i = 0; i < num; i++) {
            Element node = (Element)lists.item(i);

            String outName = node.getAttribute("name");

            if (outName.length() > 0) {
                ThemeList list = parseThemeList(node);
                if (list.getThemeCount() > 0) {
                    themeList.put(outName, list);
                }
            }
        }

        return themeList;
    }


    protected static ThemeList parseThemeList(Element node) {
        logger.debug("DescribeCollectionServiceImpl.parseThemeList");

        NodeList themes = node.getElementsByTagNameNS(
            ArtifactNamespaceContext.NAMESPACE_URI,
            "facet");

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

        List<Theme> themeList = new ArrayList<Theme>(num);

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

            if (theme != null) {
                themeList.add(theme);
            }
        }

        return new ThemeList(themeList);
    }


    protected static Theme parseTheme(Element ele) {
        logger.debug("DescribeCollectionServiceImpl.parseTheme");

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        NamedNodeMap attrMap = ele.getAttributes();
        int          attrNum = attrMap != null ? attrMap.getLength() : 0;

        AttributedTheme t = new AttributedTheme();

        for (int i = 0; i < attrNum; i++) {
            Node attr = attrMap.item(i);

            String prefix = attr.getPrefix();
            String name   = attr.getNodeName().replace(prefix + ":", "");
            String value  = attr.getNodeValue();

            t.addAttr(name, value);
        }

        return t;
    }


    /**
     * This method extracts the CollectionItem from <i>node</i> with its output
     * modes. The output modes are parsed using the parseOutputModes() method.
     *
     * @param node A node that contains information about a CollectionItem.
     *
     * @return a CollectionItem.
     */
    protected static CollectionItem parseCollectionItem(
        Element node,
        boolean outs
    ) {
        logger.debug("AddArtifactServiceImpl.parseCollectionItem");

        if (node == null) {
            logger.debug("The node for parsing CollectionItem is null!");
            return null;
        }

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        String uuid = node.getAttributeNS(uri, "uuid");
        String hash = node.getAttributeNS(uri, "hash");

        if (uuid == null || uuid.length() == 0) {
            logger.warn("Found an invalid CollectionItem!");
            return null;
        }

        List<OutputMode> modes = new ArrayList<OutputMode>();

        if (outs) {
            NodeList outputmodes = node.getElementsByTagNameNS(
                uri, "outputmodes");

            if (outputmodes.getLength() < 1) {
                return null;
            }

            Element om = (Element)outputmodes.item(0);

            modes = parseOutputModes(om);
        }

        return new DefaultCollectionItem(uuid, hash, modes);
    }


    /**
     * This method extracts the OutputModes available for a specific
     * CollectionItem and returns them as list.
     *
     * @param node The root node of the outputmodes list.
     *
     * @return a list of OutputModes.
     */
    protected static List<OutputMode> parseOutputModes(Element node) {
        logger.debug("AddArtifactServiceImpl.parseOutputModes");

        if (node == null) {
            logger.debug("The node for parsing OutputModes is null!");
            return null;
        }

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        NodeList list = node.getElementsByTagNameNS(uri, "output");

        int size = list.getLength();

        if (size == 0) {
            logger.debug("No outputmode nodes found!");
            return null;
        }

        List<OutputMode> modes = new ArrayList<OutputMode>(size);

        for (int i = 0; i < size; i++) {
            Element tmp = (Element)list.item(i);

            String name = tmp.getAttributeNS(uri, "name");
            String desc = tmp.getAttributeNS(uri, "description");
            String mime = tmp.getAttributeNS(uri, "mime-type");
            String type = tmp.getAttributeNS(uri, "type");

            if (name.length() == 0) {
                logger.warn("Found an invalid output mode.");
                continue;
            }

            OutputMode outmode = null;
            List<Facet> fs     = extractFacets(tmp);

            if (type.equals("export")) {
                outmode = new ExportMode(name, desc, mime, fs);
            }
            else if (type.equals("report")) {
                outmode = new ReportMode(name, desc, mime, fs);
            }
            else if (type.equals("chart")){
                outmode = new ChartMode(name, desc, mime, fs);
            }
            else if (type.equals("map")){
                outmode = new MapMode(name, desc, mime, fs);
            }
            else {
                logger.warn("Broken Output mode without type found.");
                continue;
            }

            modes.add(outmode);
        }

        return modes;
    }


    protected static List<Facet> extractFacets(Element outmode) {
        logger.debug("DescribeCollectionServiceImpl - extractFacets()");

        NodeList facetList = (NodeList) XMLUtils.xpath(
            outmode,
            XPATH_FACETS,
            XPathConstants.NODESET,
            ArtifactNamespaceContext.INSTANCE);

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

        List<Facet> facets = new ArrayList<Facet>(num);

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        for (int i = 0; i < num; i++) {
            Element facetEl = (Element) facetList.item(i);

            String name  = facetEl.getAttributeNS(uri, "name");
            String desc  = facetEl.getAttributeNS(uri, "description");
            String index = facetEl.getAttributeNS(uri, "index");

            if (name != null && name.length() > 0 && index != null) {
                facets.add(new DefaultFacet(name, Integer.valueOf(index),desc));
            }
        }

        return facets;
    }


    public static List<Recommendation> parseRecommendations(Document doc) {
        logger.debug("DescribeCollectionServiceImpl.parseRecommendations");

        NodeList list = (NodeList) XMLUtils.xpath(
            doc,
            XPATH_LOADED_RECOMMENDATIONS,
            XPathConstants.NODESET,
            ArtifactNamespaceContext.INSTANCE);

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

        List<Recommendation> recs = new ArrayList<Recommendation>(num);

        String uri = ArtifactNamespaceContext.NAMESPACE_URI;

        for (int i = 0; i < num; i++) {
            Element rec = (Element) list.item(i);

            String factory = rec.getAttributeNS(uri, "factory");
            String dbids   = rec.getAttributeNS(uri, "ids");

            if (factory != null && factory.length() > 0) {
                recs.add(new Recommendation(factory, dbids));
            }
        }

        return recs;
    }


    /**
     * Add an artifact to a collection.
     * @param collection Collection to add artifact to.
     * @param artifact   Artifact to add to collection
     */
    public static Collection addArtifact(
        Collection collection,
        Artifact   artifact,
        String     url,
        String     locale)
    throws ServerException
    {
        logger.debug("Collection.addArtifact");

        if (collection == null) {
            logger.warn("The given Collection is null!");
            return null;
        }

        Document add = ClientProtocolUtils.newAddArtifactDocument(
            artifact.getUuid(), null);

        HttpClient client = new HttpClientImpl(url, locale);

        try {
            logger.debug("Do HTTP request now.");

            Document response = (Document) client.doCollectionAction(
                add, collection.identifier(), new DocumentResponseHandler());

            logger.debug(
                "Finished HTTP request successfully. Parse Collection now.");

            Collection c = CollectionHelper.parseCollection(response);

            if (c == null) {
                throw new ServerException(ERROR_ADD_ARTIFACT);
            }

            return c;
        }
        catch (ConnectionException ce) {
            logger.error(ce, ce);
        }
        catch (Exception e) {
            logger.error(e, e);
        }

        throw new ServerException(ERROR_ADD_ARTIFACT);
    }


    /**
     * Remove an artifact from a collection.
     * @param collection Collection to remove artifact to.
     * @param artifact   Artifact to add to collection
     */
    public static Collection removeArtifact(
        Collection collection,
        String     artifactId,
        String     url,
        String     locale)
    throws ServerException
    {
        logger.debug("Collection.removeArtifact");

        if (collection == null) {
            logger.warn("The given Collection is null!");
            return null;
        }

        Document remove = ClientProtocolUtils.newRemoveArtifactDocument(
            artifactId);

        HttpClient client = new HttpClientImpl(url, locale);

        try {
            logger.debug("Do HTTP request now.");

            Document response = (Document) client.doCollectionAction(
                remove, collection.identifier(), new DocumentResponseHandler());

            logger.debug(
                "Finished HTTP request successfully. Parse Collection now.");
            logger.debug(XMLUtils.toString(response));

            Collection c = CollectionHelper.parseCollection(response);

            if (c == null) {
                throw new ServerException(ERROR_REMOVE_ARTIFACT);
            }

            return c;
        }
        catch (ConnectionException ce) {
            logger.error(ce, ce);
        }
        catch (Exception e) {
            logger.error(e, e);
        }
        throw new ServerException(ERROR_REMOVE_ARTIFACT);
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org