/*
* #%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.matrix.model.hashmatrix;
import org.gitools.api.matrix.IMatrix;
import org.gitools.api.matrix.IMatrixDimension;
import org.gitools.api.matrix.IMatrixLayer;
import org.gitools.api.matrix.MatrixDimensionKey;
import org.gitools.matrix.model.AbstractMatrix;
import org.gitools.matrix.model.MatrixLayers;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class HashMatrix extends AbstractMatrix<MatrixLayers<IMatrixLayer>, HashMatrixDimension> {
private Map<String, Map> values;
public HashMatrix(MatrixLayers layers, MatrixDimensionKey... dimensions) {
this(layers, createHashMatrixDimensions(dimensions));
}
public HashMatrix(MatrixLayers<? extends IMatrixLayer> layers, IMatrixDimension... dimensions) {
this(layers, createHashMatrixDimensions(dimensions));
}
public HashMatrix(MatrixLayers<? extends IMatrixLayer> layers, HashMatrixDimension... dimensions) {
super((MatrixLayers<IMatrixLayer>) layers, dimensions);
this.values = new ConcurrentHashMap<>();
for (IMatrixLayer layer : layers) {
this.values.put(layer.getId(), new ConcurrentHashMap());
}
}
@Override
public <T> T get(IMatrixLayer<T> layer, String... identifiers) {
Map result;
try {
result = values.get(layer.getId());
} catch (NullPointerException e) {
return null;
}
if (result == null) {
return null;
}
String identifier = identifiers[0];
for (int i = 1; i < identifiers.length; i++) {
if (identifier == null || !result.containsKey(identifier)) {
return null;
}
result = (Map) result.get(identifier);
identifier = identifiers[i];
}
if (identifier == null || !result.containsKey(identifier)) {
return null;
}
return (T) result.get(identifier);
}
public <T> void set(IMatrixLayer<T> layer, T value, String... identifiers) {
assert identifiers.length == getDimensionKeys().length : "Matrix of dimension " + getDimensionKeys().length + " and position of " + identifiers.length + " elements";
if (value == null) {
return;
}
Map result = values.get(layer.getId());
if (result == null) {
throw new UnsupportedOperationException("Layer '" + layer.getId() + "' not found");
}
String identifier = identifiers[0];
HashMatrixDimension dimension = getDimension(getDimensionKeys()[0]);
for (int i = 1; i < identifiers.length; i++) {
if (!result.containsKey(identifier)) {
result.put(identifier, new ConcurrentHashMap());
dimension.add(identifier);
}
result = (Map) result.get(identifier);
identifier = identifiers[i];
dimension = getDimension(getDimensionKeys()[i]);
}
if (!result.containsKey(identifier)) {
dimension.add(identifier);
}
result.put(identifier, value);
hasChanged = true;
}
/**
* You must call this method after load all values into the matrix.
* Some implementations must prepare the matrix to be read and improve
* the read performance. Also the @see isChanged() will be true only
* if there is any change after this method has been call.
*/
public void init() {
this.hasChanged = false;
}
public void addLayer(IMatrixLayer layer) {
getLayers().add(layer);
values.put(layer.getId(), new ConcurrentHashMap());
}
public void removeLayer(IMatrixLayer layer) {
getLayers().remove(layer);
values.remove(layer.getId());
hasChanged = true;
}
public void copyLayerValues(String layerId, HashMatrix fromMatrix) {
if (getLayers().get(layerId) == null) {
throw new UnsupportedOperationException("Unknown '" + layerId + "' layer in this matrix");
}
// Copy values map
Map values = fromMatrix.values.get(layerId);
this.values.put(layerId, values);
// Update dimension identifiers
for (MatrixDimensionKey dimensionKey : getDimensionKeys()) {
HashMatrixDimension thisDimension = getDimension(dimensionKey);
HashMatrixDimension fromDimension = fromMatrix.getDimension(dimensionKey);
if (fromDimension == null) {
throw new UnsupportedOperationException("Impossible to copy a matrix with different dimensions.");
}
for (String identifier : fromDimension) {
thisDimension.add(identifier);
}
}
this.hasChanged = true;
}
private static HashMatrixDimension[] createHashMatrixDimensions(MatrixDimensionKey[] identifiers) {
HashMatrixDimension[] dimensions = new HashMatrixDimension[identifiers.length];
for (int i = 0; i < identifiers.length; i++) {
dimensions[i] = new HashMatrixDimension(identifiers[i]);
}
return dimensions;
}
private static HashMatrixDimension[] createHashMatrixDimensions(IMatrixDimension[] dimensions) {
HashMatrixDimension[] hashDimensions = new HashMatrixDimension[dimensions.length];
for (int i = 0; i < dimensions.length; i++) {
IMatrixDimension dimension = dimensions[i];
if (dimension instanceof HashMatrixDimension) {
hashDimensions[i] = (HashMatrixDimension) dimension;
} else {
hashDimensions[i] = new HashMatrixDimension(dimensions[i].getId(), dimensions[i]);
}
}
return hashDimensions;
}
@Override
public IMatrix subset(IMatrixDimension... dimensionSubsets) {
Map<MatrixDimensionKey, HashMatrixDimension> allDimensions = new HashMap<>(getDimensionKeys().length);
// Load all dimensions
for (MatrixDimensionKey key : getDimensionKeys()) {
allDimensions.put(key, getDimension(key));
}
// Override subset dimensions
for (IMatrixDimension dimension : dimensionSubsets) {
if (dimension instanceof HashMatrixDimension) {
allDimensions.put(dimension.getId(), (HashMatrixDimension) dimension);
} else {
allDimensions.put(dimension.getId(), new HashMatrixDimension(dimension.getId(), dimension));
}
}
return new SubMatrix(getLayers(), allDimensions.values());
}
private class SubMatrix extends AbstractMatrix<MatrixLayers<IMatrixLayer>, HashMatrixDimension> {
public SubMatrix(MatrixLayers layers, Collection<HashMatrixDimension> identifiers) {
super(layers, (HashMatrixDimension[]) identifiers.toArray());
}
@Override
public <T> T get(IMatrixLayer<T> layer, String... identifiers) {
return HashMatrix.this.get(layer, identifiers);
}
@Override
public <T> void set(IMatrixLayer<T> layer, T value, String... identifiers) {
throw new UnsupportedOperationException("The subset matrix are read only matrix");
}
@Override
public boolean isChanged() {
return false;
}
}
}