co.cask.cdap.internal.io.ReflectionReader Maven / Gradle / Ivy
/*
* Copyright © 2015 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.internal.io;
import co.cask.cdap.api.data.schema.Schema;
import co.cask.cdap.common.lang.Instantiator;
import co.cask.cdap.common.lang.InstantiatorFactory;
import com.google.common.collect.Maps;
import com.google.common.primitives.Longs;
import com.google.common.reflect.TypeToken;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.UUID;
/**
* Reflection based reader.
*
* @param type of object to read from
* @param type of object to read
*/
public abstract class ReflectionReader {
private final Map, Instantiator>> creators;
private final InstantiatorFactory creatorFactory;
private final FieldAccessorFactory fieldAccessorFactory;
protected final Schema schema;
protected final TypeToken type;
protected ReflectionReader(Schema schema, TypeToken type) {
this.creatorFactory = new InstantiatorFactory(true);
this.creators = Maps.newIdentityHashMap();
this.fieldAccessorFactory = new ReflectionFieldAccessorFactory();
this.schema = schema;
this.type = type;
}
/**
* Read from the source to create the target type with target schema.
*
* @param source the source from which to read the object
* @param sourceSchema the write schema of the object
* @return the object of given type and schema
* @throws IOException if there was some exception reading the object
*/
public TO read(FROM source, Schema sourceSchema) throws IOException {
return read(source, sourceSchema, schema, type);
}
@SuppressWarnings("unchecked")
protected TO read(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException {
if (sourceSchema.getType() != Schema.Type.UNION && targetSchema.getType() == Schema.Type.UNION) {
// Try every target schemas
for (Schema schema : targetSchema.getUnionSchemas()) {
try {
return (TO) doRead(source, sourceSchema, schema, targetTypeToken);
} catch (IOException e) {
// Continue;
}
}
throw new IOException(String.format("No matching schema to resolve %s to %s", sourceSchema, targetSchema));
}
return (TO) doRead(source, sourceSchema, targetSchema, targetTypeToken);
}
protected abstract Object readNull(FROM source) throws IOException;
protected abstract boolean readBool(FROM source) throws IOException;
protected abstract int readInt(FROM source) throws IOException;
protected abstract long readLong(FROM source) throws IOException;
protected abstract float readFloat(FROM source) throws IOException;
protected abstract double readDouble(FROM source) throws IOException;
protected abstract String readString(FROM source) throws IOException;
protected abstract ByteBuffer readBytes(FROM source) throws IOException;
protected abstract Object readEnum(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException;
protected abstract Object readArray(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException;
protected abstract Object readMap(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException;
protected abstract Object readUnion(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException;
protected abstract Object readRecord(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException;
protected Object doRead(FROM source, Schema sourceSchema, Schema targetSchema,
TypeToken> targetTypeToken) throws IOException {
Schema.Type sourceType = sourceSchema.getType();
Schema.Type targetType = targetSchema.getType();
switch(sourceType) {
case NULL:
check(sourceType == targetType, "Fails to resolve %s to %s", sourceType, targetType);
return readNull(source);
case BYTES:
check(sourceType == targetType, "Fails to resolve %s to %s", sourceType, targetType);
ByteBuffer buffer = readBytes(source);
if (targetTypeToken.getRawType().equals(byte[].class)) {
if (buffer.hasArray()) {
byte[] array = buffer.array();
if (buffer.remaining() == array.length) {
return array;
}
byte[] bytes = new byte[buffer.remaining()];
System.arraycopy(array, buffer.arrayOffset() + buffer.position(), bytes, 0, buffer.remaining());
return bytes;
} else {
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
return bytes;
}
} else if (targetTypeToken.getRawType().equals(UUID.class) && buffer.remaining() == Longs.BYTES * 2) {
return new UUID(buffer.getLong(), buffer.getLong());
}
return buffer;
case ENUM:
check(sourceType == targetType, "Fails to resolve %s to %s", sourceType, targetType);
return readEnum(source, sourceSchema, targetSchema, targetTypeToken);
case ARRAY:
check(sourceType == targetType, "Fails to resolve %s to %s", sourceType, targetType);
return readArray(source, sourceSchema, targetSchema, targetTypeToken);
case MAP:
check(sourceType == targetType, "Fails to resolve %s to %s", sourceType, targetType);
return readMap(source, sourceSchema, targetSchema, targetTypeToken);
case RECORD:
check(sourceType == targetType, "Fails to resolve %s to %s", sourceType, targetType);
return readRecord(source, sourceSchema, targetSchema, targetTypeToken);
case UNION:
return readUnion(source, sourceSchema, targetSchema, targetTypeToken);
}
// For simple type other than NULL and BYTES
if (sourceType.isSimpleType()) {
return resolveType(source, sourceType, targetType, targetTypeToken);
}
throw new IOException(String.format("Fails to resolve %s to %s", sourceSchema, targetSchema));
}
private Object resolveType(FROM source, Schema.Type sourceType, Schema.Type targetType,
TypeToken> targetTypeToken) throws IOException {
switch(sourceType) {
case BOOLEAN:
switch(targetType) {
case BOOLEAN:
return readBool(source);
case STRING:
return String.valueOf(readBool(source));
}
break;
case INT:
switch(targetType) {
case INT:
Class> targetClass = targetTypeToken.getRawType();
int value = readInt(source);
if (targetClass.equals(byte.class) || targetClass.equals(Byte.class)) {
return (byte) value;
}
if (targetClass.equals(char.class) || targetClass.equals(Character.class)) {
return (char) value;
}
if (targetClass.equals(short.class) || targetClass.equals(Short.class)) {
return (short) value;
}
return value;
case LONG:
return (long) readInt(source);
case FLOAT:
return (float) readInt(source);
case DOUBLE:
return (double) readInt(source);
case STRING:
return String.valueOf(readInt(source));
}
break;
case LONG:
switch(targetType) {
case LONG:
return readLong(source);
case FLOAT:
return (float) readLong(source);
case DOUBLE:
return (double) readLong(source);
case STRING:
return String.valueOf(readLong(source));
}
break;
case FLOAT:
switch(targetType) {
case FLOAT:
return readFloat(source);
case DOUBLE:
return (double) readFloat(source);
case STRING:
return String.valueOf(readFloat(source));
}
break;
case DOUBLE:
switch(targetType) {
case DOUBLE:
return readDouble(source);
case STRING:
return String.valueOf(readDouble(source));
}
break;
case STRING:
switch(targetType) {
case STRING:
String str = readString(source);
Class> targetClass = targetTypeToken.getRawType();
if (targetClass.equals(URI.class)) {
return URI.create(str);
} else if (targetClass.equals(URL.class)) {
return new URL(str);
}
return str;
}
break;
}
throw new IOException("Fail to resolve type " + sourceType + " to type " + targetType);
}
protected void check(boolean condition, String message, Object... objs) throws IOException {
if (!condition) {
throw new IOException(String.format(message, objs));
}
}
protected IOException propagate(Throwable t) throws IOException {
if (t instanceof IOException) {
throw (IOException) t;
}
throw new IOException(t);
}
protected Object create(TypeToken> type) {
Class> rawType = type.getRawType();
Instantiator> creator = creators.get(rawType);
if (creator == null) {
creator = creatorFactory.get(type);
creators.put(rawType, creator);
}
return creator.create();
}
protected FieldAccessor getFieldAccessor(TypeToken> targetTypeToken, String fieldName) {
return fieldAccessorFactory.getFieldAccessor(targetTypeToken, fieldName);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy