/*
* #%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);
}
}