teichmann@5831: package org.dive4elements.river.themes; ingo@340: teichmann@5831: import org.dive4elements.artifacts.common.utils.XMLUtils; teichmann@5831: import org.dive4elements.river.artifacts.FLYSArtifact; teichmann@5831: import org.dive4elements.river.artifacts.context.FLYSContext; christian@3464: 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; 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: * felix@1822: * Mapping-matching rules: felix@1822: * ingo@340: */ ingo@340: public class ThemeFactory { ingo@340: ingo@340: private static Logger logger = Logger.getLogger(ThemeFactory.class); ingo@340: felix@1822: /** Trivial, hidden constructor. */ ingo@340: private ThemeFactory() { ingo@340: } ingo@340: ingo@340: ingo@340: /** ingo@340: * Creates a new theme from config. ingo@340: * ingo@340: * @param themeCfg The theme config document that is used to fetch parent ingo@340: * themes. ingo@340: * @param config The theme config node. ingo@340: * ingo@340: * @return a new theme. ingo@340: */ ingo@340: public static Theme createTheme(Document themeCfg, Node config) { ingo@340: String name = getName(config); ingo@340: String desc = getDescription(config); ingo@340: ingo@340: logger.debug("Create new theme: " + name); ingo@340: ingo@340: Theme 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@340: ingo@345: /** felix@1822: * Get first matching theme for facet. felix@1822: * felix@1828: * @param name Name to match "from" of theme mapping. felix@1822: * @param pattern String to 'compare' to pattern in mapping. felix@1828: * @param output Name of the current output felix@1822: * felix@1822: * @return First matching theme. felix@1822: */ felix@1828: public static Theme getTheme( felix@1828: FLYSContext c, felix@1828: String name, felix@1828: String pattern, raimund@2737: String output, raimund@2737: String groupName) felix@1828: { sascha@3555: if (logger.isDebugEnabled()) { sascha@3555: logger.debug( sascha@3555: "Search theme for: " + name + " - pattern: " + pattern); sascha@3555: } felix@1822: ingo@345: if (c == null || name == null) { ingo@345: logger.warn("Cannot search for theme."); ingo@345: return null; ingo@345: } ingo@345: felix@1822: // Fetch mapping and themes. christian@3464: @SuppressWarnings("unchecked") ingo@1747: Map> map = (Map>) ingo@345: c.get(FLYSContext.THEME_MAPPING); ingo@345: christian@3464: @SuppressWarnings("unchecked") raimund@2737: List tgs = (List) ingo@345: c.get(FLYSContext.THEMES); ingo@345: raimund@2737: ThemeGroup group = null; raimund@2737: for (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) { ingo@3781: logger.warn("No theme group found: '" + groupName + "'"); raimund@2737: return null; raimund@2737: } raimund@2737: raimund@2737: Map t = group.getThemes(); raimund@2737: felix@1822: FLYSArtifact artifact = (FLYSArtifact) c.get(FLYSContext.ARTIFACT_KEY); felix@1822: sascha@3555: if (map == null || map.isEmpty() || t == null || t.isEmpty()) { ingo@345: logger.warn("No mappings or themes found. Cannot retrieve theme!"); ingo@345: return null; ingo@345: } ingo@345: ingo@1747: List mapping = map.get(name); ingo@345: ingo@1747: if (mapping == null) { felix@1822: logger.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. ingo@1747: for (ThemeMapping tm: mapping) { felix@1822: if (name.equals(tm.getFrom()) felix@1822: && tm.applyPattern(pattern) felix@1828: && tm.masterAttrMatches(artifact) felix@1828: && tm.outputMatches(output)) felix@1822: { ingo@3781: String target = tm.getTo(); ingo@3785: ingo@3781: logger.debug("Found theme '" + target + "'"); ingo@3781: return t.get(target); ingo@1747: } ingo@1747: } ingo@1747: ingo@1747: String msg = ingo@1747: "No theme found for '" + name + felix@1828: "' with pattern '" + pattern + "' and output " + output + "."; ingo@1747: ingo@1747: logger.warn(msg); ingo@1747: ingo@1747: return null; ingo@345: } ingo@345: ingo@345: christian@3464: @SuppressWarnings("unchecked") raimund@2737: public static List getThemeGroups(FLYSContext c) { raimund@2737: List tgs = (List) raimund@2737: c.get(FLYSContext.THEMES); raimund@2737: return tgs; raimund@2737: } raimund@2737: raimund@2737: christian@3464: @SuppressWarnings("unchecked") raimund@2737: public static List getThemes (FLYSContext c, String name) { raimund@2737: List tgs = (List) raimund@2737: c.get(FLYSContext.THEMES); raimund@2737: if (tgs == null) { raimund@2737: return null; raimund@2737: } raimund@2737: raimund@2737: List themes = new ArrayList(); raimund@2737: for (ThemeGroup tg: tgs) { raimund@2737: themes.add(tg.getThemeByName(name)); raimund@2737: } raimund@2737: return themes; raimund@2737: } raimund@2737: ingo@340: protected static String getName(Node config) { raimund@2742: return ((Element)config).getAttribute("name"); ingo@340: } ingo@340: ingo@340: ingo@340: protected static String getDescription(Node config) { raimund@2742: return ((Element)config).getAttribute("desc"); ingo@340: } ingo@340: ingo@340: ingo@340: protected static void parseInherits(Document themeCfg, Node cfg, Theme t) { raimund@2737: parseInherits(themeCfg, cfg, t, null); raimund@2737: } raimund@2737: raimund@2737: protected static void parseInherits( raimund@2737: Document themeCfg, raimund@2737: Node cfg, raimund@2737: Theme t, raimund@2737: Map themes raimund@2737: ) { ingo@340: logger.debug("ThemeFactory.parseInherits"); ingo@340: raimund@2742: NodeList inherits = ((Element)cfg).getElementsByTagName("inherit"); ingo@340: raimund@2742: int num = inherits.getLength(); ingo@340: ingo@340: if (num == 0) { ingo@340: logger.debug("Theme does not inherit from other themes."); ingo@340: return; ingo@340: } ingo@340: ingo@340: logger.debug("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++) { ingo@340: Node inherit = inherits.item(i); raimund@2737: String from = ((Element)inherit).getAttribute("from"); ingo@340: raimund@2737: 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: raimund@2737: protected static Map buildThemeMap(Document themeCfg) { raimund@2737: Map map = new HashMap(); raimund@2737: String xpath = "/themes/themegroup/theme"; ingo@340: raimund@2737: NodeList nodes = (NodeList)XMLUtils.xpath( raimund@2737: themeCfg, xpath, XPathConstants.NODESET); raimund@2737: raimund@2737: if (nodes != null) { raimund@2737: for (int i = 0, N = nodes.getLength(); i < N; ++i) { raimund@2737: Node node = nodes.item(i); raimund@2737: String name = ((Element)node).getAttribute("name"); raimund@2737: map.put(name, node); raimund@2737: } ingo@340: } raimund@2737: return map; ingo@340: } ingo@340: ingo@340: ingo@340: protected static void parseFields(Node config, Theme theme) { ingo@340: if (config == null || theme == null) { ingo@340: logger.warn("Parsing fields without node or theme is senseless!"); ingo@340: return; ingo@340: } ingo@340: raimund@2742: NodeList fields = ((Element)config).getElementsByTagName("field"); ingo@340: raimund@2742: int num = fields.getLength(); ingo@340: ingo@340: logger.debug("Found " + num + " own fields in this theme."); ingo@340: ingo@340: if (num == 0) { ingo@340: logger.debug("Theme has no own fields."); ingo@340: return; ingo@340: } ingo@340: ingo@340: for (int i = 0; i < num; i++) { ingo@340: Node field = fields.item(i); ingo@340: ingo@340: addField(theme, field); ingo@340: } ingo@340: } ingo@340: ingo@340: ingo@340: protected static void addField(Theme theme, Node field) { raimund@2737: String name = ((Element)field).getAttribute("name"); ingo@340: christian@3155: logger.debug("Add field " + name + " to theme " + theme.getName()); ingo@340: ingo@340: NamedNodeMap attrs = field.getAttributes(); ingo@340: ingo@340: int num = attrs != null ? attrs.getLength() : 0; ingo@340: ingo@340: if (num == 0) { ingo@340: logger.warn("This field has no attributes."); ingo@340: return; ingo@340: } ingo@340: ingo@340: ThemeField theField = new DefaultThemeField(name); ingo@340: ingo@340: for (int i = 0; i < num; i++) { ingo@340: Node attr = attrs.item(i); ingo@340: ingo@340: String key = attr.getNodeName(); ingo@340: 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: ingo@340: ingo@340: protected static void parseAttrs(Node config, Theme theme) { ingo@340: NamedNodeMap attrs = config.getAttributes(); ingo@340: ingo@340: int num = attrs != null ? attrs.getLength() : 0; ingo@340: ingo@340: if (num == 0) { ingo@340: logger.debug("Theme has no attributes set."); ingo@340: return; ingo@340: } ingo@340: ingo@340: for (int i = 0; i < num; i++) { ingo@340: Node attr = attrs.item(i); ingo@340: ingo@340: String name = attr.getNodeName(); ingo@340: String value = attr.getNodeValue(); ingo@340: ingo@340: theme.addAttribute(name, value); ingo@340: } ingo@340: } ingo@340: } ingo@340: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :