MemberData.java

/*
** Module   : MemberData.java
** Abstract : container for state related to a class member
**
** Copyright (c) 2007-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------------Description---------------------------------------
** 001 GES 20220522 Moved from an existing inner class to a standalone class. Added some state. 
**     CA  20220727 Improved memory management for parsing; cleanup is done in two phases:
**                  1. after each legacy class file has finished parsing, the SchemaDictionary will keep only 
**                     protected temp-tables (all private tables are removed, as these can't be reached from 
**                     sub-classes; all permanent tables are removed, as each class has its own reference to 
**                     the permanent tables).  Also, the class def instance will reduce its own used memory, 
**                     which is not required when parsing sub-classes.
**                  2. after parsing of the entire file set is finished, any SchemaDictionary or other ASTs 
**                     referenced by the ClassDefinition are released.
**     CA  20220728 Fixed CA/20220727 - allow the Variable instance to survive parsing, but release the 'def' 
**                  AST when full parsing finishes.
** 002 OM  20230115 Replaced absolutePath(), relativePath(), upPath() and downPath() with faster
**                  versions, based on node types rather on string paths.
** 003 CA  20230928 Refactored the fuzzy method resolution to be used by runtime, too.
** 004 CA  20241026 Avoid abends in case of parsing issues when the parent for a reference can't be calculated.
** 005 AS  20250411 Added the suffix attribute.
*/

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

import java.util.*;
import java.util.function.*;

import com.goldencode.ast.*;

/**
 * Container to track data associated with a member of a class.
 */
class MemberData
implements ProgressParserTokenTypes,
           MethodData
{
   /** Text name. */
   String name = null;
   
   /** Token type. */
   int type = -1;
   
   /** Extent value (defaults to 0, which means scalar) of the return type. */
   int extent = 0;
   
   /** Access mode. */
   int access = -1;
   
   /** Temporary ID. */
   int tempIdx = -1;
   
   /** Static or instance member. */
   boolean isStatic = false;
   
   /** Class type name for members that return an object instance. */
   String qname = null;
   
   /** Parameter signature for methods. */
   ParameterKey[] signature = null;
   
   /** Associated variable instance for data members. */
   Variable var = null; // used only during parsing/front phase
   
   /** The containing class where this was defined. */
   ClassDefinition container = null;
   
   /** The parameter modes. */
   String modes = null;
   
   /**
    * Marker for OO vars which are gathered during pre-scan, and they might not be actual class
    * members (as pre-scan processes DEFINE VAR statements at all levels, including methods).
    */
   boolean provisional = false;
   
   /** The AST for this member definition. After parsing is finished, this is cleared. */
   Aast ast = null;
   
   /** The AST's ID for this member definition. */
   Long astId = null;

   /** All annotations set at the method's return type. */
   // WARNING: this is not set by initialize, in case of METHOD
   Map<String, Object> retType;
   
   /** 
    * The converted Java name for this member. Used only for previously parsed classes, or
    * legacy builtin classes.
    */
   String javaname = null;
   
   /** For methods, this defines if the method overrides a parent class/interface method. */
   boolean override = false;

   /** For methods, this defines if the method is an abstract method. */
   boolean abstr = false;

   /** The suffix that needs to be appended in case of a javaname collision. */
   int suffix = 1;
   
   /**
    * Create an instance, setting the data members.
    *
    * @param    name
    *           The name of the member.
    * @param    var
    *           Associated variable instance for data members or <code>null</code>.
    * @param    signature
    *           Parameter signature for methods or <code>null</code>.
    * @param    type
    *           The token type associated with the member.
    * @param    access
    *           Access mode (<code>KW_PUBLIC</code>,
    *           <code>KW_PROTECTD</code> or <code>KW_PRIVATE</code>).
    * @param    isStatic
    *           <code>true</code> if the variable/property is static.
    * @param    qname
    *           Fully qualified class name where <code>type</code> equals
    *           <code>OO_METH_CLASS</code>. <code>null</code> if this method
    *           does not return an object instance.
    * @param    container
    *           The container class definition.
    * @param    tempIdx
    *           Temporary ID.
    */
   MemberData(String              name,
              Variable            var,
              ParameterKey[]      signature,
              int                 type,
              int                 access,
              boolean             isStatic,
              String              qname,
              ClassDefinition     container,
              int                 tempIdx,
              Map<String, Object> retType)
   {
      this.name      = name;
      this.var       = var;
      this.signature = signature;
      this.type      = type;
      this.access    = access;
      this.isStatic  = isStatic;
      this.qname     = qname;
      this.retType   = retType;
      this.container = container;
      this.tempIdx   = tempIdx;
      
      if (signature != null)
      {
         this.modes = "";
         for (ParameterKey pk : signature)
         {
            if (pk.mode == null)
            {
               modes = modes + "B";
               continue;
            }
            
            switch (pk.mode)
            {
               case KW_INPUT:
                  modes = modes + "I";
                  break;
               case KW_OUTPUT:
                  modes = modes + "O";
                  break;
               case KW_IN_OUT:
                  modes = modes + "U";
                  break;
               case KW_BUFFER:
                  modes = modes + "B";
                  break;
            }
         }
      }
   }

   /**
    * Get the signature of this method.
    * 
    * @return   See above.
    */
   @Override
   public ParameterKey[] getSignature()
   {
      return signature;
   }

   /**
    * Get the signature for the parameter at the specified 0-based index.
    * 
    * @param    idx
    *           The 0-based index for the parameter.
    *           
    * @return   See above.
    */
   @Override
   public ParameterKey getSignature(int idx)
   {
      return signature[idx];
   }   

   /**
    * Render the state of the instance in a text form.
    *
    * @return   The rendered instance.
    */
   public String toString()
   {
      StringBuilder sb = new StringBuilder();

      String qual = (qname == null) ? "" : String.format(" - %s", qname);
      
      sb.append(String.format("%s%s member %s (%s%s), extent %d, javaname %s, override %b, " +
                              "abstract %b in class %s, tempIdx %d\n",
                              ProgressParser.lookupTokenName(access),
                              isStatic ? " static" : " instance",
                              name,
                              ProgressParser.lookupTokenName(type),
                              qual,
                              extent,
                              javaname == null ? "NONE" : javaname,
                              override,
                              abstr,
                              container.getName(),
                              tempIdx));
       
      if (signature != null)
      {
         sb.append(String.format("   PARAMETERS: %d\n", signature.length));
         
         for (int i = 0; i < signature.length; i++)
         {
            sb.append(String.format("      %s\n", signature[i]));
         }
      }
      
      return sb.toString();
   }
   
   /**
    * The parse for this entire file has finished, and the AST IDs have been computed.  
    * This is the time the capture the AST IDs or add other annotations, as is executed just
    * before the AST is persisted.
    * 
    * @param    global
    *           Flag indicating that the parsing of all files has finished, and a global cleanup can be done 
    *           for this instance.
    */
   void parseFinished(boolean global)
   {
      BiConsumer<Aast, Aast> p = (ast, parent) ->
      {
         if (ast.isAnnotation("access-mode"))
         {
            parent.putAnnotation("access-mode", (Long) ast.getAnnotation("access-mode"));
            ast.removeAnnotation("access-mode");
         }
         if (ast.isAnnotation("oo-data-store"))
         {
            parent.putAnnotation("oo-data-store", (String) ast.getAnnotation("oo-data-store"));
            ast.removeAnnotation("oo-data-store");
         }
         if (ast.isAnnotation("static"))
         {
            parent.putAnnotation("static", (Boolean) ast.getAnnotation("static"));
            ast.removeAnnotation("static");
         }
      };
      if (ast != null)
      {
         if (ast.getParent() == null)
         {
            System.out.println("WARNING: Can not copy annotations for member in " + container.getName() + ": parent is null");
            return;
         }
         
         int type = ast.getType();
         if (type == KW_TEMP_TAB ||
             (type == SYMBOL && ast.getParent().getType() == DEFINE_BUFFER))
         {
            Aast parent = ast.getParent();
            p.accept(ast, parent);
            
            if (type == KW_TEMP_TAB || type == KW_WORK_TAB)
            {
               ast = parent;
            }
            else
            {
               ast = parent;
               ast = ast.getImmediateChild(KW_FOR, null);
               ast = (Aast) ast.getFirstChild();
            }
         }
         if (type == SYMBOL && ast.upPath(DEFINE_QUERY, KW_QUERY))
         {
            Aast parent = ast.getAncestor(2);
            p.accept(ast, parent);
            
            parent.putAnnotation("name", ast.getText());
            ast = parent;
         }
         
         astId = ast.getId();
         ast = null;
      }
      
      if (var != null)
      {
         var.parseFinished(global);
      }
   }
   
   /**
    * Set the {@link #provisional} state of this member.
    * 
    * @param    provisional
    *           The new state.
    */
   void setProvisional(boolean provisional)
   {
      this.provisional = provisional;
   }   
}