001    package edu.rice.cs.cunit.classFile.attributes;
002    
003    import edu.rice.cs.cunit.classFile.ClassFileTools;
004    import edu.rice.cs.cunit.classFile.attributes.visitors.IAttributeVisitor;
005    import edu.rice.cs.cunit.classFile.code.instructions.LineNumberTable;
006    import edu.rice.cs.cunit.classFile.constantPool.*;
007    import edu.rice.cs.cunit.classFile.constantPool.visitors.*;
008    import edu.rice.cs.cunit.util.Types;
009    
010    /**
011     * Represents the InnerClasses attribute in a class file.
012     *
013     * @author Mathias Ricken
014     */
015    public class InnerClassesAttributeInfo extends AAttributeInfo {
016        /**
017         * Storage class for line number/program counter _pairs.
018         */
019        public static class InnerClassesRecord {
020            public APoolInfo innerClass;
021            public APoolInfo outerClass;
022            public APoolInfo innerName;
023            public short innerFlags;
024    
025            public InnerClassesRecord(APoolInfo innerClass, APoolInfo outerClass, APoolInfo innerName, short innerFlags) {
026                this.innerClass = innerClass;
027                this.outerClass = outerClass;
028                this.innerName = innerName;
029                this.innerFlags = innerFlags;
030            }
031        }
032    
033        /**
034         * Constructor.
035         *
036         * @param name attribute name
037         * @param data attribute data
038         * @param cp   constant pool
039         *
040         * @throws ClassFormatError
041         */
042        public InnerClassesAttributeInfo(AUTFPoolInfo name, byte[] data, ConstantPool cp) throws ClassFormatError {
043            super(name, data, cp);
044        }
045    
046        /**
047         * Return the number of inner class records.
048         *
049         * @return number of inner class
050         *
051         * @throws ClassFormatError
052         */
053        public int getInnerClassCount() {
054            int res = Types.ushortFromBytes(_data, 0);
055            assert(res <= 0xffff);
056            return res;
057        }
058    
059        /**
060         * Return a copy of the inner class records.
061         *
062         * @return array of inner class records
063         *
064         * @throws ClassFormatError
065         */
066        public InnerClassesRecord[] getInnerClasses() throws ClassFormatError {
067            int count = getInnerClassCount();
068            assert(count <= 0xffff);
069            InnerClassesRecord[] arr = new InnerClassesRecord[count];
070            for(short i = 0; i < count; ++i) {
071                arr[i] = new InnerClassesRecord(
072                             _constantPool.get(Types.ushortFromBytes(_data, 2 + 8 * i)).execute(CheckClassOrEmptyVisitor.singleton(), null),
073                             _constantPool.get(Types.ushortFromBytes(_data, 4 + 8 * i)).execute(CheckClassOrEmptyVisitor.singleton(), null),
074                             _constantPool.get(Types.ushortFromBytes(_data, 6 + 8 * i)).execute(CheckUTFOrEmptyVisitor.singleton(), null),
075                             (short)(Types.ushortFromBytes(_data, 8 + 8 * i) & 0xffff));
076            }
077            return arr;
078        }
079    
080        /**
081         * Set the inner class records.
082         *
083         * @param arr array of inner class records
084         */
085        public void setLocalVariables(InnerClassesRecord[] arr) {
086            byte[] newData = new byte[2 + 8 * arr.length];
087            Types.bytesFromShort((short)arr.length, newData, 0);
088            for(int i = 0; i < arr.length; ++i) {
089                Types.bytesFromShort(_constantPool.indexOf(arr[i].innerClass), newData, 2 + 8 * i);
090                Types.bytesFromShort(_constantPool.indexOf(arr[i].outerClass), newData, 4 + 8 * i);
091                Types.bytesFromShort(_constantPool.indexOf(arr[i].innerName), newData, 6 + 8 * i);
092                Types.bytesFromShort(arr[i].innerFlags, newData, 8 + 8 * i);
093            }
094            setData(newData);
095        }
096    
097        /**
098         * Execute a visitor on this attribute.
099         *
100         * @param visitor visitor
101         * @param param   visitor-specific parameter
102         *
103         * @return visitor-specific return value
104         */
105        public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) {
106            return visitor.innerClassesCase(this, param);
107        }
108    
109        /**
110         * Return a human-readable version of this attribute.
111         *
112         * @return string
113         */
114        public String toString() {
115            StringBuilder x = new StringBuilder();
116            x.append(_name + " <" + getInnerClassCount() + " inner classes { ");
117            boolean first = true;
118            for(InnerClassesRecord inc : getInnerClasses()) {
119                if (first) {
120                    first = false;
121                }
122                else {
123                    x.append(", ");
124                }
125                x.append("(inner class=" +
126                         inc.innerClass.execute(new ADefaultPoolInfoVisitor<String, Object>() {
127                             public String defaultCase(APoolInfo host, Object param) {
128                                 return null;
129                             }
130                             public String emptyCase(EmptyPoolInfo host, Object param) {
131                                 return "null";
132                             }
133                             public String classCase(ClassPoolInfo host, Object param) {
134                                 return host.getName().toString();
135                             }
136                         }, null) + ", outer class=" +
137                         inc.outerClass.execute(new ADefaultPoolInfoVisitor<String, Object>() {
138                             public String defaultCase(APoolInfo host, Object param) {
139                                 return null;
140                             }
141                             public String emptyCase(EmptyPoolInfo host, Object param) {
142                                 return "<not a member>";
143                             }
144                             public String classCase(ClassPoolInfo host, Object param) {
145                                 return host.getName().toString();
146                             }
147                         }, null) + ", inner name=" +
148                         inc.innerName.execute(new ADefaultPoolInfoVisitor<String, Object>() {
149                             public String defaultCase(APoolInfo host, Object param) {
150                                 return null;
151                             }
152                             public String emptyCase(EmptyPoolInfo host, Object param) {
153                                 return "<anonymous>";
154                             }
155                             public String asciizCase(ASCIIPoolInfo host, Object param) {
156                                 return host.toString();
157                             }
158                             public String unicodeCase(UnicodePoolInfo host, Object param) {
159                                 return host.toString();
160                             }
161                         }, null) + ", flags=" +
162                         ClassFileTools.getAccessString(inc.innerFlags) + ")");
163            }
164            x.append(" } >");
165            return x.toString();
166        }
167    
168        /**
169         * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them.
170         *
171         * @param startPC program counter to start at
172         * @param deltaPC change in program counter values
173         */
174        public void adjustPC(int startPC, int deltaPC) {
175            // nothing to do
176        }
177    
178        /**
179         * Translate the program counter values contained in this attribute from an old line number table to a new one.
180         *
181         * @param index      critical point (insertion or deletion point)
182         * @param deltaIndex delta value to add to all old line numbers greater than the critical point
183         * @param oldLnt     old line number table
184         * @param newLnt     new line number table
185         */
186        public void translatePC(int index, int deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) {
187            // nothing to do
188        }
189    
190        /**
191         * Creates and returns a copy of this object.
192         */
193        public Object clone() throws CloneNotSupportedException {
194            return super.clone();
195        }
196    
197        /**
198         * Returns the name of the attribute as it appears in the class file.
199         *
200         * @return name of the attribute.
201         */
202        public static String getAttributeName() {
203            return "InnerClasses";
204        }
205    }