org.caleydo.data.tcga.internal.TCGABrowserStartupAddon.java Source code

Java tutorial

Introduction

Here is the source code for org.caleydo.data.tcga.internal.TCGABrowserStartupAddon.java

Source

/*******************************************************************************
 * Caleydo - Visualization for Molecular Biology - http://caleydo.org
 * Copyright (c) The Caleydo Team. All rights reserved.
 * Licensed under the new BSD license, available at http://caleydo.org/license
 ******************************************************************************/
package org.caleydo.data.tcga.internal;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.caleydo.core.manager.GeneralManager;
import org.caleydo.core.startup.IStartupAddon;
import org.caleydo.core.startup.IStartupProcedure;
import org.caleydo.core.startup.LoadProjectStartupProcedure;
import org.caleydo.core.util.logging.Logger;
import org.caleydo.core.util.system.BrowserUtils;
import org.caleydo.core.util.system.RemoteFile;
import org.caleydo.data.tcga.internal.model.AdditionalInfo;
import org.caleydo.data.tcga.internal.model.ClinicalInfo;
import org.caleydo.data.tcga.internal.model.RunOverview;
import org.caleydo.data.tcga.internal.model.TumorProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ILazyTreeContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.StyledString.Styler;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.TextStyle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ExpandBar;
import org.eclipse.swt.widgets.ExpandItem;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableColumn;

import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;

/**
 * addon that parses out json file and create a selection dialog
 *
 * @author Samuel Gratzl
 *
 */
public class TCGABrowserStartupAddon implements IStartupAddon {
    private static final Logger log = Logger.create(TCGABrowserStartupAddon.class);

    private static final String PREFIX = GeneralManager.DATA_URL_PREFIX + "tcga/";
    private static final String JSONFILE = PREFIX + "tcga_analysis_runs.json";
    private URL selectedChoice = null;
    private final Gson gson = new GsonBuilder().create();

    private TreeViewer tree;
    private ExpandItem genomicInfos;
    private TableViewer genomicViewer;
    private ExpandItem nonGenomicInfos;
    private TableViewer nonGenomicViewer;

    private volatile boolean loaded = false;
    private File file;

    private Control progress;

    @Override
    public boolean init() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                File f;
                try {
                    f = RemoteFile.of(new URL(JSONFILE)).getOrLoad(true, new NullProgressMonitor());
                } catch (MalformedURLException e) {
                    log.error("malformed url: " + JSONFILE, e);
                    f = null;
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                final File ff = f;
                Display.getDefault().asyncExec(new Runnable() {
                    @Override
                    public void run() {
                        loaded = true;
                        file = ff;
                        if (genomicViewer != null && !genomicViewer.getTable().isDisposed()) { // already shown
                            updateData();
                        }
                    }
                });
            }
        }).start();
        return false;
    }

    @Override
    public Composite create(Composite parent, final WizardPage page, Listener listener) {
        parent = new Composite(parent, SWT.NONE);
        parent.setLayout(new GridLayout(1, false));

        Link sourceLabel = new Link(parent, SWT.NO_BACKGROUND);
        sourceLabel.addSelectionListener(BrowserUtils.LINK_LISTENER);
        sourceLabel.setText(
                "We provide direct access to comprehensive data packages based on semi-automated analyses of the TCGA data set by the Firehose system developed and maintained by the <a href=\"http://gdac.broadinstitute.org\">TCGA Genome Data Analysis Center at the Broad Institute</a>.");
        sourceLabel.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));

        Link label = new Link(parent, SWT.NO_BACKGROUND);
        label.addSelectionListener(BrowserUtils.LINK_LISTENER);
        label.setText(
                "Please be advised that downloading \"The Cancer Genome Atlas\" data constitutes agreement to the <a href=\"http://cancergenome.nih.gov/abouttcga/policies/policiesguidelines\">policies and guidelines on data usage and publications</a>.");
        label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));

        if (!loaded) {
            Composite p = new Composite(parent, SWT.NONE);
            p.setLayout(new FillLayout(SWT.VERTICAL));
            p.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
            this.progress = p;
            ProgressBar progressBar = new ProgressBar(p, SWT.INDETERMINATE);
            progressBar.setState(SWT.NORMAL);
            new Label(p, SWT.NONE | SWT.CENTER).setText("Downloading package descriptions...");
        }
        if (loaded && file == null) {
            Label l = new Label(parent, SWT.WRAP);
            l.setText("Can't download:\n" + JSONFILE);
            l.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));
        } else {
            SashForm form = new SashForm(parent, SWT.VERTICAL);
            form.setLayout(new FillLayout());
            form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

            tree = createSelectionTree(form, page, listener);
            updateData();

            Group g = new Group(form, SWT.BORDER_SOLID);
            g.setText("Additional project information:");
            g.setLayout(new FillLayout());
            ExpandBar expandBar = new ExpandBar(g, SWT.V_SCROLL);

            this.genomicInfos = new ExpandItem(expandBar, SWT.NONE);
            genomicInfos.setText("Molecular Data Types");

            this.genomicViewer = createGenomicTableViewer(expandBar);
            genomicInfos.setControl(this.genomicViewer.getTable().getParent());
            genomicInfos.setExpanded(false);

            this.nonGenomicInfos = new ExpandItem(expandBar, SWT.NONE);
            nonGenomicInfos.setText("Other Data Types");

            this.nonGenomicViewer = createNonGenomicTableViewer(expandBar);
            nonGenomicInfos.setControl(this.nonGenomicViewer.getTable().getParent());
            nonGenomicInfos.setExpanded(false);
            form.setWeights(new int[] { 40, 60 });
            form.setMaximizedControl(tree.getControl());
        }
        return parent;
    }

    void updateData() {
        if (file == null || tree == null)
            return;
        RunOverview[] model = createModel(file);
        tree.setInput(model);
        tree.getTree().setItemCount(model.length);

        if (progress != null) {
            Composite p = progress.getParent();
            progress.dispose();
            progress = null;
            p.layout(true);
        }
        // try to force an update
        tree.refresh(true);
        tree.getTree().getParent().layout(true, true);
    }

    private TableViewer createTableViewer(Composite parent) {
        final TableViewer t = new TableViewer(parent, SWT.BORDER | SWT.FULL_SELECTION);
        t.getTable().setHeaderVisible(true);
        t.getTable().setLinesVisible(true);
        t.setLabelProvider(new LabelProvider());
        t.setContentProvider(ArrayContentProvider.getInstance());
        return t;
    }

    private TreeViewer createSelectionTree(final SashForm form, final WizardPage page, final Listener listener) {
        final TreeViewer v = new TreeViewer(form, SWT.VIRTUAL | SWT.BORDER);
        v.setLabelProvider(new LabelProvider());
        v.setContentProvider(new MyContentProvider(v));
        v.setUseHashlookup(true);
        v.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection s = (IStructuredSelection) event.getSelection();
                Object f = s.getFirstElement();
                if (f instanceof TumorProject) {
                    selectedChoice = ((TumorProject) f).getProject();
                    // page.setPageComplete(true);
                } else {
                    selectedChoice = null;
                    // page.setPageComplete(false);
                }

                if (f instanceof TumorProject) {
                    updateDetailInfo((TumorProject) f);
                    form.setMaximizedControl(null);
                } else if (f instanceof RunOverview) {
                    updateDetailInfo((RunOverview) f);
                    clearDetailInfo();
                    form.setMaximizedControl(v.getControl());
                } else {
                    clearDetailInfo();
                    form.setMaximizedControl(v.getControl());
                }
                // This is a bit hacky, but there is no way to register a swt listener to a treeviewer.
                listener.handleEvent(null);
            }
        });
        return v;
    }

    private TableViewer createGenomicTableViewer(Composite parent) {
        parent = new Composite(parent, SWT.NONE);
        final TableViewer t = createTableViewer(parent);
        // Data Type #Patients #Patient Stratifications #Genes #Gene Stratifications
        createTableViewerColumn(t, "Data Type").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, ?> entry = (Map.Entry<String, ?>) element;
                return entry.getKey();
            }
        });
        createTableViewerColumn(t, "#Patients").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, AdditionalInfo> entry = (Map.Entry<String, AdditionalInfo>) element;
                return String.valueOf(entry.getValue().getSampleCount());
            }
        });
        createTableViewerColumn(t, "#Patient Stratifications").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, AdditionalInfo> entry = (Map.Entry<String, AdditionalInfo>) element;
                return String.valueOf(entry.getValue().getSampleStratifications());
            }
        });
        createTableViewerColumn(t, "#Genes").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, AdditionalInfo> entry = (Map.Entry<String, AdditionalInfo>) element;
                return String.valueOf(entry.getValue().getGeneCount());
            }
        });
        createTableViewerColumn(t, "#Gene Stratifications").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, AdditionalInfo> entry = (Map.Entry<String, AdditionalInfo>) element;
                return String.valueOf(entry.getValue().getGeneStratifications());
            }
        });

        parent.setLayout(layoutTable(t, 3, 1, 2, 1, 2));
        return t;
    }

    private TableViewer createNonGenomicTableViewer(Composite parent) {
        parent = new Composite(parent, SWT.NONE);
        final TableViewer t = createTableViewer(parent);
        // Data Type #Patients Parameters
        createTableViewerColumn(t, "Data Type").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, ?> entry = (Map.Entry<String, ?>) element;
                return entry.getKey();
            }
        });
        createTableViewerColumn(t, "#Patients").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, ClinicalInfo> entry = (Map.Entry<String, ClinicalInfo>) element;
                return String.valueOf(entry.getValue().getCount());
            }
        });
        createTableViewerColumn(t, "Parameters").setLabelProvider(new ColumnLabelProvider() {
            @Override
            public String getText(Object element) {
                @SuppressWarnings("unchecked")
                Map.Entry<String, ClinicalInfo> entry = (Map.Entry<String, ClinicalInfo>) element;
                return StringUtils.join(entry.getValue().getParameters(), ',');
            }
        });
        parent.setLayout(layoutTable(t, 3, 1, 5));
        return t;
    }

    private static TableColumnLayout layoutTable(TableViewer t, int... weights) {
        TableColumnLayout l = new TableColumnLayout();
        TableColumn[] columns = t.getTable().getColumns();
        for (int i = 0; i < columns.length; ++i)
            l.setColumnData(columns[i], new ColumnWeightData(weights[i], true));
        return l;
    }

    private TableViewerColumn createTableViewerColumn(final TableViewer t, String label) {
        TableViewerColumn c = new TableViewerColumn(t, SWT.READ_ONLY);
        c.getColumn().setText(label);
        c.getColumn().setResizable(true);
        c.getColumn().setMoveable(true);
        return c;
    }

    /**
     * @param tumor
     *
     */
    protected void updateDetailInfo(TumorProject tumor) {
        genomicViewer.setInput(tumor.getGenomic().entrySet());
        genomicInfos.setHeight(genomicViewer.getTable().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
        genomicInfos.setExpanded(true);
        nonGenomicViewer.setInput(tumor.getNonGenomic().entrySet());
        nonGenomicInfos.setHeight(nonGenomicViewer.getTable().computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
        nonGenomicInfos.setExpanded(true);
    }

    protected void updateDetailInfo(RunOverview f) {
        // TODO Auto-generated method stub

    }

    /**
     *
     */
    protected void clearDetailInfo() {
        genomicViewer.setInput(null);
        genomicInfos.setExpanded(false);
        nonGenomicViewer.setInput(null);
        nonGenomicInfos.setExpanded(false);
    }

    private static final Styler INCOMPATIBLE_STYLE = new Styler() {

        @Override
        public void applyStyles(TextStyle textStyle) {
            textStyle.foreground = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
        }
    };

    private static final class LabelProvider extends StyledCellLabelProvider {
        @Override
        public void update(ViewerCell cell) {
            Object element = cell.getElement();
            StyledString text = new StyledString();

            if (element instanceof TumorProject) {
                TumorProject item = (TumorProject) element;
                text.append(item.toString());
                cell.setText(text.toString());
                cell.setStyleRanges(text.getStyleRanges());
            } else if (element instanceof RunOverview) {
                RunOverview item = (RunOverview) element;
                text.append(item.toString());
                if (item.isResolved()) {
                    if (item.isCompatible())
                        text.append(String.format(" (%d)", item.size()), StyledString.COUNTER_STYLER);
                    else
                        text.append(" INCOMPATIBLE!", INCOMPATIBLE_STYLE);
                }
                cell.setText(text.toString());
                cell.setStyleRanges(text.getStyleRanges());
            } else if (element instanceof String) {
                text.append(" INCOMPATIBLE!", INCOMPATIBLE_STYLE);
                cell.setText(text.toString());
                cell.setStyleRanges(text.getStyleRanges());
            }
            super.update(cell);
        }
    }

    private class MyContentProvider implements ILazyTreeContentProvider {
        private TreeViewer viewer;
        private RunOverview[] elements;

        public MyContentProvider(TreeViewer viewer) {
            this.viewer = viewer;
        }

        @Override
        public void dispose() {

        }

        @Override
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            this.elements = (RunOverview[]) newInput;
        }

        @Override
        public Object getParent(Object element) {
            if (element instanceof TumorProject)
                return ((TumorProject) element).getParent();
            return elements;
        }

        @Override
        public void updateChildCount(Object element, int currentChildCount) {
            if (element instanceof RunOverview) {
                RunOverview overview = (RunOverview) element;
                resolve(overview);
                viewer.setChildCount(element, overview.isCompatible() ? overview.size() : 0);
                viewer.update(element, null);
            }
        }

        private void resolve(RunOverview overview) {
            try {
                overview.resolve(gson, PREFIX);
            } catch (JsonSyntaxException | JsonIOException | IOException e) {
                log.error("can't resolve " + overview, e);
            }
        }

        @Override
        public void updateElement(Object parent, int index) {
            Object element;
            if (parent instanceof RunOverview) {
                RunOverview overview = (RunOverview) parent;
                updateChildCount(parent, -1);
                if (!overview.isCompatible()) {
                    element = "Incompatible to your current Caleydo version";
                } else
                    element = overview.getProject(index);
                viewer.replace(parent, index, element);
                viewer.setChildCount(element, 0); // none
            } else {
                element = elements[index];
                viewer.replace(parent, index, element);
                viewer.setChildCount(element, 1); // guess at least one
            }
        }

    }

    private RunOverview[] createModel(File file) {
        Gson gson = new Gson();
        RunOverview[] fromJson;
        try (Reader r = Files.newReader(file, Charset.forName("UTF-8"))) {
            fromJson = gson.fromJson(r, RunOverview[].class);
            Arrays.sort(fromJson, Collections.reverseOrder());
            return fromJson;
        } catch (JsonSyntaxException | JsonIOException | IOException e) {
            log.error("can't parse " + file.getAbsolutePath(), e);
            return new RunOverview[0];
        }
    }

    @Override
    public boolean validate() {
        return selectedChoice != null;
        // if (this.selectedChoice == null)
        // return false;
        // // Try to download the file with interruption
        // RemoteFile file = RemoteFile.of(this.selectedChoice);
        // if (!file.inCache(true)) {
        // try {
        // file.delete();
        // new ProgressMonitorDialog(new Shell()).run(true, true, file);
        // } catch (InvocationTargetException | InterruptedException e) {
        // Status status = new Status(IStatus.ERROR, this.getClass().getSimpleName(), "Error during downloading: "
        // + selectedChoice, e);
        // ErrorDialog.openError(null, "Download Error", "Error during downloading: " + selectedChoice, status);
        // Logger.log(status);
        // }
        // }
        // return file.inCache(false);
    }

    @Override
    public IStartupProcedure create() {
        RemoteFile file = RemoteFile.of(this.selectedChoice);
        if (!file.inCache(true)) {
            try {
                file.delete();
                new ProgressMonitorDialog(new Shell()).run(true, true, file);
            } catch (InvocationTargetException | InterruptedException e) {
                Status status = new Status(IStatus.ERROR, this.getClass().getSimpleName(),
                        "Error during downloading: " + selectedChoice, e);
                ErrorDialog.openError(null, "Download Error", "Error during downloading: " + selectedChoice,
                        status);
                Logger.log(status);
            }
        }
        if (!file.inCache(false))
            return null;
        return new LoadProjectStartupProcedure(RemoteFile.of(selectedChoice).getFile().getAbsolutePath(), false);
    }
}