com.datastax.oss.driver.internal.core.type.codec.extras.array.ObjectListToArrayCodec Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.oss.driver.internal.core.type.codec.extras.array;
import com.datastax.oss.driver.api.core.ProtocolVersion;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.Array;
import java.nio.ByteBuffer;
import java.util.Objects;
import net.jcip.annotations.Immutable;
/**
* Codec dealing with Java object arrays. Serialization and deserialization of elements in the array
* is delegated to the provided element codec.
*
* For example, to create a codec that maps {@code list} to {@code String[]}, declare the
* following:
*
* {@code
* ObjectListToArrayCodec stringArrayCodec = new ObjectListToArrayCodec<>(TypeCodecs.TEXT);
* }
*
* @param The Java array component type this codec handles
*/
@Immutable
public class ObjectListToArrayCodec extends AbstractListToArrayCodec {
private final TypeCodec elementCodec;
public ObjectListToArrayCodec(@NonNull TypeCodec elementCodec) {
super(
DataTypes.listOf(
Objects.requireNonNull(elementCodec, "elementCodec must not be null").getCqlType()),
GenericType.arrayOf(elementCodec.getJavaType()));
this.elementCodec = elementCodec;
}
@Override
public boolean accepts(@NonNull Object value) {
Objects.requireNonNull(value);
Class clazz = value.getClass();
return clazz.isArray()
&& clazz.getComponentType().equals(elementCodec.getJavaType().getRawType());
}
@Nullable
@Override
public ByteBuffer encode(@Nullable ElementT[] value, @NonNull ProtocolVersion protocolVersion) {
if (value == null) {
return null;
}
int i = 0;
ByteBuffer[] encodedElements = new ByteBuffer[value.length];
int toAllocate = 4; // initialize with number of elements
for (ElementT elt : value) {
if (elt == null) {
throw new NullPointerException("Collection elements cannot be null");
}
ByteBuffer encodedElement;
try {
encodedElement = elementCodec.encode(elt, protocolVersion);
} catch (ClassCastException e) {
throw new IllegalArgumentException(
String.format(
"Invalid type for %s element, expecting %s but got %s",
cqlType, elementCodec.getJavaType(), elt.getClass()),
e);
}
if (encodedElement == null) {
throw new NullPointerException("Collection elements cannot encode to CQL NULL");
}
encodedElements[i++] = encodedElement;
toAllocate += 4 + encodedElement.remaining(); // the element preceded by its size
}
ByteBuffer result = ByteBuffer.allocate(toAllocate);
result.putInt(value.length);
for (ByteBuffer encodedElement : encodedElements) {
result.putInt(encodedElement.remaining());
result.put(encodedElement);
}
result.flip();
return result;
}
@Nullable
@Override
public ElementT[] decode(@Nullable ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) {
if (bytes == null || bytes.remaining() == 0) {
return newInstance(0);
}
ByteBuffer input = bytes.duplicate();
int size = input.getInt();
ElementT[] result = newInstance(size);
for (int i = 0; i < size; i++) {
ElementT element;
int elementSize = input.getInt();
// Allow null elements on the decode path, because Cassandra might return such collections
// for some computed values in the future -- e.g. SELECT ttl(some_collection)
if (elementSize < 0) {
element = null;
} else {
ByteBuffer encodedElement = input.slice();
encodedElement.limit(elementSize);
element = elementCodec.decode(encodedElement, protocolVersion);
input.position(input.position() + elementSize);
}
result[i] = element;
}
return result;
}
@Override
protected void formatElement(
@NonNull StringBuilder output, @NonNull ElementT[] array, int index) {
output.append(elementCodec.format(array[index]));
}
@Override
protected void parseElement(@NonNull String input, @NonNull ElementT[] array, int index) {
array[index] = elementCodec.parse(input);
}
@NonNull
@Override
@SuppressWarnings("unchecked")
protected ElementT[] newInstance(int size) {
return (ElementT[]) Array.newInstance(getJavaType().getRawType().getComponentType(), size);
}
}