org.dishevelled.variation.cytoscape3.internal.VariationModel.java Source code

Java tutorial

Introduction

Here is the source code for org.dishevelled.variation.cytoscape3.internal.VariationModel.java

Source

/*
    
dsh-variation-cytoscape3-app  Variation Cytoscape3 app.
Copyright (c) 2013-2015 held jointly by the individual authors.
    
This library 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 library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; with out 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 library;  if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
    
> http://www.fsf.org/licensing/licenses/lgpl.html
> http://www.opensource.org/licenses/lgpl-license.php
    
*/
package org.dishevelled.variation.cytoscape3.internal;

import static com.google.common.base.Preconditions.checkNotNull;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Range;

import org.cytoscape.model.CyNetwork;
import org.cytoscape.model.CyNode;

import org.dishevelled.variation.Feature;
import org.dishevelled.variation.FeatureService;
import org.dishevelled.variation.Variation;
import org.dishevelled.variation.VariationService;
import org.dishevelled.variation.VariationConsequence;
import org.dishevelled.variation.VariationConsequenceService;
import org.dishevelled.variation.VariationConsequencePredictionService;

import org.nmdp.ngs.range.tree.CenteredRangeTree;
import org.nmdp.ngs.range.tree.RangeTree;

/**
 * Variation model.
 *
 * @author  Michael Heuer
 */
final class VariationModel {
    /** Species. */
    private String species;

    /** Reference. */
    private String reference;

    /** Ensembl gene id column. */
    private String ensemblGeneIdColumn;

    /** True if this variation model should use canonical transcripts only. */
    private boolean canonical;

    /** True if this variation model should include somatic variations. */
    private boolean somatic;

    /** Current network. */
    private CyNetwork network;

    /** Feature service. */
    private FeatureService featureService;

    /** Variation service. */
    private VariationService variationService;

    /** Variation consequence service. */
    private VariationConsequenceService variationConsequenceService;

    /** Variation consequence prediction service. */
    private VariationConsequencePredictionService variationConsequencePredictionService;

    /** List of nodes. */
    private final EventList<CyNode> nodes;

    /** List of features. */
    private final EventList<Feature> features;

    /** List of variations. */
    private final EventList<Variation> variations;

    /** List of variation consequences. */
    private final EventList<VariationConsequence> variationConsequences;

    /** List of column names for the current network. */
    private final EventList<String> columnNames;

    /** Map of features keyed by node. */
    private final ListMultimap<CyNode, Feature> nodesToFeatures;

    /** Map of nodes keyed by feature. */
    private final ListMultimap<Feature, CyNode> featuresToNodes;

    /** Bidirectional mapping of features to ranges. */
    private final BiMap<Feature, Range<Long>> featuresToRanges;

    /** Map of range trees keyed by region name. */
    private final Map<String, RangeTree<Long>> rangeTrees;

    /** Map of lists of variation consequences by feature. */
    private final ListMultimap<Feature, VariationConsequence> featuresToConsequences;

    /** Property change support. */
    private final PropertyChangeSupport propertyChangeSupport;

    /** Default species, <code>human</code>. */
    private static final String DEFAULT_SPECIES = "human";

    /** Default reference, <code>GRCh38</code>. */
    private static final String DEFAULT_REFERENCE = "GRCh38";

    /** Default Ensembl gene id column, <code>ensembl</code>. */
    static final String DEFAULT_ENSEMBL_GENE_ID_COLUMN = "ensembl";

    /** Use canonical transcripts only by default. */
    private static final boolean DEFAULT_CANONICAL = true;

    /** Do not include somatic variations by default. */
    private static final boolean DEFAULT_SOMATIC = false;

    /**
     * Create a new variation model.
     */
    VariationModel() {
        this.species = DEFAULT_SPECIES;
        this.reference = DEFAULT_REFERENCE;
        this.ensemblGeneIdColumn = DEFAULT_ENSEMBL_GENE_ID_COLUMN;
        this.canonical = DEFAULT_CANONICAL;
        this.somatic = DEFAULT_SOMATIC;
        nodes = GlazedLists.eventList(new ArrayList<CyNode>());
        features = GlazedLists.eventList(new ArrayList<Feature>());
        variations = GlazedLists.eventList(new ArrayList<Variation>());
        variationConsequences = GlazedLists.eventList(new ArrayList<VariationConsequence>());
        columnNames = GlazedLists.eventList(new ArrayList<String>());
        featuresToNodes = ArrayListMultimap.create();
        nodesToFeatures = ArrayListMultimap.create();
        featuresToRanges = HashBiMap.create();
        rangeTrees = Maps.newHashMap();
        featuresToConsequences = ArrayListMultimap.create();

        propertyChangeSupport = new PropertyChangeSupport(this);
    }

    // bound properties

    /**
     * Return the species for this variation model.
     *
     * @return the species for this variation model
     */
    public String getSpecies() {
        return species;
    }

    /**
     * Set the species for this variation model to <code>species</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param species species for this variation model
     */
    public void setSpecies(final String species) {
        String oldSpecies = this.species;
        this.species = species;
        propertyChangeSupport.firePropertyChange("species", oldSpecies, this.species);
    }

    /**
     * Return the reference for this variation model.
     *
     * @return the reference for this variation model
     */
    public String getReference() {
        return reference;
    }

    /**
     * Set the reference for this variation model to <code>reference</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param reference reference for this variation model
     */
    public void setReference(final String reference) {
        String oldReference = this.reference;
        this.reference = reference;
        propertyChangeSupport.firePropertyChange("reference", oldReference, this.reference);
    }

    /**
     * Return the Ensembl gene id column for this variation model.
     *
     * @return the Ensembl gene id column for this variation model
     */
    public String getEnsemblGeneIdColumn() {
        return ensemblGeneIdColumn;
    }

    /**
     * Set the Ensembl gene id column for this variation model to <code>ensemblGeneIdColumn</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param ensemblGeneIdColumn Ensembl gene id column for this variation model
     */
    public void setEnsemblGeneIdColumn(final String ensemblGeneIdColumn) {
        String oldEnsemblGeneIdColumn = this.ensemblGeneIdColumn;
        this.ensemblGeneIdColumn = ensemblGeneIdColumn;
        propertyChangeSupport.firePropertyChange("ensemblGeneIdColumn", oldEnsemblGeneIdColumn,
                this.ensemblGeneIdColumn);
    }

    /**
     * Return true if this variation model should use canonical transcripts only.
     *
     * @return true if this variation model should use canonical transcripts only
     */
    public boolean isCanonical() {
        return canonical;
    }

    /**
     * Set to true if this variation model should use canonical transcripts only.
     *
     * <p>This is a bound property.</p>
     *
     * @param canonical true if this variation model should use canonical transcripts only
     */
    public void setCanonical(final boolean canonical) {
        boolean oldCanonical = this.canonical;
        this.canonical = canonical;
        propertyChangeSupport.firePropertyChange("canonical", oldCanonical, this.canonical);
    }

    /**
     * Return true if this variation model should include somatic variations.
     *
     * @return true if this variation model should include somatic variations
     */
    public boolean isSomatic() {
        return somatic;
    }

    /**
     * Set to true if this variation model should include somatic variations.
     *
     * <p>This is a bound property.</p>
     *
     * @param somatic true if this variation model should include somatic variations
     */
    public void setSomatic(final boolean somatic) {
        boolean oldSomatic = this.somatic;
        this.somatic = somatic;
        propertyChangeSupport.firePropertyChange("somatic", oldSomatic, this.somatic);
    }

    /**
     * Return the network for this variation model.
     *
     * @return the network for this variation model
     */
    public CyNetwork getNetwork() {
        return network;
    }

    /**
     * Set the network for this variation model to <code>network</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param network network for this variation model
     */
    public void setNetwork(final CyNetwork network) {
        CyNetwork oldNetwork = this.network;
        this.network = network;
        propertyChangeSupport.firePropertyChange("network", oldNetwork, this.network);

        // or move these to property change listener

        // update list of nodes
        nodes.clear();
        if (this.network != null) {
            nodes.addAll(this.network.getNodeList());
        }

        // also need to remove/add network node change listener or similar
    }

    /**
     * Refresh columns.
     */
    void refreshColumns() {
        // update list of column names
        columnNames.clear();
        if (this.network != null) {
            columnNames.addAll(VariationUtils.columnNames(this.network));
        }
    }

    /**
     * Return the feature service for this variation model.
     *
     * @return the feature service for this variation model
     */
    public FeatureService getFeatureService() {
        return featureService;
    }

    /**
     * Set the feature service for this variation model to <code>featureService</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param featureService feature service for this variation model
     */
    public void setFeatureService(final FeatureService featureService) {
        FeatureService oldFeatureService = this.featureService;
        this.featureService = featureService;
        propertyChangeSupport.firePropertyChange("featureService", oldFeatureService, this.featureService);
    }

    /**
     * Return the variation service for this variation model.
     *
     * @return the variation service for this variation model
     */
    public VariationService getVariationService() {
        return variationService;
    }

    /**
     * Set the variation service for this variation model to <code>variationService</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param variationService variation service for this variation model
     */
    public void setVariationService(final VariationService variationService) {
        VariationService oldVariationService = this.variationService;
        this.variationService = variationService;
        propertyChangeSupport.firePropertyChange("variationService", oldVariationService, this.variationService);
    }

    /**
     * Return the variation consequence service for this variation model.
     *
     * @return the variation consequence service for this variation model
     */
    public VariationConsequenceService getVariationConsequenceService() {
        return variationConsequenceService;
    }

    /**
     * Set the variation consequence service for this variation model to <code>variationConsequenceService</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param variationConsequenceService variation consequence service for this variation model
     */
    public void setVariationConsequenceService(final VariationConsequenceService variationConsequenceService) {
        VariationConsequenceService oldVariationConsequenceService = this.variationConsequenceService;
        this.variationConsequenceService = variationConsequenceService;
        propertyChangeSupport.firePropertyChange("variationConsequenceService", oldVariationConsequenceService,
                this.variationConsequenceService);
    }

    /**
     * Return the variation consequence prediction service for this variation model.
     *
     * @return the variation consequence prediction service for this variation model
     */
    public VariationConsequencePredictionService getVariationConsequencePredictionService() {
        return variationConsequencePredictionService;
    }

    /**
     * Set the variation consequence prediction service for this variation model to <code>variationConsequencePredictionService</code>.
     *
     * <p>This is a bound property.</p>
     *
     * @param variationConsequencePredictionService variation consequence prediction service for this variation model
     */
    public void setVariationConsequencePredictionService(
            final VariationConsequencePredictionService variationConsequencePredictionService) {
        VariationConsequencePredictionService oldVariationConsequencePredictionService = this.variationConsequencePredictionService;
        this.variationConsequencePredictionService = variationConsequencePredictionService;
        propertyChangeSupport.firePropertyChange("variationConsequencePredictionService",
                oldVariationConsequencePredictionService, this.variationConsequencePredictionService);
    }

    // event lists

    /**
     * Return the list of nodes for this variation model.
     *
     * @return the list of nodes for this variation model
     */
    EventList<CyNode> nodes() {
        return nodes;
    }

    /**
     * Return the list of features for this variation model.
     *
     * @return the list of features for this variation model
     */
    EventList<Feature> features() {
        return features;
    }

    /**
     * Return the list of variations for this variation model.
     *
     * @return the list of variations for this variation model
     */
    EventList<Variation> variations() {
        return variations;
    }

    /**
     * Return the list of variation consequences for this variation model.
     *
     * @return the list of variation consequences for this variation model
     */
    EventList<VariationConsequence> variationConsequences() {
        return variationConsequences;
    }

    /**
     * Return the list of column names for the current network.
     *
     * @return the list of column names for the current network
     */
    EventList<String> columnNames() {
        return columnNames;
    }

    // indexes

    /**
     * Rebuild range trees.
     */
    void rebuildTrees() {
        rangeTrees.clear();
        ListMultimap<String, Feature> featuresByRegion = ArrayListMultimap.create();
        for (Feature feature : features) {
            featuresByRegion.put(feature.getRegion(), feature);
        }
        for (String region : featuresByRegion.keySet()) {
            List<Feature> regionFeatures = featuresByRegion.get(region);
            List<Range<Long>> ranges = Lists.newArrayListWithCapacity(regionFeatures.size());
            for (Feature feature : regionFeatures) {
                Range<Long> range = Range.closedOpen(feature.getStart(), feature.getEnd());
                ranges.add(range);
                featuresToRanges.put(feature, range);
            }
            rangeTrees.put(region, CenteredRangeTree.create(ranges));
        }
    }

    /**
     * Associate the specified feature with the specified node.
     *
     * @param node node, must not be null
     * @param feature feature, must not be null
     */
    void add(final CyNode node, final Feature feature) {
        checkNotNull(node);
        checkNotNull(feature);

        // todo:  merge strategy
        if (!nodes.contains(node)) {
            nodes.add(node);
        }
        if (!features.contains(feature)) {
            features.add(feature);
        }
        featuresToNodes.put(feature, node);
        nodesToFeatures.put(node, feature);
        // or create column in node table for feature, add node back-reference to Feature
    }

    /**
     * Associate the specified feature with the specified list of variation consequences.
     *
     * @param feature feature, must not be null
     * @param variationConsequences list of variation consequences, must not be null
     */
    void add(final Feature feature, final List<VariationConsequence> variationConsequences) {
        checkNotNull(feature);
        checkNotNull(variationConsequences);

        featuresToConsequences.putAll(feature, variationConsequences);
    }

    /**
     * Return zero or more features that overlap with the specified variation.
     *
     * @param variation variation, must not be null
     * @return zero or more features that overlap with the specified variation
     */
    Iterable<Feature> hit(final Variation variation) {
        checkNotNull(variation);
        List<Feature> hits = Lists.newArrayList();
        RangeTree<Long> rangeTree = rangeTrees.get(variation.getRegion());

        Range<Long> query;
        if (variation.getStart() == variation.getEnd()) {
            query = Range.singleton(variation.getStart());
        }
        // todo: Ensembl REST client variation query returns end < start for deletions, e.g ref - alt [AAA]
        else if (variation.getEnd() < variation.getStart()) {
            query = Range.closedOpen(variation.getEnd(), variation.getStart());
        } else {
            query = Range.closedOpen(variation.getStart(), variation.getEnd());
        }

        for (Range<Long> range : rangeTree.intersect(query)) {
            hits.add(featuresToRanges.inverse().get(range));
        }
        return hits;
    }

    /**
     * Return the features associated with the specified node, if any.
     *
     * @param node node, must not be null
     * @return the features associated with the specified node, or an empty list if no such feature exists
     */
    Iterable<Feature> featuresFor(final CyNode node) {
        checkNotNull(node);
        return nodesToFeatures.get(node);
    }

    /**
     * Return the nodes associated with the specified feature, if any.
     *
     * @param feature feature, must not be null
     * @return the nodes associated with the specified feature, or an empty list if no such node exists
     */
    Iterable<CyNode> nodesFor(final Feature feature) {
        checkNotNull(feature);
        return featuresToNodes.get(feature);
    }

    /**
     * Return the list of variation consequences associated with the specified feature, if any.
     *
     * @param feature feature, must not be null
     * @return the list of variation consequences associated with the specified feature, or <code>null</code> if no such feature exists (or is it an empty list?)
     */
    List<VariationConsequence> consequencesFor(final Feature feature) {
        checkNotNull(feature);
        return featuresToConsequences.get(feature);
    }

    // property change support

    /**
     * Add the specified property change listener.
     *
     * @param propertyChangeListener property change listener to add
     */
    public void addPropertyChangeListener(final PropertyChangeListener propertyChangeListener) {
        propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
    }

    /**
     * Add the specified property change listener.
     *
     * @param propertyName property name
     * @param propertyChangeListener property change listener to add
     */
    public void addPropertyChangeListener(final String propertyName,
            final PropertyChangeListener propertyChangeListener) {
        propertyChangeSupport.addPropertyChangeListener(propertyName, propertyChangeListener);
    }

    /**
     * Remove the specified property change listener.
     *
     * @param propertyChangeListener property change listener to remove
     */
    public void removePropertyChangeListener(final PropertyChangeListener propertyChangeListener) {
        propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
    }

    /**
     * Remove the specified property change listener.
     *
     * @param propertyName property name
     * @param propertyChangeListener property change listener to remove
     */
    public void removePropertyChangeListener(final String propertyName,
            final PropertyChangeListener propertyChangeListener) {
        propertyChangeSupport.removePropertyChangeListener(propertyName, propertyChangeListener);
    }
}