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.bytecode;
17
18import java.io.DataInputStream;
19import java.util.Map;
20import java.io.IOException;
21
22/**
23 * <code>InnerClasses_attribute</code>.
24 */
25public class InnerClassesAttribute extends AttributeInfo {
26    /**
27     * The name of this attribute <code>"InnerClasses"</code>.
28     */
29    public static final String tag = "InnerClasses";
30
31    InnerClassesAttribute(ConstPool cp, int n, DataInputStream in)
32        throws IOException
33    {
34        super(cp, n, in);
35    }
36
37    private InnerClassesAttribute(ConstPool cp, byte[] info) {
38        super(cp, tag, info);
39    }
40
41    /**
42     * Constructs an empty InnerClasses attribute.
43     *
44     * @see #append(String, String, String, int)
45     */
46    public InnerClassesAttribute(ConstPool cp) {
47        super(cp, tag, new byte[2]);
48        ByteArray.write16bit(0, get(), 0);
49    }
50
51    /**
52     * Returns <code>number_of_classes</code>.
53     */
54    public int tableLength() { return ByteArray.readU16bit(get(), 0); }
55
56    /**
57     * Returns <code>classes[nth].inner_class_info_index</code>.
58     */
59    public int innerClassIndex(int nth) {
60        return ByteArray.readU16bit(get(), nth * 8 + 2);
61    }
62
63    /**
64     * Returns the class name indicated
65     * by <code>classes[nth].inner_class_info_index</code>.
66     *
67     * @return null or the class name.
68     */
69    public String innerClass(int nth) {
70        int i = innerClassIndex(nth);
71        if (i == 0)
72            return null;
73        else
74            return constPool.getClassInfo(i);
75    }
76
77    /**
78     * Sets <code>classes[nth].inner_class_info_index</code> to
79     * the given index.
80     */
81    public void setInnerClassIndex(int nth, int index) {
82        ByteArray.write16bit(index, get(), nth * 8 + 2);
83    }
84
85    /**
86     * Returns <code>classes[nth].outer_class_info_index</code>.
87     */
88    public int outerClassIndex(int nth) {
89        return ByteArray.readU16bit(get(), nth * 8 + 4);
90    }
91
92    /**
93     * Returns the class name indicated
94     * by <code>classes[nth].outer_class_info_index</code>.
95     *
96     * @return null or the class name.
97     */
98    public String outerClass(int nth) {
99        int i = outerClassIndex(nth);
100        if (i == 0)
101            return null;
102        else
103            return constPool.getClassInfo(i);
104    }
105
106    /**
107     * Sets <code>classes[nth].outer_class_info_index</code> to
108     * the given index.
109     */
110    public void setOuterClassIndex(int nth, int index) {
111        ByteArray.write16bit(index, get(), nth * 8 + 4);
112    }
113
114    /**
115     * Returns <code>classes[nth].inner_name_index</code>.
116     */
117    public int innerNameIndex(int nth) {
118        return ByteArray.readU16bit(get(), nth * 8 + 6);
119    }
120
121    /**
122     * Returns the simple class name indicated
123     * by <code>classes[nth].inner_name_index</code>.
124     *
125     * @return null or the class name.
126     */
127    public String innerName(int nth) {
128        int i = innerNameIndex(nth);
129        if (i == 0)
130            return null;
131        else
132            return constPool.getUtf8Info(i);
133    }
134
135    /**
136     * Sets <code>classes[nth].inner_name_index</code> to
137     * the given index.
138     */
139    public void setInnerNameIndex(int nth, int index) {
140        ByteArray.write16bit(index, get(), nth * 8 + 6);
141    }
142
143    /**
144     * Returns <code>classes[nth].inner_class_access_flags</code>.
145     */
146    public int accessFlags(int nth) {
147        return ByteArray.readU16bit(get(), nth * 8 + 8);
148    }
149
150    /**
151     * Sets <code>classes[nth].inner_class_access_flags</code> to
152     * the given index.
153     */
154    public void setAccessFlags(int nth, int flags) {
155        ByteArray.write16bit(flags, get(), nth * 8 + 8);
156    }
157
158    /**
159     * Appends a new entry.
160     *
161     * @param inner     <code>inner_class_info_index</code>
162     * @param outer     <code>outer_class_info_index</code>
163     * @param name      <code>inner_name_index</code>
164     * @param flags     <code>inner_class_access_flags</code>
165     */
166    public void append(String inner, String outer, String name, int flags) {
167        int i = constPool.addClassInfo(inner);
168        int o = constPool.addClassInfo(outer);
169        int n = constPool.addUtf8Info(name);
170        append(i, o, n, flags);
171    }
172
173    /**
174     * Appends a new entry.
175     *
176     * @param inner     <code>inner_class_info_index</code>
177     * @param outer     <code>outer_class_info_index</code>
178     * @param name      <code>inner_name_index</code>
179     * @param flags     <code>inner_class_access_flags</code>
180     */
181    public void append(int inner, int outer, int name, int flags) {
182        byte[] data = get();
183        int len = data.length;
184        byte[] newData = new byte[len + 8];
185        for (int i = 2; i < len; ++i)
186            newData[i] = data[i];
187
188        int n = ByteArray.readU16bit(data, 0);
189        ByteArray.write16bit(n + 1, newData, 0);
190
191        ByteArray.write16bit(inner, newData, len);
192        ByteArray.write16bit(outer, newData, len + 2);
193        ByteArray.write16bit(name, newData, len + 4);
194        ByteArray.write16bit(flags, newData, len + 6);
195
196        set(newData);
197    }
198
199    /**
200     * Makes a copy.  Class names are replaced according to the
201     * given <code>Map</code> object.
202     *
203     * @param newCp     the constant pool table used by the new copy.
204     * @param classnames        pairs of replaced and substituted
205     *                          class names.
206     */
207    public AttributeInfo copy(ConstPool newCp, Map classnames) {
208        byte[] src = get();
209        byte[] dest = new byte[src.length];
210        ConstPool cp = getConstPool();
211        InnerClassesAttribute attr = new InnerClassesAttribute(newCp, dest);
212        int n = ByteArray.readU16bit(src, 0);
213        ByteArray.write16bit(n, dest, 0);
214        int j = 2;
215        for (int i = 0; i < n; ++i) {
216            int innerClass = ByteArray.readU16bit(src, j);
217            int outerClass = ByteArray.readU16bit(src, j + 2);
218            int innerName = ByteArray.readU16bit(src, j + 4);
219            int innerAccess = ByteArray.readU16bit(src, j + 6);
220
221            if (innerClass != 0)
222                innerClass = cp.copy(innerClass, newCp, classnames);
223
224            ByteArray.write16bit(innerClass, dest, j);
225
226            if (outerClass != 0)
227                outerClass = cp.copy(outerClass, newCp, classnames);
228
229            ByteArray.write16bit(outerClass, dest, j + 2);
230
231            if (innerName != 0)
232                innerName = cp.copy(innerName, newCp, classnames);
233
234            ByteArray.write16bit(innerName, dest, j + 4);
235            ByteArray.write16bit(innerAccess, dest, j + 6);
236            j += 8;
237        }
238
239        return attr;
240    }
241}
242