/* * 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.hibernate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import org.hibernate.*; import org.hibernate.criterion.Restrictions; import org.obiba.core.domain.IEntity; import org.obiba.magma.*; import org.obiba.magma.datasource.hibernate.converter.HibernateValueLoaderFactory; import org.obiba.magma.datasource.hibernate.converter.VariableConverter; import org.obiba.magma.datasource.hibernate.domain.VariableState; import org.obiba.magma.type.BinaryType; import javax.validation.constraints.NotNull; import java.io.Serializable; import java.util.Iterator; import java.util.Map; import java.util.SortedSet; /** * Gives access to the persisted {@link Variable} and its {@link Value}s. */ class HibernateVariableValueSource extends AbstractVariableValueSource implements VariableValueSource, VectorSource { private HibernateValueTable table; private final String name; private Serializable variableId; private Variable variable; HibernateVariableValueSource(HibernateValueTable table, @NotNull VariableState state, boolean unmarshall) { this.table = table; //noinspection ConstantConditions if (state == null) throw new IllegalArgumentException("state cannot be null"); name = state.getName(); variableId = state.getId(); if (unmarshall) { unmarshall(state); } } private Session getCurrentSession() { return table.getDatasource().getSessionFactory().getCurrentSession(); } public VariableState getVariableState() { return (VariableState) getCurrentSession().get(VariableState.class, ensureVariableId()); } @NotNull @Override public synchronized Variable getVariable() { if (variable == null) { VariableState state = (VariableState) getCurrentSession().createCriteria(VariableState.class) .add(Restrictions.idEq(ensureVariableId())).setFetchMode("categories", FetchMode.JOIN) // .uniqueResult(); unmarshall(state); } return variable; } @NotNull @Override public Value getValue(ValueSet valueSet) { HibernateValueSet hibernateValueSet = (HibernateValueSet) valueSet; ensureVariableId(); return hibernateValueSet.getValue(getVariable()); } @Override public boolean supportVectorSource() { return true; } @NotNull @Override public VectorSource asVectorSource() { return this; } @Override public Iterable<Value> getValues(final SortedSet<VariableEntity> entities) { if (entities.isEmpty()) { return ImmutableList.of(); } return () -> new ValueIterator(entities.iterator()); } @NotNull @Override public ValueType getValueType() { return getVariable().getValueType(); } /** * Initialises the {@code variable} attribute from the provided state * * @param state */ private void unmarshall(VariableState state) { variable = VariableConverter.getInstance().unmarshal(state, null); } private Serializable ensureVariableId() { if (variableId == null) { IEntity state = (IEntity) getCurrentSession().createCriteria(VariableState.class) // .add(Restrictions.eq("name", name))// .add(Restrictions.eq("valueTable", table.getValueTableState())).uniqueResult(); if (state == null) throw new IllegalStateException("variable '" + name + "' not persisted yet."); variableId = state.getId(); } return variableId; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof HibernateVariableValueSource)) { return super.equals(obj); } HibernateVariableValueSource rhs = (HibernateVariableValueSource) obj; return name.equals(rhs.name); } @Override public int hashCode() { return name.hashCode(); } private class ValueIterator implements Iterator<Value> { private final ScrollableResults results; private boolean hasNextResults; private boolean closed; private final Iterator<VariableEntity> entities; private final Map<String, Value> valueMap = Maps.newHashMap(); private ValueIterator(Iterator<VariableEntity> entities) { this.entities = entities; Query query = getCurrentSession().getNamedQuery("allValues") // .setParameter("valueTableId", table.getValueTableState().getId()) // .setParameter("variableId", ensureVariableId()); results = query.scroll(ScrollMode.FORWARD_ONLY); hasNextResults = results.next(); } @Override public boolean hasNext() { return entities.hasNext(); } @Override public Value next() { VariableEntity entity = entities.next(); if (valueMap.containsKey(entity.getIdentifier())) return getValueFromMap(entity); boolean found = false; // Scroll until we find the required entity or reach the end of the results while (hasNextResults && !found) { String id = results.getString(0); Value value = getValue((Serializable) results.get(2), (Value) results.get(1)); valueMap.put(id, value); if (entity.getIdentifier().equals(id)) { found = true; } hasNextResults = results.next(); } closeCursorIfNecessary(); if (valueMap.containsKey(entity.getIdentifier())) return getValueFromMap(entity); return getVariable().isRepeatable() ? getValueType().nullSequence() : getValueType().nullValue(); } /** * No duplicate of entities, so remove value from map once get. * * @param entity * @return */ private Value getValueFromMap(VariableEntity entity) { Value value = valueMap.get(entity.getIdentifier()); valueMap.remove(entity.getIdentifier()); return value; } private Value getValue(Serializable valueSetId, Value value) { if (value == null) { return getVariable().isRepeatable() ? getValueType().nullSequence() : getValueType().nullValue(); } if (getValueType().equals(BinaryType.get())) { ValueLoaderFactory factory = new HibernateValueLoaderFactory(table.getDatasource().getSessionFactory(), ensureVariableId(), valueSetId); return getVariable().isRepeatable() ? BinaryType.get().sequenceOfReferences(factory, value) : BinaryType.get().valueOfReference(factory, value); } return value; } @Override public void remove() { throw new UnsupportedOperationException(); } private void closeCursorIfNecessary() { if (!closed) { // Close the cursor if we don't have any more results or no more entities to return if (!hasNextResults || !hasNext()) { closed = true; results.close(); } } } } }