content: /*
   * 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.
   */
  
  <@pp.dropOutputFile />
  <#list ["Nullable", "Single"] as mode>
  <@pp.changeOutputFile name="/org/apache/arrow/vector/complex/impl/${mode}StructWriter.java" />
  <#assign index = "idx()">
  <#if mode == "Single">
  <#assign containerClass = "NonNullableStructVector" />
  <#else>
  <#assign containerClass = "StructVector" />
  </#if>
  
  <#include "/@includes/license.ftl" />
  
  package org.apache.arrow.vector.complex.impl;
  
  <#include "/@includes/vv_imports.ftl" />
  import java.util.Map;
  import java.util.HashMap;
  
  import org.apache.arrow.vector.holders.RepeatedStructHolder;
  import org.apache.arrow.vector.AllocationHelper;
  import org.apache.arrow.vector.complex.reader.FieldReader;
  import org.apache.arrow.vector.complex.writer.FieldWriter;
  
  <#function is_timestamp_tz type>
    <#return type?starts_with("TimeStamp") && type?ends_with("TZ")>
  </#function>
  
  /*
   * This class is generated using FreeMarker and the ${.template_name} template.
   */
  @SuppressWarnings("unused")
  public class ${mode}StructWriter extends AbstractFieldWriter {
  
    protected final ${containerClass} container;
    private int initialCapacity;
    private final Map<String, FieldWriter> fields = new HashMap<>();
    public ${mode}StructWriter(${containerClass} container) {
      <#if mode == "Single">
      if (container instanceof StructVector) {
        throw new IllegalArgumentException("Invalid container: " + container);
      }
      </#if>
      this.container = container;
      this.initialCapacity = 0;
      for (Field child : container.getField().getChildren()) {
        MinorType minorType = Types.getMinorTypeForArrowType(child.getType());
        addVectorAsNullable = child.isNullable();
        switch (minorType) {
        case STRUCT:
          struct(child.getName());
          break;
        case LIST:
          list(child.getName());
          break;
        case LISTVIEW:
          listView(child.getName());
          break;
        case MAP: {
          ArrowType.Map arrowType = (ArrowType.Map) child.getType();
          map(child.getName(), arrowType.getKeysSorted());
          break;
        }
        case DENSEUNION: {
          FieldType fieldType = new FieldType(addVectorAsNullable, MinorType.DENSEUNION.getType(), null, null);
          DenseUnionWriter writer = new DenseUnionWriter(container.addOrGet(child.getName(), fieldType, DenseUnionVector.class), getNullableStructWriterFactory());
          fields.put(handleCase(child.getName()), writer);
          break;
        }
        case EXTENSIONTYPE:
          extension(child.getName(), child.getType());
          break;
        case UNION:
          FieldType fieldType = new FieldType(addVectorAsNullable, MinorType.UNION.getType(), null, null);
          UnionWriter writer = new UnionWriter(container.addOrGet(child.getName(), fieldType, UnionVector.class), getNullableStructWriterFactory());
          fields.put(handleCase(child.getName()), writer);
          break;
  <#list vv.types as type><#list type.minor as minor>
  <#assign lowerName = minor.class?uncap_first />
  <#if lowerName == "int" ><#assign lowerName = "integer" /></#if>
  <#assign upperName = minor.class?upper_case />
        case ${upperName}: {
          <#if minor.typeParams?? >
          ${minor.arrowType} arrowType = (${minor.arrowType})child.getType();
          ${lowerName}(child.getName()<#list minor.typeParams as typeParam>, arrowType.get${typeParam.name?cap_first}()</#list>);
          <#else>
          ${lowerName}(child.getName());
          </#if>
          break;
        }
  </#list></#list>
          default:
            throw new UnsupportedOperationException("Unknown type: " + minorType);
        }
      }
    }
  
    protected String handleCase(final String input) {
      return input.toLowerCase();
    }
  
    protected NullableStructWriterFactory getNullableStructWriterFactory() {
      return NullableStructWriterFactory.getNullableStructWriterFactoryInstance();
    }
  
    @Override
    public int getValueCapacity() {
      return container.getValueCapacity();
    }
  
    public void setInitialCapacity(int initialCapacity) {
      this.initialCapacity = initialCapacity;
      container.setInitialCapacity(initialCapacity);
    }
  
    @Override
    public boolean isEmptyStruct() {
      return 0 == container.size();
    }
  
    @Override
    public Field getField() {
        return container.getField();
    }
  
    @Override
    public StructWriter struct(String name) {
      String finalName = handleCase(name);
      FieldWriter writer = fields.get(finalName);
      if(writer == null){
        int vectorCount=container.size();
        FieldType fieldType = new FieldType(addVectorAsNullable, MinorType.STRUCT.getType(), null, null);
        StructVector vector = container.addOrGet(name, fieldType, StructVector.class);
        writer = new PromotableWriter(vector, container, getNullableStructWriterFactory());
        if(vectorCount != container.size()) {
          writer.allocate();
        }
        writer.setPosition(idx());
        fields.put(finalName, writer);
      } else {
        if (writer instanceof PromotableWriter) {
          // ensure writers are initialized
          ((PromotableWriter)writer).getWriter(MinorType.STRUCT);
        }
      }
      return writer;
    }
  
    @Override
    public ExtensionWriter extension(String name, ArrowType arrowType) {
      String finalName = handleCase(name);
      FieldWriter writer = fields.get(finalName);
      if(writer == null){
        int vectorCount=container.size();
        FieldType fieldType = new FieldType(addVectorAsNullable, arrowType, null, null);
        ExtensionTypeVector vector = container.addOrGet(name, fieldType, ExtensionTypeVector.class);
        writer = new PromotableWriter(vector, container, getNullableStructWriterFactory());
        if(vectorCount != container.size()) {
          writer.allocate();
        }
        writer.setPosition(idx());
        fields.put(finalName, writer);
      } else {
        if (writer instanceof PromotableWriter) {
          // ensure writers are initialized
          ((PromotableWriter)writer).getWriter(MinorType.EXTENSIONTYPE, arrowType);
        }
      }
      return (ExtensionWriter) writer;
    }
  
    @Override
    public void close() throws Exception {
      clear();
      container.close();
    }
  
    @Override
    public void allocate() {
      container.allocateNew();
      for(final FieldWriter w : fields.values()) {
        w.allocate();
      }
    }
  
    @Override
    public void clear() {
      container.clear();
      for(final FieldWriter w : fields.values()) {
        w.clear();
      }
    }
  
    @Override
    public ListWriter list(String name) {
      String finalName = handleCase(name);
      FieldWriter writer = fields.get(finalName);
      int vectorCount = container.size();
      if(writer == null) {
        FieldType fieldType = new FieldType(addVectorAsNullable, MinorType.LIST.getType(), null, null);
        writer = new PromotableWriter(container.addOrGet(name, fieldType, ListVector.class), container, getNullableStructWriterFactory());
        if (container.size() > vectorCount) {
          writer.allocate();
        }
        writer.setPosition(idx());
        fields.put(finalName, writer);
      } else {
        if (writer instanceof PromotableWriter) {
          // ensure writers are initialized
          ((PromotableWriter)writer).getWriter(MinorType.LIST);
        }
      }
      return writer;
    }
  
    @Override
    public ListWriter listView(String name) {
      String finalName = handleCase(name);
      FieldWriter writer = fields.get(finalName);
      int vectorCount = container.size();
      if(writer == null) {
        FieldType fieldType = new FieldType(addVectorAsNullable, MinorType.LISTVIEW.getType(), null, null);
        writer = new PromotableViewWriter(container.addOrGet(name, fieldType, ListViewVector.class), container, getNullableStructWriterFactory());
        if (container.size() > vectorCount) {
          writer.allocate();
        }
        writer.setPosition(idx());
        fields.put(finalName, writer);
      } else {
        if (writer instanceof PromotableViewWriter) {
          // ensure writers are initialized
          ((PromotableViewWriter) writer).getWriter(MinorType.LISTVIEW);
        } else {
          writer = ((PromotableWriter) writer).toViewWriter();
          ((PromotableViewWriter) writer).getWriter(MinorType.LISTVIEW);
        }
      }
      return writer;
    }
  
    @Override
    public MapWriter map(String name) {
      return map(name, false);
    }
  
    @Override
    public MapWriter map(String name, boolean keysSorted) {
      FieldWriter writer = fields.get(handleCase(name));
      if(writer == null) {
        ValueVector vector;
        ValueVector currentVector = container.getChild(name);
        MapVector v = container.addOrGet(name,
            new FieldType(addVectorAsNullable,
              new ArrowType.Map(keysSorted)
            ,null, null),
            MapVector.class);
        writer = new PromotableWriter(v, container, getNullableStructWriterFactory());
        vector = v;
        if (currentVector == null || currentVector != vector) {
          if(this.initialCapacity > 0) {
            vector.setInitialCapacity(this.initialCapacity);
          }
          vector.allocateNewSafe();
        }
        writer.setPosition(idx());
        fields.put(handleCase(name), writer);
      } else {
        if (writer instanceof PromotableWriter) {
          // ensure writers are initialized
          ((PromotableWriter)writer).getWriter(MinorType.MAP, new ArrowType.Map(keysSorted));
        }
      }
      return writer;
    }
  
    public void setValueCount(int count) {
      container.setValueCount(count);
    }
  
    @Override
    public void setPosition(int index) {
      super.setPosition(index);
      for(final FieldWriter w: fields.values()) {
        w.setPosition(index);
      }
    }
  
    <#if mode="Nullable">
    @Override
    public void writeNull() {
      container.setNull(idx());
      setValueCount(idx()+1);
      super.setPosition(idx()+1);
    }
    </#if>
  
    @Override
    public void start() {
      <#if mode == "Single">
      <#else>
      container.setIndexDefined(idx());
      </#if>
    }
  
    @Override
    public void end() {
      setPosition(idx()+1);
    }
  
    <#list vv.types as type><#list type.minor as minor>
    <#assign lowerName = minor.class?uncap_first />
    <#if lowerName == "int" ><#assign lowerName = "integer" /></#if>
    <#assign upperName = minor.class?upper_case />
    <#assign capName = minor.class?cap_first />
    <#assign vectName = capName />
  
    <#if minor.typeParams?? >
    @Override
    public ${minor.class}Writer ${lowerName}(String name) {
      // returns existing writer
      final FieldWriter writer = fields.get(handleCase(name));
      Preconditions.checkNotNull(writer);
      return writer;
    }
  
    @Override
    public ${minor.class}Writer ${lowerName}(String name<#list minor.typeParams as typeParam>, ${typeParam.type} ${typeParam.name}</#list>) {
    <#else>
    @Override
    public ${minor.class}Writer ${lowerName}(String name) {
    </#if>
      FieldWriter writer = fields.get(handleCase(name));
      if(writer == null) {
        ValueVector vector;
        ValueVector currentVector = container.getChild(name);
        ${vectName}Vector v = container.addOrGet(name,
            new FieldType(addVectorAsNullable,
            <#if minor.typeParams??>
              <#if minor.arrowTypeConstructorParams??>
                <#assign constructorParams = minor.arrowTypeConstructorParams />
              <#else>
                <#assign constructorParams = [] />
                <#list minor.typeParams?reverse as typeParam>
                  <#assign constructorParams = constructorParams + [ typeParam.name ] />
                </#list>
              </#if>
              new ${minor.arrowType}(${constructorParams?join(", ")}<#if minor.class?starts_with("Decimal")>, ${vectName}Vector.TYPE_WIDTH * 8</#if>)
            <#else>
              MinorType.${upperName}.getType()
            </#if>
            ,null, null),
            ${vectName}Vector.class);
        writer = new PromotableWriter(v, container, getNullableStructWriterFactory());
        vector = v;
        if (currentVector == null || currentVector != vector) {
          if(this.initialCapacity > 0) {
            vector.setInitialCapacity(this.initialCapacity);
          }
          vector.allocateNewSafe();
        }
        writer.setPosition(idx());
        fields.put(handleCase(name), writer);
      } else {
        if (writer instanceof PromotableWriter) {
          // ensure writers are initialized
          <#if minor.class?starts_with("Decimal")>
          ((PromotableWriter)writer).getWriter(MinorType.${upperName}<#if minor.class?starts_with("Decimal")>, new ${minor.arrowType}(precision, scale, ${vectName}Vector.TYPE_WIDTH * 8)</#if>);
          <#elseif is_timestamp_tz(minor.class) || minor.class == "Duration" || minor.class == "FixedSizeBinary">
            <#if minor.arrowTypeConstructorParams??>
              <#assign constructorParams = minor.arrowTypeConstructorParams />
            <#else>
              <#assign constructorParams = [] />
              <#list minor.typeParams?reverse as typeParam>
                <#assign constructorParams = constructorParams + [ typeParam.name ] />
              </#list>
            </#if>
          ArrowType arrowType = new ${minor.arrowType}(${constructorParams?join(", ")});
          ((PromotableWriter)writer).getWriter(MinorType.${upperName}, arrowType);
          <#else>
          ((PromotableWriter)writer).getWriter(MinorType.${upperName});
          </#if>
        }
      }
      return writer;
    }
  
    </#list></#list>
  
  }
  </#list>
  