/*
* 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.spss;
import java.io.IOException;
import java.util.*;
import javax.annotation.Nullable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.obiba.magma.Datasource;
import org.obiba.magma.Disposable;
import org.obiba.magma.NoSuchValueSetException;
import org.obiba.magma.Timestamps;
import org.obiba.magma.Value;
import org.obiba.magma.ValueSet;
import org.obiba.magma.ValueType;
import org.obiba.magma.VariableEntity;
import org.obiba.magma.datasource.spss.support.SpssDatasourceParsingException;
import org.obiba.magma.datasource.spss.support.SpssVariableTypeMapper;
import org.obiba.magma.datasource.spss.support.SpssVariableValueFactory;
import org.obiba.magma.datasource.spss.support.SpssVariableValueSourceFactory;
import org.obiba.magma.support.AbstractValueTable;
import org.obiba.magma.support.DatasourceParsingException;
import org.obiba.magma.support.VariableEntityBean;
import org.obiba.magma.support.VariableEntityProvider;
import org.obiba.magma.type.DateTimeType;
import org.opendatafoundation.data.spss.SPSSFile;
import org.opendatafoundation.data.spss.SPSSVariable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableSet;
public class SpssValueTable extends AbstractValueTable implements Disposable {
private final static Logger log = LoggerFactory.getLogger(SpssValueTable.class);
private final SPSSFile spssFile;
private final String locale;
private final String idVariable;
private int idVariableIndex = 0;
private Map<String, List<Integer>> entityToVariableIndex = new HashMap<>();
public SpssValueTable(Datasource datasource, String name, String entityType, String locale, String idVariable, SPSSFile spssFile) {
super(datasource, name);
this.spssFile = spssFile;
this.locale = locale;
this.idVariable = idVariable == null || idVariable.trim().isEmpty() ? null : idVariable;
loadMetadata();
if (this.idVariable != null) {
for (int i = 0; i<spssFile.getVariableCount(); i++) {
SPSSVariable var = spssFile.getVariable(i);
if (var.getName().equals(idVariable)) {
idVariableIndex = i;
break;
}
}
}
setVariableEntityProvider(new SpssVariableEntityProvider(entityType, this.idVariableIndex));
}
@Override
public void initialise() {
initializeVariableSources();
super.initialise();
}
@Override
public ValueSet getValueSet(VariableEntity entity) throws NoSuchValueSetException {
return new SpssValueSet(this, entity, idVariableIndex, spssFile, entityToVariableIndex.get(entity.getIdentifier()), isMultilines());
}
@NotNull
@Override
public Timestamps getTimestamps() {
return new Timestamps() {
@NotNull
@Override
public Value getLastUpdate() {
Date lastModified = new Date(spssFile.file.lastModified());
return DateTimeType.get().valueOf(lastModified);
}
@NotNull
@Override
public Value getCreated() {
// Not currently possible to read a file creation timestamp. Coming in JDK 7 NIO.
return DateTimeType.get().nullValue();
}
};
}
public boolean isMultilines() {
// either detected or configured
return ((SpssVariableEntityProvider)getVariableEntityProvider()).isMultilines() || getSpssDatasource().isMultilines();
}
//
// Private methods
//
public SpssDatasource getSpssDatasource() {
return (SpssDatasource) super.getDatasource();
}
private void initializeVariableSources() {
addVariableValueSources(new SpssVariableValueSourceFactory(spssFile, getEntityType(), locale, idVariableIndex, entityToVariableIndex, isMultilines() ? getName() : null));
}
private void loadMetadata() {
if(spssFile.isMetadataLoaded) {
return;
}
try {
spssFile.loadMetadata();
} catch(Exception e) {
String fileName = spssFile.file.getName();
throw new DatasourceParsingException("Failed load meta data in file " + fileName, e, "SpssFailedToLoadMetadata",
fileName);
}
}
@Override
public void dispose() {
if(spssFile != null) {
try {
spssFile.close();
} catch(IOException e) {
log.warn("Error occurred while closing SPSS file: {}", e.getMessage());
}
}
}
//
// Inner Classes
//
private class SpssVariableEntityProvider implements VariableEntityProvider {
@NotNull
private final String entityType;
private final int idVariableIndex;
private Set<VariableEntity> variableEntities;
boolean multilines = false;
private SpssVariableEntityProvider(@Nullable String entityType, int idVariableIndex) {
this.entityType = entityType == null || entityType.trim().isEmpty() ? "Participant" : entityType.trim();
this.idVariableIndex = idVariableIndex;
}
@NotNull
@Override
public String getEntityType() {
return entityType;
}
@Override
public boolean isForEntityType(String anEntityType) {
return getEntityType().equals(anEntityType);
}
@NotNull
@Override
public Set<VariableEntity> getVariableEntities() {
if(variableEntities == null) {
loadData();
variableEntities = getVariableEntitiesInternal();
}
return variableEntities;
}
private boolean isMultilines() {
// make sure entities have been scanned
getVariableEntities();
return multilines;
}
private ImmutableSet<VariableEntity> getVariableEntitiesInternal() {
Collection<String> entityIdentifiers = new HashSet<>();
ImmutableSet.Builder<VariableEntity> entitiesBuilder = ImmutableSet.builder();
SPSSVariable entityVariable = spssFile.getVariable(idVariableIndex);
int numberOfObservations = entityVariable.getNumberOfObservations();
ValueType valueType = SpssVariableTypeMapper.map(entityVariable);
for(int i = 1; i <= numberOfObservations; i++) {
Value identifierValue = new SpssVariableValueFactory(Lists.newArrayList(i), entityVariable, valueType, true, false).create();
if(identifierValue.isNull()) {
throw new SpssDatasourceParsingException("Empty entity identifier found.", "SpssEmptyIdentifier",
entityVariable.getName(), i).dataInfo(entityVariable.getName(), i);
}
String identifier = identifierValue.getValue().toString();
if(entityIdentifiers.contains(identifier)) {
multilines = true;
entityToVariableIndex.get(identifier).add(i);
} else {
entityToVariableIndex.put(identifier, Lists.newArrayList(i));
entitiesBuilder.add(new VariableEntityBean(entityType, identifier));
entityIdentifiers.add(identifier);
}
}
return entitiesBuilder.build();
}
private void loadData() {
if(spssFile.isDataLoaded) {
return;
}
try {
if(!spssFile.isMetadataLoaded) {
loadMetadata();
}
spssFile.loadData();
} catch(Exception e) {
String fileName = spssFile.file.getName();
throw new DatasourceParsingException("Failed load data in file " + fileName, e, "SpssFailedToLoadData", fileName);
}
}
}
}