/*
* Copyright (c) 2017 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.obiba.magma.views;
import java.util.Map;
import java.util.Set;
import javax.validation.constraints.NotNull;
import org.obiba.magma.Datasource;
import org.obiba.magma.MagmaCacheExtension;
import org.obiba.magma.MagmaEngine;
import org.obiba.magma.NoSuchValueTableException;
import org.obiba.magma.Timestamped;
import org.obiba.magma.Timestamps;
import org.obiba.magma.Value;
import org.obiba.magma.ValueTable;
import org.obiba.magma.ValueTableWriter;
import org.obiba.magma.support.AbstractDatasourceWrapper;
import org.obiba.magma.support.Disposables;
import org.obiba.magma.support.Initialisables;
import org.obiba.magma.support.UnionTimestamps;
import org.obiba.magma.type.DateTimeType;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public class ViewAwareDatasource extends AbstractDatasourceWrapper {
private final Map<String, View> views;
private Value lastUpdate = DateTimeType.get().nullValue();
public ViewAwareDatasource(Datasource datasource, Iterable<View> views) {
super(datasource);
if(views == null) throw new IllegalArgumentException("views cannot be null");
this.views = Maps.newLinkedHashMap();
for(View view : views) {
this.views.put(view.getName(), view);
}
}
@Override
public void initialise() {
super.initialise();
// Initialise the views.
for(View view : views.values()) {
view.setDatasource(this);
Initialisables.initialise(view);
}
}
@Override
public void dispose() {
Disposables.dispose(getWrappedDatasource(), views);
}
@NotNull
@Override
public ValueTableWriter createWriter(@NotNull String tableName, @NotNull String entityType) {
if(hasView(tableName)) {
throw new UnsupportedOperationException("Cannot write to a View");
}
return getWrappedDatasource().createWriter(tableName, entityType);
}
@Override
public ValueTable getValueTable(String name) throws NoSuchValueTableException {
return hasView(name) ? getView(name) : getWrappedDatasource().getValueTable(name);
}
@Override
public Set<ValueTable> getValueTables() {
return Sets.union(getWrappedTables(), Sets.newHashSet(views.values()));
}
@Override
public boolean hasValueTable(String name) {
return hasView(name) || getWrappedDatasource().hasValueTable(name);
}
@Override
public boolean canDropTable(String name) {
return hasView(name) || getWrappedDatasource().canDropTable(name);
}
@Override
public void dropTable(String name) {
if(hasView(name)) {
removeView(name);
} else {
getWrappedDatasource().dropTable(name);
}
}
@Override
public boolean canRenameTable(String name) {
return hasView(name) || getWrappedDatasource().canRenameTable(name);
}
@Override
public void renameTable(String name, String newName) {
if(hasView(name)) {
renameView(name, newName);
} else {
getWrappedDatasource().renameTable(name, newName);
}
}
@NotNull
@Override
public Timestamps getTimestamps() {
final Timestamps ts = super.getTimestamps();
ImmutableSet.Builder<Timestamped> builder = ImmutableSet.builder();
builder.addAll(getViews()) //
.add(new Timestamped() {
@NotNull
@Override
public Timestamps getTimestamps() {
return ts;
}
}) //
.add(new ViewAwareDatasourceTimestamped());
return new UnionTimestamps(builder.build());
}
public Set<View> getViews() {
return ImmutableSet.copyOf(views.values());
}
/**
* Add or replace View.
*/
public synchronized void addView(View view) {
if(getWrappedDatasource().hasValueTable(view.getName())) {
throw new IllegalArgumentException(
"can't add view to datasource: a table with this name '" + view.getName() + "' already exists");
}
if(hasView(view.getName())) {
view.setCreated(getView(view.getName()).getTimestamps().getCreated());
view.setUpdated(DateTimeType.get().now());
removeView(view.getName());
}
Initialisables.initialise(view);
views.put(view.getName(), view);
view.setDatasource(this);
lastUpdate = DateTimeType.get().now();
}
public synchronized void removeView(String name) {
if(views.containsKey(name)) {
View view = views.get(name);
evictVariableEntitiesCache(view);
views.remove(name);
Disposables.dispose(view);
lastUpdate = DateTimeType.get().now();
}
}
public synchronized void renameView(String name, String newName) {
if(views.containsKey(name)) {
View view = views.remove(name);
evictVariableEntitiesCache(view);
view.setName(newName);
views.put(newName, view);
lastUpdate = DateTimeType.get().now();
}
}
private void evictVariableEntitiesCache(View view) {
if (MagmaEngine.get().hasExtension(MagmaCacheExtension.class)) {
MagmaCacheExtension cacheExtension = MagmaEngine.get().getExtension(MagmaCacheExtension.class);
if (cacheExtension.hasVariableEntitiesCache()) {
cacheExtension.getVariableEntitiesCache().evict(view.getTableReference());
}
}
}
public boolean hasView(String name) {
return views.get(name) != null;
}
@NotNull
public View getView(String name) throws NoSuchValueTableException {
View view = views.get(name);
if(view != null) {
return view;
}
throw new NoSuchValueTableException(getName(), name);
}
private Set<ValueTable> getWrappedTables() {
return getWrappedDatasource().getValueTables();
}
private class ViewAwareDatasourceTimestamped implements Timestamped {
@NotNull
@Override
public Timestamps getTimestamps() {
return new Timestamps() {
@NotNull
@Override
public Value getLastUpdate() {
return lastUpdate;
}
@NotNull
@Override
public Value getCreated() {
return DateTimeType.get().nullValue();
}
};
}
}
}