J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 1994-2003 Sun Microsystems, Inc. All Rights Reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun in the LICENSE file that accompanied this code. |
| 10 | * |
| 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 14 | * version 2 for more details (a copy is included in the LICENSE file that |
| 15 | * accompanied this code). |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License version |
| 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 20 | * |
| 21 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any questions. |
| 24 | */ |
| 25 | |
| 26 | package sun.tools.java; |
| 27 | |
| 28 | import java.util.Hashtable; |
| 29 | import java.io.PrintStream; |
| 30 | import java.util.Enumeration; |
| 31 | |
| 32 | /** |
| 33 | * A class to represent identifiers.<p> |
| 34 | * |
| 35 | * An identifier instance is very similar to a String. The difference |
| 36 | * is that identifier can't be instanciated directly, instead they are |
| 37 | * looked up in a hash table. This means that identifiers with the same |
| 38 | * name map to the same identifier object. This makes comparisons of |
| 39 | * identifiers much faster.<p> |
| 40 | * |
| 41 | * A lot of identifiers are qualified, that is they have '.'s in them. |
| 42 | * Each qualified identifier is chopped up into the qualifier and the |
| 43 | * name. The qualifier is cached in the value field.<p> |
| 44 | * |
| 45 | * Unqualified identifiers can have a type. This type is an integer that |
| 46 | * can be used by a scanner as a token value. This value has to be set |
| 47 | * using the setType method.<p> |
| 48 | * |
| 49 | * WARNING: The contents of this source file are not part of any |
| 50 | * supported API. Code that depends on them does so at its own risk: |
| 51 | * they are subject to change or removal without notice. |
| 52 | * |
| 53 | * @author Arthur van Hoff |
| 54 | */ |
| 55 | |
| 56 | public final |
| 57 | class Identifier implements Constants { |
| 58 | /** |
| 59 | * The hashtable of identifiers |
| 60 | */ |
| 61 | static Hashtable hash = new Hashtable(3001, 0.5f); |
| 62 | |
| 63 | /** |
| 64 | * The name of the identifier |
| 65 | */ |
| 66 | String name; |
| 67 | |
| 68 | /** |
| 69 | * The value of the identifier, for keywords this is an |
| 70 | * instance of class Integer, for qualified names this is |
| 71 | * another identifier (the qualifier). |
| 72 | */ |
| 73 | Object value; |
| 74 | |
| 75 | /** |
| 76 | * The Type which corresponds to this Identifier. This is used as |
| 77 | * cache for Type.tClass() and shouldn't be used outside of that |
| 78 | * context. |
| 79 | */ |
| 80 | Type typeObject = null; |
| 81 | |
| 82 | /** |
| 83 | * The index of INNERCLASS_PREFIX in the name, or -1 if none. |
| 84 | */ |
| 85 | private int ipos; |
| 86 | |
| 87 | /** |
| 88 | * Construct an identifier. Don't call this directly, |
| 89 | * use lookup instead. |
| 90 | * @see Identifier.lookup |
| 91 | */ |
| 92 | private Identifier(String name) { |
| 93 | this.name = name; |
| 94 | this.ipos = name.indexOf(INNERCLASS_PREFIX); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Get the type of the identifier. |
| 99 | */ |
| 100 | int getType() { |
| 101 | return ((value != null) && (value instanceof Integer)) ? |
| 102 | ((Integer)value).intValue() : IDENT; |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * Set the type of the identifier. |
| 107 | */ |
| 108 | void setType(int t) { |
| 109 | value = new Integer(t); |
| 110 | //System.out.println("type(" + this + ")=" + t); |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * Lookup an identifier. |
| 115 | */ |
| 116 | public static synchronized Identifier lookup(String s) { |
| 117 | //System.out.println("lookup(" + s + ")"); |
| 118 | Identifier id = (Identifier)hash.get(s); |
| 119 | if (id == null) { |
| 120 | hash.put(s, id = new Identifier(s)); |
| 121 | } |
| 122 | return id; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Lookup a qualified identifier. |
| 127 | */ |
| 128 | public static Identifier lookup(Identifier q, Identifier n) { |
| 129 | // lookup("", x) => x |
| 130 | if (q == idNull) return n; |
| 131 | // lookup(lookupInner(c, ""), n) => lookupInner(c, lookup("", n)) |
| 132 | if (q.name.charAt(q.name.length()-1) == INNERCLASS_PREFIX) |
| 133 | return lookup(q.name+n.name); |
| 134 | Identifier id = lookup(q + "." + n); |
| 135 | if (!n.isQualified() && !q.isInner()) |
| 136 | id.value = q; |
| 137 | return id; |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * Lookup an inner identifier. |
| 142 | * (Note: n can be idNull.) |
| 143 | */ |
| 144 | public static Identifier lookupInner(Identifier c, Identifier n) { |
| 145 | Identifier id; |
| 146 | if (c.isInner()) { |
| 147 | if (c.name.charAt(c.name.length()-1) == INNERCLASS_PREFIX) |
| 148 | id = lookup(c.name+n); |
| 149 | else |
| 150 | id = lookup(c, n); |
| 151 | } else { |
| 152 | id = lookup(c + "." + INNERCLASS_PREFIX + n); |
| 153 | } |
| 154 | id.value = c.value; |
| 155 | return id; |
| 156 | } |
| 157 | |
| 158 | /** |
| 159 | * Convert to a string. |
| 160 | */ |
| 161 | public String toString() { |
| 162 | return name; |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Check if the name is qualified (ie: it contains a '.'). |
| 167 | */ |
| 168 | public boolean isQualified() { |
| 169 | if (value == null) { |
| 170 | int idot = ipos; |
| 171 | if (idot <= 0) |
| 172 | idot = name.length(); |
| 173 | else |
| 174 | idot -= 1; // back up over previous dot |
| 175 | int index = name.lastIndexOf('.', idot-1); |
| 176 | value = (index < 0) ? idNull : Identifier.lookup(name.substring(0, index)); |
| 177 | } |
| 178 | return (value instanceof Identifier) && (value != idNull); |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Return the qualifier. The null identifier is returned if |
| 183 | * the name was not qualified. The qualifier does not include |
| 184 | * any inner part of the name. |
| 185 | */ |
| 186 | public Identifier getQualifier() { |
| 187 | return isQualified() ? (Identifier)value : idNull; |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * Return the unqualified name. |
| 192 | * In the case of an inner name, the unqualified name |
| 193 | * will itself contain components. |
| 194 | */ |
| 195 | public Identifier getName() { |
| 196 | return isQualified() ? |
| 197 | Identifier.lookup(name.substring(((Identifier)value).name.length() + 1)) : this; |
| 198 | } |
| 199 | |
| 200 | /** A space character, which precedes the first inner class |
| 201 | * name in a qualified name, and thus marks the qualification |
| 202 | * as involving inner classes, instead of merely packages.<p> |
| 203 | * Ex: <tt>java.util.Vector. Enumerator</tt>. |
| 204 | */ |
| 205 | public static final char INNERCLASS_PREFIX = ' '; |
| 206 | |
| 207 | /* Explanation: |
| 208 | * Since much of the compiler's low-level name resolution code |
| 209 | * operates in terms of Identifier objects. This includes the |
| 210 | * code which walks around the file system and reports what |
| 211 | * classes are where. It is important to get nesting information |
| 212 | * right as early as possible, since it affects the spelling of |
| 213 | * signatures. Thus, the low-level import and resolve code must |
| 214 | * be able Identifier type must be able to report the nesting |
| 215 | * of types, which implied that that information must be carried |
| 216 | * by Identifiers--or that the low-level interfaces be significantly |
| 217 | * changed. |
| 218 | */ |
| 219 | |
| 220 | /** |
| 221 | * Check if the name is inner (ie: it contains a ' '). |
| 222 | */ |
| 223 | public boolean isInner() { |
| 224 | return (ipos > 0); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Return the class name, without its qualifier, |
| 229 | * and with any nesting flattened into a new qualfication structure. |
| 230 | * If the original identifier is inner, |
| 231 | * the result will be qualified, and can be further |
| 232 | * decomposed by means of <tt>getQualifier</tt> and <tt>getName</tt>. |
| 233 | * <p> |
| 234 | * For example: |
| 235 | * <pre> |
| 236 | * Identifier id = Identifier.lookup("pkg.Foo. Bar"); |
| 237 | * id.getName().name => "Foo. Bar" |
| 238 | * id.getFlatName().name => "Foo.Bar" |
| 239 | * </pre> |
| 240 | */ |
| 241 | public Identifier getFlatName() { |
| 242 | if (isQualified()) { |
| 243 | return getName().getFlatName(); |
| 244 | } |
| 245 | if (ipos > 0 && name.charAt(ipos-1) == '.') { |
| 246 | if (ipos+1 == name.length()) { |
| 247 | // last component is idNull |
| 248 | return Identifier.lookup(name.substring(0,ipos-1)); |
| 249 | } |
| 250 | String n = name.substring(ipos+1); |
| 251 | String t = name.substring(0,ipos); |
| 252 | return Identifier.lookup(t+n); |
| 253 | } |
| 254 | // Not inner. Just return the same as getName() |
| 255 | return this; |
| 256 | } |
| 257 | |
| 258 | public Identifier getTopName() { |
| 259 | if (!isInner()) return this; |
| 260 | return Identifier.lookup(getQualifier(), getFlatName().getHead()); |
| 261 | } |
| 262 | |
| 263 | /** |
| 264 | * Yet another way to slice qualified identifiers: |
| 265 | * The head of an identifier is its first qualifier component, |
| 266 | * and the tail is the rest of them. |
| 267 | */ |
| 268 | public Identifier getHead() { |
| 269 | Identifier id = this; |
| 270 | while (id.isQualified()) |
| 271 | id = id.getQualifier(); |
| 272 | return id; |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * @see getHead |
| 277 | */ |
| 278 | public Identifier getTail() { |
| 279 | Identifier id = getHead(); |
| 280 | if (id == this) |
| 281 | return idNull; |
| 282 | else |
| 283 | return Identifier.lookup(name.substring(id.name.length() + 1)); |
| 284 | } |
| 285 | |
| 286 | // Unfortunately, the current structure of the compiler requires |
| 287 | // that the resolveName() family of methods (which appear in |
| 288 | // Environment.java, Context.java, and ClassDefinition.java) raise |
| 289 | // no exceptions and emit no errors. When we are in resolveName() |
| 290 | // and we find a method that is ambiguous, we need to |
| 291 | // unambiguously mark it as such, so that later stages of the |
| 292 | // compiler realize that they should give an ambig.class rather than |
| 293 | // a class.not.found error. To mark it we add a special prefix |
| 294 | // which cannot occur in the program source. The routines below |
| 295 | // are used to check, add, and remove this prefix. |
| 296 | // (part of solution for 4059855). |
| 297 | |
| 298 | /** |
| 299 | * A special prefix to add to ambiguous names. |
| 300 | */ |
| 301 | private static final String ambigPrefix = "<<ambiguous>>"; |
| 302 | |
| 303 | /** |
| 304 | * Determine whether an Identifier has been marked as ambiguous. |
| 305 | */ |
| 306 | public boolean hasAmbigPrefix() { |
| 307 | return (name.startsWith(ambigPrefix)); |
| 308 | } |
| 309 | |
| 310 | /** |
| 311 | * Add ambigPrefix to `this' to make a new Identifier marked as |
| 312 | * ambiguous. It is important that this new Identifier not refer |
| 313 | * to an existing class. |
| 314 | */ |
| 315 | public Identifier addAmbigPrefix() { |
| 316 | return Identifier.lookup(ambigPrefix + name); |
| 317 | } |
| 318 | |
| 319 | /** |
| 320 | * Remove the ambigPrefix from `this' to get the original identifier. |
| 321 | */ |
| 322 | public Identifier removeAmbigPrefix() { |
| 323 | if (hasAmbigPrefix()) { |
| 324 | return Identifier.lookup(name.substring(ambigPrefix.length())); |
| 325 | } else { |
| 326 | return this; |
| 327 | } |
| 328 | } |
| 329 | } |