teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
teichmann@5863: * Software engineering by Intevation GmbH
teichmann@5863: *
teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3)
teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the
teichmann@5994: * documentation coming with Dive4Elements River for details.
teichmann@5863: */
teichmann@5863:
teichmann@5831: package org.dive4elements.river.themes;
ingo@340:
christian@3464: import java.util.ArrayList;
christian@3464: import java.util.HashMap;
ingo@1747: import java.util.List;
ingo@345: import java.util.Map;
ingo@345:
ingo@340: import javax.xml.xpath.XPathConstants;
ingo@340:
ingo@340: import org.apache.log4j.Logger;
gernotbelger@9555: import org.dive4elements.artifacts.common.utils.XMLUtils;
gernotbelger@9555: import org.dive4elements.river.artifacts.D4EArtifact;
gernotbelger@9555: import org.dive4elements.river.artifacts.context.RiverContext;
ingo@340: import org.w3c.dom.Document;
christian@3464: import org.w3c.dom.Element;
ingo@340: import org.w3c.dom.NamedNodeMap;
ingo@340: import org.w3c.dom.Node;
ingo@340: import org.w3c.dom.NodeList;
ingo@340:
ingo@340: /**
ingo@340: * @author Ingo Weinzierl
felix@1822: *
gernotbelger@9555: * Mapping-matching rules:
felix@1822: *
ingo@340: */
ingo@340: public class ThemeFactory {
ingo@340:
teichmann@8202: private static Logger log = Logger.getLogger(ThemeFactory.class);
ingo@340:
felix@1822: /** Trivial, hidden constructor. */
ingo@340: private ThemeFactory() {
ingo@340: }
ingo@340:
ingo@340: /**
ingo@340: * Creates a new theme from config.
ingo@340: *
gernotbelger@9555: * @param themeCfg
gernotbelger@9555: * The theme config document that is used to fetch parent
gernotbelger@9555: * themes.
gernotbelger@9555: * @param config
gernotbelger@9555: * The theme config node.
ingo@340: *
ingo@340: * @return a new theme.
ingo@340: */
gernotbelger@9555: public static Theme createTheme(final Document themeCfg, final Node config) {
gernotbelger@9555: final String name = getName(config);
gernotbelger@9555: final String desc = getDescription(config);
ingo@340:
andre@8707: log.trace("Create new theme: " + name);
ingo@340:
gernotbelger@9555: final DefaultTheme theme = new DefaultTheme(name, desc);
ingo@340:
ingo@340: parseInherits(themeCfg, config, theme);
ingo@340: parseFields(config, theme);
ingo@340: parseAttrs(config, theme);
ingo@340:
ingo@340: return theme;
ingo@340: }
ingo@340:
ingo@345: /**
felix@1822: * Get first matching theme for facet.
felix@1822: *
gernotbelger@9555: * @param name
gernotbelger@9555: * Name to match "from" of theme mapping.
gernotbelger@9555: * @param pattern
gernotbelger@9555: * String to 'compare' to pattern in mapping.
gernotbelger@9555: * @param output
gernotbelger@9555: * Name of the current output
felix@1822: *
felix@1822: * @return First matching theme.
felix@1822: */
gernotbelger@9555: public static Theme getTheme(final RiverContext c, final String name, final String pattern, final String output, final String groupName) {
teichmann@8202: if (log.isDebugEnabled()) {
gernotbelger@9555: log.debug("Search theme for: " + name + " - pattern: " + pattern);
sascha@3555: }
felix@1822:
ingo@345: if (c == null || name == null) {
teichmann@8202: log.warn("Cannot search for theme.");
ingo@345: return null;
ingo@345: }
ingo@345:
felix@1822: // Fetch mapping and themes.
christian@3464: @SuppressWarnings("unchecked")
gernotbelger@9555: final
gernotbelger@9555: Map> map = (Map>) c.get(RiverContext.THEME_MAPPING);
ingo@345:
christian@3464: @SuppressWarnings("unchecked")
gernotbelger@9555: final
gernotbelger@9555: List tgs = (List) c.get(RiverContext.THEMES);
ingo@345:
raimund@2737: ThemeGroup group = null;
gernotbelger@9555: for (final ThemeGroup tg : tgs) {
sascha@3555: if (tg.getName().equals(groupName)) {
raimund@2737: group = tg;
sascha@3555: break;
raimund@2737: }
raimund@2737: }
raimund@2737:
raimund@2737: if (group == null) {
teichmann@8202: log.warn("No theme group found: '" + groupName + "'");
raimund@2737: return null;
raimund@2737: }
raimund@2737:
gernotbelger@9555: final Map t = group.getThemes();
raimund@2737:
gernotbelger@9555: final D4EArtifact artifact = (D4EArtifact) c.get(RiverContext.ARTIFACT_KEY);
felix@1822:
sascha@3555: if (map == null || map.isEmpty() || t == null || t.isEmpty()) {
teichmann@8202: log.warn("No mappings or themes found. Cannot retrieve theme!");
ingo@345: return null;
ingo@345: }
ingo@345:
gernotbelger@9555: final List mapping = map.get(name);
ingo@345:
ingo@1747: if (mapping == null) {
teichmann@8202: log.warn("No theme found for mapping: " + name);
ingo@1747: return null;
ingo@345: }
ingo@345:
felix@1822: // Take first mapping of which all conditions are satisfied.
gernotbelger@9555: for (final ThemeMapping tm : mapping) {
gernotbelger@9555: if (name.equals(tm.getFrom()) && tm.applyPattern(pattern) && tm.masterAttrMatches(artifact) && tm.outputMatches(output)) {
gernotbelger@9555: final String target = tm.getTo();
ingo@3785:
teichmann@8202: log.debug("Found theme '" + target + "'");
ingo@3781: return t.get(target);
ingo@1747: }
ingo@1747: }
ingo@1747:
gernotbelger@9555: final String msg = "No theme found for '" + name + "' with pattern '" + pattern + "' and output " + output + ".";
ingo@1747:
teichmann@8202: log.warn(msg);
ingo@1747:
ingo@1747: return null;
ingo@345: }
ingo@345:
christian@3464: @SuppressWarnings("unchecked")
gernotbelger@9555: public static List getThemeGroups(final RiverContext c) {
gernotbelger@9555: final List tgs = (List) c.get(RiverContext.THEMES);
raimund@2737: return tgs;
raimund@2737: }
raimund@2737:
christian@3464: @SuppressWarnings("unchecked")
gernotbelger@9555: public static List getThemes(final RiverContext c, final String name) {
gernotbelger@9555: final List tgs = (List) c.get(RiverContext.THEMES);
raimund@2737: if (tgs == null) {
raimund@2737: return null;
raimund@2737: }
raimund@2737:
gernotbelger@9555: final List themes = new ArrayList<>();
gernotbelger@9555: for (final ThemeGroup tg : tgs) {
raimund@2737: themes.add(tg.getThemeByName(name));
raimund@2737: }
raimund@2737: return themes;
raimund@2737: }
raimund@2737:
gernotbelger@9555: private static String getName(final Node config) {
gernotbelger@9555: return ((Element) config).getAttribute("name");
ingo@340: }
ingo@340:
gernotbelger@9555: private static String getDescription(final Node config) {
gernotbelger@9555: return ((Element) config).getAttribute("desc");
ingo@340: }
ingo@340:
gernotbelger@9555: private static void parseInherits(final Document themeCfg, final Node cfg, final DefaultTheme t) {
raimund@2737: parseInherits(themeCfg, cfg, t, null);
raimund@2737: }
raimund@2737:
gernotbelger@9555: private static void parseInherits(final Document themeCfg, final Node cfg, final DefaultTheme t, Map themes) {
andre@8707: log.trace("ThemeFactory.parseInherits");
ingo@340:
gernotbelger@9555: final NodeList inherits = ((Element) cfg).getElementsByTagName("inherit");
ingo@340:
gernotbelger@9555: final int num = inherits.getLength();
ingo@340:
ingo@340: if (num == 0) {
andre@8707: log.trace("Theme does not inherit from other themes.");
ingo@340: return;
ingo@340: }
ingo@340:
andre@8707: log.trace("Theme inherits from " + num + " other themes.");
ingo@340:
raimund@2737: if (themes == null) {
raimund@2737: themes = buildThemeMap(themeCfg);
raimund@2737: }
raimund@2737:
ingo@340: for (int i = 0; i < num; i++) {
gernotbelger@9555: final Node inherit = inherits.item(i);
gernotbelger@9555: final String from = ((Element) inherit).getAttribute("from");
ingo@340:
gernotbelger@9555: final Node tmp = themes.get(from);
ingo@340:
raimund@2737: parseInherits(themeCfg, tmp, t, themes);
ingo@340: parseFields(tmp, t);
ingo@340: }
ingo@340: }
ingo@340:
gernotbelger@9555: private static Map buildThemeMap(final Document themeCfg) {
gernotbelger@9555: final Map map = new HashMap<>();
gernotbelger@9555: final String xpath = "/themes/themegroup/theme";
ingo@340:
gernotbelger@9555: final NodeList nodes = (NodeList) XMLUtils.xpath(themeCfg, xpath, XPathConstants.NODESET);
raimund@2737:
raimund@2737: if (nodes != null) {
raimund@2737: for (int i = 0, N = nodes.getLength(); i < N; ++i) {
gernotbelger@9555: final Node node = nodes.item(i);
gernotbelger@9555: final String name = ((Element) node).getAttribute("name");
raimund@2737: map.put(name, node);
raimund@2737: }
ingo@340: }
raimund@2737: return map;
ingo@340: }
ingo@340:
gernotbelger@9555: private static void parseFields(final Node config, final DefaultTheme theme) {
ingo@340: if (config == null || theme == null) {
teichmann@8202: log.warn("Parsing fields without node or theme is senseless!");
ingo@340: return;
ingo@340: }
ingo@340:
gernotbelger@9555: final NodeList fields = ((Element) config).getElementsByTagName("field");
ingo@340:
gernotbelger@9555: final int num = fields.getLength();
ingo@340:
andre@8707: log.trace("Found " + num + " own fields in this theme.");
ingo@340:
ingo@340: if (num == 0) {
andre@8707: log.trace("Theme has no own fields.");
ingo@340: return;
ingo@340: }
ingo@340:
ingo@340: for (int i = 0; i < num; i++) {
gernotbelger@9555: final Node field = fields.item(i);
ingo@340:
ingo@340: addField(theme, field);
ingo@340: }
ingo@340: }
ingo@340:
gernotbelger@9555: private static void addField(final DefaultTheme theme, final Node field) {
gernotbelger@9555: final String name = ((Element) field).getAttribute("name");
ingo@340:
andre@8707: log.trace("Add field " + name + " to theme " + theme.getName());
ingo@340:
gernotbelger@9555: final NamedNodeMap attrs = field.getAttributes();
ingo@340:
gernotbelger@9555: final int num = attrs != null ? attrs.getLength() : 0;
ingo@340:
ingo@340: if (num == 0) {
teichmann@8202: log.warn("This field has no attributes.");
ingo@340: return;
ingo@340: }
ingo@340:
gernotbelger@9555: final ThemeField theField = new DefaultThemeField(name);
ingo@340:
ingo@340: for (int i = 0; i < num; i++) {
gernotbelger@9555: final Node attr = attrs.item(i);
ingo@340:
gernotbelger@9555: final String key = attr.getNodeName();
gernotbelger@9555: final String value = attr.getNodeValue();
ingo@340:
ingo@340: theField.setAttribute(key, value);
ingo@340: }
ingo@340:
ingo@340: theme.addField(name, theField);
ingo@340: }
ingo@340:
gernotbelger@9555: private static void parseAttrs(final Node config, final DefaultTheme theme) {
gernotbelger@9555: final NamedNodeMap attrs = config.getAttributes();
ingo@340:
gernotbelger@9555: final int num = attrs != null ? attrs.getLength() : 0;
ingo@340:
ingo@340: if (num == 0) {
andre@8707: log.trace("Theme has no attributes set.");
ingo@340: return;
ingo@340: }
ingo@340:
ingo@340: for (int i = 0; i < num; i++) {
gernotbelger@9555: final Node attr = attrs.item(i);
ingo@340:
gernotbelger@9555: final String name = attr.getNodeName();
gernotbelger@9555: final String value = attr.getNodeValue();
ingo@340:
ingo@340: theme.addAttribute(name, value);
ingo@340: }
ingo@340: }
gernotbelger@9555:
gernotbelger@9555: public static Theme getLegendTheme(final RiverContext flysContext, final String themeType) {
gernotbelger@9555:
gernotbelger@9555: @SuppressWarnings("unchecked")
gernotbelger@9555: final
gernotbelger@9555: Map themes = (Map) flysContext.get(RiverContext.LEGEND);
gernotbelger@9555: return themes.get(themeType);
gernotbelger@9555: }
gernotbelger@9555: }