ingo@105: /* ingo@105: * Copyright (c) 2011 by Intevation GmbH ingo@105: * ingo@105: * This program is free software under the LGPL (>=v2.1) ingo@105: * Read the file LGPL.txt coming with the software for details ingo@105: * or visit http://www.gnu.org/licenses/ if it does not exist. ingo@105: */ teichmann@475: package org.dive4elements.artifactdatabase.state; ingo@105: ingo@209: import java.util.ArrayList; ingo@105: import java.util.HashMap; ingo@209: import java.util.List; ingo@105: import java.util.Map; ingo@105: ingo@106: import javax.xml.xpath.XPathConstants; ingo@106: tom@570: import org.apache.logging.log4j.Logger; tom@570: import org.apache.logging.log4j.LogManager; ingo@226: ingo@105: import org.w3c.dom.Document; ingo@205: import org.w3c.dom.Element; ingo@105: import org.w3c.dom.Node; ingo@209: import org.w3c.dom.NodeList; ingo@105: teichmann@475: import org.dive4elements.artifacts.Artifact; teichmann@475: import org.dive4elements.artifacts.ArtifactNamespaceContext; teichmann@475: import org.dive4elements.artifacts.CallContext; teichmann@475: import org.dive4elements.artifacts.CallMeta; ingo@105: teichmann@475: import org.dive4elements.artifacts.common.utils.Config; teichmann@475: import org.dive4elements.artifacts.common.utils.XMLUtils; ingo@106: teichmann@475: import org.dive4elements.artifactdatabase.data.StateData; ingo@105: ingo@105: ingo@105: /** ingo@105: * An abstract implementation of a {@link State}. It implements some basic ingo@105: * methods that return the id, description and data. The methods ingo@105: * describe() and setup() depend on the concrete class ingo@105: * and need to be implemented by those. ingo@105: */ ingo@105: public abstract class AbstractState implements State { ingo@105: ingo@106: /** The XPath to the ID of the state relative to the state node in the ingo@106: * configuration. */ ingo@106: public static final String XPATH_ID = "@id"; ingo@106: ingo@106: /** The XPath to the description of the state relative to the state node in ingo@106: * the configuration. */ ingo@106: public static final String XPATH_DESCRIPTION = "@description"; ingo@106: ingo@386: /** The XPath that points to the help text.*/ ingo@386: public static final String XPATH_HELP_TEXT = "@helpText"; ingo@386: felix@367: /** The XPath to the output nodes of the state configuration. */ ingo@209: public static final String XPATH_OUTPUT_MODES = "outputmodes/outputmode"; ingo@209: ingo@226: /** The XPath to the list of facets relative to the output mode it belongs felix@367: * to. */ ingo@226: public static final String XPATH_FACETS = "facets/facet"; ingo@226: teichmann@468: public static final String XPATH_HELP_URL = "/artifact-database/help-url/text()"; teichmann@468: teichmann@468: public static final String HELP_URL = "${help.url}"; teichmann@468: ingo@226: felix@367: /** The logger that is used in this class. */ tom@570: private static Logger logger = LogManager.getLogger(AbstractState.class); ingo@226: ingo@106: ingo@105: /** The ID of the state. */ ingo@105: protected String id; ingo@105: ingo@105: /** The description of the state. */ ingo@105: protected String description; ingo@105: ingo@386: /** The help text for this state.*/ ingo@386: protected String helpText; ingo@386: ingo@105: /** The data provided by this state. */ ingo@105: protected Map data; ingo@105: felix@367: /** A list of output modes which are available for this state. */ ingo@209: protected List outputs; ingo@209: teichmann@468: private static String helpUrl; teichmann@468: ingo@105: ingo@205: public AbstractState() { ingo@209: outputs = new ArrayList(); ingo@205: } ingo@205: teichmann@468: public static synchronized final String getHelpUrl() { teichmann@468: if (helpUrl == null) { teichmann@468: helpUrl = Config.getStringXPath(XPATH_HELP_URL, HELP_URL); teichmann@468: } teichmann@468: return helpUrl; teichmann@468: } teichmann@468: teichmann@468: public static String replaceHelpUrl(String string) { teichmann@468: return string.replace(HELP_URL, getHelpUrl()); teichmann@468: } teichmann@468: ingo@205: ingo@105: /** ingo@105: * The default constructor. ingo@105: * ingo@105: * @param id The ID of the state. ingo@105: * @param description The description of the state. ingo@105: */ ingo@105: public AbstractState(String id, String description) { ingo@209: super(); ingo@209: ingo@105: this.id = id; ingo@105: this.description = description; ingo@105: } ingo@105: ingo@105: ingo@386: public AbstractState(String id, String description, String helpText) { ingo@386: this(id, description); teichmann@468: this.helpText = replaceHelpUrl(helpText); ingo@386: } ingo@386: ingo@386: ingo@105: /** ingo@105: * Returns the ID of the state. ingo@105: * ingo@105: * @return the ID of the state. ingo@105: */ ingo@105: public String getID() { ingo@105: return id; ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@105: * Set the ID of the state. ingo@105: * ingo@105: * @param id The ID of the state. ingo@105: */ ingo@105: public void setID(String id) { ingo@105: this.id = id; ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@105: * Returns the description of the state. ingo@105: * ingo@105: * @return the description of the state. ingo@105: */ ingo@105: public String getDescription() { ingo@105: return description; ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@105: * Set the description of the state. ingo@105: * ingo@105: * @param description The description of the state. ingo@105: */ ingo@105: public void setDescription(String description) { ingo@105: this.description = description; ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@386: * Returns the help text of this state. ingo@386: * ingo@386: * @return the help text. ingo@386: */ ingo@386: public String getHelpText() { ingo@386: return helpText; ingo@386: } ingo@386: ingo@386: ingo@386: /** ingo@386: * Set the help text for this state. ingo@386: * ingo@386: * @param helpText The help text. ingo@386: */ ingo@386: public void setHelpText(String helpText) { teichmann@468: this.helpText = replaceHelpUrl(helpText); ingo@386: } ingo@386: ingo@386: ingo@386: /** ingo@105: * Returns the data of the state. ingo@105: * ingo@105: * @return the data of the state. ingo@105: */ ingo@105: public Map getData() { ingo@105: return data; ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@256: * Returns a specific data object of the state. ingo@256: * ingo@256: * @param name The name of the data object. ingo@256: * ingo@256: * @return a data object of the state or null if no such data object exists. ingo@256: */ ingo@256: public StateData getData(String name) { ingo@373: if (data != null) { ingo@373: return data.get(name); ingo@373: } ingo@373: ingo@373: return null; ingo@256: } ingo@256: ingo@256: ingo@256: /** ingo@105: * Add new data to the state. NOTE: If there is already an object existing ingo@105: * with the key name, this object is overwritten by the new value. ingo@105: * ingo@105: * @param name The name of the data object. felix@400: * @param data The data object. ingo@105: */ ingo@105: public void addData(String name, StateData data) { ingo@105: if (this.data == null) { ingo@105: this.data = new HashMap(); ingo@105: } ingo@105: ingo@105: this.data.put(name, data); ingo@105: } ingo@105: ingo@105: ingo@105: /** ingo@209: * Returns the list of possible outputs of this state. The list is empty ingo@209: * if no output is available for this state. ingo@209: * ingo@209: * @return a list of possible outputs of this state. ingo@209: */ ingo@209: public List getOutputs() { ingo@209: return outputs; ingo@209: } ingo@209: ingo@209: ingo@209: /** ingo@106: * Initialize the state based on the state node in the configuration. ingo@105: * ingo@105: * @param config The state configuration node. ingo@105: */ ingo@106: public void setup(Node config) { ingo@226: logger.info("AbstractState.setup"); ingo@226: ingo@106: id = (String) XMLUtils.xpath(config, XPATH_ID, XPathConstants.STRING); ingo@106: ingo@106: description = (String) XMLUtils.xpath( ingo@106: config, XPATH_DESCRIPTION, XPathConstants.STRING); ingo@209: ingo@386: helpText = (String) XMLUtils.xpath( ingo@386: config, XPATH_HELP_TEXT, XPathConstants.STRING); ingo@386: teichmann@468: if (helpUrl != null) { teichmann@468: helpUrl = replaceHelpUrl(helpUrl); teichmann@468: } teichmann@468: ingo@209: setupOutputs(config); ingo@209: } ingo@209: ingo@209: ingo@209: /** ingo@363: * This default implementation does nothing at all. ingo@365: * ingo@365: * @param orig ingo@365: * @param owner ingo@365: * @param context ingo@365: * @param callMeta ingo@363: */ ingo@365: public void initialize( ingo@365: Artifact orig, ingo@365: Artifact owner, ingo@365: Object context, ingo@365: CallMeta callMeta ingo@365: ) { ingo@363: // do nothing. ingo@363: } ingo@363: ingo@363: ingo@363: /** ingo@209: * This method tries reading the available output nodes configured in the ingo@209: * state configuration and adds possible Outputs to the outputs list. ingo@209: * ingo@209: * @param config The state configuration node. ingo@209: */ ingo@209: protected void setupOutputs(Node config) { ingo@209: NodeList outs = (NodeList) XMLUtils.xpath( ingo@209: config, ingo@209: XPATH_OUTPUT_MODES, ingo@209: XPathConstants.NODESET, ingo@209: ArtifactNamespaceContext.INSTANCE); ingo@209: ingo@209: if (outs == null || outs.getLength() == 0) { ingo@209: return; ingo@209: } ingo@209: ingo@209: int size = outs.getLength(); ingo@209: ingo@209: for (int i = 0; i < size; i++) { bjoern@425: addOutput(buildOutput(outs.item(i))); ingo@209: } ingo@209: } ingo@209: bjoern@425: /** bjoern@425: * This methods allows subclasses to manually add outputs bjoern@425: * bjoern@425: * @param out The output to add bjoern@425: */ bjoern@425: protected void addOutput(Output out) { bjoern@425: outputs.add(out); bjoern@425: } ingo@209: ingo@209: /** ingo@209: * A helper method that creates an Output object based on the out ingo@209: * node. ingo@209: * ingo@209: * @param out The output node configuration. ingo@209: * ingo@209: * @return an Output object. ingo@209: */ ingo@209: protected Output buildOutput(Node out) { ingo@209: String name = XMLUtils.xpathString( ingo@209: out, "@name", ArtifactNamespaceContext.INSTANCE); ingo@209: ingo@209: String desc = XMLUtils.xpathString( ingo@209: out, "@description", ArtifactNamespaceContext.INSTANCE); ingo@209: ingo@209: String mimetype = XMLUtils.xpathString( ingo@209: out, "@mime-type", ArtifactNamespaceContext.INSTANCE); ingo@209: ingo@290: String type = XMLUtils.xpathString( ingo@290: out, "@type", ArtifactNamespaceContext.INSTANCE); ingo@290: ingo@226: if (name == null) { ingo@226: return null; ingo@226: } ingo@226: ingo@226: NodeList facets = (NodeList) XMLUtils.xpath( ingo@226: out, ingo@226: XPATH_FACETS, ingo@226: XPathConstants.NODESET, ingo@226: ArtifactNamespaceContext.INSTANCE); ingo@226: ingo@226: if (facets == null || facets.getLength() == 0) { ingo@290: return new DefaultOutput(name, desc, mimetype, type); ingo@226: } ingo@226: ingo@226: int num = facets.getLength(); ingo@226: ingo@226: List facetList = new ArrayList(num); ingo@226: ingo@226: for (int i = 0; i < num; i++) { ingo@226: Facet facet = buildFacet(facets.item(i)); ingo@226: ingo@226: if (facet != null) { ingo@226: facetList.add(facet); ingo@226: } ingo@226: } ingo@226: ingo@290: return new DefaultOutput(name, desc, mimetype, facetList, type); ingo@226: } ingo@226: ingo@226: ingo@226: /** ingo@226: * A helper method that creates a Facet object based on the facet ingo@226: * node. ingo@226: * ingo@226: * @param facet The facet node. ingo@226: * ingo@226: * @return a Facet object or null if no valid Facet was found. ingo@226: */ ingo@226: protected Facet buildFacet(Node facet) { ingo@226: String name = XMLUtils.xpathString( ingo@226: facet, "@name", ArtifactNamespaceContext.INSTANCE); ingo@226: ingo@226: String desc = XMLUtils.xpathString( ingo@226: facet, "@description", ArtifactNamespaceContext.INSTANCE); ingo@226: ingo@226: return name != null ? new DefaultFacet(name, desc) : null; ingo@106: } ingo@105: ingo@105: ingo@105: /** ingo@105: * Describes the UI of the state. This method needs to be implemented by ingo@105: * concrete subclasses. ingo@105: * ingo@244: * @param artifact A reference to the artifact this state belongs to. ingo@105: * @param document Describe doucment. ingo@105: * @param rootNode Parent node for all new elements. ingo@105: * @param context The CallContext. ingo@105: * @param uuid The uuid of an artifact. ingo@105: */ ingo@205: public abstract Element describe( ingo@244: Artifact artifact, ingo@105: Document document, ingo@105: Node rootNode, ingo@105: CallContext context, ingo@105: String uuid ingo@105: ); ingo@327: ingo@327: ingo@327: @Override ingo@327: public void endOfLife(Artifact artifact, Object context) { ingo@327: // nothing to do here ingo@327: } ingo@105: } ingo@105: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :