"Invalid family alias: '%s'.", name));
}
}
if (familyDesc.getId() > 0) {
mId = new ColumnId(familyDesc.getId());
}
if (reference != null) {
if ((mId != null) && !mId.equals(reference.getId())) {
throw new InvalidLayoutException(String.format(
"Descriptor for family '%s' has ID %s but reference ID is %s.",
getName(), mId, reference.getId()));
}
mId = reference.getId();
familyDesc.setId(mId.getId());
// Cannot change family type (group-type vs map-type):
if (reference.isMapType() != this.isMapType()) {
throw new InvalidLayoutException(String.format(
"Invalid layout update for family '%s' from reference type %s to type %s.",
getName(),
reference.isMapType() ? "map" : "group",
this.isMapType() ? "map" : "group"));
}
}
if (this.isMapType()) {
// Force validation of schema:
final CellSchema referenceSchema =
(null != reference) ? reference.getDesc().getMapSchema() : null;
validateCellSchema(mLayoutVersion, mDesc.getMapSchema(), referenceSchema);
}
// Build columns:
/**
* Map of columns from the reference layout.
* Entries are removed as they are processed and linked to column descriptors.
* At the end of the process, this map must be empty.
*/
final BiMap<String, ColumnId> refCIdMap = (reference != null)
? HashBiMap.create(reference.getColumnIdNameMap().inverse())
: HashBiMap.<String, ColumnId>create();
final List<ColumnLayout> columns = Lists.newArrayList();
final Map<String, ColumnLayout> columnMap = Maps.newHashMap();
/** Map of columns in the new layout. */
final BiMap<ColumnId, String> idMap = HashBiMap.create();
/** Columns with no ID assigned yet. */
final List<ColumnLayout> unassigned = Lists.newArrayList();
final Iterator<ColumnDesc> itColumnDesc = familyDesc.getColumns().iterator();
while (itColumnDesc.hasNext()) {
final ColumnDesc columnDesc = itColumnDesc.next();
final boolean isRename = (columnDesc.getRenamedFrom() != null);
final String refCName = isRename ? columnDesc.getRenamedFrom() : columnDesc.getName();
columnDesc.setRenamedFrom(null);
if (isRename && (reference == null)) {
throw new InvalidLayoutException(String.format(
"Invalid renaming: cannot find reference family for column '%s:%s'.",
getName(), refCName));
}
final ColumnLayout refCLayout =
(reference != null) ? reference.getColumnMap().get(refCName) : null;
if (isRename && (refCLayout == null)) {
throw new InvalidLayoutException(String.format(
"Invalid renaming: cannot find column '%s:%s' in reference family.",
getName(), refCName));
}
final ColumnId refCId = refCIdMap.remove(refCName);
if (columnDesc.getDelete()) {
if (refCId == null) {
throw new InvalidLayoutException(String.format(
"Deleted column '%s:%s' does not exist in reference layout.",
mDesc.getName(), refCName));
}
itColumnDesc.remove();
continue;
}
final ColumnLayout cLayout = new ColumnLayout(columnDesc, refCLayout);
columns.add(cLayout);
for (String columnName : cLayout.getNames()) {
if (null != columnMap.put(columnName, cLayout)) {
throw new InvalidLayoutException(String.format(
"Family '%s' contains duplicate column qualifier '%s'.",
getName(), columnName));
}
}
if (cLayout.getId() != null) {
final String previous = idMap.put(cLayout.getId(), cLayout.getName());
Preconditions.checkState(previous == null,
String.format("Duplicate column ID '%s' associated to '%s' and '%s'.",
cLayout.getId(), cLayout.getName(), previous));
} else {
unassigned.add(cLayout);
}
}
if (!refCIdMap.isEmpty()) {
throw new InvalidLayoutException(String.format(
"Descriptor for family '%s' is missing columns: %s.",
getName(), Joiner.on(",").join(refCIdMap.keySet())));
}
mColumns = ImmutableList.copyOf(columns);
mColumnMap = ImmutableMap.copyOf(columnMap);
// Assign IDs to columns, build ID maps
int nextColumnId = 1;
for (ColumnLayout column : unassigned) {
Preconditions.checkState(column.getId() == null);
while (true) {
final ColumnId columnId = new ColumnId(nextColumnId);
nextColumnId += 1;
if (!idMap.containsKey(columnId)) {
column.setId(columnId);
idMap.put(columnId, column.getName());
break;