1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist;
17
18import javassist.bytecode.Descriptor;
19
20/**
21 * A hash table associating class names with different names.
22 *
23 * <p>This hashtable is used for replacing class names in a class
24 * definition or a method body.  Define a subclass of this class
25 * if a more complex mapping algorithm is needed.  For example,
26 *
27 * <ul><pre>class MyClassMap extends ClassMap {
28 *   public Object get(Object jvmClassName) {
29 *     String name = toJavaName((String)jvmClassName);
30 *     if (name.startsWith("java."))
31 *         return toJvmName("java2." + name.substring(5));
32 *     else
33 *         return super.get(jvmClassName);
34 *   }
35 * }</pre></ul>
36 *
37 * <p>This subclass maps <code>java.lang.String</code> to
38 * <code>java2.lang.String</code>.  Note that <code>get()</code>
39 * receives and returns the internal representation of a class name.
40 * For example, the internal representation of <code>java.lang.String</code>
41 * is <code>java/lang/String</code>.
42 *
43 * @see #get(Object)
44 * @see CtClass#replaceClassName(ClassMap)
45 * @see CtNewMethod#copy(CtMethod,String,CtClass,ClassMap)
46 */
47public class ClassMap extends java.util.HashMap {
48    private ClassMap parent;
49
50    /**
51     * Constructs a hash table.
52     */
53    public ClassMap() { parent = null; }
54
55    ClassMap(ClassMap map) { parent = map; }
56
57    /**
58     * Maps a class name to another name in this hashtable.
59     * The names are obtained with calling <code>Class.getName()</code>.
60     * This method translates the given class names into the
61     * internal form used in the JVM before putting it in
62     * the hashtable.
63     *
64     * @param oldname   the original class name
65     * @param newname   the substituted class name.
66     */
67    public void put(CtClass oldname, CtClass newname) {
68        put(oldname.getName(), newname.getName());
69    }
70
71    /**
72     * Maps a class name to another name in this hashtable.
73     * If the hashtable contains another mapping from the same
74     * class name, the old mapping is replaced.
75     * This method translates the given class names into the
76     * internal form used in the JVM before putting it in
77     * the hashtable.
78     *
79     * <p>If <code>oldname</code> is identical to
80     * <code>newname</code>, then this method does not
81     * perform anything; it does not record the mapping from
82     * <code>oldname</code> to <code>newname</code>.  See
83     * <code>fix</code> method.
84     *
85     * @param oldname   the original class name.
86     * @param newname   the substituted class name.
87     * @see #fix(String)
88     */
89    public void put(String oldname, String newname) {
90        if (oldname == newname)
91            return;
92
93        String oldname2 = toJvmName(oldname);
94        String s = (String)get(oldname2);
95        if (s == null || !s.equals(oldname2))
96            super.put(oldname2, toJvmName(newname));
97    }
98
99    /**
100     * Is equivalent to <code>put()</code> except that
101     * the given mapping is not recorded into the hashtable
102     * if another mapping from <code>oldname</code> is
103     * already included.
104     *
105     * @param oldname       the original class name.
106     * @param newname       the substituted class name.
107     */
108    public void putIfNone(String oldname, String newname) {
109        if (oldname == newname)
110            return;
111
112        String oldname2 = toJvmName(oldname);
113        String s = (String)get(oldname2);
114        if (s == null)
115            super.put(oldname2, toJvmName(newname));
116    }
117
118    protected final void put0(Object oldname, Object newname) {
119        super.put(oldname, newname);
120    }
121
122    /**
123     * Returns the class name to wihch the given <code>jvmClassName</code>
124     * is mapped.  A subclass of this class should override this method.
125     *
126     * <p>This method receives and returns the internal representation of
127     * class name used in the JVM.
128     *
129     * @see #toJvmName(String)
130     * @see #toJavaName(String)
131     */
132    public Object get(Object jvmClassName) {
133        Object found = super.get(jvmClassName);
134        if (found == null && parent != null)
135            return parent.get(jvmClassName);
136        else
137            return found;
138    }
139
140    /**
141     * Prevents a mapping from the specified class name to another name.
142     */
143    public void fix(CtClass clazz) {
144        fix(clazz.getName());
145    }
146
147    /**
148     * Prevents a mapping from the specified class name to another name.
149     */
150    public void fix(String name) {
151        String name2 = toJvmName(name);
152        super.put(name2, name2);
153    }
154
155    /**
156     * Converts a class name into the internal representation used in
157     * the JVM.
158     */
159    public static String toJvmName(String classname) {
160        return Descriptor.toJvmName(classname);
161    }
162
163    /**
164     * Converts a class name from the internal representation used in
165     * the JVM to the normal one used in Java.
166     */
167    public static String toJavaName(String classname) {
168        return Descriptor.toJavaName(classname);
169    }
170}
171