views.MetadataUploadView.java Source code

Java tutorial

Introduction

Here is the source code for views.MetadataUploadView.java

Source

/*******************************************************************************
 * QBiC Project Wizard enables users to create hierarchical experiments including different study
 * conditions using factorial design. Copyright (C) "2016" Andreas Friedrich
 *
 * 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/>.
 *******************************************************************************/
package views;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.JAXBException;

import org.apache.commons.lang3.StringUtils;

import parser.XMLParser;
import properties.Property;
import uicomponents.UploadComponent;

import logging.Log4j2Logger;
import main.ProjectwizardUI;
import uicomponents.Styles;
import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.PropertyType;
import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
import control.Functions;
import io.DBVocabularies;
import life.qbic.openbis.openbisclient.IOpenBisClient;
import uicomponents.Styles.*;

import com.opencsv.CSVParser;
import com.opencsv.CSVParserBuilder;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.vaadin.data.Item;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.validator.RegexpValidator;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.combobox.FilteringMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.Label;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TabSheet.SelectedTabChangeEvent;
import com.vaadin.ui.TabSheet.SelectedTabChangeListener;
import com.vaadin.ui.Table;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Upload.FinishedListener;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Upload.FinishedEvent;

// import au.com.bytecode.opencsv.CSVReader;

public class MetadataUploadView extends VerticalLayout {

    private OptionGroup typeOfData = new OptionGroup("Type of Metadata",
            new ArrayList<String>(Arrays.asList("Samples")));

    private Label info;
    // ConditionsPanel pane;
    // Button dlTemplate;
    private TabSheet sheet;
    private UploadComponent upload;
    private Button send;

    private XMLParser xmlParser = new XMLParser();
    private IOpenBisClient openbis;
    private Map<String, Object> metadata;
    private List<String> customProperties = new ArrayList<String>(
            Arrays.asList("IGNORE (removes column)", "[Experimental Condition]", "[Other Property]"));
    private Map<String, String> propNameToCode;
    private Map<String, Map<String, String>> propToVocabulary;
    private Map<String, Map<String, String>> propToReverseVocabulary;
    private Set<String> allowedSpaces;

    private logging.Logger logger = new Log4j2Logger(MetadataUploadView.class);
    private List<Table> sampleTables;
    private Button reload;

    private Map<String, Sample> codesToSamples;
    private String barcodeColName;

    private List<String> collisions;
    private List<String> codesInTSV;

    private boolean overWriteAllowed = false;

    public MetadataUploadView(IOpenBisClient openbis, DBVocabularies vocabularies, boolean overWriteAllowed) {
        allowedSpaces = new HashSet<String>(vocabularies.getSpaces());
        this.overWriteAllowed = overWriteAllowed;
        sheet = new TabSheet();
        sampleTables = new ArrayList<Table>();

        Map<String, String> taxMap = vocabularies.getTaxMap();
        Map<String, String> tissueMap = vocabularies.getTissueMap();

        propToVocabulary = new HashMap<String, Map<String, String>>();
        propToVocabulary.put("Q_NCBI_ORGANISM", taxMap);
        propToVocabulary.put("Q_PRIMARY_TISSUE", tissueMap);

        Map<String, String> reverseTaxMap = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : taxMap.entrySet()) {
            reverseTaxMap.put(entry.getValue(), entry.getKey());
        }
        Map<String, String> reverseTissueMap = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : tissueMap.entrySet()) {
            reverseTissueMap.put(entry.getValue(), entry.getKey());
        }
        propToReverseVocabulary = new HashMap<String, Map<String, String>>();
        propToReverseVocabulary.put("Q_NCBI_ORGANISM", reverseTaxMap);
        propToReverseVocabulary.put("Q_PRIMARY_TISSUE", reverseTissueMap);

        this.openbis = openbis;
        setSpacing(true);
        setMargin(true);
        addComponent(typeOfData);
        upload = new UploadComponent("Upload Metadata (tab-separated)", "Upload", ProjectwizardUI.tmpFolder,
                "meta_", 200000);
        upload.setVisible(false);
        addComponent(upload);
        reload = new Button("Reset columns");
        reload.setVisible(false);
        addComponent(reload);
        reload.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                try {
                    parseTSV(upload.getFile());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        send = new Button("Send to Database");
        send.setEnabled(false);
        initListeners();
    }

    private void initListeners() {
        typeOfData.addValueChangeListener(new ValueChangeListener() {
            @Override
            public void valueChange(ValueChangeEvent event) {
                upload.setVisible(true);
            }
        });
        upload.addFinishedListener(new FinishedListener() {

            @Override
            public void uploadFinished(FinishedEvent event) {
                if (upload.wasSuccess())
                    try {
                        send.setVisible(parseTSV(upload.getFile()));
                        reload.setVisible(true);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
            }
        });
        send.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                try {
                    ingestTable();
                    Styles.notification("Done!", "Your metadata was sent to the Database.",
                            NotificationType.SUCCESS);
                    sheet.removeComponent(getActiveTable());
                } catch (Exception e) {
                    e.printStackTrace();
                    Styles.notification("Something went wrong!",
                            "Sorry, your metadata could not be registered. Please contact a delevoper.",
                            NotificationType.ERROR);
                }
            }
        });
    }

    public Table getActiveTable() {
        return (Table) sheet.getSelectedTab();
    }

    protected void ingestTable() throws IllegalArgumentException, JAXBException {
        Table sampleTable = getActiveTable();
        metadata = new HashMap<String, Object>();
        List<String> types = new ArrayList<String>();
        List<Property> conditions = new ArrayList<Property>();
        List<String> codes = new ArrayList<String>();
        for (Object col : sampleTable.getContainerPropertyIds()) {
            String attribute = getSelectedProperty(col);
            if (!attribute.equals("Properties -->")) {
                String unit = null;
                properties.PropertyType propType = null;
                if (attribute.startsWith("Condition: "))
                    propType = properties.PropertyType.Factor;
                if (attribute.startsWith("Property: "))
                    propType = properties.PropertyType.Property;
                if (propType != null) {
                    attribute = attribute.replace("Condition: ", "").replace("Property: ", "");
                    Property prop = null;
                    if (attribute.contains("[") && attribute.contains("]")) {
                        unit = parseUnit(attribute);
                        attribute = attribute.replace(" [" + unit + "]", "");
                        prop = new Property(attribute, "", properties.Unit.valueOf(unit), propType);
                    } else
                        prop = new Property(attribute, "", propType);
                    conditions.add(prop);
                    attribute = prop.toString();
                } else {
                    if (propNameToCode.containsKey(attribute))
                        attribute = propNameToCode.get(attribute);
                    types.add(attribute);
                }
                Map<String, String> curTypeMap = new HashMap<String, String>();
                for (Object row : sampleTable.getItemIds()) {
                    int id = (int) row;
                    if (id != -1) {
                        String bc = getBarcodeInRow(id);
                        String val = parseLabelCell(id, col);
                        // if (propType != null) {
                        // val = propType + ": " + val;
                        // if (unit != null)
                        // val = val + " [" + unit + "]";
                        // }
                        if (!codes.contains(bc))
                            codes.add(bc);
                        if (propToVocabulary.containsKey(attribute))
                            val = propToVocabulary.get(attribute).get(val);
                        curTypeMap.put(bc, val);
                    }
                }
                metadata.put(attribute, curTypeMap);
            }
        }
        Map<String, String> xmlPropertyMap = new HashMap<String, String>();
        for (String code : codes) {
            List<Property> newFactors = new ArrayList<Property>();
            String existingXML = codesToSamples.get(code).getProperties().get("Q_PROPERTIES");
            List<Property> oldFactors = xmlParser.getExpFactorsFromXML(existingXML);
            for (Property condition : conditions) {
                for (Property f : oldFactors) {
                    Property test = null;
                    if (f.hasUnit())
                        test = new Property(f.getLabel(), "", f.getUnit(), f.getType());
                    else
                        test = new Property(f.getLabel(), "", f.getType());
                    if (!conditions.contains(test)) {
                        newFactors.add(f);
                    }
                }
                Map<String, String> condMap = (HashMap<String, String>) metadata.get(condition.toString());
                Property prop = parseProperty(condition, condMap.get(code));
                newFactors.add(prop);
            }
            if (!newFactors.isEmpty())
                // xmlPropertyMap.put(code, xmlParser.toString(xmlParser.createXMLFromFactors(newFactors)));
                xmlPropertyMap.put(code, xmlParser.toString(xmlParser.createXMLFromProperties(newFactors)));
        }
        for (Property condition : conditions) {
            metadata.remove(condition.getLabel());
        }
        if (!xmlPropertyMap.isEmpty()) {
            types.add("Q_PROPERTIES");
            metadata.put("Q_PROPERTIES", xmlPropertyMap);
        }
        metadata.put("identifiers", codes);
        metadata.put("types", types);
        logger.info("Ingesting metadata");
        openbis.ingest("DSS1", "update-sample-metadata", metadata);
    }

    private Property parseProperty(Property propWithOutVal, String value) {
        if (propWithOutVal.hasUnit())
            return new Property(propWithOutVal.getLabel(), value, propWithOutVal.getUnit(),
                    propWithOutVal.getType());
        else
            return new Property(propWithOutVal.getLabel(), value, propWithOutVal.getType());
    }

    protected boolean parseTSV(File file) throws IOException {
        for (Table t : sampleTables) {
            t.removeAllItems();
            sheet.removeComponent(t);
        }
        removeComponent(sheet);
        addComponent(sheet);
        sheet.addSelectedTabChangeListener(new SelectedTabChangeListener() {

            @Override
            public void selectedTabChange(SelectedTabChangeEvent event) {
                reactToTableChange();
            }
        });

        sampleTables.clear();
        CSVParser parser = new CSVParserBuilder().withIgnoreQuotations(true).withSeparator('\t').build();
        CSVReader reader = new CSVReaderBuilder(new FileReader(file)).withCSVParser(parser).build();

        String error = "";
        ArrayList<String[]> data = new ArrayList<String[]>();
        String[] nextLine;
        int rowID = 0;
        while ((nextLine = reader.readNext()) != null) {
            rowID++;
            if (data.isEmpty() || nextLine.length == data.get(0).length) {
                data.add(nextLine);
            } else {
                error = "Wrong number of columns in row " + rowID
                        + " Please make sure every row fits the header row.";
                Styles.notification("Parsing Error", error, NotificationType.ERROR);
                reader.close();
                return false;
            }
        }
        reader.close();
        String[] header = data.get(0);
        data.remove(0);
        int barcodeCol = -1;
        String projectCode = "";
        for (int j = 0; j < header.length; j++) {
            String word = data.get(0)[j];
            if ((Functions.isQbicBarcode(word) || word.contains("ENTITY-")) && barcodeCol == -1) {
                barcodeCol = j;
                barcodeColName = header[barcodeCol];
                projectCode = word.substring(0, 5);
            }
        }
        if (barcodeCol == -1) {
            error = "No barcode column found. Make sure one column contains QBiC Barcodes to map your information to existing samples!";
            Styles.notification("File Incomplete", error, NotificationType.ERROR);
            return false;
        }
        if (barcodeCol != 0) {
            header[barcodeCol] = header[0];
            header[0] = barcodeColName;
            for (String[] d : data) {
                String bc = d[barcodeCol];
                d[barcodeCol] = d[0];
                d[0] = bc;
            }
            barcodeCol = 0;
        }
        List<Sample> projectSamples = openbis.getSamplesWithParentsAndChildrenOfProjectBySearchService(projectCode);
        codesToSamples = new HashMap<String, Sample>();
        Map<String, List<String>> sampleTypeToAttributes = new HashMap<String, List<String>>();
        Map<String, DataTypeCode> propertyToType = new HashMap<String, DataTypeCode>();
        for (Sample s : projectSamples) {
            String space = s.getSpaceCode();
            // don't add samples the user should not be able to see
            if (allowedSpaces.contains(space))
                codesToSamples.put(s.getCode(), s);
        }
        propNameToCode = new HashMap<String, String>();
        codesInTSV = new ArrayList<String>();
        for (int i = 0; i < data.size(); i++) {
            String bc = data.get(i)[barcodeCol];
            if (!codesToSamples.containsKey(bc)) {
                // if samples don't exist or user doesn't have rights to see them, show error
                Styles.notification("Sample not found!",
                        "Sample with code " + bc + " was not found in the Database.", NotificationType.ERROR);
                return false;
            }
            String type = codesToSamples.get(bc).getSampleTypeCode();
            if (!sampleTypeToAttributes.containsKey(type)) {
                List<PropertyType> props = openbis.listPropertiesForType(openbis.getSampleTypeByString(type));
                List<String> propertyNames = new ArrayList<String>();
                for (PropertyType p : props) {
                    String propName = p.getLabel();
                    String propCode = p.getCode();
                    propNameToCode.put(propName, propCode);
                    DataTypeCode dataType = p.getDataType();
                    switch (dataType) {
                    case CONTROLLEDVOCABULARY:// TODO properties without mapping?
                        if (propToVocabulary.containsKey(propCode)) {
                            propertyToType.put(propName, dataType);
                            propertyNames.add(propName);
                        }
                        break;
                    case MATERIAL:
                        break;
                    case TIMESTAMP:
                        break;
                    case XML:
                        break;
                    default:
                        propertyToType.put(propName, dataType);
                        propertyNames.add(propName);
                    }
                }
                sampleTypeToAttributes.put(type, propertyNames);
            }
            codesInTSV.add(bc);
        }

        for (String type : sampleTypeToAttributes.keySet()) {
            Set<String> options = new HashSet<String>();
            options.addAll(customProperties);
            options.addAll(sampleTypeToAttributes.get(type));
            // options.removeAll(hiddenProperties);
            Table sampleTable = new Table(type + " Samples");
            sampleTable.setWidth("100%");
            sampleTable.setStyleName(Styles.tableTheme);
            sampleTable.addContainerProperty(header[barcodeCol], String.class, null);
            for (int i = 0; i < header.length; i++) {
                if (i != barcodeCol) {
                    sampleTable.addContainerProperty(header[i], Component.class, null);
                }
            }
            List<Object> row = new ArrayList<Object>();
            row.add("Properties -->");
            for (int i = 1; i < header.length; i++) {
                // if (i != barcodeCol) {
                String headline = header[i];
                ComboBox attributeOptions = new ComboBox("", options);
                attributeOptions.setStyleName(Styles.boxTheme);
                attributeOptions.setImmediate(true);
                attributeOptions.setInputPrompt("<Select Attribute>");
                attributeOptions.setWidth("100%");
                attributeOptions.setFilteringMode(FilteringMode.CONTAINS);
                attributeOptions.setNullSelectionAllowed(false);
                attributeOptions.addValueChangeListener(new ValueChangeListener() {

                    @Override
                    public void valueChange(ValueChangeEvent event) {
                        List<Object> toRemove = new ArrayList<Object>();
                        for (Object item : attributeOptions.getItemIds()) {
                            String val = item.toString();
                            if (val.startsWith("Condition: ") || val.startsWith("Property: "))
                                if (!attributeOptions.getValue().equals(item))
                                    toRemove.add(item);
                        }
                        for (Object item : toRemove) {
                            attributeOptions.removeItem(item);
                        }
                        if (attributeOptions.getValue() != null) {
                            String selectedProperty = (String) attributeOptions.getValue();
                            if (selectedProperty.equals("[Experimental Condition]")
                                    || selectedProperty.equals("[Other Property]"))
                                createConditionWindow(attributeOptions);
                            else {
                                if (selectedProperty.equals("IGNORE (removes column)")) {
                                    sampleTable.removeContainerProperty(headline);
                                    reactToTableChange();
                                } else {
                                    DataTypeCode dType = propertyToType.get(selectedProperty);
                                    if (dType != null) {
                                        switch (dType) {
                                        case CONTROLLEDVOCABULARY:
                                            createVocabularySelectWindow(attributeOptions, selectedProperty,
                                                    collectLabelsInCol(headline));// TODO ?
                                            break;
                                        case REAL:
                                        case INTEGER:
                                            checkForNumberConsistency(headline, dType);
                                            reactToTableChange();
                                            break;
                                        default:
                                            reactToTableChange();
                                            break;
                                        }
                                    } else {
                                        reactToTableChange();
                                    }
                                }
                            }
                        }
                    }
                });
                row.add(attributeOptions);
                // } else {
                // row.add("Properties -->");
                // }
            }
            sampleTable.addItem(row.toArray(), -1);
            for (int i = 0; i < codesInTSV.size(); i++) {
                String thisType = codesToSamples.get(codesInTSV.get(i)).getSampleTypeCode();
                if (thisType.equals(type)) {
                    row = new ArrayList<Object>();
                    row.add(codesInTSV.get(i));
                    for (int j = 0; j < header.length; j++) {
                        if (j != barcodeCol) {
                            row.add(new Label(data.get(i)[j]));
                        }
                    }
                    sampleTable.addItem(row.toArray(), i);
                }
            }
            sheet.addTab(sampleTable);
            sampleTables.add(sampleTable);
            sampleTable.setPageLength(Math.min(20, sampleTable.size()));
            styleTable(sampleTable);
            reactToTableChange();
        }
        addComponent(send);
        return true;
    }

    protected List<Label> collectLabelsInCol(String headline) {
        Table sampleTable = getActiveTable();
        List<Label> res = new ArrayList<Label>();
        for (Object itemID : sampleTable.getItemIds()) {
            int id = (int) itemID;
            if (id > -1) {
                Item item = sampleTable.getItem(id);
                Label l = (Label) item.getItemProperty(headline).getValue();
                res.add(l);
            }
        }
        return res;
    }

    protected void checkForNumberConsistency(String headline, DataTypeCode dType) {
        boolean consistent = true;
        boolean needsDelimiterChange = false;
        String moreInfo = "Not a number.";
        String barcode = "";
        for (Object item : getActiveTable().getItemIds()) {
            int id = (int) item;
            if (id != -1) {
                String val = parseLabelCell(id, headline);
                if (!val.isEmpty()) {
                    if (dType.equals(DataTypeCode.INTEGER)) {
                        try {
                            Integer.parseInt(val);
                        } catch (NumberFormatException e) {
                            consistent = false;
                        }
                    }
                    if (dType.equals(DataTypeCode.REAL)) {
                        // try normal parse
                        try {
                            Double.parseDouble(val);
                        } catch (NumberFormatException e) {
                            // normal parse unsuccessful, check for different delimiter
                            NumberFormat format = NumberFormat.getInstance(Locale.GERMANY);
                            try {
                                format.parse(val);
                                // worked, needs different delimiter
                                needsDelimiterChange = true;
                            } catch (ParseException e1) {
                                // didn't work, not a double value
                                barcode = getBarcodeInRow(id);
                                consistent = false;
                            }
                        }
                    }
                }
            }
        }
        if (consistent) {
            if (needsDelimiterChange)
                createDelimiterChangeDialogue(headline);
        } else {
            createNotRightTypeDialogue(headline, moreInfo, barcode);
        }
    }

    private void createNotRightTypeDialogue(String headline, String moreInfo, String barcode) {
        Window subWindow = new Window(" Wrong data type!");
        subWindow.setWidth("400px");

        VerticalLayout layout = new VerticalLayout();
        layout.setSpacing(true);
        layout.setMargin(true);
        Label preInfo = new Label(
                "Data of barcode " + barcode + " in this column doesn't fit the attribute type. " + moreInfo);
        layout.addComponent(preInfo);
        Button ok = new Button("Ignore Column.");
        Button no = new Button("Select different attribute.");
        ok.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                getActiveTable().removeContainerProperty(headline);
                subWindow.close();
            }
        });
        no.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                resetAttribute(headline);
                subWindow.close();
            }
        });
        layout.addComponent(ok);
        layout.addComponent(no);

        subWindow.setContent(layout);
        // Center it in the browser window
        subWindow.center();
        subWindow.setModal(true);
        subWindow.setIcon(FontAwesome.BOLT);
        subWindow.setResizable(false);
        ProjectwizardUI ui = (ProjectwizardUI) UI.getCurrent();
        ui.addWindow(subWindow);
    }

    protected void resetAttribute(String headline) {
        Item item = getActiveTable().getItem(-1);
        Object cell = item.getItemProperty(headline).getValue();
        ComboBox c = ((ComboBox) cell);
        c.setNullSelectionAllowed(true);
        c.select(null);
        c.setNullSelectionAllowed(false);
    }
    //
    // public static void main(String[] args) {
    // int infoMaxLength = 21;
    // String res = "ID01-spleen-150114-L243_TUE39";
    // System.out.println(res.substring(0, Math.min(res.length(), infoMaxLength)));
    //
    // RegexpValidator factorLabelValidator = new RegexpValidator("([a-z]+_?[a-z]*)+([a-z]|[0-9]*)",
    // "Name must start with a lower case letter and contain only lower case letters, numbers or
    // underscores ('_'). Underscores must be followed by a letter.");
    // List<String> tests = new ArrayList<String>(Arrays.asList("a", "a_1", "abc_abc_de1",
    // "isotope12c_or13c", "abc", "abc1", "abc_1", "abc_asd1_x", "abc1_abc2"));
    // for (String x : tests)
    // System.out.println(x + ": " + factorLabelValidator.isValid(x));
    // }

    private void createDelimiterChangeDialogue(String headline) {
        Window subWindow = new Window(" Unexpected number format");
        subWindow.setWidth("400px");

        VerticalLayout layout = new VerticalLayout();
        layout.setSpacing(true);
        layout.setMargin(true);
        Label preInfo = new Label("The decimal delimiter of this type needs to be replaced with '.'.");
        layout.addComponent(preInfo);
        Button ok = new Button("Change numbers in this column.");
        Button no = new Button("Select different attribute.");
        ok.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                changeDelimiterInCol(headline);
                subWindow.close();
            }
        });
        no.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                resetAttribute(headline);
                subWindow.close();
            }
        });
        layout.addComponent(ok);
        layout.addComponent(no);

        subWindow.setContent(layout);
        // Center it in the browser window
        subWindow.center();
        subWindow.setModal(true);
        subWindow.setIcon(FontAwesome.QUESTION);
        subWindow.setResizable(false);
        ProjectwizardUI ui = (ProjectwizardUI) UI.getCurrent();
        ui.addWindow(subWindow);
    }

    protected void changeDelimiterInCol(String headline) {
        for (Object item : getActiveTable().getItemIds()) {
            int id = (int) item;
            if (id != -1) {
                String val = parseLabelCell(id, headline);
                writeLabelCell(id, headline, val.replace(",", "."));
            }
        }
    }

    private void reactToTableChange() {
        collisions = new ArrayList<String>();
        Table t = getActiveTable();
        t.setCellStyleGenerator(t.getCellStyleGenerator());
        showStatus();
    }

    private void styleTable(Table table) {
        // Set cell style generator
        table.setCellStyleGenerator(new Table.CellStyleGenerator() {

            @Override
            public String getStyle(Table source, Object itemId, Object propertyId) {
                String type = null;
                if (propertyId != null) {
                    // barcode col
                    if (propertyId.equals(barcodeColName))
                        return "blue-hue1";
                    // combobox col
                    type = getSelectedProperty(propertyId);
                }
                // not set yet
                if (type == null)
                    return "red-hue";
                else {
                    // check for data in openbis that would be overwritten
                    String collision = getCollisionOrNull(propertyId, itemId);
                    if (collision != null) {
                        collisions.add(collision);
                        return "yellow-hue";
                    } else
                        return "blue-hue1";
                }
            }
        });
    }

    // make findable for table styler
    private void fillCollisionsList() {
        Table sampleTable = getActiveTable();
        for (Object propertyId : sampleTable.getContainerPropertyIds()) {
            for (Object itemId : sampleTable.getItemIds()) {
                String type = getSelectedProperty(propertyId);
                // type set
                if (type != null && !propertyId.equals(barcodeColName)) {
                    // check for data in openbis that would be overwritten
                    String collision = getCollisionOrNull(propertyId, itemId);
                    if (collision != null) {
                        collisions.add(collision);
                    }
                }
            }
        }
    }

    private void showStatus() {
        boolean ready = true;
        for (Object colName : getActiveTable().getContainerPropertyIds()) {
            String selected = getSelectedProperty(colName);
            ready &= selected != null && !selected.isEmpty();
        }
        if (ready) {
            fillCollisionsList();
            if (collisions.size() > 0) {
                Window subWindow = new Window(" Collisions found!");
                subWindow.setWidth("400px");

                VerticalLayout layout = new VerticalLayout();
                layout.setSpacing(true);
                layout.setMargin(true);
                Label preInfo = new Label("The following entries exist and would need to be overwritten:");
                layout.addComponent(preInfo);
                TextArea tf = new TextArea();
                tf.setWidth("350px");
                tf.setValue(StringUtils.join(collisions, ""));
                tf.setStyleName(Styles.areaTheme);
                layout.addComponent(tf);
                String overwriteInfo = "In order to keep your data safe, you are not allowed to overwrite existing information by default. "
                        + "You can either remove the columns in question (choose 'ignore column') or contact QBiC.";
                if (overWriteAllowed)
                    overwriteInfo = "You can either remove the columns in question (choose 'ignore column') "
                            + "before sending it to the Database or overwrite the metadata.";
                Label info = new Label(overwriteInfo);
                Button ok = new Button("Got it!");
                ok.addClickListener(new ClickListener() {

                    @Override
                    public void buttonClick(ClickEvent event) {
                        subWindow.close();
                    }
                });
                layout.addComponent(info);
                layout.addComponent(ok);

                subWindow.setContent(layout);
                // Center it in the browser window
                subWindow.center();
                subWindow.setModal(true);
                subWindow.setIcon(FontAwesome.BOLT);
                subWindow.setResizable(false);
                ProjectwizardUI ui = (ProjectwizardUI) UI.getCurrent();
                ui.addWindow(subWindow);
            } else {
                Styles.notification("No collisions found!",
                        "You can update the metadata in our database without overwriting something. To do so press 'Send to Database'",
                        NotificationType.DEFAULT);
            }
            send.setEnabled(collisions.isEmpty() || overWriteAllowed);
        } else
            send.setEnabled(false);
    }

    protected void createVocabularySelectWindow(ComboBox selected, String propName, List<Label> entries) {
        Window subWindow = new Window(" " + propName);
        subWindow.setWidth("300px");

        VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);

        // create combobox per unique value for this column to find a mapping to the source vocabulary
        Map<String, String> entriesToVocabValues = new HashMap<String, String>();
        Set<String> uniqueEntries = new HashSet<String>();
        // keep these old values in case user chooses different property afterwards
        List<String> oldEntries = new ArrayList<String>();
        ValueChangeListener resetSelectionListener = new ValueChangeListener() {
            @Override
            public void valueChange(ValueChangeEvent event) {
                // reset labels to what they were before
                for (int i = 0; i < entries.size(); i++) {
                    entries.get(i).setValue(oldEntries.get(i));
                }
                // remove reset listener, it won't be needed until a vocabulary field is selected again
                selected.removeValueChangeListener(this);
            }
        };
        selected.addValueChangeListener(resetSelectionListener);

        List<ComboBox> boxes = new ArrayList<ComboBox>();
        Set<String> vocabOptions = propToVocabulary.get(propNameToCode.get(propName)).keySet();
        for (Label l : entries) {
            String val = l.getValue();
            oldEntries.add(val);
            if (!uniqueEntries.contains(val)) {
                ComboBox b = new ComboBox(val);
                b.addItems(vocabOptions);
                b.setNullSelectionAllowed(false);
                b.setStyleName(Styles.boxTheme);
                b.setFilteringMode(FilteringMode.CONTAINS);
                layout.addComponent(b);
                boxes.add(b);
                uniqueEntries.add(val);
                val = StringUtils.capitalize(val);
                if (vocabOptions.contains(val)) {
                    b.setValue(val);
                    b.setEnabled(false);
                }
            }
        }
        Button send = new Button("Ok");
        send.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                boolean valid = true;
                for (ComboBox b : boxes) {
                    if (b.getValue() != null) {
                        String newVal = b.getValue().toString();
                        entriesToVocabValues.put(b.getCaption(), newVal);
                    } else
                        valid = false;
                }
                if (valid) {
                    for (Label l : entries) {
                        l.setValue(entriesToVocabValues.get(l.getValue()));
                    }
                    subWindow.close();

                    // check for collisions now that values have changed
                    reactToTableChange();
                } else {
                    String error = "Please select a value for each entry.";
                    Styles.notification("Missing Input", error, NotificationType.DEFAULT);
                }
            }
        });
        layout.addComponent(send);

        subWindow.setContent(layout);
        // Center it in the browser window
        subWindow.center();
        subWindow.setModal(true);
        subWindow.setIcon(FontAwesome.FLASK);
        subWindow.setResizable(false);

        ProjectwizardUI ui = (ProjectwizardUI) UI.getCurrent();
        ui.addWindow(subWindow);
    }

    protected void createConditionWindow(ComboBox selectionBox) {
        String val = (String) selectionBox.getValue();

        // val.equals("[Experimental Condition]")
        String header = " Experimental Condition Name";
        String prefix = "Condition";
        Resource icon = FontAwesome.FLASK;

        if (val.equals("[Other Property]")) {
            header = " Property Name";
            prefix = "Property";
            icon = FontAwesome.FILE_TEXT;
        }
        final String category = prefix;

        Window subWindow = new Window(header);
        subWindow.setWidth("300px");

        VerticalLayout layout = new VerticalLayout();
        layout.setSpacing(true);
        layout.setMargin(true);
        TextField label = new TextField();
        label.setRequired(true);
        label.setStyleName(Styles.fieldTheme);
        RegexpValidator factorLabelValidator = new RegexpValidator("([a-z]+_?[a-z]*)+([a-z]|[0-9]*)",
                "Name must start with a lower case letter and contain only lower case letter words, which can be connected by underscores ('_'). It can end with one or more numbers.");
        label.addValidator(factorLabelValidator);
        label.setValidationVisible(true);
        label.setImmediate(true);

        ComboBox unitSelect = new ComboBox("Unit");
        unitSelect.setNullSelectionAllowed(false);
        unitSelect.addItems(properties.Unit.values());
        String nullItem = "[None]";
        unitSelect.addItem(nullItem);
        unitSelect.select(nullItem);
        unitSelect.setStyleName(Styles.boxTheme);
        unitSelect.setImmediate(true);

        Button send = new Button("Ok");
        send.addClickListener(new ClickListener() {

            @Override
            public void buttonClick(ClickEvent event) {
                if (label.isValid()) {
                    String unit = "";
                    if (!unitSelect.getValue().equals(nullItem))
                        unit = " [" + unitSelect.getValue() + "]";
                    String name = category + ": " + label.getValue() + unit;
                    selectionBox.addItem(name);
                    selectionBox.select(name);
                    subWindow.close();
                } else {
                    String error = "Please input a name for this " + category + ".";
                    if (!label.isEmpty())
                        error = factorLabelValidator.getErrorMessage();
                    Styles.notification("Missing Input", error, NotificationType.DEFAULT);
                }
            }
        });
        layout.addComponent(label);
        layout.addComponent(unitSelect);
        layout.addComponent(send);

        subWindow.setContent(layout);
        // Center it in the browser window
        subWindow.center();
        subWindow.setModal(true);
        subWindow.setIcon(icon);
        subWindow.setResizable(false);
        ProjectwizardUI ui = (ProjectwizardUI) UI.getCurrent();
        ui.addWindow(subWindow);
    }

    private String parseLabelCell(int id, Object propertyId) {
        Item item = getActiveTable().getItem(id);
        Label l = (Label) item.getItemProperty(propertyId).getValue();
        return l.getValue();
    }

    private void writeLabelCell(int id, Object propertyId, String text) {
        Item item = getActiveTable().getItem(id);
        Label l = (Label) item.getItemProperty(propertyId).getValue();
        l.setValue(text);
    }

    private String getBarcodeInRow(int id) {
        Item item = getActiveTable().getItem(id);
        String bc = (String) item.getItemProperty(barcodeColName).getValue();
        return bc;
    }

    private String parseUnit(String label) {
        if (!label.contains("]") && !label.contains("["))
            return null;
        label = label.substring(label.indexOf("[") + 1);
        label = label.substring(0, label.indexOf("]"));
        return label;
    }

    protected String getCollisionOrNull(Object propertyId, Object itemId) {
        String typeName = getSelectedProperty(propertyId);
        String typeCode = propNameToCode.get(typeName);
        String res = null;

        int id = (int) itemId;
        if (id != -1) {
            String val = parseLabelCell(id, propertyId);
            String barcode = getBarcodeInRow(id);
            String openbisVal = "";
            Map<String, String> props = codesToSamples.get(barcode).getProperties();
            properties.PropertyType propType = null;
            if (typeName.startsWith("Condition: "))
                propType = properties.PropertyType.Factor;
            if (typeName.startsWith("Property: "))
                propType = properties.PropertyType.Property;
            if (propType != null) {
                typeName = typeName.replace("Condition: ", "").replace("Property: ", "");
                String unit = null;
                if (typeName.contains("[") && typeName.contains("]")) {
                    unit = parseUnit(typeName);
                    val = val + " " + unit;
                    typeName = typeName.replace(" [" + unit + "]", "");
                }
                openbisVal = parseXMLConditionValue(props.get("Q_PROPERTIES"), typeName, propType);
            } else
                openbisVal = props.get(typeCode);
            if (propToReverseVocabulary.containsKey(typeCode))
                openbisVal = propToReverseVocabulary.get(typeCode).get(openbisVal);

            boolean empty = openbisVal == null || openbisVal.isEmpty() || val == null || val.isEmpty();
            boolean same = val != null && val.equals(openbisVal);
            boolean collision = (!empty && !same);
            if (collision) {
                res = barcode + ": " + openbisVal + " --> " + val + "\n";
            }
        }
        return res;
    }

    private String parseXMLConditionValue(String xml, String label, properties.PropertyType type) {
        List<Property> props = new ArrayList<Property>();
        try {
            props = xmlParser.getAllPropertiesFromXML(xml);
        } catch (JAXBException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        String res = "";
        for (Property f : props) {
            if (f.getLabel().equals(label) && f.getType().equals(type)) {
                res = f.getValue();
                if (f.hasUnit())
                    res += " " + f.getUnit();
            }
        }
        return res;
    }

    protected String getSelectedProperty(Object propertyId) {
        Item item = getActiveTable().getItem(-1);
        Object cell = item.getItemProperty(propertyId).getValue();
        if (cell instanceof ComboBox)
            return (String) ((ComboBox) cell).getValue();
        else
            return cell.toString();
    }

}