private final void populateFieldFromFullyQualifiedColumn( T entity, Field field, KijiColumn column, KijiRowData row) throws IOException, IllegalAccessException { if (column.maxVersions() == 1) { // Field represents a single value from a fully-qualified column: LOG.debug( "Populating field '{}' from column '{}:{}'.", field, column.family(), column.qualifier()); KijiCell<?> cell = row.getMostRecentCell(column.family(), column.qualifier()); if (cell == null) return; Object value = cell.getData(); if (field.getType() == KijiCell.class) { value = cell; } else if (field.getType() == String.class && value != null) { value = value.toString(); } // If there is no cell for a field with a primitive type, use the default value: if ((null == value) && field.getType().isPrimitive()) { value = Defaults.defaultValue(field.getType()); } field.set(entity, value); } else { // Field represents a time-series from a fully-qualified column: if (column.pageSize() > 0) { final ColumnVersionIterator<?> iterator = new ColumnVersionIterator<Object>( row, column.family(), column.qualifier(), column.pageSize()); field.set(entity, iterator); } else { Object value = null; if (field.getType() == KijiCellValueIterator.class) { value = new KijiCellValueIterator<Object>( row.iterator(column.family(), column.qualifier())); } else if (field.getType() == TimeSeries.class) { final TimeSeries<Object> timeseries = new TimeSeries<Object>(); for (final KijiCell<Object> cell : row.asIterable(column.family(), column.qualifier())) { timeseries.put(cell.getTimestamp(), cell.getData()); } value = timeseries; } field.set(entity, value); } } }
/** * Populates an entity from a row. * * @param entity Entity object to populate from a row. * @param row Kiji row to populate the entity from. * @return the populated entity. * @throws IllegalAccessException * @throws IOException */ public T populateEntityFromRow(T entity, KijiRowData row) throws IllegalAccessException, IOException { // Populate fields from the row columns: for (final Field field : mColumnFields) { final KijiColumn column = field.getAnnotation(KijiColumn.class); Preconditions.checkState(column != null); if (column.qualifier().isEmpty()) { // Field is populated from a map-type family: populateFieldFromMapTypeFamily(entity, field, column, row); } else { // Field is populated from a fully-qualified column: populateFieldFromFullyQualifiedColumn(entity, field, column, row); } } // Populate fields from the row entity ID: for (final Field field : mEntityIdFields) { final EntityIdField eidField = field.getAnnotation(EntityIdField.class); Preconditions.checkState(eidField != null); final int index = mRowKeyComponentIndexMap.get(eidField.component()); field.set(entity, row.getEntityId().getComponentByIndex(index)); } return entity; }
/** * Populates a KijiDataRequest from this entity specification. * * @param builder Builder for the KijiDataRequest to populate. */ public void populateColumnRequests(KijiDataRequestBuilder builder) { for (final Field field : mColumnFields) { final KijiColumn column = field.getAnnotation(KijiColumn.class); Preconditions.checkState(column != null); final ColumnsDef def = ColumnsDef.create() .withMaxVersions(column.maxVersions()) .withPageSize(column.pageSize()); if (column.qualifier().isEmpty()) { def.addFamily(column.family()); } else { def.add(column.family(), column.qualifier()); } builder.addColumns(def); } }
/** * Initializes a new specification for an Entity from an annotated Java class. * * @param klass Annotated Java class to derive an entity specification from. * @param kiji Kiji instance where to fetch entities from. * @throws IOException on I/O error. */ public EntitySpec(Class<T> klass, Kiji kiji) throws IOException { mClass = klass; final KijiEntity entity = klass.getAnnotation(KijiEntity.class); Preconditions.checkArgument( entity != null, "Class '{}' has no @KijiEntity annotation.", klass); mTableName = entity.table(); final KijiTable table = kiji.openTable(mTableName); try { final KijiTableLayout layout = table.getLayout(); // TODO: Support deprecated RowKeyFormat? final RowKeyFormat2 rowKeyFormat = (RowKeyFormat2) layout.getDesc().getKeysFormat(); final Map<String, RowKeyComponent> rkcMap = Maps.newHashMap(); final Map<String, Integer> rkcIndexMap = Maps.newHashMap(); for (int index = 0; index < rowKeyFormat.getComponents().size(); ++index) { final RowKeyComponent rkc = rowKeyFormat.getComponents().get(index); rkcMap.put(rkc.getName(), rkc); rkcIndexMap.put(rkc.getName(), index); } mRowKeyComponentMap = ImmutableMap.copyOf(rkcMap); mRowKeyComponentIndexMap = ImmutableMap.copyOf(rkcIndexMap); // -------------------------------------------------------------------- // Parse fields with annotations from the entity class: final List<Field> columnFields = Lists.newArrayList(); final List<Field> entityIdFields = Lists.newArrayList(); for (final Field field : mClass.getDeclaredFields()) { final KijiColumn column = field.getAnnotation(KijiColumn.class); final EntityIdField eidField = field.getAnnotation(EntityIdField.class); if ((column != null) && (eidField != null)) { throw new IllegalArgumentException( String.format( "Field '%s' cannot have both @KijiColumn and @EntityIdField annotations.", field)); } else if (column != null) { LOG.debug("Validating column field '{}'.", field); field.setAccessible(true); columnFields.add(field); final FamilyLayout flayout = layout.getFamilyMap().get(column.family()); Preconditions.checkArgument( flayout != null, "Field '%s' maps to non-existing family '%s' from table '%s'.", field, column.family(), mTableName); if (column.qualifier().isEmpty()) { // Request for a map-type family: Preconditions.checkArgument( flayout.isMapType(), "Field '%s' maps to family '%s' from table '%s' which is not a map-type family.", field, column.family(), mTableName); // Validate field type: if (column.pageSize() > 0) { Preconditions.checkArgument( MapFamilyVersionIterator.class.isAssignableFrom(field.getType()), "Fields mapped to map-type family with paging enabled must be " + "MapFamilyVersionIterator, got '{}'.", field.getType()); } else { // TODO Validate type when no paging enabled on map-type family. } } else { // Request for a fully-qualified column: final ColumnLayout clayout = flayout.getColumnMap().get(column.qualifier()); Preconditions.checkArgument( flayout != null, "Field '%s' maps to non-existing column '%s:%s' from table '%s'.", field, column.family(), column.qualifier(), mTableName); // Validate field type: if (column.pageSize() > 0) { Preconditions.checkArgument( ColumnVersionIterator.class.isAssignableFrom(field.getType()), "Fields mapped to column with paging enabled must be " + "ColumnVersionIterator, got '{}'.", field.getType()); } else { // TODO Validate type when no paging enabled on the column. } } } else if (eidField != null) { LOG.debug("Validating entity ID field '{}'.", field); field.setAccessible(true); entityIdFields.add(field); final RowKeyComponent rkc = mRowKeyComponentMap.get(eidField.component()); Preconditions.checkArgument( rkc != null, "Field '%s' maps to unknown entity ID component '%s'.", field, eidField.component()); } else { LOG.debug("Ignoring field '{}' with no annotation.", field); } } mColumnFields = ImmutableList.copyOf(columnFields); mEntityIdFields = ImmutableList.copyOf(entityIdFields); } finally { table.release(); } }