001: /*
002: * Copyright 1994-2003 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.java;
027:
028: import java.util.Hashtable;
029: import java.io.PrintStream;
030: import java.util.Enumeration;
031:
032: /**
033: * A class to represent identifiers.<p>
034: *
035: * An identifier instance is very similar to a String. The difference
036: * is that identifier can't be instanciated directly, instead they are
037: * looked up in a hash table. This means that identifiers with the same
038: * name map to the same identifier object. This makes comparisons of
039: * identifiers much faster.<p>
040: *
041: * A lot of identifiers are qualified, that is they have '.'s in them.
042: * Each qualified identifier is chopped up into the qualifier and the
043: * name. The qualifier is cached in the value field.<p>
044: *
045: * Unqualified identifiers can have a type. This type is an integer that
046: * can be used by a scanner as a token value. This value has to be set
047: * using the setType method.<p>
048: *
049: * WARNING: The contents of this source file are not part of any
050: * supported API. Code that depends on them does so at its own risk:
051: * they are subject to change or removal without notice.
052: *
053: * @author Arthur van Hoff
054: * @version 1.31, 05/05/07
055: */
056:
057: public final class Identifier implements Constants {
058: /**
059: * The hashtable of identifiers
060: */
061: static Hashtable hash = new Hashtable(3001, 0.5f);
062:
063: /**
064: * The name of the identifier
065: */
066: String name;
067:
068: /**
069: * The value of the identifier, for keywords this is an
070: * instance of class Integer, for qualified names this is
071: * another identifier (the qualifier).
072: */
073: Object value;
074:
075: /**
076: * The Type which corresponds to this Identifier. This is used as
077: * cache for Type.tClass() and shouldn't be used outside of that
078: * context.
079: */
080: Type typeObject = null;
081:
082: /**
083: * The index of INNERCLASS_PREFIX in the name, or -1 if none.
084: */
085: private int ipos;
086:
087: /**
088: * Construct an identifier. Don't call this directly,
089: * use lookup instead.
090: * @see Identifier.lookup
091: */
092: private Identifier(String name) {
093: this .name = name;
094: this .ipos = name.indexOf(INNERCLASS_PREFIX);
095: }
096:
097: /**
098: * Get the type of the identifier.
099: */
100: int getType() {
101: return ((value != null) && (value instanceof Integer)) ? ((Integer) value)
102: .intValue()
103: : IDENT;
104: }
105:
106: /**
107: * Set the type of the identifier.
108: */
109: void setType(int t) {
110: value = new Integer(t);
111: //System.out.println("type(" + this + ")=" + t);
112: }
113:
114: /**
115: * Lookup an identifier.
116: */
117: public static synchronized Identifier lookup(String s) {
118: //System.out.println("lookup(" + s + ")");
119: Identifier id = (Identifier) hash.get(s);
120: if (id == null) {
121: hash.put(s, id = new Identifier(s));
122: }
123: return id;
124: }
125:
126: /**
127: * Lookup a qualified identifier.
128: */
129: public static Identifier lookup(Identifier q, Identifier n) {
130: // lookup("", x) => x
131: if (q == idNull)
132: return n;
133: // lookup(lookupInner(c, ""), n) => lookupInner(c, lookup("", n))
134: if (q.name.charAt(q.name.length() - 1) == INNERCLASS_PREFIX)
135: return lookup(q.name + n.name);
136: Identifier id = lookup(q + "." + n);
137: if (!n.isQualified() && !q.isInner())
138: id.value = q;
139: return id;
140: }
141:
142: /**
143: * Lookup an inner identifier.
144: * (Note: n can be idNull.)
145: */
146: public static Identifier lookupInner(Identifier c, Identifier n) {
147: Identifier id;
148: if (c.isInner()) {
149: if (c.name.charAt(c.name.length() - 1) == INNERCLASS_PREFIX)
150: id = lookup(c.name + n);
151: else
152: id = lookup(c, n);
153: } else {
154: id = lookup(c + "." + INNERCLASS_PREFIX + n);
155: }
156: id.value = c.value;
157: return id;
158: }
159:
160: /**
161: * Convert to a string.
162: */
163: public String toString() {
164: return name;
165: }
166:
167: /**
168: * Check if the name is qualified (ie: it contains a '.').
169: */
170: public boolean isQualified() {
171: if (value == null) {
172: int idot = ipos;
173: if (idot <= 0)
174: idot = name.length();
175: else
176: idot -= 1; // back up over previous dot
177: int index = name.lastIndexOf('.', idot - 1);
178: value = (index < 0) ? idNull : Identifier.lookup(name
179: .substring(0, index));
180: }
181: return (value instanceof Identifier) && (value != idNull);
182: }
183:
184: /**
185: * Return the qualifier. The null identifier is returned if
186: * the name was not qualified. The qualifier does not include
187: * any inner part of the name.
188: */
189: public Identifier getQualifier() {
190: return isQualified() ? (Identifier) value : idNull;
191: }
192:
193: /**
194: * Return the unqualified name.
195: * In the case of an inner name, the unqualified name
196: * will itself contain components.
197: */
198: public Identifier getName() {
199: return isQualified() ? Identifier.lookup(name
200: .substring(((Identifier) value).name.length() + 1))
201: : this ;
202: }
203:
204: /** A space character, which precedes the first inner class
205: * name in a qualified name, and thus marks the qualification
206: * as involving inner classes, instead of merely packages.<p>
207: * Ex: <tt>java.util.Vector. Enumerator</tt>.
208: */
209: public static final char INNERCLASS_PREFIX = ' ';
210:
211: /* Explanation:
212: * Since much of the compiler's low-level name resolution code
213: * operates in terms of Identifier objects. This includes the
214: * code which walks around the file system and reports what
215: * classes are where. It is important to get nesting information
216: * right as early as possible, since it affects the spelling of
217: * signatures. Thus, the low-level import and resolve code must
218: * be able Identifier type must be able to report the nesting
219: * of types, which implied that that information must be carried
220: * by Identifiers--or that the low-level interfaces be significantly
221: * changed.
222: */
223:
224: /**
225: * Check if the name is inner (ie: it contains a ' ').
226: */
227: public boolean isInner() {
228: return (ipos > 0);
229: }
230:
231: /**
232: * Return the class name, without its qualifier,
233: * and with any nesting flattened into a new qualfication structure.
234: * If the original identifier is inner,
235: * the result will be qualified, and can be further
236: * decomposed by means of <tt>getQualifier</tt> and <tt>getName</tt>.
237: * <p>
238: * For example:
239: * <pre>
240: * Identifier id = Identifier.lookup("pkg.Foo. Bar");
241: * id.getName().name => "Foo. Bar"
242: * id.getFlatName().name => "Foo.Bar"
243: * </pre>
244: */
245: public Identifier getFlatName() {
246: if (isQualified()) {
247: return getName().getFlatName();
248: }
249: if (ipos > 0 && name.charAt(ipos - 1) == '.') {
250: if (ipos + 1 == name.length()) {
251: // last component is idNull
252: return Identifier.lookup(name.substring(0, ipos - 1));
253: }
254: String n = name.substring(ipos + 1);
255: String t = name.substring(0, ipos);
256: return Identifier.lookup(t + n);
257: }
258: // Not inner. Just return the same as getName()
259: return this ;
260: }
261:
262: public Identifier getTopName() {
263: if (!isInner())
264: return this ;
265: return Identifier.lookup(getQualifier(), getFlatName()
266: .getHead());
267: }
268:
269: /**
270: * Yet another way to slice qualified identifiers:
271: * The head of an identifier is its first qualifier component,
272: * and the tail is the rest of them.
273: */
274: public Identifier getHead() {
275: Identifier id = this ;
276: while (id.isQualified())
277: id = id.getQualifier();
278: return id;
279: }
280:
281: /**
282: * @see getHead
283: */
284: public Identifier getTail() {
285: Identifier id = getHead();
286: if (id == this )
287: return idNull;
288: else
289: return Identifier.lookup(name
290: .substring(id.name.length() + 1));
291: }
292:
293: // Unfortunately, the current structure of the compiler requires
294: // that the resolveName() family of methods (which appear in
295: // Environment.java, Context.java, and ClassDefinition.java) raise
296: // no exceptions and emit no errors. When we are in resolveName()
297: // and we find a method that is ambiguous, we need to
298: // unambiguously mark it as such, so that later stages of the
299: // compiler realize that they should give an ambig.class rather than
300: // a class.not.found error. To mark it we add a special prefix
301: // which cannot occur in the program source. The routines below
302: // are used to check, add, and remove this prefix.
303: // (part of solution for 4059855).
304:
305: /**
306: * A special prefix to add to ambiguous names.
307: */
308: private static final String ambigPrefix = "<<ambiguous>>";
309:
310: /**
311: * Determine whether an Identifier has been marked as ambiguous.
312: */
313: public boolean hasAmbigPrefix() {
314: return (name.startsWith(ambigPrefix));
315: }
316:
317: /**
318: * Add ambigPrefix to `this' to make a new Identifier marked as
319: * ambiguous. It is important that this new Identifier not refer
320: * to an existing class.
321: */
322: public Identifier addAmbigPrefix() {
323: return Identifier.lookup(ambigPrefix + name);
324: }
325:
326: /**
327: * Remove the ambigPrefix from `this' to get the original identifier.
328: */
329: public Identifier removeAmbigPrefix() {
330: if (hasAmbigPrefix()) {
331: return Identifier.lookup(name.substring(ambigPrefix
332: .length()));
333: } else {
334: return this;
335: }
336: }
337: }
|