com.genericworkflownodes.knime.config.writer.CTDConfigurationWriter.java Source code

Java tutorial

Introduction

Here is the source code for com.genericworkflownodes.knime.config.writer.CTDConfigurationWriter.java

Source

/**
 * Copyright (c) 2012, Stephan Aiche.
 *
 * 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 com.genericworkflownodes.knime.config.writer;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.apache.commons.lang.StringEscapeUtils;

import com.genericworkflownodes.knime.cliwrapper.CLIElement;
import com.genericworkflownodes.knime.cliwrapper.CLIMapping;
import com.genericworkflownodes.knime.config.INodeConfiguration;
import com.genericworkflownodes.knime.config.reader.handler.ParamHandler;
import com.genericworkflownodes.knime.parameter.BoolParameter;
import com.genericworkflownodes.knime.parameter.DoubleListParameter;
import com.genericworkflownodes.knime.parameter.DoubleParameter;
import com.genericworkflownodes.knime.parameter.FileListParameter;
import com.genericworkflownodes.knime.parameter.FileParameter;
import com.genericworkflownodes.knime.parameter.IFileParameter;
import com.genericworkflownodes.knime.parameter.IntegerListParameter;
import com.genericworkflownodes.knime.parameter.IntegerParameter;
import com.genericworkflownodes.knime.parameter.ListParameter;
import com.genericworkflownodes.knime.parameter.Parameter;
import com.genericworkflownodes.knime.parameter.StringChoiceParameter;
import com.genericworkflownodes.knime.parameter.StringListParameter;
import com.genericworkflownodes.knime.parameter.StringParameter;
import com.genericworkflownodes.knime.port.Port;
import com.genericworkflownodes.knime.relocator.Relocator;
import com.genericworkflownodes.util.StringUtils;

/**
 * @author aiche
 */
public class CTDConfigurationWriter {

    private static final String REMOVE_TRAILING_DOT_RE = "\\.$";
    private static final String REMOVE_TRAILING_0_RE = "0*$";
    private BufferedWriter outputWriter;
    private int currentIndent;
    private List<String> currentNodeState;
    private INodeConfiguration currentConfig;

    private String xmlEscapeText(String t) {
        return StringEscapeUtils.escapeXml(t);
    }

    /**
     * C'tor for CTDConfigurationWriter.
     * 
     * @param target
     *            The file where the ctd will be stored.
     * @throws IOException
     *             If io operations fail.
     */
    public CTDConfigurationWriter(File target) throws IOException {
        outputWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(target), "UTF-8"));
        currentIndent = 0;
        currentNodeState = null;
    }

    /**
     * Construct writer directly using a {@link BufferedWriter}.
     * 
     * @param out
     *            The {@link BufferedWriter} where the content will be written.
     */
    public CTDConfigurationWriter(BufferedWriter out) {
        outputWriter = out;
        currentIndent = 0;
        currentNodeState = null;
    }

    /**
     * Adds the opening tags to the document.
     * 
     * @throws IOException
     */
    private void openCTDDocument() throws IOException {
        streamPut("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        streamPut(String.format("<tool name=\"%s\" version=\"%s\" docurl=\"%s\" category=\"%s\">",
                xmlEscapeText(currentConfig.getName()), xmlEscapeText(currentConfig.getVersion()),
                xmlEscapeText(currentConfig.getDocUrl()), xmlEscapeText(currentConfig.getCategory())));
        indent();
    }

    /**
     * Adds the closing tag to the written document.
     * 
     * @throws IOException
     */
    private void closeCTDDocument() throws IOException {
        outdent();
        streamPut("</tool>");
    }

    private void streamPut(String text) throws IOException {
        for (int i = 0; i < currentIndent; ++i) {
            outputWriter.write('\t');
        }

        outputWriter.write(text);
        outputWriter.write('\n');
    }

    /**
     * 
     * @param config
     */
    private void writeHeader() throws IOException {
        streamPut(String.format("<description>%s</description>", xmlEscapeText(currentConfig.getDescription()))); // description
        streamPut(String.format("<manual>%s</manual>", xmlEscapeText(currentConfig.getManual()))); // manual

        if (currentConfig.getExecutableName() != null) {
            streamPut(String.format("<executableName>%s</executableName>",
                    xmlEscapeText(currentConfig.getExecutableName())));
        }

        if (currentConfig.getExecutablePath() != null) {
            streamPut(String.format("<executablePath>%s</executablePath>",
                    xmlEscapeText(currentConfig.getExecutablePath())));
        }

        if (currentConfig.getCLI() != null && currentConfig.getCLI().getCLIElement().size() > 0) {
            writeCLI();
        }

        if (currentConfig.getRelocators().size() != 0) {
            writeRelocators();
        }
    }

    private void writeRelocators() throws IOException {
        streamPut("<relocators>");
        indent();

        for (Relocator rel : currentConfig.getRelocators()) {
            streamPut(String.format("<relocator reference=\"%s\" locations=\"%s\" />", rel.getReferencedParamter(),
                    rel.getLocation()));
        }

        outdent();
        streamPut("</relocators>");
    }

    private void writeCLI() throws IOException {
        streamPut("<cli>");
        indent();

        for (CLIElement elem : currentConfig.getCLI().getCLIElement()) {
            streamPut(String.format("<clielement optionIdentifier=\"%s\" isList=\"%s\">",
                    elem.getOptionIdentifier(), (elem.isList() ? "true" : "false")));

            indent();
            for (CLIMapping mapping : elem.getMapping()) {
                streamPut(String.format("<mapping referenceName=\"%s\" />", mapping.getReferenceName()));
            }
            outdent();

            streamPut("</clielement>");
        }

        outdent();
        streamPut("</cli>");
    }

    private void outdent() {
        --currentIndent;
    }

    private void indent() {
        ++currentIndent;
    }

    private List<String> getPrefix(String paramKey) {
        String[] keys = paramKey.split("\\.");
        assert keys.length > 1;
        List<String> prefixes = new ArrayList<String>();
        for (int i = 0; i < (keys.length - 1); ++i) {
            prefixes.add(keys[i]);
        }

        return prefixes;
    }

    private void writeParamXML() throws IOException {

        currentNodeState = new ArrayList<String>();

        streamPut("<PARAMETERS version=\"1.6.2\" "
                + "xsi:noNamespaceSchemaLocation=\"http://open-ms.sourceforge.net/schemas/Param_1_6_2.xsd\" "
                + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">");
        indent();

        for (String key : currentConfig.getParameterKeys()) {
            List<String> prefix = getPrefix(key);
            updateNodes(prefix);

            // output the actual parameter
            Parameter<?> p = currentConfig.getParameter(key);
            // handle depending on list or not
            if (p instanceof ListParameter) {
                writeItemList(key, p);
            } else {
                writeItem(key, p);
            }
        }

        closeRemainingNodes();

        outdent();
        streamPut("</PARAMETERS>");
    }

    private void writeItem(String key, Parameter<?> p) throws IOException {
        // construct parameter entry
        StringBuffer item = new StringBuffer();
        item.append("<ITEM name=\"");
        item.append(p.getKey());
        item.append('\"');

        // value
        item.append(" value=\"");
        if (p.getValue() != null && p.getValue().toString() != null) {
            item.append(xmlEscapeText(p.getValue().toString()));
        }
        item.append('\"');

        // type
        addItemType(key, p, item);

        // description
        addDescription(p, item);

        // add tags
        addParameterAttributes(p, item);

        // restrictions
        if (p instanceof BoolParameter) {
            addBoolRestrictions(item);
        } else if (p instanceof DoubleParameter || p instanceof IntegerParameter) {
            addNumberRestrictions(item, p);
        } else if (p instanceof StringChoiceParameter) {
            addStringChoices(item, p);
        } else if (p instanceof FileParameter) {
            addMimeTypeRestrictions(item, p);
        }

        item.append(" />");
        streamPut(item.toString());
    }

    private void writeItemList(String key, Parameter<?> p) throws IOException {
        StringBuffer item = new StringBuffer();
        item.append("<ITEMLIST name=\"");
        item.append(p.getKey());
        item.append('\"');

        // type
        addListType(key, p, item);

        // description
        addDescription(p, item);

        // add restrictions
        if (p instanceof DoubleListParameter || p instanceof IntegerListParameter) {
            addNumberRestrictions(item, p);
        } else if (p instanceof FileListParameter) {
            addMimeTypeRestrictions(item, p);
        }

        // add tags
        addParameterAttributes(p, item);

        item.append('>');
        streamPut(item.toString());

        indent();
        for (String val : ((ListParameter) p).getStrings()) {
            streamPut(String.format("<LISTITEM value=\"%s\"/>", xmlEscapeText(val)));
        }
        outdent();
        streamPut("</ITEMLIST>");
    }

    private void addItemType(String key, Parameter<?> p, StringBuffer item) {
        item.append(" type=\"");
        if (p instanceof FileParameter) {
            addFileType(key, item);
        } else if (p instanceof BoolParameter || p instanceof StringParameter
                || p instanceof StringChoiceParameter) {
            item.append("string");
        } else if (p instanceof DoubleParameter) {
            item.append("double");
        } else if (p instanceof IntegerParameter) {
            item.append("int");
        }
        item.append('\"');
    }

    private void addFileType(String key, StringBuffer item) {
        if (currentConfig.getInputPortByName(key) != null) {
            if (currentConfig.getInputPortByName(key).isPrefix()) {
                item.append("input-prefix");
            } else {
                item.append("input-file");
            }
        } else if (currentConfig.getOutputPortByName(key) != null) {
            if (currentConfig.getOutputPortByName(key).isPrefix()) {
                item.append("output-prefix");
            } else {
                item.append("output-file");
            }
        }
    }

    private void addListType(String key, Parameter<?> p, StringBuffer item) {
        item.append(" type=\"");
        if (p instanceof FileListParameter) {
            addFileType(key, item);
        } else if (p instanceof StringListParameter) {
            item.append("string");
        } else if (p instanceof DoubleListParameter) {
            item.append("double");
        } else if (p instanceof IntegerListParameter) {
            item.append("int");
        }
        item.append('\"');
    }

    private void addMimeTypeRestrictions(StringBuffer item, Parameter<?> p) {
        Port associatedPort = ((IFileParameter) p).getPort();

        item.append(" supported_formats=\"");
        String sep = "";
        for (String mt : associatedPort.getMimeTypes()) {
            item.append(sep);
            sep = ",";
            item.append(String.format("*.%s", mt));
        }
        item.append('\"');
    }

    private void addParameterAttributes(Parameter<?> p, StringBuffer item) {
        item.append(" advanced=\"" + (p.isAdvanced() ? "true" : "false") + "\"");
        item.append(" required=\"" + (p.isOptional() ? "false" : "true") + "\"");
    }

    private void addDescription(Parameter<?> p, StringBuffer item) {
        item.append(" description=\"");
        if (p.getDescription() != null) {
            item.append(xmlEscapeText(p.getDescription()));
        }
        item.append('\"');
    }

    private void addStringChoices(StringBuffer item, Parameter<?> p) {
        StringChoiceParameter scp = (StringChoiceParameter) p;
        // the list of allowed values will contain the empty string depending on
        // whether the parameter is optional or not
        List<String> values = scp.getAllowedValues();
        item.append(" restrictions=\"");
        String sep = "";
        for (String rv : values) {
            if (scp.isOptional() && "".equals(rv)) {
                continue;
            }
            item.append(sep);
            item.append(rv);
            sep = ",";
        }
        item.append('\"');
    }

    private void addNumberRestrictions(StringBuffer item, Parameter<?> p) {
        StringBuffer restriction = new StringBuffer();
        if (p instanceof DoubleParameter) {
            addDoubleParameterRestrictions(p, restriction);
        } else if (p instanceof IntegerParameter) {
            addIntegerParameterRestrictions(p, restriction);
        } else if (p instanceof IntegerListParameter) {
            addIntegerListParameterRestrictions(p, restriction);
        } else if (p instanceof DoubleListParameter) {
            addDoubleListParameterRestrictions(p, restriction);
        }
        if (!"".equals(restriction.toString())) {
            item.append(" restrictions=\"");
            item.append(restriction.toString());
            item.append('\"');
        }
    }

    private void addDoubleListParameterRestrictions(Parameter<?> p, StringBuffer restriction) {
        DoubleListParameter dlp = (DoubleListParameter) p;
        boolean lbSet = Double.NEGATIVE_INFINITY != dlp.getLowerBound().doubleValue();
        boolean ubSet = Double.POSITIVE_INFINITY != dlp.getUpperBound().doubleValue();
        if (lbSet) {
            restriction.append(String.format(Locale.ENGLISH, "%f", dlp.getLowerBound())
                    .replaceAll(REMOVE_TRAILING_0_RE, "").replaceAll(REMOVE_TRAILING_DOT_RE, ""));
        }
        if (ubSet || lbSet) {
            restriction.append(':');
        }
        if (ubSet) {
            restriction.append(String.format(Locale.ENGLISH, "%f", dlp.getUpperBound())
                    .replaceAll(REMOVE_TRAILING_0_RE, "").replaceAll(REMOVE_TRAILING_DOT_RE, ""));
        }
    }

    private void addIntegerListParameterRestrictions(Parameter<?> p, StringBuffer restriction) {
        IntegerListParameter ilp = (IntegerListParameter) p;
        boolean lbSet = Integer.MIN_VALUE != ilp.getLowerBound().doubleValue();
        boolean ubSet = Integer.MAX_VALUE != ilp.getUpperBound().doubleValue();

        if (lbSet) {
            restriction.append(String.format("%d", ilp.getLowerBound()));
        }
        if (ubSet || lbSet) {
            restriction.append(':');
        }
        if (ubSet) {
            restriction.append(String.format("%d", ilp.getUpperBound()));
        }
    }

    private void addIntegerParameterRestrictions(Parameter<?> p, StringBuffer restriction) {
        IntegerParameter ip = (IntegerParameter) p;
        boolean lbSet = Integer.MIN_VALUE != ip.getLowerBound().doubleValue();
        boolean ubSet = Integer.MAX_VALUE != ip.getUpperBound().doubleValue();
        if (lbSet) {
            restriction.append(String.format("%d", ip.getLowerBound()));
        }
        if (ubSet || lbSet) {
            restriction.append(':');
        }
        if (ubSet) {
            restriction.append(String.format("%d", ip.getUpperBound()));
        }
    }

    private void addDoubleParameterRestrictions(Parameter<?> p, StringBuffer restriction) {
        DoubleParameter dp = (DoubleParameter) p;
        boolean lbSet = Double.NEGATIVE_INFINITY != dp.getLowerBound().doubleValue();
        boolean ubSet = Double.POSITIVE_INFINITY != dp.getUpperBound().doubleValue();
        if (lbSet) {
            restriction.append(String.format(Locale.ENGLISH, "%f", dp.getLowerBound())
                    .replaceAll(REMOVE_TRAILING_0_RE, "").replaceAll(REMOVE_TRAILING_DOT_RE, ""));
        }
        if (ubSet || lbSet) {
            restriction.append(':');
        }
        if (ubSet) {
            restriction.append(String.format(Locale.ENGLISH, "%f", dp.getUpperBound())
                    .replaceAll(REMOVE_TRAILING_0_RE, "").replaceAll(REMOVE_TRAILING_DOT_RE, ""));
        }
    }

    private void addBoolRestrictions(StringBuffer item) {
        item.append(" restrictions=\"true,false\"");
    }

    private void closeRemainingNodes() throws IOException {
        // close remaining prefixes
        for (int i = 0; i < currentNodeState.size(); ++i) {
            outdent();
            streamPut("</NODE>");
        }
        // clean up
        currentNodeState = null;
    }

    private void updateNodes(List<String> prefix) throws IOException {
        // find matching prefix
        int commonPrefix = 0;
        int smallerListSize = Math.min(prefix.size(), currentNodeState.size());
        for (; commonPrefix < smallerListSize; ++commonPrefix) {
            if (!prefix.get(commonPrefix).equals(currentNodeState.get(commonPrefix))) {
                break;
            }
        }

        for (int i = commonPrefix; i < currentNodeState.size(); ++i) {
            outdent();
            streamPut("</NODE>");
        }
        for (int i = commonPrefix; i < prefix.size(); ++i) {
            String description = currentConfig.getSectionDescription(concatenate(prefix, i));
            if (description == null) {
                description = "";
            }

            streamPut(String.format("<NODE name=\"%s\" description=\"%s\">", prefix.get(i),
                    xmlEscapeText(description)));
            indent();
        }
        currentNodeState = prefix;
    }

    private String concatenate(List<String> prefix, int end) {
        return StringUtils.join(prefix.subList(0, ((end + 1) < prefix.size() ? end + 1 : prefix.size())),
                String.valueOf(ParamHandler.PATH_SEPARATOR));
    }

    /**
     * Writes the given {@link INodeConfiguration} to the stream.
     * 
     * @param config
     *            The config that should be written to the stream.
     * @throws IOException
     *             If io operations fail.
     */
    public void write(INodeConfiguration config) throws IOException {
        currentIndent = 0;
        currentConfig = config;

        // prepare the document
        openCTDDocument();

        // write the CTD-header information
        writeHeader();

        // add the param-xml part to the file
        writeParamXML();

        closeCTDDocument();
        outputWriter.close();
    }

}