/* * 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.common.Bytes; import co.cask.cdap.api.data.schema.Schema; import co.cask.cdap.api.dataset.table.Put; import co.cask.cdap.api.dataset.table.Table; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collection; import java.util.List; import java.util.Map; /** * Encodes an object as a {@link Put} for storing it into a {@link Table}. Assumes that objects to write are * records. Fields of simple types are encoded as columns in the table. Complex types (arrays, maps, records, enums), * are not supported. * * @param <T> the type of object to encode as a {@link Put} */ public class ReflectionPutWriter<T> extends ReflectionWriter<Put, T> { private final List<String> fieldNames; private int index; public ReflectionPutWriter(Schema schema) { super(schema); Preconditions.checkArgument(schema.getType() == Schema.Type.RECORD, "Schema must be a record."); List<Schema.Field> schemaFields = schema.getFields(); int numFields = schemaFields.size(); Preconditions.checkArgument(numFields > 0, "Record must contain at least one field."); this.fieldNames = Lists.newArrayListWithCapacity(numFields); for (Schema.Field schemaField : schemaFields) { this.fieldNames.add(schemaField.getName()); } this.index = 0; } private String nextField() { String name = fieldNames.get(index); index++; return name; } @Override public void write(T object, Put put) throws IOException { index = 0; seenRefs = Sets.newIdentityHashSet(); super.writeRecord(put, object, schema); } @Override protected void writeNull(Put put) throws IOException { nextField(); } @Override protected void writeBool(Put put, Boolean val) throws IOException { put.add(nextField(), val); } @Override protected void writeInt(Put put, int val) throws IOException { put.add(nextField(), val); } @Override protected void writeLong(Put put, long val) throws IOException { put.add(nextField(), val); } @Override protected void writeFloat(Put put, Float val) throws IOException { put.add(nextField(), val); } @Override protected void writeDouble(Put put, Double val) throws IOException { put.add(nextField(), val); } @Override protected void writeString(Put put, String val) throws IOException { put.add(nextField(), val); } @Override protected void writeBytes(Put put, ByteBuffer val) throws IOException { put.add(nextField(), Bytes.toBytes(val)); } @Override protected void writeBytes(Put put, byte[] val) throws IOException { put.add(nextField(), val); } @Override protected void writeEnum(Put put, String val, Schema schema) throws IOException { throw new UnsupportedOperationException("Enums are not supported."); } @Override protected void writeArray(Put put, Collection<?> val, Schema componentSchema) throws IOException { throw new UnsupportedOperationException("Arrays are not supported."); } @Override protected void writeArray(Put put, Object val, Schema componentSchema) throws IOException { throw new UnsupportedOperationException("Arrays are not supported."); } @Override protected void writeMap(Put put, Map<?, ?> val, Map.Entry<Schema, Schema> mapSchema) throws IOException { throw new UnsupportedOperationException("Maps are not supported."); } @Override protected void writeRecord(Put put, Object record, Schema recordSchema) throws IOException { throw new UnsupportedOperationException("Records are not supported."); } @Override protected void writeUnion(Put put, Object val, Schema schema) throws IOException { // only support unions if its for a nullable. if (!schema.isNullable()) { throw new UnsupportedOperationException("Unions that do not represent nullables are not supported."); } if (val != null) { seenRefs.remove(val); write(put, val, schema.getNonNullable()); } else { // if the value is null, we want to generate a put with value null, to make sure to delete any existing values. put.add(nextField(), (byte[]) null); } } }