jdk/src/jdk.rmic/share/classes/sun/tools/java/Identifier.java
changeset 25859 3317bb8137f4
parent 25799 1afc4675dc75
child 32769 c9520bbd6754
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.rmic/share/classes/sun/tools/java/Identifier.java	Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+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 {
+    /**
+     * The hashtable of identifiers
+     */
+    static Hashtable<String, Identifier> hash = new Hashtable<>(3001, 0.5f);
+
+    /**
+     * The name of the identifier
+     */
+    String name;
+
+    /**
+     * The value of the identifier, for keywords this is an
+     * instance of class Integer, for qualified names this is
+     * another identifier (the qualifier).
+     */
+    Object value;
+
+    /**
+     * The Type which corresponds to this Identifier.  This is used as
+     * cache for Type.tClass() and shouldn't be used outside of that
+     * context.
+     */
+    Type typeObject = null;
+
+    /**
+     * The index of INNERCLASS_PREFIX in the name, or -1 if none.
+     */
+    private int ipos;
+
+    /**
+     * Construct an identifier. Don't call this directly,
+     * use lookup instead.
+     * @see Identifier.lookup
+     */
+    private Identifier(String name) {
+        this.name = name;
+        this.ipos = name.indexOf(INNERCLASS_PREFIX);
+    }
+
+    /**
+     * Get the type of the identifier.
+     */
+    int getType() {
+        return ((value != null) && (value instanceof Integer)) ?
+                ((Integer)value).intValue() : IDENT;
+    }
+
+    /**
+     * Set the type of the identifier.
+     */
+    void setType(int t) {
+        value = t;
+        //System.out.println("type(" + this + ")=" + t);
+    }
+
+    /**
+     * Lookup an identifier.
+     */
+    public static synchronized Identifier lookup(String s) {
+        //System.out.println("lookup(" + s + ")");
+        Identifier id = hash.get(s);
+        if (id == null) {
+            hash.put(s, id = new Identifier(s));
+        }
+        return id;
+    }
+
+    /**
+     * Lookup a qualified identifier.
+     */
+    public static Identifier lookup(Identifier q, Identifier n) {
+        // lookup("", x) => x
+        if (q == idNull)  return n;
+        // lookup(lookupInner(c, ""), n) => lookupInner(c, lookup("", 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;
+    }
+
+    /**
+     * Lookup an inner identifier.
+     * (Note:  n can be idNull.)
+     */
+    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;
+    }
+
+    /**
+     * Convert to a string.
+     */
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Check if the name is qualified (ie: it contains a '.').
+     */
+    public boolean isQualified() {
+        if (value == null) {
+            int idot = ipos;
+            if (idot <= 0)
+                idot = name.length();
+            else
+                idot -= 1;      // back up over previous dot
+            int index = name.lastIndexOf('.', idot-1);
+            value = (index < 0) ? idNull : Identifier.lookup(name.substring(0, index));
+        }
+        return (value instanceof Identifier) && (value != idNull);
+    }
+
+    /**
+     * Return the qualifier. The null identifier is returned if
+     * the name was not qualified.  The qualifier does not include
+     * any inner part of the name.
+     */
+    public Identifier getQualifier() {
+        return isQualified() ? (Identifier)value : idNull;
+    }
+
+    /**
+     * Return the unqualified name.
+     * In the case of an inner name, the unqualified name
+     * will itself contain components.
+     */
+    public Identifier getName() {
+        return isQualified() ?
+            Identifier.lookup(name.substring(((Identifier)value).name.length() + 1)) : this;
+    }
+
+    /** A space character, which precedes the first inner class
+     *  name in a qualified name, and thus marks the qualification
+     *  as involving inner classes, instead of merely packages.<p>
+     *  Ex:  <tt>java.util.Vector. Enumerator</tt>.
+     */
+    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.
+     */
+
+    /**
+     * Check if the name is inner (ie: it contains a ' ').
+     */
+    public boolean isInner() {
+        return (ipos > 0);
+    }
+
+    /**
+     * Return the class name, without its qualifier,
+     * and with any nesting flattened into a new qualfication structure.
+     * If the original identifier is inner,
+     * the result will be qualified, and can be further
+     * decomposed by means of <tt>getQualifier</tt> and <tt>getName</tt>.
+     * <p>
+     * For example:
+     * <pre>
+     * Identifier id = Identifier.lookup("pkg.Foo. Bar");
+     * id.getName().name      =>  "Foo. Bar"
+     * id.getFlatName().name  =>  "Foo.Bar"
+     * </pre>
+     */
+    public Identifier getFlatName() {
+        if (isQualified()) {
+            return getName().getFlatName();
+        }
+        if (ipos > 0 && name.charAt(ipos-1) == '.') {
+            if (ipos+1 == name.length()) {
+                // last component is idNull
+                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);
+        }
+        // Not inner.  Just return the same as getName()
+        return this;
+    }
+
+    public Identifier getTopName() {
+        if (!isInner())  return this;
+        return Identifier.lookup(getQualifier(), getFlatName().getHead());
+    }
+
+    /**
+     * Yet another way to slice qualified identifiers:
+     * The head of an identifier is its first qualifier component,
+     * and the tail is the rest of them.
+     */
+    public Identifier getHead() {
+        Identifier id = this;
+        while (id.isQualified())
+            id = id.getQualifier();
+        return id;
+    }
+
+    /**
+     * @see getHead
+     */
+    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).
+
+    /**
+     * A special prefix to add to ambiguous names.
+     */
+    private static final String ambigPrefix = "<<ambiguous>>";
+
+    /**
+     * Determine whether an Identifier has been marked as ambiguous.
+     */
+    public boolean hasAmbigPrefix() {
+        return (name.startsWith(ambigPrefix));
+    }
+
+    /**
+     * Add ambigPrefix to `this' to make a new Identifier marked as
+     * ambiguous.  It is important that this new Identifier not refer
+     * to an existing class.
+     */
+    public Identifier addAmbigPrefix() {
+        return Identifier.lookup(ambigPrefix + name);
+    }
+
+    /**
+     * Remove the ambigPrefix from `this' to get the original identifier.
+     */
+    public Identifier removeAmbigPrefix() {
+        if (hasAmbigPrefix()) {
+            return Identifier.lookup(name.substring(ambigPrefix.length()));
+        } else {
+            return this;
+        }
+    }
+}