package com.datastax.oss.protocol.internal.response.result;
import com.datastax.oss.protocol.internal.PrimitiveCodec;
import com.datastax.oss.protocol.internal.PrimitiveSizes;
import com.datastax.oss.protocol.internal.ProtocolConstants;
import com.datastax.oss.protocol.internal.util.Flags;
import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableList;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class RowsMetadata {
public final List<ColumnSpec> columnSpecs;
public final int columnCount;
public final ByteBuffer pagingState;
public final int[] pkIndices;
public final byte[] newResultMetadataId;
public final int flags;
public RowsMetadata(
List<ColumnSpec> columnSpecs,
ByteBuffer pagingState,
int[] pkIndices,
byte[] newResultMetadataId) {
this(
computeFlags(false, columnSpecs, pagingState, newResultMetadataId),
columnSpecs,
columnSpecs.size(),
pagingState,
pkIndices,
newResultMetadataId);
}
public RowsMetadata(
int columnCount, ByteBuffer pagingState, int[] pkIndices, byte[] newResultMetadataId) {
this(
computeFlags(true, Collections.emptyList(), pagingState, newResultMetadataId),
Collections.emptyList(),
columnCount,
pagingState,
pkIndices,
newResultMetadataId);
}
public RowsMetadata(
int flags,
List<ColumnSpec> columnSpecs,
int columnCount,
ByteBuffer pagingState,
int[] pkIndices,
byte[] newResultMetadataId) {
this.columnSpecs = columnSpecs;
this.columnCount = columnCount;
this.pagingState = pagingState;
this.pkIndices = pkIndices;
this.newResultMetadataId = newResultMetadataId;
this.flags = flags;
}
protected static int computeFlags(
boolean noMetadata,
List<ColumnSpec> columnSpecs,
ByteBuffer pagingState,
byte[] newResultMetadataId) {
int flags = 0;
if (noMetadata) {
flags = Flags.add(flags, ProtocolConstants.RowsFlag.NO_METADATA);
} else if (haveSameTable(columnSpecs)) {
flags = Flags.add(flags, ProtocolConstants.RowsFlag.GLOBAL_TABLES_SPEC);
}
if (pagingState != null) {
flags = Flags.add(flags, ProtocolConstants.RowsFlag.HAS_MORE_PAGES);
}
if (newResultMetadataId != null) {
flags = Flags.add(flags, ProtocolConstants.RowsFlag.METADATA_CHANGED);
}
return flags;
}
public <B> void encode(
B dest, PrimitiveCodec<B> encoder, boolean withPkIndices, int protocolVersion) {
encoder.writeInt(flags, dest);
encoder.writeInt(columnCount, dest);
if (withPkIndices) {
if (pkIndices == null) {
encoder.writeInt(0, dest);
} else {
encoder.writeInt(pkIndices.length, dest);
for (int pkIndex : pkIndices) {
encoder.writeUnsignedShort(pkIndex, dest);
}
}
}
if (Flags.contains(flags, ProtocolConstants.RowsFlag.HAS_MORE_PAGES)) {
encoder.writeBytes(pagingState, dest);
}
if (Flags.contains(flags, ProtocolConstants.RowsFlag.METADATA_CHANGED)) {
encoder.writeShortBytes(newResultMetadataId, dest);
}
if (!Flags.contains(flags, ProtocolConstants.RowsFlag.NO_METADATA) && !columnSpecs.isEmpty()) {
boolean globalTable = Flags.contains(flags, ProtocolConstants.RowsFlag.GLOBAL_TABLES_SPEC);
if (globalTable) {
ColumnSpec firstSpec = columnSpecs.get(0);
encoder.writeString(firstSpec.ksName, dest);
encoder.writeString(firstSpec.tableName, dest);
}
for (ColumnSpec spec : columnSpecs) {
if (!globalTable) {
encoder.writeString(spec.ksName, dest);
encoder.writeString(spec.tableName, dest);
}
encoder.writeString(spec.name, dest);
spec.type.encode(dest, encoder, protocolVersion);
}
}
}
public int encodedSize(boolean withPkIndices, int protocolVersion) {
int size = PrimitiveSizes.INT;
size += PrimitiveSizes.INT;
if (Flags.contains(flags, ProtocolConstants.RowsFlag.METADATA_CHANGED)) {
size += PrimitiveSizes.sizeOfShortBytes(newResultMetadataId);
}
if (withPkIndices) {
size += PrimitiveSizes.INT;
if (pkIndices != null) {
size += pkIndices.length * PrimitiveSizes.SHORT;
}
}
if (Flags.contains(flags, ProtocolConstants.RowsFlag.HAS_MORE_PAGES)) {
size += PrimitiveSizes.sizeOfBytes(pagingState);
}
if (!Flags.contains(flags, ProtocolConstants.RowsFlag.NO_METADATA) && !columnSpecs.isEmpty()) {
boolean globalTable = Flags.contains(flags, ProtocolConstants.RowsFlag.GLOBAL_TABLES_SPEC);
if (globalTable) {
ColumnSpec firstSpec = columnSpecs.get(0);
size += PrimitiveSizes.sizeOfString(firstSpec.ksName);
size += PrimitiveSizes.sizeOfString(firstSpec.tableName);
}
for (ColumnSpec spec : columnSpecs) {
if (!globalTable) {
size += PrimitiveSizes.sizeOfString(spec.ksName);
size += PrimitiveSizes.sizeOfString(spec.tableName);
}
size += PrimitiveSizes.sizeOfString(spec.name);
size += spec.type.encodedSize(protocolVersion);
}
}
return size;
}
public static <B> RowsMetadata decode(
B source, PrimitiveCodec<B> decoder, boolean withPkIndices, int protocolVersion) {
int flags = decoder.readInt(source);
int columnCount = decoder.readInt(source);
int[] pkIndices = null;
int pkCount;
if (withPkIndices && (pkCount = decoder.readInt(source)) > 0) {
pkIndices = new int[pkCount];
for (int i = 0; i < pkCount; i++) {
pkIndices[i] = decoder.readUnsignedShort(source);
}
}
ByteBuffer state =
(Flags.contains(flags, ProtocolConstants.RowsFlag.HAS_MORE_PAGES))
? decoder.readBytes(source)
: null;
byte[] newResultMetadataId =
(Flags.contains(flags, ProtocolConstants.RowsFlag.METADATA_CHANGED))
? decoder.readShortBytes(source)
: null;
List<ColumnSpec> columnSpecs;
if (Flags.contains(flags, ProtocolConstants.RowsFlag.NO_METADATA)) {
columnSpecs = Collections.emptyList();
} else {
boolean globalTablesSpec =
Flags.contains(flags, ProtocolConstants.RowsFlag.GLOBAL_TABLES_SPEC);
String globalKsName = null;
String globalCfName = null;
if (globalTablesSpec) {
globalKsName = decoder.readString(source);
globalCfName = decoder.readString(source);
}
NullAllowingImmutableList.Builder<ColumnSpec> builder =
NullAllowingImmutableList.builder(columnCount);
for (int i = 0; i < columnCount; i++) {
String ksName = globalTablesSpec ? globalKsName : decoder.readString(source);
String cfName = globalTablesSpec ? globalCfName : decoder.readString(source);
String name = decoder.readString(source);
RawType type = RawType.decode(source, decoder, protocolVersion);
builder.add(new ColumnSpec(ksName, cfName, name, i, type));
}
columnSpecs = builder.build();
}
return new RowsMetadata(flags, columnSpecs, columnCount, state, pkIndices, newResultMetadataId);
}
private static boolean haveSameTable(List<ColumnSpec> specs) {
if (specs.isEmpty()) {
return false;
}
boolean first = true;
String ksName = null;
String tableName = null;
for (ColumnSpec spec : specs) {
if (first) {
first = false;
ksName = spec.ksName;
tableName = spec.tableName;
} else if (!Objects.equals(spec.ksName, ksName)
|| !Objects.equals(spec.tableName, tableName)) {
return false;
}
}
return true;
}
}