package cassandra.cql;
import cassandra.cql.RowMetadata.Column;
import cassandra.cql.type.CQL3Type;
import cassandra.cql.type.CQL3TypeError;
import java.util.*;
public class RowMetadata implements Iterable<Column> {
private final Column[] columns;
private final Map<String, int[]> namemap;
public RowMetadata(Column[] columns) {
this.columns = columns;
namemap = new HashMap<String, int[]>(columns.length);
for (int i = 0; i < columns.length; i++) {
String name = columns[i].name().toLowerCase();
int[] idxs;
int[] prev = namemap.get(name);
if (prev == null) {
idxs = new int[]{i};
} else {
idxs = new int[prev.length + 1];
System.arraycopy(prev, 0, idxs, 0, prev.length);
idxs[idxs.length - 1] = i;
}
namemap.put(name, idxs);
}
}
public String getKeyspaceName(int column) {
return columns[column].keyspace();
}
public String getKeyspaceName(String column) {
return getKeyspaceName(getColumnIndex(column));
}
public String getTableName(int column) {
return columns[column].table();
}
public String getTableName(String column) {
return getTableName(getColumnIndex(column));
}
public String getColumnName(int column) {
return columns[column].name();
}
public CQL3Type getColumnType(int column) {
return columns[column].type();
}
public CQL3Type getColumnType(String column) {
return getColumnType(getColumnIndex(column));
}
public int getColumnIndex(String column) {
return getColumnIndexArray(column)[0];
}
public int[] getColumnIndexArray(String column) {
String name = column;
boolean caseSensitive = false;
if (name.length() >= 2 && name.charAt(0) == '"' && name.charAt(name.length() - 1) == '"') {
name = name.substring(1, name.length() - 1);
caseSensitive = true;
}
int[] idxs = namemap.get(name.toLowerCase());
if (idxs == null) {
throw new IllegalArgumentException(String.format("no matching column found, %s", column));
}
if (!caseSensitive) {
return idxs;
}
int found = 0;
for (int idx : idxs) {
if (name.equals(columns[idx].name)) {
found++;
}
}
if (found == idxs.length) {
return idxs;
}
int[] result = new int[found];
int j = 0;
for (int idx : idxs) {
if (name.equals(columns[idx].name)) {
result[j++] = idx;
}
}
return result;
}
public CQL3Type validateColumnType(int column, CQL3Type.Name name) {
CQL3Type columnType = getColumnType(column);
if (name != columnType.name()) {
throw new CQL3TypeError(String.format("column type does not match: %s (expected: %s)", getColumnName(column), columnType.name()));
}
return columnType;
}
public CQL3Type validateColumnType(int column, CQL3Type.Name name1, CQL3Type.Name name2) {
CQL3Type columnType = getColumnType(column);
if (name1 != columnType.name() && name2 != columnType.name()) {
throw new CQL3TypeError(String.format("column type does not match: %s (expected: %s)", getColumnName(column), columnType.name()));
}
return columnType;
}
public CQL3Type validateColumnType(int column, CQL3Type.Name name1, CQL3Type.Name name2, CQL3Type.Name name3) {
CQL3Type columnType = getColumnType(column);
if (name1 != columnType.name() && name2 != columnType.name() && name3 != columnType.name()) {
throw new CQL3TypeError(String.format("column type does not match: %s (expected: %s)", getColumnName(column), columnType.name()));
}
return columnType;
}
public CQL3Type validateColumnType(int column, CQL3Type columnType, Object value) {
String columnName = getColumnName(column);
CQL3Type.Name actualName = getColumnType(column).name();
if (actualName != columnType.name()) {
throw new CQL3TypeError(String.format("column type does not match: %s, %s (expected: %s)", columnName, columnType.name(), actualName));
}
switch (columnType.name()) {
case LIST:
List<?> list = (List<?>)value;
if (!list.isEmpty()) {
Class<?> providedClass = list.get(0).getClass();
Class<?> expectedClass = columnType.typeArguments().get(0).name().javaType;
if (!expectedClass.isAssignableFrom(providedClass)) {
throw new CQL3TypeError(String.format("column type does not match: %s, List<%s> (expected: List<%s>)", columnName, providedClass.getName(), expectedClass.getName()));
}
}
break;
case SET:
Set<?> set = (Set<?>)value;
if (!set.isEmpty()) {
Class<?> providedClass = set.iterator().next().getClass();
Class<?> expectedClass = columnType.typeArguments().get(0).name().javaType;
if (!expectedClass.isAssignableFrom(providedClass)) {
throw new CQL3TypeError(String.format("column type does not match: %s, Set<%s> (expected: Set<%s>)", columnName, providedClass.getName(), expectedClass.getName()));
}
}
break;
case MAP:
Map<?, ?> map = (Map<?, ?>)value;
if (!map.isEmpty()) {
Map.Entry<?, ?> entry = map.entrySet().iterator().next();
Class<?> providedKeysClass = entry.getKey().getClass();
Class<?> providedValuesClass = entry.getValue().getClass();
Class<?> expectedKeyClass = columnType.typeArguments().get(0).name().javaType;
Class<?> expectedValueClass = columnType.typeArguments().get(1).name().javaType;
if (!expectedKeyClass.isAssignableFrom(providedKeysClass) || !expectedValueClass.isAssignableFrom(providedValuesClass)) {
throw new CQL3TypeError(String.format("column type does not match: %s, Map<%s, %s> (expected: Map<%s, %s>)", columnName, providedKeysClass.getName(), providedValuesClass.getName(), expectedKeyClass.getName(), expectedValueClass.getName()));
}
}
break;
default:
Class<?> providedClass = value.getClass();
Class<?> expectedClass = columnType.name().javaType;
if (!expectedClass.isAssignableFrom(providedClass)) {
throw new CQL3TypeError(String.format("column type does not match: %s, %s (expected: %s)", columnName, providedClass.getName(), expectedClass.getName()));
}
break;
}
return columnType;
}
public Column getColumn(int index) {
return columns[index];
}
public int getColumnCount() {
return columns.length;
}
@Override
public Iterator<Column> iterator() {
return Arrays.asList(columns).iterator();
}
public static class Column {
private String keyspace;
private String table;
private String name;
private CQL3Type type;
public Column(String keyspace, String table, String name, CQL3Type type) {
this.keyspace = keyspace;
this.table = table;
this.name = name;
this.type = type;
}
public String keyspace() {
return keyspace;
}
public String table() {
return table;
}
public String name() {
return name;
}
public CQL3Type type() {
return type;
}
@Override
public String toString() {
return String.format("%s.%s.%s(%s)", keyspace, table, name, type.name());
}
}
}