# IndexDefinition.java

    /*
    ** Module   : IndexDefinition.java
    ** Abstract : A container for a table index metadata.
    **
    ** Copyright (c) 2022-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 IAS 20221104 Created initial version.
    ** 002 OM  20230517 Fixed deserialization. Improved javadocs and local optimization.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.persist;

    import static com.goldencode.util.NativeTypeSerializer.*;
    import java.io.*;
    import java.util.*;
    import java.util.stream.*;
    import com.goldencode.p2j.persist.TableMapper.*;
    import com.goldencode.p2j.util.*;

    /**
     * Container for a table index metadata. Used to send the metadata of a
     * DMO to a remote side, when remote appserver calls are involved.
     */
    public class IndexDefinition
    implements Externalizable
    {
       /** Flag for default (none) index attribute. */
       private static final int IA_NONE = 0;

       /** Flag for 'primary' / 'effective primary' index attribute. */
       private static final int IA_PRIMARY = 1;

       /** Flag for 'unique' index attribute. */
       private static final int IA_UNIQUE = 2;

       /** Flag for 'word' index attribute. */
       private static final int IA_WORD = 4;

       /** The legacy name of this index. */
       private String legacyName;

       /**
        * The collection of index attribute. It is a OR-ed combination of the following flags:
        * <ul>
        *    <li>{@code IA_PRIMARY} - determines if the index is a primary index: the one with the "primary"
        *          annotation or, if there is no such one, the first in the definition order;</li>
        *    <li>{@code IA_UNIQUE} - determines if the index is a unique index;</li>
        *    <li>{@code IA_WORD} - determines if the index is a word-index index.</li>
        * </ul>
        * The collection is serialized as a single byte because there are only 3 attributes. When/if we include
        * additional attributes and the 8 bits of the byte are not enough, we will use larger data type.
        */
       private int indexAttribute = IA_NONE;

       /** Index components (fields with sorting directions). */
       private List<IndexComponentDefinition> components;

       /**
        * Default c'tor
        */
       public IndexDefinition()
       {
       }

       /**
        * Constructor. Creates an {@code IndexDefinition} staring from the {@code TableMapper} legacy data.
        *
        * @param   lii
        *          ORM index info
        */
       public IndexDefinition(LegacyIndexInfo lii)
       {
          this.legacyName = lii.getLegacyName();
          indexAttribute = (lii.isEffectivePrimary() ? IA_PRIMARY : IA_NONE) |
                           (lii.isUnique()           ? IA_UNIQUE  : IA_NONE) |
                           (lii.isWord()             ? IA_WORD    : IA_NONE);
          this.components = lii.indexComponents().map(IndexComponentDefinition::new).collect(Collectors.toList());
       }

       /**
        * Add current index to the {@code TableBuilder}.
        *
        * @param   tb
        *          {@code TableBuilder} instance.
        */
       public void addTo(TempTableBuilder tb)
       {
          tb.addNewIndex(new character(legacyName),
                         new logical((indexAttribute & IA_UNIQUE) != 0),
                         new logical((indexAttribute & IA_PRIMARY) != 0),
                         new logical((indexAttribute & IA_WORD) != 0));
          for (int i = 0; i < components.size(); i++)
          {
             IndexComponentDefinition c = components.get(i);
             tb.addFieldToIndex(legacyName, c.fieldLegacyName, c.effectiveDescending ? "desc" : "asc");
          }
       }

       /**
        * Read the index definition from the specified input source.
        *
        * @param   in
        *          The input source from which the index definition will be read.
        *
        * @throws  IOException
        *          In case of I/O errors.
        * @throws  ClassNotFoundException
        *          If the class of property could not be found/loaded.
        */
       @Override
       public void readExternal(ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          legacyName = readString(in);
          indexAttribute = in.readByte(); // reading a single byte because there are only 3 attributes
          components = readList(in, () -> new IndexComponentDefinition());
       }

       /**
        * Send the index definition to the specified output destination.
        *
        * @param    out
        *           The output destination to which the index definition will be sent.
        *
        * @throws   IOException
        *           In case of I/O errors.
        */
       @Override
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          writeString(out, legacyName);
          out.writeByte(indexAttribute); // writing a single byte because there are only 3 attributes
          writeList(out, components);
       }

       /** A container with an index component information. */
       public static class IndexComponentDefinition
       implements Externalizable
       {
          /** The legacy name of the field participating in the index. */
          private String fieldLegacyName;

          /** Determines if this component has descending sorting. */
          private boolean effectiveDescending;

          /**
           * Default c'tor
           */
          public IndexComponentDefinition()
          {
          }

          /**
           * Constructor.
           *
           * @param lici
           *        DMO ORM index component info.
           */
          public IndexComponentDefinition(LegacyIndexComponentInfo lici)
          {
             this.fieldLegacyName = lici.fieldLegacyName;
             this.effectiveDescending = lici.effectiveDescending;
          }

          /**
           * Read the index definition from the specified input source.
           *
           * @param   in
           *          The input source from which the index component definition will be read.
           *
           * @throws  IOException
           *          In case of I/O errors.
           * @throws  ClassNotFoundException
           *          If the class of property could not be found/loaded.
           */
          @Override
          public void readExternal(ObjectInput in)
          throws IOException,
                 ClassNotFoundException
          {
             fieldLegacyName = readString(in);
             effectiveDescending = in.readByte() != 0;
          }

          /**
           * Send the index definition to the specified output destination.
           *
           * @param    out
           *           The output destination to which the index component definition will be sent.
           *
           * @throws   IOException
           *           In case of I/O errors.
           */
          @Override
          public void writeExternal(ObjectOutput out)
          throws IOException
          {
             writeString(out, fieldLegacyName);
             out.writeByte(effectiveDescending ? 1 : 0);
          }
       }
    }