SiLeBAT/FSK-Lab

View on GitHub
de.bund.bfr.knime.foodprocess.pcml/src/de/bund/bfr/knime/pcml/port/PCMLUtil.java

Summary

Maintainability
B
5 hrs
Test Coverage
/*******************************************************************************
 * Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Contributors:
 *     Department Biological Safety - BfR
 *******************************************************************************/
package de.bund.bfr.knime.pcml.port;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.knime.core.node.KNIMEConstants;
import org.knime.core.node.NodeLogger;
import org.knime.core.node.port.PortObject;

import de.bund.bfr.knime.util.Agent;
import de.bund.bfr.knime.util.Matrix;
import de.bund.bfr.knime.util.ValueAndUnit;
import de.bund.bfr.pcml10.AgentIncredientDocument.AgentIncredient;
import de.bund.bfr.pcml10.AgentRecipeDocument.AgentRecipe;
import de.bund.bfr.pcml10.ApplicationDocument.Application;
import de.bund.bfr.pcml10.HeaderDocument.Header;
import de.bund.bfr.pcml10.MatrixIncredientDocument.MatrixIncredient;
import de.bund.bfr.pcml10.MatrixRecipeDocument.MatrixRecipe;
import de.bund.bfr.pcml10.NameAndDatabaseId;
import de.bund.bfr.pcml10.OutportDocument.Outport;
import de.bund.bfr.pcml10.PCMLDocument;
import de.bund.bfr.pcml10.PCMLDocument.PCML;
import de.bund.bfr.pcml10.ProcessChainDataDocument.ProcessChainData;
import de.bund.bfr.pcml10.ProcessChainDocument.ProcessChain;
import de.bund.bfr.pcml10.ProcessDataDocument.ProcessData;
import de.bund.bfr.pcml10.ProcessNodeDocument.ProcessNode;

public class PCMLUtil {
    /** Creates an empty PCMLDocument. */
    public static PCMLDocument create() {
        PCMLDocument doc = PCMLDocument.Factory.newInstance();
        PCML pcml = doc.addNewPCML();
        pcml.setVersion("1.0");

        Header header = pcml.addNewHeader();

        String owner = System.getProperty("user.name");
        if (owner == null || owner.isEmpty()) {
            owner = "KNIME";
        }
        header.setCopyright(owner);

        Application application = header.addNewApplication();
        application.setName("KNIME");
        application.setVersion(KNIMEConstants.MAJOR + "."
                + KNIMEConstants.MINOR + "." + KNIMEConstants.REV);
        header.setApplication(application);

        return doc;
    }
    
    /** Adds an outport to the ProcessNode that is initialized with the given
     * properties of the spec
     * @param processNode
     * @param spec
     * @return the added Outport
     */
    public static Outport addOutport(final ProcessNode processNode, 
            final PCMLPortObjectSpec spec) {
        Outport outport = processNode.addNewOutport();
        // add matrices
        MatrixRecipe matrixRecipe = outport.addNewMatrixRecipe();
        for (Entry<Matrix, Double> matrix : spec.getMatrices().entrySet()) {
            MatrixIncredient incredient = matrixRecipe.addNewMatrixIncredient();
            NameAndDatabaseId pcmlMatrix = incredient.addNewMatrix();
            if (matrix.getKey().getId() > -1) {
                pcmlMatrix.setDbId(matrix.getKey().getId());
            }
            pcmlMatrix.setName(matrix.getKey().getName());
            incredient.setFraction(matrix.getValue());
        }
        // add agents
        AgentRecipe agentRecipe = outport.addNewAgentRecipe();
        for (Entry<Agent, ValueAndUnit> agent : spec.getAgents().entrySet()) {
            AgentIncredient incredient = agentRecipe.addNewAgentIncredient();
            NameAndDatabaseId pcmlAgent = incredient.addNewAgent();
            if (agent.getKey().getId() > -1) {
                pcmlAgent.setDbId(agent.getKey().getId());
            }
            pcmlAgent.setName(agent.getKey().getName());
            if (agent.getValue().getValue() == null) incredient.setFraction(0);
            else incredient.setFraction(agent.getValue().getValue());
            incredient.setUnit(agent.getValue().getUnit());                
        }
        // add physical properties
        if (null != spec.getVolume()) {
            outport.setVolume(Double.toString(spec.getVolume()));
        }
        if (null != spec.getTemperature()) {
            outport.setTemperature(Double.toString(spec.getTemperature()));
        }
        if (null != spec.getPressure()) {
            outport.setPressure(Double.toString(spec.getPressure()));
        }
        if (null != spec.getPH_value()) {
            outport.setPH(Double.toString(spec.getPH_value()));
        }
        if (null != spec.getAw_value()) {
            outport.setAw(Double.toString(spec.getAw_value()));
        }
        return outport;
    }

    /** Merges the PCMLDocument of the given ports to a single document. */
    public static PCMLDocument merge(final PortObject[] portObjects) {        
        PCMLDocument doc = ((PCMLPortObject)portObjects[0]).getPcmlDoc();
        // do a deep copy
        doc = (PCMLDocument)doc.copy();
        for (int i = 1; i < portObjects.length; i++) {
            if (null != portObjects[i]) {
                PCMLUtil.append(doc, 
                        ((PCMLPortObject)portObjects[i]).getPcmlDoc());
            }
        }
        return doc;
    }
    
    /** Appends the second PCMLDocument to the first. 
     * @param doc the document that will be appended
     * @param toAppend appends doc with elements of this document 
     */
    public static void append(final PCMLDocument doc, 
            final PCMLDocument toAppend) {
        Map<String, XmlObject> newNodes = PCMLUtil.append(doc, toAppend, 
                "ProcessNode");
        // TODO: this might be automated by getting the parent of ProcessNode.
        // Does such an automation make really sense?
//        XmlCursor cursor = xbean.newCursor();
//        Cursor.toParent();
//        XmlObject parentXbean = cursor.getObject();
//        cursor.dispose();

        ProcessChain processChain = doc.getPCML().getProcessChain() != null ?
                doc.getPCML().getProcessChain() 
                : doc.getPCML().addNewProcessChain();
        for (XmlObject xmlObject : newNodes.values()) {
            ProcessNode processNode = (ProcessNode)xmlObject;
            processChain.addNewProcessNode().set(processNode);
        }
        
        // Add ProcessNodeData elements of all new nodes
        ProcessChainData processChainData = 
            doc.getPCML().getProcessChainData() != null 
                ? doc.getPCML().getProcessChainData() 
                : doc.getPCML().addNewProcessChainData();
        for (XmlObject xmlObject : newNodes.values()) {
            ProcessNode processNode = (ProcessNode)xmlObject;
            String pID = processNode.getId();
            
            XmlObject[] xmlObjects = toAppend.selectPath(
                      "declare namespace s='" + getPCMLNamespace(doc) + "' " +
                      ".//s:ProcessData");
            for (XmlObject dataObject : xmlObjects) {
                ProcessData pData = (ProcessData)dataObject;
                if (pData.getRef().equals(pID)) {
                    processChainData.addNewProcessData().set(pData);
                }
            }
        }
    }
    
    private static Map<String, XmlObject> append(final PCMLDocument doc, 
            final PCMLDocument toAppend, final String element) {
        Map<String, XmlObject> docIds = PCMLUtil.getIds(doc, element);
        Map<String, XmlObject> toAppendIds = PCMLUtil.getIds(toAppend, element);

        toAppendIds.keySet().removeAll(docIds.keySet());
        return toAppendIds;
    }    

    private static Map<String, XmlObject> getIds(final PCMLDocument doc, 
            final String element) {
        XmlObject[] xmlObjects = doc.selectPath(
                  "declare namespace s='" + getPCMLNamespace(doc) + "' " +
                  ".//s:" + element);
        Map<String, XmlObject> map = new LinkedHashMap<String, XmlObject>();
        for (XmlObject xmlObject : xmlObjects) {
            // get the id
            String id = xmlObject.getDomNode().getAttributes()
                .getNamedItem("id").getNodeValue();
            map.put(id, xmlObject);
        }
        return map;
    }
    
    /** Get the namespace ot the PCML element in the given docuement. */
    public static String getPCMLNamespace(final PCMLDocument doc) {
        return "http://www.bfr.bund.de/PCML-1_0";
    }

    /** Validate PCML document and sent errors to the logger.*/
    public static boolean validate(final PCMLDocument pcmlDoc, 
            final NodeLogger logger) {
        // Set up the validation error listener.
        @SuppressWarnings("rawtypes")
        ArrayList validationErrors = new ArrayList();
        XmlOptions validationOptions = new XmlOptions();
        validationOptions.setErrorListener(validationErrors);

        // During validation, errors are added to the ArrayList
        boolean valid = pcmlDoc.validate(validationOptions);

        // Print the errors if the XML is invalid.
        if (!valid) {
            for (Object validationError : validationErrors) {
                logger.debug(validationError);
            }
        }
        return valid;
    }
}