Skip to content

Commit

Permalink
Add ability to lookup codec by Java type only
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandre Dutra authored and olim7t committed Apr 21, 2018
1 parent a32a294 commit 7230077
Show file tree
Hide file tree
Showing 4 changed files with 292 additions and 104 deletions.
Expand Up @@ -70,6 +70,26 @@ default <T> TypeCodec<T> codecFor(DataType cqlType, Class<T> javaType) {
*/
<T> TypeCodec<T> codecFor(DataType cqlType);

/**
* Returns a codec to convert the given Java type to the CQL type deemed most appropriate to
* represent it.
*
* <p>The definition of "most appropriate" is unspecified, and left to the appreciation of the
* registry implementor.
*/
<T> TypeCodec<T> codecFor(GenericType<T> javaType);

/**
* Returns a codec to convert the given Java type to the CQL type deemed most appropriate to
* represent it.
*
* <p>The definition of "most appropriate" is unspecified, and left to the appreciation of the
* registry implementor.
*/
default <T> TypeCodec<T> codecFor(Class<T> javaType) {
return codecFor(GenericType.of(javaType));
}

/**
* Returns a codec to convert the given Java object to the CQL type deemed most appropriate to
* represent it.
Expand Down
Expand Up @@ -51,7 +51,7 @@
* not be modified after construction.
*
* <p>This class is abstract in order to be agnostic from the cache implementation. Subclasses must
* implement {@link #getCachedCodec(DataType, GenericType)}.
* implement {@link #getCachedCodec(DataType, GenericType, boolean)}.
*/
@ThreadSafe
public abstract class CachingCodecRegistry implements CodecRegistry {
Expand Down Expand Up @@ -80,10 +80,11 @@ protected CachingCodecRegistry(
* Gets a complex codec from the cache.
*
* <p>If the codec does not exist in the cache, this method must generate it with {@link
* #createCodec(DataType, GenericType)} (and most likely put it in the cache too for future
* calls).
* #createCodec(DataType, GenericType, boolean)} (and most likely put it in the cache too for
* future calls).
*/
protected abstract TypeCodec<?> getCachedCodec(DataType cqlType, GenericType<?> javaType);
protected abstract TypeCodec<?> getCachedCodec(
DataType cqlType, GenericType<?> javaType, boolean isJavaCovariant);

@Override
public <T> TypeCodec<T> codecFor(DataType cqlType, GenericType<T> javaType) {
Expand All @@ -99,7 +100,7 @@ public <T> TypeCodec<T> codecFor(DataType cqlType, GenericType<T> javaType) {
return uncheckedCast(userCodec);
}
}
return uncheckedCast(getCachedCodec(cqlType, javaType));
return uncheckedCast(getCachedCodec(cqlType, javaType, false));
}

@Override
Expand All @@ -116,7 +117,7 @@ public <T> TypeCodec<T> codecFor(DataType cqlType, Class<T> javaType) {
return uncheckedCast(userCodec);
}
}
return uncheckedCast(getCachedCodec(cqlType, GenericType.of(javaType)));
return uncheckedCast(getCachedCodec(cqlType, GenericType.of(javaType), false));
}

@Override
Expand All @@ -133,7 +134,7 @@ public <T> TypeCodec<T> codecFor(DataType cqlType) {
return uncheckedCast(userCodec);
}
}
return uncheckedCast(getCachedCodec(cqlType, null));
return uncheckedCast(getCachedCodec(cqlType, null, false));
}

@Override
Expand Down Expand Up @@ -162,7 +163,25 @@ public <T> TypeCodec<T> codecFor(T value) {

GenericType<?> javaType = inspectType(value);
LOG.trace("[{}] Continuing based on inferred type {}", logPrefix, javaType);
return uncheckedCast(getCachedCodec(null, javaType));
return uncheckedCast(getCachedCodec(null, javaType, true));
}

@Override
public <T> TypeCodec<T> codecFor(GenericType<T> javaType) {
LOG.trace("[{}] Looking up codec for Java type {}", logPrefix, javaType);
for (TypeCodec<?> primitiveCodec : primitiveCodecs) {
if (primitiveCodec.accepts(javaType)) {
LOG.trace("[{}] Found matching primitive codec {}", logPrefix, primitiveCodec);
return uncheckedCast(primitiveCodec);
}
}
for (TypeCodec<?> userCodec : userCodecs) {
if (userCodec.accepts(javaType)) {
LOG.trace("[{}] Found matching user codec {}", logPrefix, userCodec);
return uncheckedCast(userCodec);
}
}
return uncheckedCast(getCachedCodec(null, javaType, false));
}

// Not exposed publicly, this is only used for the recursion from
Expand All @@ -181,7 +200,7 @@ protected TypeCodec<?> covariantCodecFor(GenericType<?> javaType) {
return uncheckedCast(userCodec);
}
}
return uncheckedCast(getCachedCodec(null, javaType));
return uncheckedCast(getCachedCodec(null, javaType, true));
}

protected GenericType<?> inspectType(Object value) {
Expand Down Expand Up @@ -219,14 +238,15 @@ protected GenericType<?> inspectType(Object value) {
}

// Try to create a codec when we haven't found it in the cache
protected TypeCodec<?> createCodec(DataType cqlType, GenericType<?> javaType) {
protected TypeCodec<?> createCodec(
DataType cqlType, GenericType<?> javaType, boolean isJavaCovariant) {
LOG.trace("[{}] Cache miss, creating codec", logPrefix);
// Either type can be null, but not both.
if (javaType == null) {
assert cqlType != null;
return createCodec(cqlType);
} else if (cqlType == null) {
return createCovariantCodec(javaType);
return isJavaCovariant ? createCovariantCodec(javaType) : createCodec(javaType);
}
TypeToken<?> token = javaType.__getToken();
if (cqlType instanceof ListType && List.class.isAssignableFrom(token.getRawType())) {
Expand Down Expand Up @@ -280,6 +300,34 @@ protected TypeCodec<?> createCodec(DataType cqlType, GenericType<?> javaType) {
throw new CodecNotFoundException(cqlType, javaType);
}

// Try to create a codec when we haven't found it in the cache.
// Variant where the CQL type is unknown.
protected TypeCodec<?> createCodec(GenericType<?> javaType) {
TypeToken<?> token = javaType.__getToken();
if (List.class.isAssignableFrom(token.getRawType())
&& token.getType() instanceof ParameterizedType) {
Type[] typeArguments = ((ParameterizedType) token.getType()).getActualTypeArguments();
GenericType<?> elementType = GenericType.of(typeArguments[0]);
TypeCodec<?> elementCodec = codecFor(elementType);
return TypeCodecs.listOf(elementCodec);
} else if (Set.class.isAssignableFrom(token.getRawType())
&& token.getType() instanceof ParameterizedType) {
Type[] typeArguments = ((ParameterizedType) token.getType()).getActualTypeArguments();
GenericType<?> elementType = GenericType.of(typeArguments[0]);
TypeCodec<?> elementCodec = codecFor(elementType);
return TypeCodecs.setOf(elementCodec);
} else if (Map.class.isAssignableFrom(token.getRawType())
&& token.getType() instanceof ParameterizedType) {
Type[] typeArguments = ((ParameterizedType) token.getType()).getActualTypeArguments();
GenericType<?> keyType = GenericType.of(typeArguments[0]);
GenericType<?> valueType = GenericType.of(typeArguments[1]);
TypeCodec<?> keyCodec = codecFor(keyType);
TypeCodec<?> valueCodec = codecFor(valueType);
return TypeCodecs.mapOf(keyCodec, valueCodec);
}
throw new CodecNotFoundException(null, javaType);
}

// Try to create a codec when we haven't found it in the cache.
// Variant where the CQL type is unknown. Note that this is only used for lookups by Java value,
// and therefore covariance is allowed.
Expand Down
Expand Up @@ -74,7 +74,7 @@ public DefaultCodecRegistry(
new CacheLoader<CacheKey, TypeCodec<?>>() {
@Override
public TypeCodec<?> load(CacheKey key) throws Exception {
return createCodec(key.cqlType, key.javaType);
return createCodec(key.cqlType, key.javaType, key.isJavaCovariant);
}
};
if (cacheRemovalListener != null) {
Expand All @@ -101,10 +101,11 @@ public DefaultCodecRegistry(
}

@Override
protected TypeCodec<?> getCachedCodec(DataType cqlType, GenericType<?> javaType) {
protected TypeCodec<?> getCachedCodec(
DataType cqlType, GenericType<?> javaType, boolean isJavaCovariant) {
LOG.trace("[{}] Checking cache", logPrefix);
try {
return cache.getUnchecked(new CacheKey(cqlType, javaType));
return cache.getUnchecked(new CacheKey(cqlType, javaType, isJavaCovariant));
} catch (UncheckedExecutionException | ExecutionError e) {
// unwrap exception cause and throw it directly.
Throwable cause = e.getCause();
Expand All @@ -122,10 +123,12 @@ public static final class CacheKey {

public final DataType cqlType;
public final GenericType<?> javaType;
public final boolean isJavaCovariant;

public CacheKey(DataType cqlType, GenericType<?> javaType) {
public CacheKey(DataType cqlType, GenericType<?> javaType, boolean isJavaCovariant) {
this.javaType = javaType;
this.cqlType = cqlType;
this.isJavaCovariant = isJavaCovariant;
}

@Override
Expand All @@ -135,15 +138,16 @@ public boolean equals(Object other) {
} else if (other instanceof CacheKey) {
CacheKey that = (CacheKey) other;
return Objects.equals(this.cqlType, that.cqlType)
&& Objects.equals(this.javaType, that.javaType);
&& Objects.equals(this.javaType, that.javaType)
&& this.isJavaCovariant == that.isJavaCovariant;
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(cqlType, javaType);
return Objects.hash(cqlType, javaType, isJavaCovariant);
}
}
}

0 comments on commit 7230077

Please sign in to comment.