view artifacts/src/main/java/org/dive4elements/river/themes/ThemeFactory.java @ 9555:ef5754ba5573

Implemented legend aggregation based on type of themes. Added theme-editor style configuration for aggregated legend entries. Only configured themes get aggregated.
author gernotbelger
date Tue, 23 Oct 2018 16:26:48 +0200
parents f0ea2063b58e
children
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.themes;

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.dive4elements.artifacts.common.utils.XMLUtils;
import org.dive4elements.river.artifacts.D4EArtifact;
import org.dive4elements.river.artifacts.context.RiverContext;
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;

/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 *
 *         Mapping-matching rules:
 *
 */
public class ThemeFactory {

    private static Logger log = Logger.getLogger(ThemeFactory.class);

    /** Trivial, hidden constructor. */
    private ThemeFactory() {
    }

    /**
     * Creates a new theme from <i>config</i>.
     *
     * @param themeCfg
     *            The theme config document that is used to fetch parent
     *            themes.
     * @param config
     *            The theme config node.
     *
     * @return a new theme.
     */
    public static Theme createTheme(final Document themeCfg, final Node config) {
        final String name = getName(config);
        final String desc = getDescription(config);

        log.trace("Create new theme: " + name);

        final DefaultTheme theme = new DefaultTheme(name, desc);

        parseInherits(themeCfg, config, theme);
        parseFields(config, theme);
        parseAttrs(config, theme);

        return theme;
    }

    /**
     * Get first matching theme for facet.
     *
     * @param name
     *            Name to match "from" of theme mapping.
     * @param pattern
     *            String to 'compare' to pattern in mapping.
     * @param output
     *            Name of the current output
     *
     * @return First matching theme.
     */
    public static Theme getTheme(final RiverContext c, final String name, final String pattern, final String output, final String groupName) {
        if (log.isDebugEnabled()) {
            log.debug("Search theme for: " + name + " - pattern: " + pattern);
        }

        if (c == null || name == null) {
            log.warn("Cannot search for theme.");
            return null;
        }

        // Fetch mapping and themes.
        @SuppressWarnings("unchecked")
        final
        Map<String, List<ThemeMapping>> map = (Map<String, List<ThemeMapping>>) c.get(RiverContext.THEME_MAPPING);

        @SuppressWarnings("unchecked")
        final
        List<ThemeGroup> tgs = (List<ThemeGroup>) c.get(RiverContext.THEMES);

        ThemeGroup group = null;
        for (final ThemeGroup tg : tgs) {
            if (tg.getName().equals(groupName)) {
                group = tg;
                break;
            }
        }

        if (group == null) {
            log.warn("No theme group found: '" + groupName + "'");
            return null;
        }

        final Map<String, Theme> t = group.getThemes();

        final D4EArtifact artifact = (D4EArtifact) c.get(RiverContext.ARTIFACT_KEY);

        if (map == null || map.isEmpty() || t == null || t.isEmpty()) {
            log.warn("No mappings or themes found. Cannot retrieve theme!");
            return null;
        }

        final List<ThemeMapping> mapping = map.get(name);

        if (mapping == null) {
            log.warn("No theme found for mapping: " + name);
            return null;
        }

        // Take first mapping of which all conditions are satisfied.
        for (final ThemeMapping tm : mapping) {
            if (name.equals(tm.getFrom()) && tm.applyPattern(pattern) && tm.masterAttrMatches(artifact) && tm.outputMatches(output)) {
                final String target = tm.getTo();

                log.debug("Found theme '" + target + "'");
                return t.get(target);
            }
        }

        final String msg = "No theme found for '" + name + "' with pattern '" + pattern + "' and output " + output + ".";

        log.warn(msg);

        return null;
    }

    @SuppressWarnings("unchecked")
    public static List<ThemeGroup> getThemeGroups(final RiverContext c) {
        final List<ThemeGroup> tgs = (List<ThemeGroup>) c.get(RiverContext.THEMES);
        return tgs;
    }

    @SuppressWarnings("unchecked")
    public static List<Theme> getThemes(final RiverContext c, final String name) {
        final List<ThemeGroup> tgs = (List<ThemeGroup>) c.get(RiverContext.THEMES);
        if (tgs == null) {
            return null;
        }

        final List<Theme> themes = new ArrayList<>();
        for (final ThemeGroup tg : tgs) {
            themes.add(tg.getThemeByName(name));
        }
        return themes;
    }

    private static String getName(final Node config) {
        return ((Element) config).getAttribute("name");
    }

    private static String getDescription(final Node config) {
        return ((Element) config).getAttribute("desc");
    }

    private static void parseInherits(final Document themeCfg, final Node cfg, final DefaultTheme t) {
        parseInherits(themeCfg, cfg, t, null);
    }

    private static void parseInherits(final Document themeCfg, final Node cfg, final DefaultTheme t, Map<String, Node> themes) {
        log.trace("ThemeFactory.parseInherits");

        final NodeList inherits = ((Element) cfg).getElementsByTagName("inherit");

        final int num = inherits.getLength();

        if (num == 0) {
            log.trace("Theme does not inherit from other themes.");
            return;
        }

        log.trace("Theme inherits from " + num + " other themes.");

        if (themes == null) {
            themes = buildThemeMap(themeCfg);
        }

        for (int i = 0; i < num; i++) {
            final Node inherit = inherits.item(i);
            final String from = ((Element) inherit).getAttribute("from");

            final Node tmp = themes.get(from);

            parseInherits(themeCfg, tmp, t, themes);
            parseFields(tmp, t);
        }
    }

    private static Map<String, Node> buildThemeMap(final Document themeCfg) {
        final Map<String, Node> map = new HashMap<>();
        final String xpath = "/themes/themegroup/theme";

        final NodeList nodes = (NodeList) XMLUtils.xpath(themeCfg, xpath, XPathConstants.NODESET);

        if (nodes != null) {
            for (int i = 0, N = nodes.getLength(); i < N; ++i) {
                final Node node = nodes.item(i);
                final String name = ((Element) node).getAttribute("name");
                map.put(name, node);
            }
        }
        return map;
    }

    private static void parseFields(final Node config, final DefaultTheme theme) {
        if (config == null || theme == null) {
            log.warn("Parsing fields without node or theme is senseless!");
            return;
        }

        final NodeList fields = ((Element) config).getElementsByTagName("field");

        final int num = fields.getLength();

        log.trace("Found " + num + " own fields in this theme.");

        if (num == 0) {
            log.trace("Theme has no own fields.");
            return;
        }

        for (int i = 0; i < num; i++) {
            final Node field = fields.item(i);

            addField(theme, field);
        }
    }

    private static void addField(final DefaultTheme theme, final Node field) {
        final String name = ((Element) field).getAttribute("name");

        log.trace("Add field " + name + " to theme " + theme.getName());

        final NamedNodeMap attrs = field.getAttributes();

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

        if (num == 0) {
            log.warn("This field has no attributes.");
            return;
        }

        final ThemeField theField = new DefaultThemeField(name);

        for (int i = 0; i < num; i++) {
            final Node attr = attrs.item(i);

            final String key = attr.getNodeName();
            final String value = attr.getNodeValue();

            theField.setAttribute(key, value);
        }

        theme.addField(name, theField);
    }

    private static void parseAttrs(final Node config, final DefaultTheme theme) {
        final NamedNodeMap attrs = config.getAttributes();

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

        if (num == 0) {
            log.trace("Theme has no attributes set.");
            return;
        }

        for (int i = 0; i < num; i++) {
            final Node attr = attrs.item(i);

            final String name = attr.getNodeName();
            final String value = attr.getNodeValue();

            theme.addAttribute(name, value);
        }
    }

    public static Theme getLegendTheme(final RiverContext flysContext, final String themeType) {

        @SuppressWarnings("unchecked")
        final
        Map<String, Theme> themes = (Map<String, Theme>) flysContext.get(RiverContext.LEGEND);
        return themes.get(themeType);
    }
}

http://dive4elements.wald.intevation.org