/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.datastore.document.association.spi;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.ogm.datastore.spi.TupleSnapshot;
import org.hibernate.ogm.grid.AssociationKey;
import org.hibernate.ogm.grid.RowKey;
/**
* A {@link TupleSnapshot} which represents one row of an association.
* <p>
* It obtains its values from the store-specific native representation of the association row as well as the association
* key. Column values from both sources are exposed in a uniformed manner. It retains a reference to the store-specific
* association row, retrieving values from it via the {@link AssociationRowAccessor} contract.
* <p>
* The key of the row can be retrieved via {@link #getKey()}, again its values are obtained from the association key if
* present there or from the native association representation otherwise.
*
* @author Gunnar Morling
*/
public class AssociationRow<R> implements TupleSnapshot {
/**
* Contract for obtaining association tuple values from the store-specific representation of an association row.
* Columns not present in the association key will be retrieved via this contract.
* <p>
* Implementations should be stateless and one single instance should be used to access all associations of a given
* datastore.
*
* @param <R> The store-specific type for representing association rows
*/
public interface AssociationRowAccessor<R> {
Set<String> getColumnNames(R row);
Object get(R row, String column);
}
private final AssociationKey associationKey;
private final AssociationRowAccessor<R> accessor;
private final R row;
private final Set<String> columnNames;
private final RowKey rowKey;
public AssociationRow(AssociationKey associationKey, AssociationRowAccessor<R> accessor, R row) {
this.associationKey = associationKey;
this.accessor = accessor;
this.row = row;
this.columnNames = Collections.unmodifiableSet( buildColumnNames( associationKey, accessor.getColumnNames( row ) ) );
this.rowKey = buildRowKey( associationKey, row, accessor );
}
private static Set<String> buildColumnNames(AssociationKey associationKey, Set<String> columnsFromRow) {
Set<String> columnNames = new HashSet<String>( columnsFromRow.size() + associationKey.getColumnNames().length );
columnNames.addAll( columnsFromRow );
Collections.addAll( columnNames, associationKey.getColumnNames() );
return columnNames;
}
/**
* Creates the row key of the given association row; columns present in the given association key will be obtained
* from there, all other columns from the given native association row.
*/
private static <R> RowKey buildRowKey(AssociationKey associationKey, R row, AssociationRowAccessor<R> accessor) {
String table = associationKey.getTable();
String[] columnNames = associationKey.getMetadata().getRowKeyColumnNames();
Object[] columnValues = new Object[columnNames.length];
for ( int i = 0; i < columnNames.length; i++ ) {
String columnName = columnNames[i];
columnValues[i] = associationKey.getMetadata().isKeyColumn( columnName ) ? associationKey.getColumnValue( columnName ) : accessor.get( row, columnName );
}
return new RowKey( table , columnNames, columnValues, null );
}
@Override
public Object get(String column) {
return associationKey.getMetadata().isKeyColumn( column ) ? associationKey.getColumnValue( column ) : accessor.get( row, column );
}
@Override
public boolean isEmpty() {
return columnNames.isEmpty();
}
@Override
public Set<String> getColumnNames() {
return columnNames;
}
/**
* Returns the key of this association row.
*
* @return The key of this association row
*/
public RowKey getKey() {
return rowKey;
}
}