|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.java; |
|
|
|
import java.util.Hashtable; |
|
import java.io.PrintStream; |
|
import java.util.Enumeration; |
|
|
|
/** |
|
* A class to represent identifiers.<p> |
|
* |
|
* An identifier instance is very similar to a String. The difference |
|
* is that identifier can't be instanciated directly, instead they are |
|
* looked up in a hash table. This means that identifiers with the same |
|
* name map to the same identifier object. This makes comparisons of |
|
* identifiers much faster.<p> |
|
* |
|
* A lot of identifiers are qualified, that is they have '.'s in them. |
|
* Each qualified identifier is chopped up into the qualifier and the |
|
* name. The qualifier is cached in the value field.<p> |
|
* |
|
* Unqualified identifiers can have a type. This type is an integer that |
|
* can be used by a scanner as a token value. This value has to be set |
|
* using the setType method.<p> |
|
* |
|
* WARNING: The contents of this source file are not part of any |
|
* supported API. Code that depends on them does so at its own risk: |
|
* they are subject to change or removal without notice. |
|
* |
|
* @author Arthur van Hoff |
|
*/ |
|
|
|
public final |
|
class Identifier implements Constants { |
|
|
|
|
|
*/ |
|
static Hashtable hash = new Hashtable(3001, 0.5f); |
|
|
|
|
|
|
|
*/ |
|
String name; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Object value; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Type typeObject = null; |
|
|
|
|
|
|
|
*/ |
|
private int ipos; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Identifier(String name) { |
|
this.name = name; |
|
this.ipos = name.indexOf(INNERCLASS_PREFIX); |
|
} |
|
|
|
|
|
|
|
*/ |
|
int getType() { |
|
return ((value != null) && (value instanceof Integer)) ? |
|
((Integer)value).intValue() : IDENT; |
|
} |
|
|
|
|
|
|
|
*/ |
|
void setType(int t) { |
|
value = new Integer(t); |
|
//System.out.println("type(" + this + ")=" + t); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static synchronized Identifier lookup(String s) { |
|
|
|
Identifier id = (Identifier)hash.get(s); |
|
if (id == null) { |
|
hash.put(s, id = new Identifier(s)); |
|
} |
|
return id; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public static Identifier lookup(Identifier q, Identifier n) { |
|
|
|
if (q == idNull) return n; |
|
|
|
if (q.name.charAt(q.name.length()-1) == INNERCLASS_PREFIX) |
|
return lookup(q.name+n.name); |
|
Identifier id = lookup(q + "." + n); |
|
if (!n.isQualified() && !q.isInner()) |
|
id.value = q; |
|
return id; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static Identifier lookupInner(Identifier c, Identifier n) { |
|
Identifier id; |
|
if (c.isInner()) { |
|
if (c.name.charAt(c.name.length()-1) == INNERCLASS_PREFIX) |
|
id = lookup(c.name+n); |
|
else |
|
id = lookup(c, n); |
|
} else { |
|
id = lookup(c + "." + INNERCLASS_PREFIX + n); |
|
} |
|
id.value = c.value; |
|
return id; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String toString() { |
|
return name; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean isQualified() { |
|
if (value == null) { |
|
int idot = ipos; |
|
if (idot <= 0) |
|
idot = name.length(); |
|
else |
|
idot -= 1; |
|
int index = name.lastIndexOf('.', idot-1); |
|
value = (index < 0) ? idNull : Identifier.lookup(name.substring(0, index)); |
|
} |
|
return (value instanceof Identifier) && (value != idNull); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier getQualifier() { |
|
return isQualified() ? (Identifier)value : idNull; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier getName() { |
|
return isQualified() ? |
|
Identifier.lookup(name.substring(((Identifier)value).name.length() + 1)) : this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final char INNERCLASS_PREFIX = ' '; |
|
|
|
/* Explanation: |
|
* Since much of the compiler's low-level name resolution code |
|
* operates in terms of Identifier objects. This includes the |
|
* code which walks around the file system and reports what |
|
* classes are where. It is important to get nesting information |
|
* right as early as possible, since it affects the spelling of |
|
* signatures. Thus, the low-level import and resolve code must |
|
* be able Identifier type must be able to report the nesting |
|
* of types, which implied that that information must be carried |
|
* by Identifiers--or that the low-level interfaces be significantly |
|
* changed. |
|
*/ |
|
|
|
|
|
|
|
*/ |
|
public boolean isInner() { |
|
return (ipos > 0); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier getFlatName() { |
|
if (isQualified()) { |
|
return getName().getFlatName(); |
|
} |
|
if (ipos > 0 && name.charAt(ipos-1) == '.') { |
|
if (ipos+1 == name.length()) { |
|
|
|
return Identifier.lookup(name.substring(0,ipos-1)); |
|
} |
|
String n = name.substring(ipos+1); |
|
String t = name.substring(0,ipos); |
|
return Identifier.lookup(t+n); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
public Identifier getTopName() { |
|
if (!isInner()) return this; |
|
return Identifier.lookup(getQualifier(), getFlatName().getHead()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier getHead() { |
|
Identifier id = this; |
|
while (id.isQualified()) |
|
id = id.getQualifier(); |
|
return id; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Identifier getTail() { |
|
Identifier id = getHead(); |
|
if (id == this) |
|
return idNull; |
|
else |
|
return Identifier.lookup(name.substring(id.name.length() + 1)); |
|
} |
|
|
|
// Unfortunately, the current structure of the compiler requires |
|
// that the resolveName() family of methods (which appear in |
|
// Environment.java, Context.java, and ClassDefinition.java) raise |
|
// no exceptions and emit no errors. When we are in resolveName() |
|
// and we find a method that is ambiguous, we need to |
|
// unambiguously mark it as such, so that later stages of the |
|
// compiler realize that they should give an ambig.class rather than |
|
// a class.not.found error. To mark it we add a special prefix |
|
// which cannot occur in the program source. The routines below |
|
// are used to check, add, and remove this prefix. |
|
// (part of solution for 4059855). |
|
|
|
|
|
|
|
*/ |
|
private static final String ambigPrefix = "<<ambiguous>>"; |
|
|
|
|
|
|
|
*/ |
|
public boolean hasAmbigPrefix() { |
|
return (name.startsWith(ambigPrefix)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier addAmbigPrefix() { |
|
return Identifier.lookup(ambigPrefix + name); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Identifier removeAmbigPrefix() { |
|
if (hasAmbigPrefix()) { |
|
return Identifier.lookup(name.substring(ambigPrefix.length())); |
|
} else { |
|
return this; |
|
} |
|
} |
|
} |