0N/A/*
2362N/A * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.tools.java;
0N/A
0N/Aimport java.util.Hashtable;
0N/Aimport java.io.PrintStream;
0N/Aimport java.util.Enumeration;
0N/A
0N/A/**
0N/A * A class to represent identifiers.<p>
0N/A *
0N/A * An identifier instance is very similar to a String. The difference
0N/A * is that identifier can't be instanciated directly, instead they are
0N/A * looked up in a hash table. This means that identifiers with the same
0N/A * name map to the same identifier object. This makes comparisons of
0N/A * identifiers much faster.<p>
0N/A *
0N/A * A lot of identifiers are qualified, that is they have '.'s in them.
0N/A * Each qualified identifier is chopped up into the qualifier and the
0N/A * name. The qualifier is cached in the value field.<p>
0N/A *
0N/A * Unqualified identifiers can have a type. This type is an integer that
0N/A * can be used by a scanner as a token value. This value has to be set
0N/A * using the setType method.<p>
0N/A *
0N/A * WARNING: The contents of this source file are not part of any
0N/A * supported API. Code that depends on them does so at its own risk:
0N/A * they are subject to change or removal without notice.
0N/A *
0N/A * @author Arthur van Hoff
0N/A */
0N/A
0N/Apublic final
0N/Aclass Identifier implements Constants {
0N/A /**
0N/A * The hashtable of identifiers
0N/A */
0N/A static Hashtable hash = new Hashtable(3001, 0.5f);
0N/A
0N/A /**
0N/A * The name of the identifier
0N/A */
0N/A String name;
0N/A
0N/A /**
0N/A * The value of the identifier, for keywords this is an
0N/A * instance of class Integer, for qualified names this is
0N/A * another identifier (the qualifier).
0N/A */
0N/A Object value;
0N/A
0N/A /**
0N/A * The Type which corresponds to this Identifier. This is used as
0N/A * cache for Type.tClass() and shouldn't be used outside of that
0N/A * context.
0N/A */
0N/A Type typeObject = null;
0N/A
0N/A /**
0N/A * The index of INNERCLASS_PREFIX in the name, or -1 if none.
0N/A */
0N/A private int ipos;
0N/A
0N/A /**
0N/A * Construct an identifier. Don't call this directly,
0N/A * use lookup instead.
0N/A * @see Identifier.lookup
0N/A */
0N/A private Identifier(String name) {
0N/A this.name = name;
0N/A this.ipos = name.indexOf(INNERCLASS_PREFIX);
0N/A }
0N/A
0N/A /**
0N/A * Get the type of the identifier.
0N/A */
0N/A int getType() {
0N/A return ((value != null) && (value instanceof Integer)) ?
0N/A ((Integer)value).intValue() : IDENT;
0N/A }
0N/A
0N/A /**
0N/A * Set the type of the identifier.
0N/A */
0N/A void setType(int t) {
0N/A value = new Integer(t);
0N/A //System.out.println("type(" + this + ")=" + t);
0N/A }
0N/A
0N/A /**
0N/A * Lookup an identifier.
0N/A */
0N/A public static synchronized Identifier lookup(String s) {
0N/A //System.out.println("lookup(" + s + ")");
0N/A Identifier id = (Identifier)hash.get(s);
0N/A if (id == null) {
0N/A hash.put(s, id = new Identifier(s));
0N/A }
0N/A return id;
0N/A }
0N/A
0N/A /**
0N/A * Lookup a qualified identifier.
0N/A */
0N/A public static Identifier lookup(Identifier q, Identifier n) {
0N/A // lookup("", x) => x
0N/A if (q == idNull) return n;
0N/A // lookup(lookupInner(c, ""), n) => lookupInner(c, lookup("", n))
0N/A if (q.name.charAt(q.name.length()-1) == INNERCLASS_PREFIX)
0N/A return lookup(q.name+n.name);
0N/A Identifier id = lookup(q + "." + n);
0N/A if (!n.isQualified() && !q.isInner())
0N/A id.value = q;
0N/A return id;
0N/A }
0N/A
0N/A /**
0N/A * Lookup an inner identifier.
0N/A * (Note: n can be idNull.)
0N/A */
0N/A public static Identifier lookupInner(Identifier c, Identifier n) {
0N/A Identifier id;
0N/A if (c.isInner()) {
0N/A if (c.name.charAt(c.name.length()-1) == INNERCLASS_PREFIX)
0N/A id = lookup(c.name+n);
0N/A else
0N/A id = lookup(c, n);
0N/A } else {
0N/A id = lookup(c + "." + INNERCLASS_PREFIX + n);
0N/A }
0N/A id.value = c.value;
0N/A return id;
0N/A }
0N/A
0N/A /**
0N/A * Convert to a string.
0N/A */
0N/A public String toString() {
0N/A return name;
0N/A }
0N/A
0N/A /**
0N/A * Check if the name is qualified (ie: it contains a '.').
0N/A */
0N/A public boolean isQualified() {
0N/A if (value == null) {
0N/A int idot = ipos;
0N/A if (idot <= 0)
0N/A idot = name.length();
0N/A else
0N/A idot -= 1; // back up over previous dot
0N/A int index = name.lastIndexOf('.', idot-1);
0N/A value = (index < 0) ? idNull : Identifier.lookup(name.substring(0, index));
0N/A }
0N/A return (value instanceof Identifier) && (value != idNull);
0N/A }
0N/A
0N/A /**
0N/A * Return the qualifier. The null identifier is returned if
0N/A * the name was not qualified. The qualifier does not include
0N/A * any inner part of the name.
0N/A */
0N/A public Identifier getQualifier() {
0N/A return isQualified() ? (Identifier)value : idNull;
0N/A }
0N/A
0N/A /**
0N/A * Return the unqualified name.
0N/A * In the case of an inner name, the unqualified name
0N/A * will itself contain components.
0N/A */
0N/A public Identifier getName() {
0N/A return isQualified() ?
0N/A Identifier.lookup(name.substring(((Identifier)value).name.length() + 1)) : this;
0N/A }
0N/A
0N/A /** A space character, which precedes the first inner class
0N/A * name in a qualified name, and thus marks the qualification
0N/A * as involving inner classes, instead of merely packages.<p>
0N/A * Ex: <tt>java.util.Vector. Enumerator</tt>.
0N/A */
0N/A public static final char INNERCLASS_PREFIX = ' ';
0N/A
0N/A /* Explanation:
0N/A * Since much of the compiler's low-level name resolution code
0N/A * operates in terms of Identifier objects. This includes the
0N/A * code which walks around the file system and reports what
0N/A * classes are where. It is important to get nesting information
0N/A * right as early as possible, since it affects the spelling of
0N/A * signatures. Thus, the low-level import and resolve code must
0N/A * be able Identifier type must be able to report the nesting
0N/A * of types, which implied that that information must be carried
0N/A * by Identifiers--or that the low-level interfaces be significantly
0N/A * changed.
0N/A */
0N/A
0N/A /**
0N/A * Check if the name is inner (ie: it contains a ' ').
0N/A */
0N/A public boolean isInner() {
0N/A return (ipos > 0);
0N/A }
0N/A
0N/A /**
0N/A * Return the class name, without its qualifier,
0N/A * and with any nesting flattened into a new qualfication structure.
0N/A * If the original identifier is inner,
0N/A * the result will be qualified, and can be further
0N/A * decomposed by means of <tt>getQualifier</tt> and <tt>getName</tt>.
0N/A * <p>
0N/A * For example:
0N/A * <pre>
0N/A * Identifier id = Identifier.lookup("pkg.Foo. Bar");
0N/A * id.getName().name => "Foo. Bar"
0N/A * id.getFlatName().name => "Foo.Bar"
0N/A * </pre>
0N/A */
0N/A public Identifier getFlatName() {
0N/A if (isQualified()) {
0N/A return getName().getFlatName();
0N/A }
0N/A if (ipos > 0 && name.charAt(ipos-1) == '.') {
0N/A if (ipos+1 == name.length()) {
0N/A // last component is idNull
0N/A return Identifier.lookup(name.substring(0,ipos-1));
0N/A }
0N/A String n = name.substring(ipos+1);
0N/A String t = name.substring(0,ipos);
0N/A return Identifier.lookup(t+n);
0N/A }
0N/A // Not inner. Just return the same as getName()
0N/A return this;
0N/A }
0N/A
0N/A public Identifier getTopName() {
0N/A if (!isInner()) return this;
0N/A return Identifier.lookup(getQualifier(), getFlatName().getHead());
0N/A }
0N/A
0N/A /**
0N/A * Yet another way to slice qualified identifiers:
0N/A * The head of an identifier is its first qualifier component,
0N/A * and the tail is the rest of them.
0N/A */
0N/A public Identifier getHead() {
0N/A Identifier id = this;
0N/A while (id.isQualified())
0N/A id = id.getQualifier();
0N/A return id;
0N/A }
0N/A
0N/A /**
0N/A * @see getHead
0N/A */
0N/A public Identifier getTail() {
0N/A Identifier id = getHead();
0N/A if (id == this)
0N/A return idNull;
0N/A else
0N/A return Identifier.lookup(name.substring(id.name.length() + 1));
0N/A }
0N/A
0N/A // Unfortunately, the current structure of the compiler requires
0N/A // that the resolveName() family of methods (which appear in
0N/A // Environment.java, Context.java, and ClassDefinition.java) raise
0N/A // no exceptions and emit no errors. When we are in resolveName()
0N/A // and we find a method that is ambiguous, we need to
0N/A // unambiguously mark it as such, so that later stages of the
0N/A // compiler realize that they should give an ambig.class rather than
0N/A // a class.not.found error. To mark it we add a special prefix
0N/A // which cannot occur in the program source. The routines below
0N/A // are used to check, add, and remove this prefix.
0N/A // (part of solution for 4059855).
0N/A
0N/A /**
0N/A * A special prefix to add to ambiguous names.
0N/A */
0N/A private static final String ambigPrefix = "<<ambiguous>>";
0N/A
0N/A /**
0N/A * Determine whether an Identifier has been marked as ambiguous.
0N/A */
0N/A public boolean hasAmbigPrefix() {
0N/A return (name.startsWith(ambigPrefix));
0N/A }
0N/A
0N/A /**
0N/A * Add ambigPrefix to `this' to make a new Identifier marked as
0N/A * ambiguous. It is important that this new Identifier not refer
0N/A * to an existing class.
0N/A */
0N/A public Identifier addAmbigPrefix() {
0N/A return Identifier.lookup(ambigPrefix + name);
0N/A }
0N/A
0N/A /**
0N/A * Remove the ambigPrefix from `this' to get the original identifier.
0N/A */
0N/A public Identifier removeAmbigPrefix() {
0N/A if (hasAmbigPrefix()) {
0N/A return Identifier.lookup(name.substring(ambigPrefix.length()));
0N/A } else {
0N/A return this;
0N/A }
0N/A }
0N/A}