org.ballproject.knime.base.config.CTDFileNodeConfigurationReader.java Source code

Java tutorial

Introduction

Here is the source code for org.ballproject.knime.base.config.CTDFileNodeConfigurationReader.java

Source

/*
 * Copyright (c) 2011, Marc Rttig.
 *
 * This file is part of GenericKnimeNodes.
 * 
 * GenericKnimeNodes is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.ballproject.knime.base.config;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;

import org.ballproject.knime.base.mime.MIMEtype;
import org.ballproject.knime.base.parameter.BoolParameter;
import org.ballproject.knime.base.parameter.DoubleListParameter;
import org.ballproject.knime.base.parameter.DoubleParameter;
import org.ballproject.knime.base.parameter.FileListParameter;
import org.ballproject.knime.base.parameter.IntegerListParameter;
import org.ballproject.knime.base.parameter.IntegerParameter;
import org.ballproject.knime.base.parameter.Parameter;
import org.ballproject.knime.base.parameter.StringChoiceParameter;
import org.ballproject.knime.base.parameter.StringListParameter;
import org.ballproject.knime.base.parameter.StringParameter;
import org.ballproject.knime.base.port.Port;
import org.ballproject.knime.base.schemas.SchemaProvider;
import org.ballproject.knime.base.schemas.SimpleErrorHandler;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

public class CTDFileNodeConfigurationReader implements INodeConfigurationReader {

    @SuppressWarnings("unused")
    private static Logger log = Logger.getLogger(CTDFileNodeConfigurationReader.class.getCanonicalName());

    private Document doc;
    private NodeConfiguration config = new NodeConfiguration();

    public CTDFileNodeConfigurationReader() {
    }

    protected String SECTION_NODE_NAME = "NODE";
    protected String INPUTFILE_TAG = "input file";
    protected String OUTPUTFILE_TAG = "output file";

    protected Set<String> captured_ports = new HashSet<String>();

    private static List<Port> in_ports;
    private static List<Port> out_ports;

    private void readPorts() throws Exception {
        in_ports = new ArrayList<Port>();
        out_ports = new ArrayList<Port>();

        Node node = this.doc.selectSingleNode("/tool/PARAMETERS");
        Element root = (Element) node;
        this.processIOPorts(root);

        this.config.setInports(in_ports.toArray(new Port[in_ports.size()]));
        this.config.setOutports(out_ports.toArray(new Port[out_ports.size()]));
    }

    @SuppressWarnings("unchecked")
    public void processIOPorts(Element root) throws Exception {
        List<Node> items = root.selectNodes("//ITEM[contains(@tags,'" + this.OUTPUTFILE_TAG + "')]");
        for (Node n : items) {
            this.createPortFromNode(n, false);
        }
        items = root.selectNodes("//ITEM[contains(@tags,'" + this.INPUTFILE_TAG + "')]");
        for (Node n : items) {
            this.createPortFromNode(n, false);
        }
        items = root.selectNodes("//ITEMLIST[contains(@tags,'" + this.INPUTFILE_TAG + "')]");
        for (Node n : items) {
            this.createPortFromNode(n, true);
        }
        items = root.selectNodes("//ITEMLIST[contains(@tags,'" + this.OUTPUTFILE_TAG + "')]");
        for (Node n : items) {
            this.createPortFromNode(n, true);
        }
    }

    @SuppressWarnings("unchecked")
    public void readParameters() throws Exception {
        Node root = this.doc.selectSingleNode("/tool/PARAMETERS");
        List<Node> items = root.selectNodes("//ITEM[not(contains(@tags,'" + this.OUTPUTFILE_TAG
                + "')) and not(contains(@tags,'" + this.INPUTFILE_TAG + "'))]");
        for (Node n : items) {
            this.processItem(n);
        }
        items = root.selectNodes("//ITEMLIST[not(contains(@tags,'" + this.OUTPUTFILE_TAG
                + "')) and not(contains(@tags,'" + this.INPUTFILE_TAG + "'))]");
        for (Node n : items) {
            this.processMultiItem(n);
        }
    }

    public String getPath(Node n) {

        List<String> path_nodes = new ArrayList<String>();
        while (n != null && !n.getName().equals("PARAMETERS")) {
            path_nodes.add(n.valueOf("@name"));
            n = n.getParent();
        }

        Collections.reverse(path_nodes);

        String ret = "";
        int N = path_nodes.size();
        for (int i = 0; i < N; i++) {
            if (i == N - 1) {
                ret += path_nodes.get(i);
            } else {
                ret += path_nodes.get(i) + ".";
            }
        }
        return ret;
    }

    private void createPortFromNode(Node node, boolean multi) throws Exception {

        Element elem = (Element) node;

        String name = node.valueOf("@name");
        String descr = node.valueOf("@description");
        String tags = node.valueOf("@tags");

        if (name.equals("write_ini") || name.equals("write_par") || name.equals("par") || name.equals("help"))
            return;

        Port port = new Port();

        port.setMultiFile(multi);

        if (tags.contains(this.INPUTFILE_TAG) || tags.contains(this.OUTPUTFILE_TAG)) {
            String[] file_extensions = null;

            if (elem.attributeValue("supported_formats") == null) {
                if (elem.attributeValue("restrictions") != null) {
                    String formats = node.valueOf("@restrictions");
                    file_extensions = formats.split(",");
                    for (int i = 0; i < file_extensions.length; i++) {
                        file_extensions[i] = file_extensions[i].replace("*.", "");
                    }
                } else
                    throw new Exception("i/o item '" + elem.attributeValue("name")
                            + "' with missing attribute supported_formats detected");
            } else {
                String formats = node.valueOf("@supported_formats");
                file_extensions = formats.split(",");
                for (int i = 0; i < file_extensions.length; i++) {
                    file_extensions[i] = file_extensions[i].trim();
                }
            }

            String path = this.getPath(node);
            port.setName(path);

            port.setDescription(descr);

            boolean optional = true;
            if (tags.contains("mandatory") || tags.contains("required")) {
                optional = false;
            } else {
                optional = true;
            }
            port.setOptional(optional);

            for (String mt : file_extensions) {
                port.addMimeType(new MIMEtype(mt.trim()));
            }

        }
        if (tags.contains(this.OUTPUTFILE_TAG)) {
            out_ports.add(port);
            this.captured_ports.add(port.getName());

            if (multi) {
                String path = this.getPath(node);
                FileListParameter param = new FileListParameter(name, new ArrayList<String>());
                param.setPort(port);
                param.setDescription(descr);
                param.setIsOptional(false);
                this.config.addParameter(path, param);
            }
        }
        if (tags.contains(this.INPUTFILE_TAG)) {
            in_ports.add(port);
            this.captured_ports.add(port.getName());
        }

    }

    public void processItem(Node elem) throws Exception {
        String name = elem.valueOf("@name");

        String path = this.getPath(elem);

        if (this.captured_ports.contains(path))
            return;

        if (name.equals("write_ini") || name.equals("write_par") || name.equals("par") || name.equals("help"))
            return;

        Parameter<?> param = this.getParameterFromNode(elem);
        this.config.addParameter(path, param);
    }

    public void processMultiItem(Node elem) throws Exception {
        String name = elem.valueOf("@name");

        String path = this.getPath(elem);

        if (this.captured_ports.contains(path))
            return;

        if (name.equals("write_ini") || name.equals("write_par") || name.equals("par") || name.equals("help"))
            return;

        Parameter<?> param = this.getMultiParameterFromNode(elem);
        this.config.addParameter(path, param);
    }

    private void readDescription() throws Exception {
        Node node = this.doc.selectSingleNode("/tool");
        if (node == null)
            throw new Exception("CTD has no root named tool");

        String lstatus = node.valueOf("@status");
        if (lstatus != null && lstatus.equals(""))
            throw new Exception("CTD has no status");

        this.config.setStatus(lstatus);

        node = this.doc.selectSingleNode("/tool/name");
        if (node == null)
            throw new Exception("CTD has no tool name");
        String name = node.valueOf("text()");
        if (name.equals(""))
            throw new Exception("CTD has no tool name");
        this.config.setName(name);

        node = this.doc.selectSingleNode("/tool/description");
        String sdescr = "";
        if (node != null) {
            sdescr = node.valueOf("text()");
        }
        this.config.setDescription(sdescr);

        node = this.doc.selectSingleNode("/tool/path");
        String spath = "";
        if (node != null) {
            spath = node.valueOf("text()");
        }

        this.config.setCommand(spath);

        node = this.doc.selectSingleNode("/tool/manual");
        String ldescr = "";
        if (node != null) {
            ldescr = node.valueOf("text()");
        }
        this.config.setManual(ldescr);

        node = this.doc.selectSingleNode("/tool/version");
        String lversion = "";
        if (node != null) {
            lversion = node.valueOf("text()");
        }
        this.config.setVersion(lversion);

        node = this.doc.selectSingleNode("/tool/docurl");
        String docurl = "";
        if (node != null) {
            docurl = node.valueOf("text()");
        }
        this.config.setDocUrl(docurl);

        node = this.doc.selectSingleNode("/tool/category");
        String cat = "";
        if (node != null) {
            cat = node.valueOf("text()");
        }
        this.config.setCategory(cat);
    }

    private Parameter<?> getParameterFromNode(Node node) throws Exception {
        Parameter<?> ret = null;
        String type = node.valueOf("@type");
        String name = node.valueOf("@name");
        String value = node.valueOf("@value");
        String restrs = node.valueOf("@restrictions");
        String descr = node.valueOf("@description");
        String tags = node.valueOf("@tags");

        if (type.toLowerCase().equals("double") || type.toLowerCase().equals("float")) {
            ret = this.processDoubleParameter(name, value, restrs, tags);
        } else {
            if (type.toLowerCase().equals("int")) {
                ret = this.processIntParameter(name, value, restrs, tags);
            } else {
                if (type.toLowerCase().equals("string")) {
                    ret = this.processStringParameter(name, value, restrs, tags);
                }
            }
        }

        ret.setDescription(descr);
        Set<String> tagset = tokenSet(tags);

        if (tagset.contains("mandatory") || tagset.contains("required")) {
            ret.setIsOptional(false);
        }

        if (tagset.contains("advanced")) {
            ret.setAdvanced(true);
        }

        return ret;
    }

    private Parameter<?> getMultiParameterFromNode(Node node) throws Exception {
        String type = node.valueOf("@type");
        String name = node.valueOf("@name");
        String restrs = node.valueOf("@restrictions");
        String descr = node.valueOf("@description");
        String tags = node.valueOf("@tags");

        Set<String> tagset = tokenSet(tags);

        @SuppressWarnings("unchecked")
        List<Node> subnodes = node.selectNodes("LISTITEM");

        List<String> values = new ArrayList<String>();
        for (Node n : subnodes) {
            values.add(n.valueOf("@value"));
        }

        Parameter<?> param = null;

        if (type.toLowerCase().equals("double") || type.toLowerCase().equals("float")) {
            param = this.processDoubleListParameter(name, values, restrs, tags);
        } else {
            if (type.toLowerCase().equals("int")) {
                param = this.processIntListParameter(name, values, restrs, tags);
            } else {
                if (type.toLowerCase().equals("string")) {
                    param = this.processStringListParameter(name, values, restrs, tags);
                }
            }
        }

        param.setDescription(descr);

        if (tagset.contains("mandatory") || tagset.contains("required")) {
            param.setIsOptional(false);
        }

        if (tagset.contains("advanced")) {
            param.setAdvanced(true);
        }

        return param;
    }

    private Parameter<?> processStringListParameter(String name, List<String> values, String restrs, String tags) {
        return new StringListParameter(name, values);
    }

    private Parameter<?> processIntListParameter(String name, List<String> values, String restrs, String tags) {
        List<Integer> vals = new ArrayList<Integer>();
        for (String cur_val : values) {
            vals.add(Integer.parseInt(cur_val));
        }

        IntegerListParameter ret = new IntegerListParameter(name, vals);

        Integer[] bounds = new Integer[2];
        this.getIntegerBoundsFromRestrictions(restrs, bounds);
        ret.setLowerBound(bounds[0]);
        ret.setUpperBound(bounds[1]);

        return ret;
    }

    private Parameter<?> processDoubleListParameter(String name, List<String> values, String restrs, String tags) {
        List<Double> vals = new ArrayList<Double>();
        for (String cur_val : values) {
            vals.add(Double.parseDouble(cur_val));
        }

        DoubleListParameter ret = new DoubleListParameter(name, vals);

        Double[] bounds = new Double[2];
        this.getDoubleBoundsFromRestrictions(restrs, bounds);
        ret.setLowerBound(bounds[0]);
        ret.setUpperBound(bounds[1]);

        return ret;
    }

    private void getDoubleBoundsFromRestrictions(String restrs, Double[] bounds) {
        Double UB = Double.POSITIVE_INFINITY;
        Double LB = Double.NEGATIVE_INFINITY;

        if (restrs.equals("")) {
            bounds[0] = LB;
            bounds[1] = UB;
            return;
        }

        String[] toks = restrs.split(":");
        if (toks.length != 0) {
            if (toks[0].equals("")) {
                // upper bounded only
                double ub;
                try {
                    ub = Double.parseDouble(toks[1]);
                } catch (NumberFormatException e) {
                    throw new RuntimeException(e);
                }
                UB = ub;
            } else {
                // lower and upper bounded
                if (toks.length == 2) {
                    double lb;
                    double ub;
                    try {
                        lb = Double.parseDouble(toks[0]);
                        ub = Double.parseDouble(toks[1]);
                    } catch (NumberFormatException e) {
                        throw new RuntimeException(e);
                    }
                    LB = lb;
                    UB = ub;
                } else {
                    // lower bounded only
                    double lb;
                    try {
                        lb = Double.parseDouble(toks[0]);
                    } catch (NumberFormatException e) {
                        throw new RuntimeException(e);
                    }
                    LB = lb;
                }
            }
        }
        bounds[0] = LB;
        bounds[1] = UB;
    }

    private Parameter<?> processDoubleParameter(String name, String value, String restrs, String tags)
            throws Exception {
        DoubleParameter retd = new DoubleParameter(name, value);
        Double[] bounds = new Double[2];
        this.getDoubleBoundsFromRestrictions(restrs, bounds);
        retd.setLowerBound(bounds[0]);
        retd.setUpperBound(bounds[1]);
        return retd;
    }

    private static Set<String> tokenSet(String s) {
        Set<String> ret = new HashSet<String>();
        String[] toks = s.split(",");
        for (String tok : toks) {
            ret.add(tok);
        }
        return ret;
    }

    private void getIntegerBoundsFromRestrictions(String restrs, Integer[] bounds) {
        Integer UB = Integer.MAX_VALUE;
        Integer LB = Integer.MIN_VALUE;

        if (restrs.equals("")) {
            bounds[0] = LB;
            bounds[1] = UB;
            return;
        }

        String[] toks = restrs.split(":");
        if (toks.length != 0) {
            if (toks[0].equals("")) {
                // upper bounded only
                int ub;
                try {
                    ub = Integer.parseInt(toks[1]);
                } catch (NumberFormatException e) {
                    throw new RuntimeException(e);
                }
                UB = ub;
            } else {
                // lower and upper bounded
                if (toks.length == 2) {
                    int lb;
                    int ub;
                    try {
                        lb = Integer.parseInt(toks[0]);
                        ub = Integer.parseInt(toks[1]);
                    } catch (NumberFormatException e) {
                        throw new RuntimeException(e);
                    }
                    LB = lb;
                    UB = ub;
                } else {
                    // lower bounded only
                    int lb;
                    try {
                        lb = Integer.parseInt(toks[0]);
                    } catch (NumberFormatException e) {
                        throw new RuntimeException(e);
                    }
                    LB = lb;
                }
            }
        }
        bounds[0] = LB;
        bounds[1] = UB;
    }

    private Parameter<?> processIntParameter(String name, String value, String restrs, String tags)
            throws Exception {
        IntegerParameter reti = new IntegerParameter(name, value);
        Integer[] bounds = new Integer[2];
        this.getIntegerBoundsFromRestrictions(restrs, bounds);
        reti.setLowerBound(bounds[0]);
        reti.setUpperBound(bounds[1]);
        return reti;
    }

    private Parameter<?> processStringParameter(String name, String value, String restrs, String tags)
            throws Exception {
        Parameter<?> rets = null;

        String[] toks = restrs.split(",");

        for (int i = 0; i < toks.length; i++) {
            toks[i] = toks[i].trim();
        }

        if (restrs.length() > 0) {
            if ((toks[0].equals("true") && toks[1].equals("false"))
                    || (toks[0].equals("false") && toks[1].equals("true"))) {
                rets = new BoolParameter(name, value);
            } else {
                rets = new StringChoiceParameter(name, toks);
                ((StringChoiceParameter) rets).setValue(value);
            }
        } else {
            rets = new StringParameter(name, value);
        }

        return rets;
    }

    @Override
    public INodeConfiguration read(InputStream xmlstream) throws CTDNodeConfigurationReaderException {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");

            factory.setSchema(schemaFactory
                    .newSchema(new Source[] { new StreamSource(SchemaProvider.class.getResourceAsStream("CTD.xsd")),
                            new StreamSource(SchemaProvider.class.getResourceAsStream("Param_1_3.xsd")) }));

            SAXParser parser = factory.newSAXParser();

            SAXReader reader = new SAXReader(parser.getXMLReader());
            reader.setValidation(false);

            SimpleErrorHandler errorHandler = new SimpleErrorHandler();

            reader.setErrorHandler(errorHandler);

            this.doc = reader.read(xmlstream);

            if (!errorHandler.isValid()) {
                System.err.println(errorHandler.getErrorReport());
                throw new Exception("CTD file is not valid !");
            }

            this.readPorts();
            this.readParameters();
            this.readDescription();
            this.readMapping();
            this.config.setXml(this.doc.asXML());

            return this.config;
        } catch (Exception e) {
            throw new CTDNodeConfigurationReaderException(e);
        }
    }

    private void readMapping() throws Exception {
        Node node = this.doc.selectSingleNode("/tool/mapping");
        if (node == null && this.config.getStatus().equals("external"))
            throw new Exception("CTD has no mapping tag and is an external tool");
        if (node == null)
            return;

        String mapping = node.valueOf("text()");
        if (mapping.equals(""))
            throw new Exception("CTD has an empty mapping tag");
        this.config.setMapping(mapping);
    }
}