/* * 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.jdbc; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.obiba.magma.Timestamps; import org.obiba.magma.Value; import org.obiba.magma.Variable; import org.obiba.magma.VariableEntity; import org.obiba.magma.support.ValueSetBean; import javax.validation.constraints.NotNull; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * Provide the {@link Value}s for a {@link VariableEntity}. Non binary data are cached, whereas the binary ones are * fetched upon request (more database extractions but less memory usage). */ public class JdbcValueSet extends ValueSetBean { private final Map<String, Value> resultSetCache = Maps.newHashMap(); private final JdbcValueSetFetcher fetcher; public JdbcValueSet(final JdbcValueTable valueTable, VariableEntity variableEntity) { super(valueTable, variableEntity); this.fetcher = new JdbcValueSetFetcher(valueTable); } @NotNull @Override public JdbcValueTable getValueTable() { return (JdbcValueTable) super.getValueTable(); } @NotNull @Override public Timestamps getTimestamps() { return new JdbcValueSetTimestamps(this); } public Value getValue(Variable variable) { if (variable.getValueType().isBinary()) return getBinaryValue(variable); loadResultSetCache(); Value value = convertValue(variable, resultSetCache.get(variable.getName())); resultSetCache.put(variable.getName(), value); return value; } public Value getCreated() { loadResultSetCache(); if (!getValueTable().hasCreatedTimestampColumn()) return null; String createdColName = getValueTable().getCreatedTimestampColumnName(); if (resultSetCache.containsKey(createdColName)) return resultSetCache.get(createdColName); return null; } public Value getUpdated() { loadResultSetCache(); if (!getValueTable().hasUpdatedTimestampColumn()) return null; String updatedColName = getValueTable().getUpdatedTimestampColumnName(); if (resultSetCache.containsKey(updatedColName)) return resultSetCache.get(updatedColName); return null; } // // Private methods // private Value getBinaryValue(Variable variable) { List<Map<String, Value>> res = fetcher.loadVariableValues(variable, getVariableEntity()); if (res.isEmpty()) return variable.isRepeatable() ? variable.getValueType().nullSequence() : variable.getValueType().nullValue(); Value value = variable.isRepeatable() && getValueTable().isMultilines() ? variable.getValueType().sequenceOf(res.stream().map(m -> m.get(variable.getName())).collect(Collectors.toList())) : res.get(0).get(variable.getName()); return convertValue(variable, value); } /** * Convert the value as loaded from the SQL table into the expected variable value type (column type * may not match exactly the variable value type). * * @param variable * @param value * @return */ private Value convertValue(Variable variable, Value value) { if (value == null) return variable.isRepeatable() ? variable.getValueType().nullSequence() : variable.getValueType().nullValue(); if (value.getValueType() != variable.getValueType()) { return variable.isRepeatable() ? convertToSequence(variable, value) : variable.getValueType().convert(value); } if (variable.isRepeatable() && !value.isSequence()) { return convertToSequence(variable, value); } if (!variable.isRepeatable() && value.isSequence()) { return value.asSequence().getSize() == 0 ? variable.getValueType().nullValue() : value.asSequence().get(0); } return value; } private Value convertToSequence(Variable variable, Value value) { if (value.isSequence()) return value; if (getValueTable().isMultilines()) { // the observed value is not a sequence because there was only one line read for the entity return variable.getValueType().sequenceOf(Lists.newArrayList(value)); } return value.isNull() ? variable.getValueType().nullSequence() : variable.getValueType().sequenceOf(value.toString()); } private synchronized void loadResultSetCache() { if (resultSetCache.isEmpty()) { populateResultSetCache(fetcher.loadNonBinaryVariableValues(getVariableEntity())); } } void populateResultSetCache(List<Map<String, Value>> rows) { if (rows == null) return; rows.forEach(row -> row.forEach((key, value) -> { if (resultSetCache.containsKey(key)) { Value current = resultSetCache.get(key); if (current.isSequence()) { ((List<Value>)resultSetCache.get(key).asSequence().getValue()).add(value); } else { resultSetCache.put(key, current.getValueType().sequenceOf(Lists.newArrayList(current, value))); } } else { resultSetCache.put(key, value); } }) ); } }