# EntityName.java

    /*
    ** Module   : EntityName.java
    ** Abstract : Normalized representation of a schema entity name
    **
    ** Copyright (c) 2004-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
    ** 001 ECF 20041217   @19166 Created initial version.
    ** 002 ECF 20050118   @19340 Added methods to safely expose name component type constants.
    ** 003 ECF 20050411   @20704 Removed unused toNameNode method.
    ** 004 ECF 20150715          Replace StringBuffer with StringBuilder.
    ** 005 OM  20320111          Small optimization. Code maintenance.
    ** 006 GBB 20230512          Logging methods replaced by CentralLogger/ConversionStatus.
    */

    /*
    ** 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.schema;

    import com.goldencode.p2j.convert.*;

    import java.util.*;
    import java.util.logging.*;

    /**
     * Representation of a qualified Progress schema entity name, reflecting the
     * qualified name hierarchy represented by the Progress language naming
     * conventions for schema entity references. Possible permutations are:
     * <pre>
     *    database.table.field
     *    database.table
     *    table.field
     *    database
     *    table
     *    field
     * </pre>
     * where each part of a fully qualified name represents an entity which has
     * a one-to-many relationship with the entity to its right (if any), and a
     * one-to-one relationship with the entity to its left (if any).
     * <p>
     * The primary purpose of this class is to normalize entity names which may
     * be fully qualified, partially qualified, or unqualified into an object
     * which contains all parts of the entity name (even if this requires that
     * some remain null).
     */
    public final class EntityName
    {
       /** Logger. */
       private static final ConversionStatus LOG = ConversionStatus.get(EntityName.class);

       /** Constant indicating name represents a database */
       public static final int DATABASE = 0;

       /** Constant indicating name represents a table */
       public static final int TABLE = 1;

       /** Constant indicating name represents a field */
       public static final int FIELD = 2;

       /** Array of database, table, and field parts of qualified entity name */
       private final String[] names = new String[3];

       /**
        * Get the descriptive name of the specified entity type.
        *
        * @param   type
        *          Constant indicating type: database, table, or field.
        *
        * @return  Descriptive name of the type.
        */
       public static String getTypeName(int type)
       throws IllegalArgumentException
       {
          validateType(type);

          final String[] typeNames =
          {
             "database",
             "table",
             "field",
          };

          return typeNames[type];
       }

       /**
        * Establish whether <code>type</code> (presumably the type of an entity
        * name qualifier) has an associated "child" type, as described by the
        * {@link #getChildType} method description.
        *
        * @param   type
        *          Type of the qualifier using the type constants of this class.
        *
        * @return  <code>true</code> if <code>type</code> is either
        *          <code>DATABASE</code> or <code>TABLE</code>.
        *
        * @throws  IllegalArgumentException
        *          if <code>type</code> is neither <code>DATABASE</code> nor
        *          <code>TABLE</code>.
        *
        * @see     #getChildType
        * @see     #DATABASE
        * @see     #TABLE
        * @see     #FIELD
        */
       public static boolean hasChildType(int type)
       {
          return (type >= DATABASE && type < FIELD);
       }

       /**
        * Get the type (i.e., <code>TABLE</code> or <code>FIELD</code>) of the
        * next part of the entity name (as read from left to right), given the
        * type of a qualifier.
        *
        * @param   type
        *          Type of the qualifier; must be <code>DATABASE</code> or
        *          <code>TABLE</code>.
        *
        * @return  Type of the "child" of the qualifier type represented by the
        *          <code>type</code> argument. Will be <code>TABLE</code> if
        *          <code>type</code> is <code>DATABASE</code>; will be
        *          <code>FIELD</code> if <code>type</code> is <code>TABLE</code>.
        *
        * @throws  IllegalArgumentException
        *          if <code>type</code> is neither <code>DATABASE</code> nor
        *          <code>TABLE</code>.
        *
        * @see     #hasChildType
        * @see     #DATABASE
        * @see     #TABLE
        * @see     #FIELD
        */
       public static int getChildType(int type)
       throws IllegalArgumentException
       {
          if (!hasChildType(type))
          {
             throw new IllegalArgumentException("Invalid parent entity name type: " + type);
          }

          return (type + 1);
       }

       /**
        * Validate that <code>type</code> represents a valid entity name component
        * type (i.e., is one of the type constants defined by this class).
        *
        * @param   type
        *          Type constant to test for validity.
        *
        * @throws  IllegalArgumentException
        *          if <code>type</code> is not one of the defined type constants.
        *
        * @see     #getChildType
        * @see     #DATABASE
        * @see     #TABLE
        * @see     #FIELD
        */
       private static void validateType(int type)
       throws IllegalArgumentException
       {
          if (type < DATABASE || type > FIELD)
          {
             throw new IllegalArgumentException("Invalid entity name type: " + type);
          }
       }

       /**
        * Constructor which parses a qualified entity name into its constituent
        * parts.
        *
        * @param   type
        *          Constant indicating whether this object represents a database,
        *          table, or field.
        * @param   name
        *          Qualified or unqualified entity name to be parsed.
        *
        * @throws  IllegalArgumentException
        *          if <code>type</code> is not a valid type constant or if
        *          <code>name</code> does not conform to the accepted schema
        *          entity {@link EntityName naming conventions}.
        */
       public EntityName(int type, String name)
       throws IllegalArgumentException
       {
          parseName(type, name);
       }

       /**
        * Get the name for the specified part of the entity name.
        *
        * @param   type
        *          Constant indicating whether to return the database, table, or
        *          field portion of the name.
        *
        * @return  The name part corresponding with <code>type</code>. May be
        *          <code>null</code> if this object does not contain the specified
        *          part.
        */
       public String getPart(int type)
       {
          return names[type];
       }

       /**
        * Get the database qualifier part of the entity name.
        *
        * @return  The database qualifier part of the entity name. May be
        *          <code>null</code> if this entity name has no database
        *          qualifier.
        */
       public String getDatabase()
       {
          return names[DATABASE];
       }

       /**
        * Get the table qualifier part of the entity name.
        *
        * @return  The table qualifier part of the entity name. May be
        *          <code>null</code> if this entity name has no table
        *          qualifier, or if it does not represent a table.
        */
       public String getTable()
       {
          return names[TABLE];
       }

       /**
        * Get the field part of the entity name.
        *
        * @return  The field part of the entity name. May be <code>null</code> if
        *          this entity does not represent a field.
        */
       public String getField()
       {
          return names[FIELD];
       }

       /**
        * Creates a string representation of a normalized entity name. Some parts
        * of the name may be null. This is used primarily for debug purposes.
        *
        * @return  String representation of this object's internal state.
        */
       public String toString()
       {
          return names[DATABASE] + '.' + names[TABLE] + '.' + names[FIELD];
       }

       /**
        * Parse a qualified entity name into its constituent parts. This method
        * normalizes all entity names -- whether fully qualified, partially
        * qualified, or unqualified -- into a common format.
        *
        * @param   type
        *          Constant indicating whether this object represents a database,
        *          table, or field.
        * @param   name
        *          Qualified or unqualified entity name to be parsed.
        *
        * @throws  IllegalArgumentException
        *          if <code>type</code> is not a valid type constant or if
        *          <code>name</code> does not conform to the accepted schema
        *          entity {@link EntityName naming conventions}.
        */
       private void parseName(int type, String name)
       throws IllegalArgumentException
       {
          validateType(type);

          List<String> parts = new ArrayList<>(3);
          StringTokenizer tok = new StringTokenizer(name, ".");
          while (tok.hasMoreTokens())
          {
             parts.add(tok.nextToken());
          }

          int size = parts.size();
          if (size == 0 || size > type + 1)
          {
             throw new IllegalArgumentException("Invalid " + getTypeName(type) + " name: " + name);
          }

          for (int i = type; size > 0; i--)
          {
             size --;
             names[i] = parts.get(size);
          }
       }

       /**
        * Unit test driver for this class. Accepts an entity type and fully
        * qualified, partially qualified, or unqualified entity name and prints
        * the normalized form of the name.
        *
        * @param   args
        *          Command line arguments:
        *          <ol>
        *          <li>Entity type (must use integer type constants)</li>
        *          <li>Entity name string to be parsed</li>
        *          </ol>
        */
       public static void main(String[] args)
       {
          try
          {
             int type = Integer.parseInt(args[0]);
             System.out.println((new EntityName(type, args[1])));
          }
          catch (Exception exc)
          {
             LOG.log(Level.SEVERE, "", exc);
          }
       }
    }