view gnv-artifacts/src/main/java/de/intevation/gnv/artifacts/GNVArtifactBase.java @ 522:c896282c2601

Issue 156 solved. Added width, height and points as parameter to svg and pdf output mode. Width and height have an effact on the width and height of the export, points is a boolean property which enables/disables the drawing of data points. gnv-artifacts/trunk@616 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Ingo Weinzierl <ingo.weinzierl@intevation.de>
date Mon, 25 Jan 2010 09:18:31 +0000
parents ca5048e4e515
children 5b9b74c08bbb
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.cache.CacheFactory;

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.DefaultInputData;
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.StateBase;
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.Map;

import javax.xml.xpath.XPathConstants;

import net.sf.ehcache.Cache;

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;

/**
 * @author Tim Englich         (tim.englich@intevation.de)
 * @author Ingo Weinzierl      (ingo.weinzierl@intevation.de)
 * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
 */
public abstract class GNVArtifactBase extends GNVDefaultArtifact {
    /**
     * 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;

    /**
     * Constructor
     */
    public GNVArtifactBase() {
        super();
    }


    @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.debug("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);

                // 2. Transfer Results
                next.putInputData(current.getInputData(), identifier);
                next.setParent(current);

                if (current != null) {
                    current.endOfLife(context.globalContext());
                }

                // 3. Switch to next State
                current = next;

                // 4. Initialize next Step
                current.initialize(identifier, context);

                result = createReport(
                    result, "result", "success", "Advance success"
                );
            }

            // step backward
            else if((next = getPreviousState(current, targetState)) != null) {

                // reset input data
                resetFutureStates(current, targetState);

                // remove data from future states from cache
                resetDescribeData(current, identifier, targetState);

                if (current != null) {
                    current.endOfLife(context.globalContext());
                }

                current = next;

                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);
                context.putContextValue(ProxyArtifact.REPLACE_PROXY, select);

                resetDescribeData(current, identifier, INITIAL_STATE);

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


    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;

    }


    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);
        }
    }

    public void resetDescribeData(State state, String uuid, String target) {
        CacheFactory factory = CacheFactory.getInstance();
        
        if (factory.isInitialized()) {
            Cache  cache = factory.getCache();
            String key   = uuid + StateBase.DESCRIBEDATAKEY;

            net.sf.ehcache.Element value = cache.get(key);
            if (value == null)
                return;

            List data = (List) value.getObjectValue();
            while(!target.equals(state.getID())) {
                data.remove(data.size()-1);
                state = state.getParent();

                if (state == null)
                    break;
            }

            cache.put(new net.sf.ehcache.Element(key, data));
        }
    }


    protected void resetFutureStates(State current, String name) {
        if (current == null) {
            return;
        }

        if (current.getID().equals(name)) {
            return;
        }
        else {
            current.reset(identifier);
            resetFutureStates(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);
            result = new ArtifactXMLUtilities()
            .createSuccessReport("Initialize success",
                    XMLUtils.newDocument());
        } catch (StateException e) {
            log.error(e,e);
            result = new ArtifactXMLUtilities().createExceptionReport(e
                    .getLocalizedMessage(), XMLUtils.newDocument());
        }
        return result;
    }

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

    /**
     * @see de.intevation.artifactdatabase.DefaultArtifact#feed(org.w3c.dom.Document,
     *      de.intevation.artifacts.CallContext)
     */
    @Override
    public Document feed(Document target, CallContext context) {
        log.debug("GNVArtifactBase.feed");
        Document result = XMLUtils.newDocument();
        try {
            if (this.current != null) {
                Collection<InputData> inputData = this.parseInputData(
                    target,
                    XPATH_INPUT_DATA);

                if (!inputData.isEmpty()){
                    this.current.putInputData(inputData, super.identifier);
                    result = new ArtifactXMLUtilities().createSuccessReport(
                            "Feed success", XMLUtils.newDocument());
                }else{
                    String msg = "No Inputdata given. Please select at least one Entry.";
                    log.warn(msg);
                    result = new ArtifactXMLUtilities().createExceptionReport(msg,
                            XMLUtils.newDocument());
                }
            } else {
                String msg = "No State instantiated";
                log.warn(msg);
                result = new ArtifactXMLUtilities().createExceptionReport(msg,
                        XMLUtils.newDocument());
            }
        } catch (StateException e) {
            log.error(e, e);
            result = new ArtifactXMLUtilities().createExceptionReport(e
                    .getLocalizedMessage(), XMLUtils.newDocument());
        }
        return result;
    }


    /**
     * @see de.intevation.artifactdatabase.DefaultArtifact#describe(org.w3c.dom.Document, de.intevation.artifacts.CallContext)
     */
    @Override
    public Document describe(Document data, CallContext context) {
        log.debug("GNVArtifactBase.describe");

        Document document = createDescibeOutput(
            context,
            identifier,
            getIncludeUIFromDocument(data)
        );

        // insert node for rendering product field
        Element staticNode = (Element) XMLUtils.xpath(
            document,
            XPATH_STATIC_NODE,
            XPathConstants.NODE,
            ArtifactNamespaceContext.INSTANCE
        );

        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
            document,
            XMLUtils.XFORM_URL,
            XMLUtils.XFORM_PREFIX
        );

        XMLUtils.ElementCreator artCreator = new XMLUtils.ElementCreator(
            document,
            ArtifactNamespaceContext.NAMESPACE_URI,
            ArtifactNamespaceContext.NAMESPACE_PREFIX
        );

        return document;
    }

    /**
     * @see de.intevation.artifactdatabase.DefaultArtifact#setup(java.lang.String,
     *      java.lang.Object)
     */
    @Override
    public void setup(String identifier, ArtifactFactory factory, Object context) {
        log.debug("GNVArtifactBase.setup");
        super.setup(identifier, factory, context);

        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);
                }
            }

        }
    }


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

    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);
    }

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


    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);
    }


    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();
        }
    }


    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);
    }

    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
            );
        }
    }


    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);
                        }
                    }
                }
            } else {
                log.warn("No Outputmodes given.");
            }
        }
        parent.appendChild(outputsNode);
    }

    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();
    }

    /**
     * @see de.intevation.artifactdatabase.DefaultArtifact#out(org.w3c.dom.Document,
     *      java.io.OutputStream, de.intevation.artifacts.CallContext)
     */
    @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;
    }


    public void setProduct(Product product) {
        this.product = product;        
    }

    public void endOfLife(Object globalContext) {
        super.endOfLife(globalContext);

        if (current != null) {
            current.endOfLife(globalContext);
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

http://dive4elements.wald.intevation.org