JavaSymbolResolver.java

/*
** Module   : JavaSymbolResolver.java
** Abstract : provides symbol resolution services for Java ASTs
**
** Copyright (c) 2005-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- -T- --JPRM-- ----------------Description-----------------
** 001 GES 20050323 NEW   @20489 First version, with support for keyword 
**                               processing and token type/name conversion.
** 002 TJD 20220504       Java 11 compatibility minor changes
** 003 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.uast;

import com.goldencode.p2j.convert.*;

import java.util.*;
import java.text.*;
import java.lang.reflect.*;
import java.util.logging.*;

/**
 * Provides symbol resolution services for Java ASTs.  At this time only
 * keyword resolution is provided.  Keywords are statically defined and
 * all Java keywords are reserved and processed on a case-sensitive basis.
 * <p>
 * This class also provides a service to lookup a symbolic name for a
 * Java token type and the type that goes with a symbolic token type name.
 *
 * @author    GES
 * @version   1.0.0
 */
public class JavaSymbolResolver
implements JavaTokenTypes
{
   /** Logger. */
   private static final ConversionStatus LOG = ConversionStatus.get(JavaSymbolResolver.class);
   
   /**
    * Stores all Java language keywords and provides name to token type
    * resolution.  This is a case-sensitive dictionary.
    */
   private static KeywordDictionary keywords = new KeywordDictionary(true);
   
   /** Stores a list of all names indexed by the token type. */
   private static Map typeToName = new HashMap();
   
   /** Stores a list of all types indexed by the symbolic type name. */
   private static Map nameToType = new HashMap();
   
   // Static initializer.
   static
   {
      initializeKeywordDictionary();
      buildTokenNameArray();
   }
   
   /**
    * Lookup a symbol to see if it is a reserved Java keyword.  Matches are
    * done on a case-sensitive basis.
    *
    * @param    symbol
    *           The symbol to lookup in the dictionary.
    *
    * @return   The token type of the reserved keyword or -1 if no match was 
    *           found.
    */
   public static int lookupKeyword(String symbol)
   {
      Keyword match = keywords.lookupKeyword(symbol);
      
      if (match == null)
         return -1;
      
      return match.getTokenType(); 
   }
   
   /**
    * Translates a text representation of a Java token type into the
    * actual integer value.
    *
    * @param    tokenName
    *           The text representation of the token type.
    *
    * @return   A valid integer token type value or -1 if no match was found.
    */
   public static int lookupTokenType(String tokenName)
   {
      Integer result = (Integer)nameToType.get(tokenName.toLowerCase());
      
      if (result == null)
      {
         return -1;
      }

      return result.intValue();
   }   
   
   /**
    * Translates an integer value of a Java token type into the associated 
    * human readable text representation.
    *
    * @param    type
    *           The integer token type.
    *
    * @return   A valid symbolic token type name or <code>null</code> if
    *           no match was found.
    */
   public static String lookupTokenName(int type)
   {
      return (String) typeToName.get(Integer.valueOf(type));
   }      
   
   /**
    * Assumes all <code>int</code> type fields in the {@link JavaTokenTypes}
    * interface are unique token types and it adds a bidirectional mapping
    * (name to type and type to name) for each type using reflection. This
    * assumes that all 'int' fields are token types AND that all such types
    * are UNIQUE!  This should be a safe assumption since this is the
    * purpose for that class.  The type names used as an index to the types
    * are stored in lowercase to allow case-insensitive matches.
    */
   private static void buildTokenNameArray()
   {
      try
      {
         Field[] list = JavaTokenTypes.class.getDeclaredFields();
         
         for (int i = 0; i < list.length; i++)
         {
            Class cls = list[i].getType();
            
            // assume that all "int" fields are token types AND that all such
            // types are UNIQUE!
            if ("int".equals(cls.getName()))
            {
               Integer type = Integer.valueOf(list[i].getInt(null));
               String  name = list[i].getName();
               
               nameToType.put(name.toLowerCase(), type);
               typeToName.put(type, name);
            }
         }
      }
      
      catch (Exception exc)
      {
         LOG.log(Level.SEVERE, "", exc);
      }
   }
   
   /**
    * Creates a <code>Keyword</code> array and initializes that array to
    * a the list of all recognized Java language keywords. The
    * initialization is done in-line and the source file is organized in
    * an easy to maintain column format.  A loop then processes this array
    * and adds each <code>Keyword</code> into the keyword dictionary.
    * <p>
    * The following values are needed for each <code>Keyword</code> object:
    * <ul>
    *    <li> full keyword text for the keyword
    *    <li> the minimum number of characters for matching (all keywords use
    *         0 which means no there are no abbreviations)
    *    <li> the integer token type to assign to the keyword on a match
    *         (see below)
    *    <li> whether it is a reserved keyword or not (true means it is
    *         reserved)
    * </ul>
    * <p>
    * See <code>{@link Keyword}</code> for more details.
    */
   private static void initializeKeywordDictionary()
   {
      Keyword[] kws = 
      {
         /* format: ( Full Keyword Text, Abr, Token Type       , Reserved ) */
         new Keyword( "abstract"       ,   0, KW_ABSTRACT      , true     ),
         new Keyword( "boolean"        ,   0, KW_BOOLEAN       , true     ),
         new Keyword( "break"          ,   0, KW_BREAK         , true     ),
         new Keyword( "byte"           ,   0, KW_BYTE          , true     ),
         new Keyword( "case"           ,   0, KW_CASE          , true     ),
         new Keyword( "catch"          ,   0, KW_CATCH         , true     ),
         new Keyword( "char"           ,   0, KW_CHAR          , true     ),
         new Keyword( "class"          ,   0, KW_CLASS         , true     ),
         new Keyword( "const"          ,   0, KW_CONST         , true     ),
         new Keyword( "continue"       ,   0, KW_CONTINUE      , true     ),
         new Keyword( "default"        ,   0, KW_DEFAULT       , true     ),
         new Keyword( "do"             ,   0, KW_DO            , true     ),
         new Keyword( "double"         ,   0, KW_DOUBLE        , true     ),
         new Keyword( "else"           ,   0, KW_ELSE          , true     ),
         new Keyword( "extends"        ,   0, KW_EXTENDS       , true     ),
         new Keyword( "final"          ,   0, KW_FINAL         , true     ),
         new Keyword( "finally"        ,   0, KW_FINALLY       , true     ),
         new Keyword( "float"          ,   0, KW_FLOAT         , true     ),
         new Keyword( "for"            ,   0, KW_FOR           , true     ),
         new Keyword( "goto"           ,   0, KW_GOTO          , true     ),
         new Keyword( "if"             ,   0, KW_IF            , true     ),
         new Keyword( "implements"     ,   0, KW_IMPLEMENTS    , true     ),
         new Keyword( "import"         ,   0, KW_IMPORT        , true     ),
         new Keyword( "instanceof"     ,   0, KW_INSTANCEOF    , true     ),
         new Keyword( "int"            ,   0, KW_INT           , true     ),
         new Keyword( "interface"      ,   0, KW_INTERFACE     , true     ),
         new Keyword( "long"           ,   0, KW_LONG          , true     ),
         new Keyword( "native"         ,   0, KW_NATIVE        , true     ),
         new Keyword( "new"            ,   0, KW_NEW           , true     ),
         new Keyword( "package"        ,   0, KW_PACKAGE       , true     ),
         new Keyword( "private"        ,   0, KW_PRIVATE       , true     ),
         new Keyword( "protected"      ,   0, KW_PROTECTED     , true     ),
         new Keyword( "public"         ,   0, KW_PUBLIC        , true     ),
         new Keyword( "return"         ,   0, KW_RETURN        , true     ),
         new Keyword( "short"          ,   0, KW_SHORT         , true     ),
         new Keyword( "static"         ,   0, KW_STATIC        , true     ),
         new Keyword( "strictfp"       ,   0, KW_STRICTFP      , true     ),
         new Keyword( "super"          ,   0, KW_SUPER         , true     ),
         new Keyword( "switch"         ,   0, KW_SWITCH        , true     ),
         new Keyword( "synchronized"   ,   0, KW_SYNCHRONIZED  , true     ),
         new Keyword( "this"           ,   0, KW_THIS          , true     ),
         new Keyword( "throw"          ,   0, KW_THROW         , true     ),
         new Keyword( "throws"         ,   0, KW_THROWS        , true     ),
         new Keyword( "transient"      ,   0, KW_TRANSIENT     , true     ),
         new Keyword( "try"            ,   0, KW_TRY           , true     ),
         new Keyword( "void"           ,   0, KW_VOID          , true     ),
         new Keyword( "volatile"       ,   0, KW_VOLATILE      , true     ),
         new Keyword( "while"          ,   0, KW_WHILE         , true     )
      };
      
      for ( int i = 0; i < kws.length; i++ )
      {
         keywords.addKeyword( kws[i] );
      }
   }
   
   /**
    * Simple command line driver to print out the list of Java token types
    * and the associated symbolic names.
    *
    * @param    args
    *           All command line arguments are ignored.
    */
   public static void main(String[] args)
   {
      NumberFormat fmt = NumberFormat.getInstance();
      fmt.setMinimumIntegerDigits(4);
      fmt.setGroupingUsed(false);
      
      try
      {
         for (int i = 0; i < 500; i++)
         {
            String s = JavaSymbolResolver.lookupTokenName(i);
            
            if (s != null)
               System.out.println("[" + fmt.format(i) + "]   " + s);
         }
      }
      catch (Exception exc)
      {
      }
   }   
}