/*
* 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.datasource.fs;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.io.Writer;
import javax.validation.constraints.NotNull;
import org.obiba.magma.AbstractVariableValueSource;
import org.obiba.magma.Disposable;
import org.obiba.magma.Initialisable;
import org.obiba.magma.MagmaRuntimeException;
import org.obiba.magma.NoSuchValueSetException;
import org.obiba.magma.Timestamps;
import org.obiba.magma.Value;
import org.obiba.magma.ValueSet;
import org.obiba.magma.ValueTable;
import org.obiba.magma.ValueType;
import org.obiba.magma.Variable;
import org.obiba.magma.VariableEntity;
import org.obiba.magma.VariableValueSource;
import org.obiba.magma.VectorSource;
import org.obiba.magma.datasource.fs.FsDatasource.InputCallback;
import org.obiba.magma.datasource.fs.FsDatasource.OutputCallback;
import org.obiba.magma.support.AbstractValueTable;
import org.obiba.magma.support.ValueSetBean;
import org.obiba.magma.xstream.XStreamValueSet;
import com.thoughtworks.xstream.XStream;
import de.schlichtherle.io.File;
class FsValueTable extends AbstractValueTable implements Initialisable, Disposable {
/**
* The directory of this {@code ValueTable}
*/
private final File valueTableEntry;
/**
* XStream instance used to de/serialize instances.
*/
private final XStream xstream;
/**
* Our VariableEntityProvider instance
*/
private final FsVariableEntityProvider variableEntityProvider;
FsValueTable(FsDatasource datasource, String name) {
super(datasource, name);
valueTableEntry = datasource.getEntry(name);
xstream = datasource.getXStreamInstance();
setVariableEntityProvider(variableEntityProvider = new FsVariableEntityProvider(this));
}
FsValueTable(FsDatasource datasource, String name, String entityType) {
super(datasource, name);
valueTableEntry = datasource.getEntry(name);
xstream = datasource.getXStreamInstance();
setVariableEntityProvider(variableEntityProvider = new FsVariableEntityProvider(this, entityType));
}
@Override
public void initialise() {
super.initialise();
try {
variableEntityProvider.initialise();
readVariables();
} catch(RuntimeException e) {
throw e;
} catch(Exception e) {
throw new MagmaRuntimeException(e);
}
}
@Override
public void dispose() {
variableEntityProvider.dispose();
}
@NotNull
@Override
public FsDatasource getDatasource() {
return (FsDatasource) super.getDatasource();
}
@Override
public ValueSet getValueSet(VariableEntity entity) throws NoSuchValueSetException {
return new LazyValueSet(this, entity);
}
@NotNull
@Override
public FsVariableEntityProvider getVariableEntityProvider() {
return variableEntityProvider;
}
File getEntry(String name) {
return new File(valueTableEntry, name);
}
<T> T readEntry(String name, InputCallback<T> callback) {
return getDatasource().readEntry(getEntry(name), callback);
}
<T> T writeEntry(String name, OutputCallback<T> callback) {
return getDatasource().writeEntry(getEntry(name), callback);
}
Writer createWriter(String name) {
return getDatasource().createWriter(getEntry(name));
}
private void readVariables() {
readEntry("variables.xml", new InputCallback<Void>() {
@SuppressWarnings("InfiniteLoopStatement")
@Override
public Void readEntry(Reader reader) throws IOException {
ObjectInputStream ois = xstream.createObjectInputStream(reader);
try {
while(true) {
Variable variable = (Variable) ois.readObject();
addVariableValueSource(new FsVariableValueSource(variable));
}
} catch(EOFException e) {
// We reached the end of the ois.
} catch(ClassNotFoundException e) {
throw new MagmaRuntimeException(e);
}
return null;
}
});
}
private static class FsVariableValueSource extends AbstractVariableValueSource implements VariableValueSource {
private final Variable variable;
private FsVariableValueSource(Variable variable) {
this.variable = variable;
}
@NotNull
@Override
public Variable getVariable() {
return variable;
}
@NotNull
@Override
public ValueType getValueType() {
return variable.getValueType();
}
@NotNull
@Override
public Value getValue(ValueSet valueSet) {
return ((LazyValueSet) valueSet).getValueSet().getValue(variable);
}
@Override
public boolean supportVectorSource() {
return false;
}
@NotNull
@Override
public VectorSource asVectorSource() {
throw new MagmaRuntimeException("FS Datasource does not support vector source");
}
}
private class LazyValueSet extends ValueSetBean {
@SuppressWarnings("TransientFieldInNonSerializableClass")
private transient volatile XStreamValueSet valueSet;
LazyValueSet(ValueTable table, VariableEntity entity) {
super(table, entity);
}
XStreamValueSet getValueSet() {
if(valueSet == null) {
valueSet = readValueSet(getVariableEntity());
}
return valueSet;
}
private XStreamValueSet readValueSet(VariableEntity entity) {
String entryName = variableEntityProvider.getEntityFile(entity);
if(entryName == null) {
throw new NoSuchValueSetException(FsValueTable.this, entity);
}
return readEntry(entryName, new InputCallback<XStreamValueSet>() {
@Override
public XStreamValueSet readEntry(Reader reader) throws IOException {
return (XStreamValueSet) xstream.fromXML(reader);
}
});
}
}
@NotNull
@Override
public Timestamps getTimestamps() {
return new FsTimestamps(valueTableEntry);
}
}