Mercurial > dive4elements > gnv-client
view gnv-artifacts/src/main/java/de/intevation/gnv/artifacts/GNVArtifactBase.java @ 837:43f3c0cd60f2
First implementation of an odv export of a 'Profilschnitt' (issue217).
gnv-artifacts/trunk@944 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Ingo Weinzierl <ingo.weinzierl@intevation.de> |
---|---|
date | Mon, 19 Apr 2010 10:55:25 +0000 |
parents | 22c18083225e |
children | 13bea93a070a |
line wrap: on
line source
package de.intevation.gnv.artifacts; import de.intevation.artifactdatabase.Config; import de.intevation.artifactdatabase.ProxyArtifact; import de.intevation.artifactdatabase.XMLUtils; import de.intevation.artifacts.Artifact; import de.intevation.artifacts.ArtifactDatabase; import de.intevation.artifacts.ArtifactFactory; import de.intevation.artifacts.ArtifactNamespaceContext; import de.intevation.artifacts.CallContext; import de.intevation.artifacts.CallMeta; import de.intevation.gnv.artifacts.context.GNVArtifactContext; import de.intevation.gnv.artifacts.fis.product.Product; import de.intevation.gnv.artifacts.ressource.RessourceFactory; import de.intevation.gnv.state.AutoResumeState; import de.intevation.gnv.state.DefaultInputData; import de.intevation.gnv.state.ExportMode; import de.intevation.gnv.state.InputData; import de.intevation.gnv.state.InputValue; import de.intevation.gnv.state.OutputMode; import de.intevation.gnv.state.OutputState; import de.intevation.gnv.state.State; import de.intevation.gnv.state.StateFactory; import de.intevation.gnv.state.exception.StateException; import de.intevation.gnv.transition.Transition; import de.intevation.gnv.transition.TransitionFactory; import de.intevation.gnv.utils.ArtifactXMLUtilities; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import javax.xml.xpath.XPathConstants; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * This is the major gnv artifact and handles the requests specified in * <code>de.intevation.artifactdatabase.Artifact</code>. * * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a> * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> */ public abstract class GNVArtifactBase extends GNVDefaultArtifact implements PreSettingArtifact { /** * the logger, used to log exceptions and additonaly information */ private static Logger log = Logger.getLogger(GNVArtifactBase.class); /** * The UID of this Class */ private static final long serialVersionUID = -8907096744400741458L; /** * The Identifier for the Replacement of the Artifactname */ public static final String XPATH_IDENTIFIER_REPLACE = "IDENTIFIER"; /** * The XPATH to the XML-Fragment that should be used for the Configuration */ public static final String XPATH_ARTIFACT_CONFIGURATION = "/artifact-database/artifacts/artifact[@name='" + XPATH_IDENTIFIER_REPLACE + "']"; public static final String XPATH_STATIC_NODE = "/art:result/art:ui/art:static"; public static final String XPATH_INPUT_DATA = "/art:action/art:data/art:input"; public static final String XPATH_INCLUDE_UI = "/art:action/art:include-ui"; public static final String XPATH_TARGET_NAME = "/art:action/art:target/@name"; public static final String XPATH_OUTPUT_NAME = "/art:action/art:out/@name"; public static final String XPATH_OUTPUT_PARAMS = "/art:action/art:out/art:params/art:input"; public static final String INITIAL_STATE = "product"; /** * The current State */ protected State current = null; /** * The States that can be used */ protected Map<String, State> states = null; /** * The Transitions which can switch between the different States. */ protected Collection<Transition> transitions = null; /** * The current product */ protected Product product; /** * The Name of the Artifact */ protected String name = null; /** * The Presettings of InputData which can be used to * travel through the States in different Ways or * manipulate the InputData */ private Map<String, InputData> preSettings = null; /** * Constructor */ public GNVArtifactBase() { super(); } /** * This method handles request for changing the current state of an * artifact. It is possible to step forward, backward, or to the initial * state for choosing a fis. */ @Override public Document advance(Document target, CallContext context) { log.debug("GNVArtifactBase.advance()"); Document result = XMLUtils.newDocument(); String targetState = XMLUtils.xpathString( target, XPATH_TARGET_NAME, ArtifactNamespaceContext.INSTANCE ); // no current state... if (current == null) { log.warn("No current state. Advance not possible."); result = createReport( result, "exceptionreport", "exception", "No State activated." ); return result; } State next = null; try { // step forward if (isStateCurrentlyReachable(targetState)) { next = states.get(targetState); boolean success = go2NextState(context, result, next); if (success){ result = createReport( result, "result", "success", "Advance success" ); }else{ result = ArtifactXMLUtilities. createExceptionReport("Error while going to next State.", XMLUtils.newDocument()); } } // step backward else if((next = getPreviousState(current, targetState)) != null) { if (current != null) { current.endOfLife(context.globalContext()); } current = next; // 2. Transfer Results current.reset(identifier); result = createReport( result, "result", "success", "Advance success" ); } // goto initial step else if(targetState.equals(INITIAL_STATE)) { String fis = product.getArtifactFactory(); ArtifactDatabase db = context.getDatabase(); GNVProductArtifactFactory fac = (GNVProductArtifactFactory) db.getInternalArtifactFactory(fis); Artifact select = fac.createArtifact(identifier, context, null); context.putContextValue(ProxyArtifact.REPLACE_PROXY, select); result = createReport( result, "result", "success", "Advance success" ); } // advance not possible else { log.warn("advance not possible for target: " + targetState); result = createReport( result, "exceptionreport", "exception", "Statetransition not supported" ); } } catch (StateException se) { log.error(se, se); result = createReport( result, "exceptionreport", "exception", se.getLocalizedMessage() ); } return result; } private boolean go2NextState(CallContext context, Document result, State next) throws StateException { // 2. Transfer Results next.putInputData(current.getInputData(), identifier); next.setParent(current); next.setPreSettings(this.preSettings); if (current != null) { current.endOfLife(context.globalContext()); } // 3. Switch to next State current = next; // 4. Initialize next Step current.initialize(identifier, context); return true; } protected Document createReport( Document document, String nodeName, String state, String msg ) { XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( document, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX ); Element reportNode = creator.create(nodeName); Element stateNode = creator.create(state); stateNode.setTextContent(msg); reportNode.appendChild(stateNode); document.appendChild(reportNode); return document; } /** * Step back to the previous state specified by <code>name</code>. */ protected State getPreviousState(State current, String name) { if (current == null) { return null; } if (current.getID().equals(name)) { return current; } else { return getPreviousState(current.getParent(), name); } } private boolean isStateCurrentlyReachable(String stateid){ Iterator<Transition> it = this.transitions.iterator(); String from = this.current.getID(); while (it.hasNext()){ Transition transition = it.next(); if (transition.getFrom().equals(from)){ if (transition.getTo().equals(stateid) && transition.isValid(this.current)){ return true; } } } return false; } public Document initialize(CallContext context) { Document result = XMLUtils.newDocument(); try { this.current.initialize(super.identifier, context); if (this.current instanceof AutoResumeState){ // Jump to next State using the Transitions. State next = this.getNextReachableState(this.current); if (next != null){ boolean success = go2NextState(context, result, next); if (success){ result = ArtifactXMLUtilities .createSuccessReport("Initialize success", XMLUtils.newDocument()); }else{ result = ArtifactXMLUtilities. createExceptionReport( "Error while going to next State.", XMLUtils.newDocument()); } }else{ result = ArtifactXMLUtilities. createExceptionReport("No propper State found.", XMLUtils.newDocument()); } }else{ result = ArtifactXMLUtilities .createSuccessReport("Initialize success", XMLUtils.newDocument()); } } catch (StateException e) { log.error(e,e); result = ArtifactXMLUtilities.createExceptionReport(e .getLocalizedMessage(), XMLUtils.newDocument()); } return result; } /** * Step forward to the next reachable state. * @param current Current state. * @return Reachable state. */ protected State getNextReachableState(State current){ Iterator<Transition> it = this.transitions.iterator(); String from = current.getID(); while (it.hasNext()){ Transition transition = it.next(); if (transition.getFrom().equals(from)){ if (transition.isValid(current)){ return this.states.get(transition.getTo()); } } } return null; } protected String readStateName(Document document) { String returnValue = XMLUtils.xpathString( document, XPATH_TARGET_NAME, ArtifactNamespaceContext.INSTANCE); return returnValue; } protected Node getConfigurationFragment(Document document) { log.debug("GNVArtifactBase.getConfigurationFragment"); String xpathQuery = XPATH_ARTIFACT_CONFIGURATION.replaceAll( XPATH_IDENTIFIER_REPLACE, this.name); Element configurationNode = (Element)Config.getNodeXPath(document, xpathQuery); String link = configurationNode.getAttribute("xlink:href"); if (link != null ){ String absolutFileName = Config.replaceConfigDir(link); configurationNode = (Element)new ArtifactXMLUtilities().readConfiguration(absolutFileName); } return configurationNode; } /** * Insert new data included in <code>target</code> into the current state. * @param target XML document which contains data. * @param context CallContext * @return XML document with success or error message. */ @Override public Document feed(Document target, CallContext context) { log.debug("GNVArtifactBase.feed"); RessourceFactory fac = RessourceFactory.getInstance(); Locale[] locales = fac.getLocales(); Locale locale = context.getMeta().getPreferredLocale(locales); Document result = XMLUtils.newDocument(); try { if (this.current != null) { Collection<InputData> inputData = this.parseInputData( target, XPATH_INPUT_DATA); if (!inputData.isEmpty()){ result = current.feed(context, inputData, super.identifier); }else{ //String msg = "No Inputdata given. Please select at least one Entry."; String msg = fac.getRessource( locale, EXCEPTION_NO_INPUT, EXCEPTION_NO_INPUT); log.warn(msg); result = ArtifactXMLUtilities.createInputExceptionReport( msg, XMLUtils.newDocument()); } } else { String msg = "No State instantiated"; log.warn(msg); result = ArtifactXMLUtilities.createExceptionReport(msg, XMLUtils.newDocument()); } } catch (StateException e) { log.error(e, e); result = ArtifactXMLUtilities.createExceptionReport(e .getLocalizedMessage(), XMLUtils.newDocument()); } return result; } /** * Describe the current artifact. * @return The description of the current artifact. */ @Override public Document describe(Document data, CallContext context) { log.debug("GNVArtifactBase.describe"); Document document = createDescibeOutput( context, identifier, getIncludeUIFromDocument(data) ); return document; } /** * Initialse this artifact and insert some data if <code>data</code> * contains necessary information for this artifact. */ @Override public void setup(String identifier, ArtifactFactory factory, Object context,Document data) { log.debug("GNVArtifactBase.setup"); super.setup(identifier, factory, context, data); Object localContext = context; if (context instanceof CallContext) { localContext = ((CallContext) context).globalContext(); } if (localContext instanceof GNVArtifactContext) { GNVArtifactContext gnvContext = (GNVArtifactContext) localContext; Document doc = gnvContext.getConfig(); Node artifactNode = this.getConfigurationFragment(doc); NodeList stateList = Config.getNodeSetXPath(artifactNode, "states/state"); this.states = new HashMap<String, State>(stateList .getLength()); for (int i = 0; i < stateList.getLength(); i++) { State tmpState = StateFactory.getInstance() .createState(stateList.item(i)); if (tmpState != null) { log.debug("Initiate new state: " + tmpState.getID()); this.states.put(tmpState.getID(), tmpState); if (this.current == null) { this.current = tmpState; } } } NodeList transitionList = Config.getNodeSetXPath(artifactNode, "states/transition"); this.transitions = new ArrayList<Transition>(transitionList.getLength()); for (int i = 0; i < transitionList.getLength(); i++) { Transition tmpTransition = TransitionFactory.getInstance() .createTransition(transitionList.item(i)); if (tmpTransition != null) { this.transitions.add(tmpTransition); } } } } /** * Create the xml document returned in {@link #describe(org.w3c.dom.Document, * de.intevation.artifacts.CallContext)} */ protected Document createDescibeOutput( CallContext context, String uuid, boolean incudeUI ) { log.debug("GNVArtifactBase.createDescibeOutput"); Document document = XMLUtils.newDocument(); XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator( document, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX ); Element rootNode = this.createRootNode(creator, document); this.createHeader(creator, rootNode, document, "describe"); this.createOutputs(creator, rootNode, document); this.createCurrentState(creator, rootNode, document); this.createReachableStates(creator, rootNode, document); this.createModel(creator, rootNode, document); if (incudeUI){ this.createUserInterface(creator, rootNode, document, context, uuid); } return document; } /** * Determine the wish to append the user interface description to the * describe document. * * @return True, if the user interface description should be appended, * otherwise false. */ protected boolean getIncludeUIFromDocument(Document document){ String value = XMLUtils.xpathString( document, XPATH_INCLUDE_UI, ArtifactNamespaceContext.INSTANCE); boolean includeUI = false; if (value != null){ includeUI = Boolean.parseBoolean(value); } return includeUI; } protected Element createRootNode( XMLUtils.ElementCreator creator, Document document ) { Element rootNode = creator.create("result"); document.appendChild(rootNode); return rootNode; } /** * Append information about the current artifact (uuid, hash). */ protected void createHeader( XMLUtils.ElementCreator creator, Element parent, Document document, String documentType ) { Element typeNode = creator.create("type"); creator.addAttr(typeNode, "name", documentType); parent.appendChild(typeNode); Element uuidNode = creator.create("uuid"); creator.addAttr(uuidNode, "value", super.identifier); parent.appendChild(uuidNode); Element hashNode = creator.create("hash"); creator.addAttr(hashNode, "value", this.hash()); parent.appendChild(hashNode); } /** * Create the fis select box. */ protected Element createSelectBox( XMLUtils.ElementCreator artCreator, XMLUtils.ElementCreator creator, Document document, CallContext context ) { RessourceFactory resource = RessourceFactory.getInstance(); CallMeta callMeta = (CallMeta) context.getMeta(); String productName = product.getName(); Element selectNode = creator.create("select1"); creator.addAttr(selectNode, "ref", "product"); artCreator.addAttr(selectNode, "state", INITIAL_STATE, true); Element labelNode = creator.create("label"); labelNode.setTextContent( resource.getRessource(callMeta.getLanguages(), "product", "product") ); Element choicesNode = creator.create("choices"); Element itemNode = creator.create("item"); creator.addAttr(itemNode, "selected", "true"); Element choiceLabel = creator.create("label"); choiceLabel.setTextContent(resource.getRessource( callMeta.getLanguages(), productName, productName )); Element choiceValue = creator.create("value"); choiceValue.setTextContent(productName); itemNode.appendChild(choiceLabel); itemNode.appendChild(choiceValue); choicesNode.appendChild(itemNode); selectNode.appendChild(labelNode); selectNode.appendChild(choicesNode); return selectNode; } /** * Insert all reachable states into the describe document returned by * {@link #describe(org.w3c.dom.Document, de.intevation.artifacts.CallContext)} */ protected void createReachableStates( XMLUtils.ElementCreator creator, Element parent, Document document ) { Element stateNode = creator.create("reachable-states"); if (this.current != null) { // add future states Iterator<Transition> transitions = this.transitions.iterator(); while (transitions.hasNext()) { Transition tmpTransition = transitions.next(); if (tmpTransition.getFrom().equals(current.getID()) && tmpTransition.isValid(this.current)){ Element currentNode = creator.create("state"); creator.addAttr(currentNode, "name", tmpTransition.getTo()); creator.addAttr( currentNode, "description", this.states.get(tmpTransition.getTo()).getDescription()); stateNode.appendChild(currentNode); } } // add old states appendOldReachableStates(creator, stateNode, current); } parent.appendChild(stateNode); } /** * Insert states which have been visited by this artifact into the xml * document returned by {@link #describe(org.w3c.dom.Document, * de.intevation.artifacts.CallContext)} */ protected void appendOldReachableStates( XMLUtils.ElementCreator creator, Element parent, State state ) { if (state == null) return; while (state != null) { Element currentNode = creator.create("state"); creator.addAttr(currentNode, "name", state.getID()); creator.addAttr(currentNode, "description", state.getDescription()); parent.appendChild(currentNode); state = state.getParent(); } } /** * Insert the current state into the xml document returned by * {@link #describe(org.w3c.dom.Document, de.intevation.artifacts.CallContext)} */ protected void createCurrentState( XMLUtils.ElementCreator creator, Element parent, Document document ) { Element stateNode = creator.create("state"); creator.addAttr(stateNode, "name", this.current.getID()); creator.addAttr(stateNode, "description", this.current.getDescription()); parent.appendChild(stateNode); } protected void createModel( XMLUtils.ElementCreator creator, Element parent, Document document ) { Element modelNode = creator.create("model"); if (this.current != null) { Collection<InputValue> inputValues = this.current .getRequiredInputValues(); if (inputValues != null) { Iterator<InputValue> it = inputValues.iterator(); while (it.hasNext()) { InputValue inputValue = it.next(); Element inputNode = creator.create("input"); creator.addAttr(inputNode, "name", inputValue.getName()); creator.addAttr(inputNode, "type", inputValue.getType()); modelNode.appendChild(inputNode); } } } parent.appendChild(modelNode); } /** * Append the user interface description to the document returned by * {@link #describe(org.w3c.dom.Document, de.intevation.artifacts.CallContext)} * @param creator XML element creator. * @param parent New elements are appended to this node. * @param document Current document. * @param context CallContext * @param uuid The uuid of the artifact. */ protected void createUserInterface( XMLUtils.ElementCreator creator, Element parent, Document document, CallContext context, String uuid ) { XMLUtils.ElementCreator xCreator = new XMLUtils.ElementCreator( document, XMLUtils.XFORM_URL, XMLUtils.XFORM_PREFIX ); Element uiNode = creator.create("ui"); Element staticNode = creator.create("static"); Element dynamic = creator.create("dynamic"); uiNode.appendChild(staticNode); uiNode.appendChild(dynamic); parent.appendChild(uiNode); // append fis to dynamic part appendFis(document, staticNode, context, product.getArtifactFactory()); if (this.current != null) { Element staticUI = createSelectBox( creator, xCreator, document, context ); staticNode.appendChild(staticUI); this.current.describe( document, uiNode, context, uuid ); } } /** * Append possible output targets to the document returned by * {@link #describe(org.w3c.dom.Document, de.intevation.artifacts.CallContext)} * @param creator Used to create xml elements. * @param parent New elements are appended to this node. * @param document The current document. */ protected void createOutputs( XMLUtils.ElementCreator creator, Element parent, Document document ) { log.debug("GNVArtifactBase.createOutputs"); Element outputsNode = creator.create("outputs"); if (this.current instanceof OutputState) { Collection<OutputMode> outputModes = ((OutputState) this.current) .getOutputModes(); if (outputModes != null) { Iterator<OutputMode> it = outputModes.iterator(); while (it.hasNext()) { OutputMode outputMode = it.next(); log.debug("Write Outputnode for " + outputMode.toString()); Element outputModeNode = creator.create("output"); creator.addAttr( outputModeNode, "name", outputMode.getName()); creator.addAttr( outputModeNode, "description", outputMode.getDescription()); creator.addAttr( outputModeNode, "mime-type", outputMode.getMimeType()); outputsNode.appendChild(outputModeNode); Collection<InputValue> inputParameters = outputMode .getInputParameters(); if (inputParameters != null) { Element inputParametersNode = creator.create("parameter"); outputModeNode.appendChild(inputParametersNode); Iterator<InputValue> it2 = inputParameters.iterator(); while (it2.hasNext()) { InputValue inputValue = it2.next(); Element inputParameterNode = creator.create("parameter"); creator.addAttr( inputParameterNode, "name", inputValue.getName()); creator.addAttr( inputParameterNode, "type", inputValue.getType()); creator.addAttr( inputParameterNode, "value", inputValue.getDefaultValue()); inputParametersNode.appendChild(inputParameterNode); } } // append export modes List<ExportMode> exportModes = outputMode.getExportModes(); if (exportModes != null) { Element exports = creator.create("exports"); outputModeNode.appendChild(exports); for (ExportMode exp: exportModes) { Element export = creator.create("export"); creator.addAttr( export, "name", exp.getName()); creator.addAttr( export, "description", exp.getDescription()); creator.addAttr( export, "mime-type", exp.getMimeType()); exports.appendChild(export); } } } } else { log.warn("No Outputmodes given."); } } parent.appendChild(outputsNode); } /** * Parse input data from feed-document. * @param document Feed-document * @param xPath Path to input data. * @return A collection with InputData objects. */ protected Collection<InputData> parseInputData(Document document, String xPath) { HashMap<String, InputData> returnValue = null; NodeList inputElemets = (NodeList) XMLUtils.xpath(document, xPath, XPathConstants.NODESET, ArtifactNamespaceContext.INSTANCE); if (inputElemets != null) { returnValue = new HashMap<String, InputData>(); for (int i = 0; i < inputElemets.getLength(); i++) { Element inputDataNode = (Element)inputElemets.item(i); String name = inputDataNode.getAttribute("name"); String value = inputDataNode.getAttribute("value"); if (returnValue.containsKey(name)) { InputData inputData = returnValue.get(name); inputData.concartValue(value); log.debug(inputData.toString()); returnValue.put(name, inputData); } else { InputData inputData = new DefaultInputData(name, value); returnValue.put(name, inputData); } } } return returnValue.values(); } /** * Call an output target (e.g. chart, wms, etc.) * @param format XML document which contains some further information about * the desired output. * @param outputStream Stream used for writing the output. * @param context CallContext * @throws IOException If an error occured while writing the result to the * output stream. */ @Override public void out(Document format, OutputStream outputStream, CallContext context) throws IOException { log.debug("TGNVArtifactBase.out"); try { if (current != null && current instanceof OutputState) { ((OutputState) current) .out(format, this.parseInputData( format, XPATH_OUTPUT_PARAMS), outputStream, super.identifier, context); } } catch (StateException e) { log.error(e, e); throw new IOException(e.getMessage()); } } protected static String readOutputType(Document document) { String value = XMLUtils.xpathString( document, XPATH_OUTPUT_NAME, ArtifactNamespaceContext.INSTANCE); return value; } /** * The the current product. * @param product New product. */ public void setProduct(Product product) { this.product = product; } /** * Call endOfLife of parent class and the current state. */ @Override public void endOfLife(Object globalContext) { super.endOfLife(globalContext); if (current != null) { current.endOfLife(globalContext); } } /** * Retrieves a map with given InputData which have been inserted into the * current artifact before the parameterization began. */ public Map<String, InputData> getPreSettings() { return this.preSettings; } /** * Set InputData which are used in later states. */ public void setPreSettings(Map<String, InputData> preSettings) { this.preSettings = preSettings; if (this.current != null){ this.current.setPreSettings(preSettings); } } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :