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: }