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)
{
}
}
}