AbstractConversionWorker.java

/*
** Module   : AbstractConversionWorker.java
** Abstract : Base class for pattern workers which need to convert a source
**            AST to a target AST.
**
** Copyright (c) 2005-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
** 001 ECF 20050429   @20972 Created initial version. Abstracted peer AST
**                           processing from uast.JavaPatternWorker, which
**                           now extends this class.
** 002 GES 20050928   @22871 Simplified initializeAst() processing to
**                           use centralized code and added the option to
**                           insert the node at a specified index.
** 003 GES 20090429   @42045 Match package and class name changes.
** 004 GES 20090514   @42203 Match package, class and method name changes.
** 005 GES 20090518   @42373 Import change.
** 006 SVL 20130624          Added support for virtual files.
** 007 ECF 20140405          Removed unnecessary static variable for AstManager.
** 008 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.convert;

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

import antlr.*;
import com.goldencode.ast.*;
import com.goldencode.p2j.cfg.*;
import com.goldencode.p2j.pattern.*;

/**
 * Provides common services for pattern worker subclasses which need to
 * convert the source AST being processed by the pattern engine to a new,
 * target AST. It is still up to the concrete conversion worker to handle
 * the instantiation of the proper AST type, but this class provides helpers
 * to manage peer ID cross-referencing, initialization of newly created
 * target ASTs, peer ID lookups, etc.
 */
public abstract class AbstractConversionWorker
extends AbstractPatternWorker
{
   /** Logger */
   private static final ConversionStatus LOG = ConversionStatus.get(AbstractConversionWorker.class);
   
   /** Common key for AST peerid */
   public static final String PEER_ID = "peerid";
   
   /**
    * Default constructor.
    */
   protected AbstractConversionWorker()
   {
   }
   
   /**
    * Initialize a newly created AST.  All ID setup and parent linkages are
    * established if the parent AST is passed as a parameter. Otherwise, the
    * AST is left unidentified and orphaned.  If a parent AST is passed, a
    * resolver instance should also be passed.
    * <p>
    * This method always adds the node as the last child of the given parent.
    *
    * @param   ast
    *          AST which is to be initialized.
    * @param   type
    *          The token type for the AST.
    * @param   text
    *          The text for the AST.
    * @param   parent
    *          The parent AST node or <code>null</code>.
    * @param   resolver
    *          AST symbol resolver if available, else <code>null</code>.
    */
   public static void initializeAst(Aast              ast,
                                    int               type,
                                    String            text,
                                    Aast              parent,
                                    AstSymbolResolver resolver)
   {
      initializeAst(ast, type, text, parent, resolver, -1);
   }
   
   /**
    * Initialize a newly created AST.  All ID setup and parent linkages are
    * established if the parent AST is passed as a parameter. Otherwise, the
    * AST is left unidentified and orphaned.  If a parent AST is passed, a
    * resolver instance should also be passed.
    *
    * @param   ast
    *          AST which is to be initialized.
    * @param   type
    *          The token type for the AST.
    * @param   text
    *          The text for the AST.
    * @param   parent
    *          The parent AST node or <code>null</code>.
    * @param   resolver
    *          AST symbol resolver if available, else <code>null</code>.
    * @param   index
    *          0 to insert as the first child, -1 to insert as the last
    *          child or a 0-based index to specify an exact child index
    *          at which to insert the new child.  All current children at
    *          that index or at a greater index, are moved right. If the
    *          index is greater than the current number of children, the
    *          new child will be added at the end.
    */
   public static void initializeAst(Aast              ast,
                                    int               type,
                                    String            text,
                                    Aast              parent,
                                    AstSymbolResolver resolver,
                                    int               index)
   {
      // create a token
      CommonToken tok = new CommonToken(type, text);
      
      // "install" our token inside the AST (this inserts token type and
      // text)
      ast.initialize(tok);
      
      Long fileId = null;
      
      // only handle the ID processing if we have a valid parent
      if (parent != null)
      {
         try
         {
            // add the child to the parent, force fixups of parent and id
            parent.graftAt(ast, index);
            
            if (resolver != null)
            {
               // every new AST needs to be registered with the resolver so that
               // subsequent lookups are possible while this pipeline continues
               // --> we can only do this for ASTs that have an ID
               resolver.registerAst(ast);
            }
         }
         
         catch (AstException exc)
         {
            LOG.log(Level.SEVERE, "", exc);
         }
      }
   }
   
   /**
    * Worker routine to centralize initialization of a new AST that is a 
    * root node.  A new tree ID is registered in the {@link
    * com.goldencode.ast.AstManager} and set into the AST.
    *
    * @param   ast
    *          AST which is the root node to be initialized.
    * @param   file
    *          The file in which to persist the new AST.
    * @param   resolver
    *          The current instance of the {@link
    *          com.goldencode.p2j.pattern.AstSymbolResolver} in use or
    *          <code>null</code>.
    */   
   public static void initializeRootAst(Aast              ast,
                                        File              file,
                                        AstSymbolResolver resolver)
   {
      String filename = file.getAbsolutePath();
      String normal   = Configuration.normalizeFilename(filename);

      initializeRootAst(ast, normal, resolver);
   }

   /**
    * Worker routine to centralize initialization of a new AST that is a root node.  A new tree
    * ID is registered in the {@link com.goldencode.ast.AstManager} and set into the AST.
    *
    * @param   ast
    *          AST which is the root node to be initialized.
    * @param   filename
    *          The file in which to persist the new AST. Can be real or virtual (i.e. the file
    *          can be never persisted into the file system, the name is used to map some ID to
    *          the AST).
    * @param   resolver
    *          The current instance of the {@link com.goldencode.p2j.pattern.AstSymbolResolver}
    *          in use or <code>null</code>.
    */
   public static void initializeRootAst(Aast              ast,
                                        String            filename,
                                        AstSymbolResolver resolver)
   {
      try
      {
         ast.brainwash(filename);

         if (resolver != null)
         {
            // every new AST needs to be registered with the resolver so that
            // subsequent lookups are possible while this pipeline continues
            // --> we can only do this for ASTs that have an ID
            resolver.registerAst(ast);

            // set the current target AST into the resolver
            resolver.setTargetRootAst(ast);
         }
      }
      catch (AstException exc)
      {
         LOG.log(Level.SEVERE, "", exc);
      }
   }
   
   /**
    * Cross reference two ASTs with one another as peers. This is done by
    * setting each AST's ID within a "peerid" annotation in the other AST.
    *
    * @param   ast1
    *          AST to be cross-referenced with <code>ast2</code>.
    * @param   ast2
    *          AST to be cross-referenced with <code>ast1</code>.
    */
   public static void crossReferencePeerAsts(Aast ast1, Aast ast2)
   {
      ast1.putAnnotation(PEER_ID, ast2.getId());
      ast2.putAnnotation(PEER_ID, ast1.getId());
   }
   
   /**
    * Simple helper to return the peer ID annotation from the passed-in
    * node's parent.
    *
    * @param    ast
    *           The source or target node to operate upon.  
    *
    * @return   The unique ID of the parent's peer or <code>null</code> if
    *           either the parent or the parent's <code>peerid</code>
    *           annotation do not exist.
    */
   public static Long getParentPeer(Aast ast)
   {
      Aast parent = ast.getParent();
      
      if (parent == null)
         return null;
      
      return (Long) parent.getAnnotation(PEER_ID);
   }
   
   /**
    * Search the specified AST node and up each ancestor node in the path
    * to the root to find the first instance of the 'peerid' annotation.
    * This allows target ASTs to be attached to the correct target parent
    * node, without necessarily knowing what parent might be enclosing
    * it.
    *
    * @param    ast
    *           The node at which to start the inspection.
    *
    * @return  The first 'peerid' annotation found in the current node
    *          or the closest ancestor node that has a valid peerid.  
    *          Returns -1 if no peerid is found in any ancestor.
    */
   public static long getClosestPeerId(Aast ast)
   {
      Aast node = ast;
      
      while (node != null)
      {
         Long id = (Long) node.getAnnotation(PEER_ID);
         
         if (id != null)
         {
            return id.longValue();
         }
         
         node = node.getParent();
      }
      
      return -1;
   }
}