diff gwt-client/src/main/java/org/dive4elements/river/client/server/CollectionHelper.java @ 5838:5aa05a7a34b7

Rename modules to more fitting names.
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 15:23:37 +0200
parents flys-client/src/main/java/org/dive4elements/river/client/server/CollectionHelper.java@821a02bbfb4e
children 172338b1407f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/CollectionHelper.java	Thu Apr 25 15:23:37 2013 +0200
@@ -0,0 +1,1082 @@
+package org.dive4elements.river.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 org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.ClientProtocolUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.artifacts.httpclient.exceptions.ConnectionException;
+import org.dive4elements.artifacts.httpclient.http.HttpClient;
+import org.dive4elements.artifacts.httpclient.http.HttpClientImpl;
+import org.dive4elements.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import org.dive4elements.river.client.shared.exceptions.ServerException;
+import org.dive4elements.river.client.shared.model.Artifact;
+import org.dive4elements.river.client.shared.model.AttributedTheme;
+import org.dive4elements.river.client.shared.model.ChartMode;
+import org.dive4elements.river.client.shared.model.Collection;
+import org.dive4elements.river.client.shared.model.CollectionItem;
+import org.dive4elements.river.client.shared.model.DefaultCollection;
+import org.dive4elements.river.client.shared.model.DefaultCollectionItem;
+import org.dive4elements.river.client.shared.model.DefaultFacet;
+import org.dive4elements.river.client.shared.model.ExportMode;
+import org.dive4elements.river.client.shared.model.Facet;
+import org.dive4elements.river.client.shared.model.MapMode;
+import org.dive4elements.river.client.shared.model.OutputMode;
+import org.dive4elements.river.client.shared.model.OverviewMode;
+import org.dive4elements.river.client.shared.model.ReportMode;
+import org.dive4elements.river.client.shared.model.Recommendation;
+import org.dive4elements.river.client.shared.model.Theme;
+import org.dive4elements.river.client.shared.model.ThemeList;
+import org.dive4elements.river.client.shared.model.Settings;
+import org.dive4elements.river.client.shared.model.Property;
+import org.dive4elements.river.client.shared.model.PropertyGroup;
+import org.dive4elements.river.client.shared.model.PropertySetting;
+import org.dive4elements.river.client.shared.model.StringProperty;
+import org.dive4elements.river.client.shared.model.DoubleProperty;
+import org.dive4elements.river.client.shared.model.IntegerProperty;
+import org.dive4elements.river.client.shared.model.BooleanProperty;
+import org.dive4elements.river.client.shared.model.OutputSettings;
+
+//temporary
+
+/**
+ * @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);
+            }
+        }
+
+        Document doc = out.getOwnerDocument();
+
+        ElementCreator settingscr = new ElementCreator(doc, "", "");
+
+        Settings settings = collection.getSettings(mode.getName());
+        if (settings == null ||
+            settings.getCategories().size() == 0)
+        {
+            logger.debug("No settings for output mode: " + mode.getName());
+        }
+        else {
+            Element s = createSettingsElement(settingscr, collection, settings);
+            if (s != null) {
+                out.appendChild(s);
+            }
+        }
+        logger.info(XMLUtils.toString(out));
+        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;
+    }
+
+
+    /**
+     *
+     */
+    protected static Element createSettingsElement(
+        ElementCreator cr,
+        Collection c,
+        Settings s)
+    {
+        logger.debug("CollectionHelper.createSettingsElement");
+        Element settings = cr.create("settings");
+
+        List<String> categories = s.getCategories();
+
+        for (String category: categories) {
+            Element cat =cr.create(category);
+            settings.appendChild(cat);
+            List<Property> props = s.getSettings(category);
+            for (Property p: props) {
+                if (p instanceof PropertyGroup) {
+                    Element prop = createPropertyGroupElement(cr,
+                                                              (PropertyGroup)p);
+                    cat.appendChild(prop);
+                }
+                else if (p instanceof PropertySetting) {
+                    Element prop = createPropertyElement (cr,
+                                                          (PropertySetting)p);
+                    cat.appendChild(prop);
+                }
+            }
+        }
+        return settings;
+    }
+
+
+    /**
+     *
+     */
+    protected static Element createPropertyGroupElement(
+        ElementCreator cr,
+        PropertyGroup pg)
+    {
+        Element e = cr.create(pg.getName());
+
+        List<Property> list = pg.getProperties();
+        for (Property p: list) {
+            Element pe = createPropertyElement(cr, (PropertySetting)p);
+            e.appendChild(pe);
+        }
+        return e;
+    }
+
+
+    /**
+     *
+     */
+    protected static Element createPropertyElement(
+        ElementCreator cr,
+        PropertySetting p)
+    {
+        Element e = cr.create(p.getName());
+
+        if(p instanceof BooleanProperty) {
+            cr.addAttr(e, "type", "boolean", false);
+        }
+        else if(p instanceof DoubleProperty) {
+            cr.addAttr(e, "type", "double", false);
+        }
+        else if(p instanceof IntegerProperty) {
+            cr.addAttr(e, "type", "integer", false);
+        }
+        else if(p instanceof StringProperty) {
+            cr.addAttr(e, "type", "string", false);
+        }
+
+        e.setTextContent(p.getValue().toString());
+        cr.addAttr(e, "display", p.getAttribute("display"), false);
+        return e;
+    }
+
+
+    /**
+     * 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("CollectionHelper.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 Collection (zero length ttl)!");
+            return null;
+        }
+
+
+        long ttl = -1;
+        try {
+            ttl = Long.valueOf(ttlStr);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        List<Recommendation> recommended = parseRecommendations(description);
+        Map<String, CollectionItem> collectionItems =
+            new HashMap<String, CollectionItem>();
+
+        name = (name != null && name.length() > 0) ? name : uuid;
+
+        Collection c = 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);
+                collectionItems.put(item.identifier() ,item);
+            }
+        }
+
+        Map<String, ThemeList> themeLists = parseThemeLists(description, collectionItems);
+        c.setThemeLists(themeLists);
+
+        Map<String, Settings> outSettings = parseSettings(description);
+        c.setSettings(outSettings);
+        logger.debug(
+            "Found " + c.getItemLength() + " collection items " +
+            "for the Collection '" + c.identifier() + "'.");
+
+        return c;
+    }
+
+
+    /**
+     * @param collectionItems map to look up collection item mapping a themes
+     *                        (artifact) uuid.
+     */
+    protected static Map<String, ThemeList> parseThemeLists(
+        Document desc, Map<String, CollectionItem> collectionItems
+    ) {
+        logger.debug("CollectionHelper.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, collectionItems);
+
+                if (list.getThemeCount() > 0) {
+                    themeList.put(outName, list);
+                }
+            }
+        }
+
+        return themeList;
+    }
+
+
+    /**
+     * @param collectionItems map to look up collection item mapping a themes
+     *                        (artifact) uuid.
+     */
+    protected static ThemeList parseThemeList(
+        Element node, Map<String, CollectionItem> collectionItems
+    ) {
+        logger.debug("CollectionHelper.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));
+            theme.setCollectionItem(collectionItems.get(theme.getArtifact()));
+
+            if (theme != null) {
+                themeList.add(theme);
+            }
+        }
+
+        return new ThemeList(themeList);
+    }
+
+
+    protected static Theme parseTheme(Element ele) {
+        logger.debug("CollectionHelper.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;
+    }
+
+
+    /**
+     * Parse Settings elements.
+     *
+     * @param description The collection description.
+     *
+     * @return Map containing the settings.
+     */
+    protected static Map<String, Settings> parseSettings(Document description) {
+        logger.info("parseSettings");
+
+        NodeList lists = (NodeList) XMLUtils.xpath(
+            description,
+            "/art:artifact-collection/art:attribute/art:outputs/art:output",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = lists != null ? lists.getLength() : 0;
+
+        Map<String, Settings> list = new HashMap<String, Settings>(num);
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        for (int i = 0; i < num; i++) {
+            Element node = (Element)lists.item(i);
+            String outName = node.getAttribute("name");
+            Settings s = parseSettings(outName, node);
+            list.put(outName, s);
+        }
+
+        return list;
+    }
+
+
+    /**
+     * From a document, parse the settings for an output and create an
+     * OutputSettings object.
+     */
+    protected static Settings parseSettings(String outName, Element node) {
+        OutputSettings set = new OutputSettings(outName);
+
+        NodeList elements = node.getElementsByTagName("settings");
+
+        if (elements.getLength() == 0 || elements.getLength() > 1) {
+            return set;
+        }
+
+        Element settings = (Element)elements.item(0);
+
+        // Get the categories
+        NodeList catNodes = settings.getChildNodes();
+        for (int i = 0; i < catNodes.getLength(); i++) {
+            Element catNode = (Element)catNodes.item(i);
+
+            // The category name
+            String category = catNode.getTagName();
+
+            // list of properties or groups (groups have child nodes).
+            NodeList list = catNode.getChildNodes();
+
+            // iterate through all properties or groups.
+            List<Property> props = new ArrayList<Property> ();
+            for (int j = 0; j < list.getLength(); j++) {
+                Property p;
+                Element e = (Element)list.item(j);
+                if (e.hasChildNodes() &&
+                    e.getFirstChild().getNodeType() != Node.TEXT_NODE) {
+                    p = parseSettingsGroup(e);
+                }
+                else {
+                    p = parseSetting(e);
+                }
+                props.add(p);
+            }
+            set.setSettings(category, props);
+        }
+        return set;
+    }
+
+
+    /**
+     *
+     */
+    protected static Property parseSettingsGroup(Element group) {
+        PropertyGroup p = new PropertyGroup();
+        p.setName(group.getTagName());
+
+        NodeList list = group.getChildNodes();
+        ArrayList<Property> props = new ArrayList<Property>();
+        for (int i = 0; i < list.getLength(); i++) {
+            props.add(parseSetting((Element)list.item(i)));
+        }
+        p.setProperties(props);
+        return p;
+    }
+
+
+    /**
+     * From a property element create a Property object.
+     */
+    protected static Property parseSetting(Element property){
+        NamedNodeMap attrMap = property.getAttributes();
+        int          attrNum = attrMap != null ? attrMap.getLength() : 0;
+
+        Node type = attrMap.getNamedItem("type");
+        String t = type.getNodeValue();
+        PropertySetting ps = new PropertySetting();
+
+        if(t.equals("string")) {
+            ps = new StringProperty();
+        }
+        else if (t.equals("integer")) {
+            ps = new IntegerProperty();
+        }
+        else if (t.equals("double")) {
+            ps = new DoubleProperty();
+        }
+        else if (t.equals("boolean")) {
+            ps = new BooleanProperty();
+        }
+        ps.setName(property.getTagName());
+        ps.setValue(property.getTextContent());
+
+        for (int i = 0; i < attrNum; i++) {
+            Node attr = attrMap.item(i);
+
+            String name   = attr.getNodeName();
+            String value  = attr.getNodeValue();
+            if(name.equals("type")) {
+                continue;
+            }
+            ps.setAttribute(name, value);
+        }
+        return ps;
+    }
+
+
+    /**
+     * 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("CollectionHelper.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);
+        }
+
+        HashMap<String, String> dataItems = new HashMap<String, String>();
+
+        NodeList dataItemNodes = node.getElementsByTagNameNS(
+            uri, "data-items");
+
+        Element di = (Element)dataItemNodes.item(0);
+        dataItems = parseDataItems(di);
+
+        return new DefaultCollectionItem(uuid, hash, modes, dataItems);
+    }
+
+
+    /**
+     * 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("CollectionHelper.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, type);
+            }
+            else if (type.equals("map")){
+                outmode = new MapMode(name, desc, mime, fs);
+            }
+            else if (type.equals("overview")) {
+                outmode = new OverviewMode(name, desc, mime, fs, type);
+            }
+            else {
+                logger.warn("Broken Output mode without type found.");
+                continue;
+            }
+
+            modes.add(outmode);
+        }
+
+        return modes;
+    }
+
+
+    /**
+     * Create a Key/Value map for data nodes of artifact/collectionitem.
+     */
+    protected static HashMap<String, String> parseDataItems(Element node) {
+        logger.debug("CollectionHelper.parseDataItems");
+
+        if (node == null) {
+            logger.debug("The node for parsing DataItems is null!");
+            return null;
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        NodeList list = node.getElementsByTagNameNS(uri, "data");
+
+        int size = list.getLength();
+
+        if (size == 0) {
+            logger.debug("No static data-item nodes found!");
+        }
+
+        HashMap<String, String> data = new HashMap<String, String>(size*2);
+
+        for (int i = 0; i < size; i++) {
+            Element tmp = (Element)list.item(i);
+
+            String key = tmp.getAttributeNS(uri, "name");
+
+            if (key.length() == 0) {
+                logger.warn("Found an invalid data item mode.");
+                continue;
+            }
+
+            // XXX We are restricted on 1/1 key/values in the data map here.
+            NodeList valueNodes = tmp.getElementsByTagName("art:item");
+
+            Element item = (Element) valueNodes.item(0);
+            String value = item.getAttributeNS(uri, "value");
+            logger.debug("Found a data item " + key + " : " + value);
+
+            data.put(key, value);
+        }
+
+
+        // Dynamic data.
+        list = node.getElementsByTagNameNS(uri, "select");
+
+        size = list.getLength();
+
+        if (size == 0) {
+            logger.debug("No dynamic data-item nodes found!");
+        }
+
+        for (int i = 0; i < size; i++) {
+            Element tmp = (Element)list.item(i);
+
+            String key = tmp.getAttributeNS(uri, "name");
+
+            if (key.length() == 0) {
+                logger.warn("Found an invalid data item node (missing key).");
+                continue;
+            }
+
+            String value = tmp.getAttributeNS(uri, "defaultValue");
+
+            if (value.length() == 0) {
+                logger.warn("Found an invalid data item node (missing value).");
+                continue;
+            }
+
+            logger.debug("Found a (dyn) data item " + key + " : " + value);
+
+            data.put(key, value);
+        }
+
+        return data;
+    }
+
+    protected static List<Facet> extractFacets(Element outmode) {
+        logger.debug("CollectionHelper - 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("CollectionHelper.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("CollectionHelper.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("CollectionHelper.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