1 /* 2 * Copyright (c) 1994, 2003, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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<String, Identifier> 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 = 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 = 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: {@code java.util.Vector. Enumerator}. 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 {@code getQualifier} and {@code getName}. 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 }