<#macro copyright>
/*
 * 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.
 */

// This class is generated using Freemarker and the ${.template_name} template.
</#macro>
<@pp.dropOutputFile />
<@pp.changeOutputFile name="/org/apache/drill/exec/vector/accessor/ColumnAccessors.java" />
<#macro getType drillType label>
    @Override
    public ValueType valueType() {
  <#if label == "Int">
      return ValueType.INTEGER;
  <#elseif drillType == "VarChar" || drillType == "Var16Char">
      return ValueType.STRING;
  <#elseif drillType == "VarDecimal">
      return ValueType.DECIMAL;
  <#else>
      return ValueType.${label?upper_case};
  </#if>
    }
  <#if drillType == "Date" || drillType == "Time" || drillType == "TimeStamp">

    @Override
    public ValueType extendedType() {
    <#if drillType == "Date">
      return ValueType.DATE;
    <#elseif drillType == "Time">
      return ValueType.TIME;
    <#elseif drillType == "TimeStamp">
      return ValueType.TIMESTAMP;
    <#else>
      <#-- Should not be necessary. -->
      return valueType();
    </#if>
    }
  </#if>
</#macro>
<#macro build types vectorType accessorType>
  <#if vectorType == "Repeated">
    <#assign fnPrefix = "Array" />
    <#assign classType = "Element" />
  <#else>
    <#assign fnPrefix = vectorType />
    <#assign classType = "Scalar" />
  </#if>
  <#if vectorType == "Required">
    <#assign vectorPrefix = "" />
  <#else>
    <#assign vectorPrefix = vectorType />
  </#if>
  public static void define${fnPrefix}${accessorType}s(
      Class<? extends Base${classType}${accessorType}> ${accessorType?lower_case}s[]) {
  <#list types as type>
  <#list type.minor as minor>
    <#assign drillType=minor.class>
    <#assign notyet=minor.accessorDisabled!type.accessorDisabled!false>
    <#if ! notyet>
    <#assign typeEnum=drillType?upper_case>
    ${accessorType?lower_case}s[MinorType.${typeEnum}.ordinal()] = ${vectorPrefix}${drillType}Column${accessorType}.class;
    </#if>
  </#list>
  </#list>
  }
</#macro>
<#macro writeBuf buffer drillType minor putType doCast >
  <#if varWidth>
      ${buffer}.setBytes(offset, value, 0, len);
  <#elseif drillType == "Decimal9">
      ${buffer}.setInt(offset,
        DecimalUtility.getDecimal9FromBigDecimal(value,
            type.getScale()));
  <#elseif drillType == "Decimal18">
      ${buffer}.setLong(offset,
          DecimalUtility.getDecimal18FromBigDecimal(value,
              type.getScale()));
  <#elseif drillType == "Decimal38Sparse">
      <#-- Hard to optimize this case. Just use the available tools. -->
      DecimalUtility.getSparseFromBigDecimal(value, ${buffer},
          offset, type.getScale(), 6);
  <#elseif drillType == "Decimal28Sparse">
      <#-- Hard to optimize this case. Just use the available tools. -->
      DecimalUtility.getSparseFromBigDecimal(value, ${buffer},
          offset, type.getScale(), 5);
  <#elseif drillType == "IntervalYear">
      ${buffer}.setInt(offset,
          value.getYears() * 12 + value.getMonths());
  <#elseif drillType == "IntervalDay">
      ${buffer}.setInt(offset, value.getDays());
      ${buffer}.setInt(offset + ${minor.millisecondsOffset}, DateUtilities.periodToMillis(value));
  <#elseif drillType == "Interval">
      ${buffer}.setInt(offset, DateUtilities.periodToMonths(value));
      ${buffer}.setInt(offset + ${minor.daysOffset}, value.getDays());
      ${buffer}.setInt(offset + ${minor.millisecondsOffset}, DateUtilities.periodToMillis(value));
  <#elseif drillType == "Float4">
      ${buffer}.setInt(offset, Float.floatToRawIntBits((float) value));
  <#elseif drillType == "Float8">
      ${buffer}.setLong(offset, Double.doubleToRawLongBits(value));
  <#elseif drillType == "Bit">
      ${buffer}.setByte(offset, (byte)(value & 0x01));
  <#else>
      ${buffer}.set${putType?cap_first}(offset, <#if doCast>(${putType}) </#if>value);
  </#if>
</#macro>
<@copyright />

package org.apache.drill.exec.vector.accessor;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;

import org.apache.drill.common.types.TypeProtos.MajorType;
import org.apache.drill.common.types.Types;
import org.apache.drill.exec.vector.DateUtilities;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.vector.*;
import org.apache.drill.exec.util.DecimalUtility;
import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader.BaseVarWidthReader;
import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader.BaseFixedWidthReader;
import org.apache.drill.exec.vector.accessor.reader.VectorAccessor;
import org.apache.drill.exec.vector.accessor.writer.AbstractFixedWidthWriter.BaseFixedWidthWriter;
import org.apache.drill.exec.vector.accessor.writer.AbstractFixedWidthWriter.BaseIntWriter;
import org.apache.drill.exec.vector.accessor.writer.BaseVarWidthWriter;

import org.apache.drill.shaded.guava.com.google.common.base.Charsets;

import io.netty.buffer.DrillBuf;

import org.joda.time.DateTimeZone;
import org.joda.time.Period;
import org.joda.time.Instant;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;

/**
 * Basic accessors for most Drill vector types and modes. Each class has a bare-bones
 * accessors that converts from the "native" Drill type to the vectors. Many classes
 * also have "convenience" methods that convert from other Java types.
 * <p>
 * Writers work only with single vectors. Readers work with either single
 * vectors or a "hyper vector": a collection of vectors indexed together.
 * The details are hidden behind the {@link RowIndex} interface. If the reader
 * accesses a single vector, then the mutator is cached at bind time. However,
 * if the reader works with a hyper vector, then the vector is null at bind
 * time and must be retrieved for each row (since the vector differs row-by-
 * row.)
 */

public class ColumnAccessors {

<#list vv.types as type>
  <#list type.minor as minor>
    <#assign drillType=minor.class>
    <#if drillType == "Bit">
      <#-- Bit is special, handled outside of codegen. -->
      <#continue>
    </#if>
    <#assign javaType=minor.javaType!type.javaType>
    <#assign accessorType=minor.accessorType!type.accessorType!minor.friendlyType!javaType>
    <#assign label=minor.accessorLabel!type.accessorLabel!accessorType?capitalize>
    <#assign notyet=minor.accessorDisabled!type.accessorDisabled!false>
    <#if notyet>
      <#continue>
    </#if>
   <#assign cast=minor.accessorCast!minor.accessorCast!type.accessorCast!"none">
    <#assign friendlyType=minor.friendlyType!"">
    <#if accessorType=="BigDecimal">
      <#assign label="Decimal">
    </#if>
    <#assign varWidth = drillType == "VarChar" || drillType == "Var16Char" ||
                        drillType == "VarBinary" || drillType == "VarDecimal" />
    <#assign decimal = drillType == "Decimal9" || drillType == "Decimal18" ||
                       drillType == "Decimal28Sparse" || drillType == "Decimal38Sparse" ||
                       drillType == "VarDecimal" />
    <#assign intType = drillType == "TinyInt" || drillType == "SmallInt" || drillType == "Int" ||
                       drillType == "Bit" || drillType == "UInt1" || drillType = "UInt2" />
    <#if varWidth>
      <#assign accessorType = "byte[]">
      <#assign label = "Bytes">
      <#assign putArgs = ", final int len">
    <#else>
      <#assign putArgs = "">
    </#if>
    <#if javaType == "char">
      <#assign putType = "short" />
      <#assign doCast = true />
    <#else>
      <#assign putType = javaType />
      <#assign doCast = (cast == "set") />
    </#if>
  //------------------------------------------------------------------------
  // ${drillType} readers and writers

    <#if varWidth>
  public static class ${drillType}ColumnReader extends BaseVarWidthReader {

    <#else>
  public static class ${drillType}ColumnReader extends BaseFixedWidthReader {

    private static final int VALUE_WIDTH = ${drillType}Vector.VALUE_WIDTH;

    </#if>
    <#if decimal>
    private MajorType type;

    @Override
    public void bindVector(ColumnMetadata schema, VectorAccessor va) {
      super.bindVector(schema, va);
      <#if decimal>
      type = va.type();
      </#if>
    }

    </#if>
    <@getType drillType label />
    <#if ! varWidth>

    @Override public int width() { return VALUE_WIDTH; }
    </#if>

    @Override
    public ${accessorType} get${label}() {
    <#assign getObject ="getObject"/>
    <#assign indexVar = ""/>
      final DrillBuf buf = bufferAccessor.buffer();
    <#if ! varWidth>
      final int readOffset = vectorIndex.offset();
      <#assign getOffset = "readOffset * VALUE_WIDTH">
    </#if>
    <#if varWidth>
      final long entry = offsetsReader.getEntry();
      return buf.unsafeGetMemory((int) (entry >> 32), (int) (entry & 0xFFFF_FFFF));
    <#elseif drillType == "Decimal9">
      return DecimalUtility.getBigDecimalFromPrimitiveTypes(
          buf.getInt(${getOffset}),
          type.getScale());
    <#elseif drillType == "Decimal18">
      return DecimalUtility.getBigDecimalFromPrimitiveTypes(
          buf.getLong(${getOffset}),
          type.getScale());
    <#elseif drillType == "IntervalYear">
      <#-- For Java 8:
      final int value = buf.getInt(${getOffset});
      final int years  = (value / DateUtilities.yearsToMonths);
      final int months = (value % DateUtilities.yearsToMonths);
      return Period.of(years, months, 0); -->
      return DateUtilities.fromIntervalYear(
          buf.getInt(${getOffset}));
    <#elseif drillType == "IntervalDay">
      final int offset = ${getOffset};
      <#-- Show stopper for Java 8 date/time: There is no class
           that is equivalent to a Joda Period. -->
      return DateUtilities.fromIntervalDay(
          buf.getInt(offset),
          buf.getInt(offset + ${minor.millisecondsOffset}));
    <#elseif drillType == "Interval">
      final int offset = ${getOffset};
      return DateUtilities.fromInterval(
          buf.getInt(offset),
          buf.getInt(offset + ${minor.daysOffset}),
          buf.getInt(offset + ${minor.millisecondsOffset}));
    <#elseif drillType == "Decimal28Sparse" || drillType == "Decimal38Sparse">
      return DecimalUtility.getBigDecimalFromSparse(buf, ${getOffset},
          ${minor.nDecimalDigits}, type.getScale());
    <#elseif drillType == "Decimal28Dense" || drillType == "Decimal38Dense">
      return DecimalUtility.getBigDecimalFromDense(buf, ${getOffset},
          ${minor.nDecimalDigits}, type.getScale(),
          ${minor.maxPrecisionDigits}, VALUE_WIDTH);
    <#elseif drillType == "UInt1">
      return buf.getByte(${getOffset}) & 0xFF;
    <#elseif drillType == "UInt2">
      return buf.getShort(${getOffset}) & 0xFFFF;
    <#elseif drillType == "UInt4">
      // Should be the following:
      // return ((long) buf.unsafeGetInt(${getOffset})) & 0xFFFF_FFFF;
      // else, the unsigned values of 32 bits are mapped to negative.
      return buf.getInt(${getOffset});
    <#elseif drillType == "Float4">
      return Float.intBitsToFloat(buf.getInt(${getOffset}));
    <#elseif drillType == "Float8">
      return Double.longBitsToDouble(buf.getLong(${getOffset}));
    <#elseif drillType == "Bit">
      return buf.getByte(${getOffset});
    <#else>
      return buf.get${putType?cap_first}(${getOffset});
    </#if>
    }
    <#if drillType == "VarChar">

    @Override
    public String getString() {
      return new String(getBytes(${indexVar}), Charsets.UTF_8);
    }
    <#elseif drillType == "Var16Char">

    @Override
    public String getString() {
      return new String(getBytes(${indexVar}), Charsets.UTF_16);
    }
    <#elseif drillType == "VarDecimal">

    @Override
    public BigDecimal getDecimal() {
      final byte[] bytes = getBytes();
      BigInteger unscaledValue = bytes.length == 0 ? BigInteger.ZERO : new BigInteger(bytes);
      return new BigDecimal(unscaledValue, type.getScale());
    }
    <#elseif drillType == "Date">

    @Override
    public final LocalDate getDate() {
      <#-- Java 8:
        return LocalDate.ofEpochDay(getLong() / DateUtilities.daysToStandardMillis); -->
      return new LocalDate(getLong(), DateTimeZone.UTC);
    }
    <#elseif drillType == "Time">

    @Override
    public final LocalTime getTime() {
      <#-- Java 8:
        return LocalTime.ofNanoOfDay(getInt() * 1_000_000L); -->
      return new LocalTime(getInt(), DateTimeZone.UTC);
    }
    <#elseif drillType == "TimeStamp">

    @Override
    public final Instant getTimestamp() {
      <#-- Java 8:
        return Instant.ofEpochMilli(getLong()); -->
      return new Instant(getLong());
    }
    </#if>
  }

    <#if varWidth>
  public static class ${drillType}ColumnWriter extends BaseVarWidthWriter {
    <#else>
      <#if intType>
  public static class ${drillType}ColumnWriter extends BaseIntWriter {
      <#else>
  public static class ${drillType}ColumnWriter extends BaseFixedWidthWriter {
      </#if>

    private static final int VALUE_WIDTH = ${drillType}Vector.VALUE_WIDTH;
    </#if>

    private final ${drillType}Vector vector;
    <#if drillType == "VarDecimal">
    private int precision;
    private int scale;
    <#elseif decimal>
    private MajorType type;
    </#if>

    public ${drillType}ColumnWriter(final ValueVector vector) {
    <#if varWidth>
      super(((${drillType}Vector) vector).getOffsetVector());
    </#if>
    <#if drillType == "VarDecimal">
      // VarDecimal requires a scale. If not set, assume 0
      MajorType type = vector.getField().getType();
      precision = type.hasPrecision() ? type.getPrecision() : Types.maxPrecision(type.getMinorType());
      scale = type.hasScale() ? type.getScale() : 0;
    <#elseif decimal>
      type = vector.getField().getType();
    </#if>
      this.vector = (${drillType}Vector) vector;
    }

    @Override public BaseDataValueVector vector() { return vector; }

     <#if ! varWidth>
    @Override public int width() { return VALUE_WIDTH; }

    </#if>
      <@getType drillType label />

    @Override
    public final void set${label}(final ${accessorType} value${putArgs}) {
    <#-- Must compute the write offset first; can't be inline because the
         writeOffset() function has a side effect of possibly changing the buffer
         address (bufAddr). -->
    <#if varWidth>
      final int offset = prepareWrite(len);
    <#else>
      final int offset = prepareWrite() * VALUE_WIDTH;
    </#if>
      <@writeBuf "drillBuf", drillType minor putType doCast />
    <#if varWidth>
      offsetsWriter.setNextOffset(offset + len);
    </#if>
      vectorIndex.nextElement();
    }
    <#if ! varWidth>

    public final void write${label}(final DrillBuf buf, final ${accessorType} value) {
      final int offset = 0;
      <@writeBuf "buf", drillType minor putType doCast />
      buf.writerIndex(VALUE_WIDTH);
    }
    </#if>

    <#if drillType == "VarChar" || drillType == "Var16Char" || drillType == "VarBinary">
    @Override
    public final void appendBytes(final byte[] value, final int len) {
      vectorIndex.prevElement();
      final int offset = prepareAppend(len);
      drillBuf.setBytes(offset, value, 0, len);
      offsetsWriter.reviseOffset(offset + len);
      vectorIndex.nextElement();
    }
    </#if>
    <#if drillType == "VarChar">

    @Override
    public final void setString(final String value) {
      final byte bytes[] = value.getBytes(Charsets.UTF_8);
      setBytes(bytes, bytes.length);
    }
    <#elseif drillType == "Var16Char">

    @Override
    public final void setString(final String value) {
      final byte bytes[] = value.getBytes(Charsets.UTF_16);
      setBytes(bytes, bytes.length);
    }
    <#elseif drillType == "BigInt">

    @Override
    public final void setInt(final int value) {
      setLong(value);
    }

    @Override
    public final void setDouble(final double value) {
      // Does not catch overflow from
      // double. See Math.round for details.
      setLong(Math.round(value));
    }

    @Override
    public final void setDecimal(final BigDecimal value) {
      try {
        // Catches long overflow.
        setLong(value.longValueExact());
      } catch (ArithmeticException e) {
        throw InvalidConversionError.writeError(schema(), value, e);
      }
    }
    <#elseif drillType == "Float4" || drillType == "Float8">

    @Override
    public final void setInt(final int value) {
      setDouble(value);
    }

    @Override
    public final void setLong(final long value) {
      setDouble(value);
    }

    @Override
    public final void setDecimal(final BigDecimal value) {
      setDouble(value.doubleValue());
    }
    <#elseif decimal>

    @Override
    public final void setInt(final int value) {
      setDecimal(BigDecimal.valueOf(value));
    }

    @Override
    public final void setLong(final long value) {
      setDecimal(BigDecimal.valueOf(value));
    }

    @Override
    public final void setDouble(final double value) {
      setDecimal(BigDecimal.valueOf(value));
    }
      <#if drillType == "VarDecimal">

    @Override
    public final void setDecimal(final BigDecimal value) {
      try {
        final BigDecimal rounded = value.setScale(scale, RoundingMode.HALF_UP);
        DecimalUtility.checkValueOverflow(rounded, precision, scale);
        final byte[] barr = rounded.unscaledValue().toByteArray();
        setBytes(barr,  barr.length);
      } catch (ArithmeticException e) {
        throw new InvalidConversionError("Decimal conversion failed for " + value, e);
      }
    }
      </#if>
    <#elseif drillType == "Date">

    @Override
    public final void setDate(final LocalDate value) {
      <#-- Java 8:
        setLong(value.toEpochDay() * DateUtilities.daysToStandardMillis); -->
      setLong(value.toDateTimeAtStartOfDay(DateTimeZone.UTC).toInstant().getMillis());
    }
    <#elseif drillType == "Time">

    @Override
    public final void setTime(final LocalTime value) {
      <#-- Java 8:
        setInt((int) ((value.toNanoOfDay() + 500_000) / 1_000_000L)); -->
      setInt(value.getMillisOfDay());
    }
    <#elseif drillType == "TimeStamp">

    @Override
    public final void setTimestamp(final Instant value) {
      <#-- Java 8:
        setLong(value.toEpochMilli()); -->
      setLong(value.getMillis());
    }
    </#if>
    <#if ! intType>

    @Override
    public final void setValue(final Object value) {
      <#if drillType == "VarChar">
      setString((String) value);
      <#elseif drillType = "Date">
      setDate((LocalDate) value);
      <#elseif drillType = "Time">
      setTime((LocalTime) value);
      <#elseif drillType = "TimeStamp">
      setTimestamp((Instant) value);
      <#elseif putArgs != "">
      throw new InvalidConversionError("Generic object not supported for type ${drillType}, "
          + "set${label}(${accessorType}${putArgs})");
      <#else>
      if (value != null) {
        set${label}((${accessorType}) value);
      }
      </#if>
    }
    </#if>

    <#-- Default value logic is a bit convoluted because we want to reuse the same
         (complex) template here to generate both the set-value code and the set-default
         code. This means we need a (temporary) DrillBuf for most cases except those
         where a byte array value is directly available. -->
    @Override
    public final void setDefaultValue(final Object value) {
    <#if drillType == "VarBinary">
      emptyValue = (byte[]) value;
    <#elseif drillType == "VarChar">
      emptyValue = ((String) value).getBytes(Charsets.UTF_8);
    <#elseif drillType == "Var16Char">
      emptyValue = ((String) value).getBytes(Charsets.UTF_16);
    <#elseif drillType == "VarDecimal">
      final BigDecimal rounded = ((BigDecimal) value).setScale(scale, RoundingMode.HALF_UP);
      DecimalUtility.checkValueOverflow(rounded, precision, scale);
      emptyValue = rounded.unscaledValue().toByteArray();
    <#else>
      try (DrillBuf buf = vector.getAllocator().buffer(VALUE_WIDTH)) {
      <#if drillType = "Date">
        writeLong(buf, ((LocalDate) value).toDateTimeAtStartOfDay(DateTimeZone.UTC).toInstant().getMillis());
      <#elseif drillType = "Time">
        writeInt(buf, ((LocalTime) value).getMillisOfDay());
      <#elseif drillType = "TimeStamp">
        writeLong(buf, ((Instant) value).getMillis());
      <#elseif putArgs != "">
        throw new InvalidConversionError("Generic object not supported for type ${drillType}, "
            + "set${label}(${accessorType}${putArgs})");
      <#else>
        write${label}(buf, (${accessorType}) value);
      </#if>
        emptyValue = new byte[VALUE_WIDTH];
        buf.getBytes(0, emptyValue);
      }
    </#if>
    }

    @Override
    public final void copy(ColumnReader from) {
      ${drillType}ColumnReader source = (${drillType}ColumnReader) from;
      final DrillBuf sourceBuf = source.buffer();
    <#if varWidth>
      final long entry = source.getEntry();
      final int sourceOffset = (int) (entry >> 32);
      final int len = (int) (entry & 0xFFFF_FFFF);
      final int destOffset = prepareWrite(len);
      drillBuf.setBytes(destOffset, sourceBuf, sourceOffset, len);
      offsetsWriter.setNextOffset(destOffset + len);
    <#else>
      final int sourceOffset = source.offsetIndex() * VALUE_WIDTH;
      final int destOffset = prepareWrite() * VALUE_WIDTH;
      drillBuf.setBytes(destOffset, sourceBuf, sourceOffset, VALUE_WIDTH);
    </#if>
      vectorIndex.nextElement();
    }
  }

  </#list>
</#list>
}
<@pp.changeOutputFile name="/org/apache/drill/exec/vector/accessor/ColumnAccessorUtils.java" />
<@copyright />

package org.apache.drill.exec.vector.accessor;

import org.apache.drill.common.types.TypeProtos.MinorType;
import org.apache.drill.exec.vector.accessor.ColumnAccessors.*;
import org.apache.drill.exec.vector.accessor.reader.BaseScalarReader;
import org.apache.drill.exec.vector.accessor.reader.BitColumnReader;
import org.apache.drill.exec.vector.accessor.writer.BaseScalarWriter;
import org.apache.drill.exec.vector.accessor.writer.BitColumnWriter;

public class ColumnAccessorUtils {

  private ColumnAccessorUtils() { }

<@build vv.types "Required" "Reader" />

<@build vv.types "Required" "Writer" />
}
