/* * #%L * gitools-core * %% * Copyright (C) 2013 Universitat Pompeu Fabra - Biomedical Genomics group * %% * 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/gpl-3.0.html>. * #L% */ package org.gitools.heatmap; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.gitools.api.matrix.IMatrixDimension; import org.gitools.api.matrix.MatrixDimensionKey; import org.gitools.api.matrix.view.Direction; import org.gitools.api.matrix.view.IMatrixViewDimension; import org.gitools.matrix.model.AbstractMatrixDimension; import org.gitools.matrix.model.hashmatrix.HashMatrixDimension; import org.gitools.utils.xml.adapter.StringArrayXmlAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.util.*; import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.not; import static com.google.common.collect.Collections2.filter; public abstract class AbstractMatrixViewDimension extends AbstractMatrixDimension implements IMatrixViewDimension { private static final Logger log = LoggerFactory.getLogger(AbstractMatrixViewDimension.class); public static final String PROPERTY_FOCUS = "selectionLead"; public static final String PROPERTY_SELECTED = "selected"; public static final String PROPERTY_VISIBLE = "visible"; @XmlElement(name = "visible") @XmlJavaTypeAdapter(StringArrayXmlAdapter.class) private List<String> visible; private transient Map<String, Integer> visibleToIndex; @XmlTransient private Set<String> selected; @XmlTransient private String focus; @XmlTransient private IMatrixDimension matrixDimension; public AbstractMatrixViewDimension() { this(null); } public AbstractMatrixViewDimension(IMatrixDimension matrixDimension) { super(); setSelected(new HashSet<String>()); this.focus = null; init(matrixDimension); } public void init(IMatrixDimension matrixDimension) { if (matrixDimension == null) { return; } setId(matrixDimension.getId()); this.matrixDimension = matrixDimension; if (visible == null) { visible = new ArrayList<>(matrixDimension.size()); showAll(); } else { //TODO check that all the visible are valid identifiers } visibleToIndex = null; } @Override public void showAll() { visible.clear(); for (String identifier : matrixDimension) { visible.add(identifier); } visibleToIndex = null; firePropertyChange(PROPERTY_VISIBLE, null, visible); } public List<String> toList() { return Collections.unmodifiableList(visible); } public void show(List<String> identifiers) { // Update visible visible = new ArrayList<>(identifiers); visibleToIndex = null; // Update lead if (!visible.contains(focus)) { focus = null; } // Remove non visible identifiers from the selection setSelected(new HashSet<>(filter(identifiers, in(selected)))); firePropertyChange(PROPERTY_VISIBLE, null, identifiers); } public void move(Direction direction, Set<String> identifiers) { if (identifiers == null || identifiers.isEmpty()) { return; } List<Integer> indices = new ArrayList<>(identifiers.size()); for (String identifier : identifiers) { indices.add(visible.indexOf(identifier)); } if (direction.getShift() == 1) { Collections.sort(indices, Collections.reverseOrder()); // We cannot move the last position to the right if (indices.get(0) == size() - 1) { return; } } else { Collections.sort(indices); // We cannot move the first position to the left if (indices.get(0) == 0) { return; } } for (int idx : indices) { Collections.swap(visible, idx + direction.getShift(), idx); } visibleToIndex = null; firePropertyChange(PROPERTY_VISIBLE, null, visible); } public void hide(Set<String> identifiers) { visible.removeAll(identifiers); visibleToIndex = null; firePropertyChange(PROPERTY_VISIBLE, null, visible); firePropertyChange(PROPERTY_FOCUS, null, focus); } @Override public void hide(Predicate<String> predicate) { visible = Lists.newArrayList(filter(visible, not(predicate))); visibleToIndex = null; firePropertyChange(PROPERTY_VISIBLE, null, visible); firePropertyChange(PROPERTY_FOCUS, null, focus); } @Override public void sort(Comparator<String> comparator) { Collections.sort(visible, comparator); visibleToIndex = null; firePropertyChange(PROPERTY_VISIBLE, null, visible); } @Override public void show(Predicate<String> predicate) { visible = new ArrayList<>(filter(visible, predicate)); visibleToIndex = null; firePropertyChange(PROPERTY_VISIBLE, null, visible); } /** * Returns selected without guaranteeing it's order represeting the heatmap order * * @return */ @Override public Set<String> getSelected() { return selected; } @Override public void select(Set<String> selected) { setSelected(selected); } @Override public void select(Predicate<String> predicate) { setSelected(Sets.newHashSet(filter(visible, predicate))); } public void selectAll() { setSelected(new HashSet<>(visible)); } public String getFocus() { return focus; } public void setFocus(String focus) { if (Objects.equal(this.focus, focus)) { return; } this.focus = focus; firePropertyChange(PROPERTY_FOCUS, null, this.focus); } @Override public MatrixDimensionKey getId() { return matrixDimension.getId(); } @Override /** * Size of items (rows or columsn) */ public int size() { return visible.size(); } @Override public String getLabel(int index) { if (index < 0 || index > size() - 1) { return null; } return visible.get(index); } @Override public int indexOf(String label) { if (label == null) { return -1; } Integer index = getVisibleToIndex().get(label); return (index == null ? -1 : index); } @Override public Iterator<String> iterator() { return Iterators.unmodifiableIterator(visible.iterator()); } private void setSelected(Set<String> selected) { this.selected = new ObservableSet<String>(selected) { @Override protected void fire() { firePropertyChange(PROPERTY_SELECTED, null, this); } }; firePropertyChange(PROPERTY_SELECTED, null, this.selected); } private Map<String, Integer> getVisibleToIndex() { if (visibleToIndex == null) { visibleToIndex = new HashMap<>(visible.size()); try { for (int i = 0; i < visible.size(); i++) { visibleToIndex.put(visible.get(i), i); } } catch (NullPointerException e) { log.error("NullPointerException @ AbstractMatrixViewDimension", e); } } return visibleToIndex; } @Override public IMatrixDimension subset(Set<String> identifiers) { return new HashMatrixDimension(getId(), Iterables.filter(visible, Predicates.in(identifiers))); } @Override public void forceUpdate(String property) { firePropertyChange(PROPERTY_VISIBLE, null, visible); } }