001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 *
017 */
018package org.apache.bcel.classfile;
019
020import java.io.DataInput;
021import java.io.DataOutputStream;
022import java.io.IOException;
023
024import org.apache.bcel.Const;
025
026/**
027 * This class represents a inner class attribute, i.e., the class
028 * indices of the inner and outer classes, the name and the attributes
029 * of the inner class.
030 *
031 * @see InnerClasses
032 */
033public final class InnerClass implements Cloneable, Node {
034
035    private int innerClassIndex;
036    private int outerClassIndex;
037    private int innerNameIndex;
038    private int innerAccessFlags;
039
040
041    /**
042     * Initialize from another object.
043     */
044    public InnerClass(final InnerClass c) {
045        this(c.getInnerClassIndex(), c.getOuterClassIndex(), c.getInnerNameIndex(), c
046                .getInnerAccessFlags());
047    }
048
049
050    /**
051     * Construct object from file stream.
052     * @param file Input stream
053     * @throws IOException
054     */
055    InnerClass(final DataInput file) throws IOException {
056        this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file
057                .readUnsignedShort());
058    }
059
060
061    /**
062     * @param innerClassIndex Class index in constant pool of inner class
063     * @param outerClassIndex Class index in constant pool of outer class
064     * @param innerNameIndex  Name index in constant pool of inner class
065     * @param innerAccessFlags Access flags of inner class
066     */
067    public InnerClass(final int innerClassIndex, final int outerClassIndex, final int innerNameIndex,
068            final int innerAccessFlags) {
069        this.innerClassIndex = innerClassIndex;
070        this.outerClassIndex = outerClassIndex;
071        this.innerNameIndex = innerNameIndex;
072        this.innerAccessFlags = innerAccessFlags;
073    }
074
075
076    /**
077     * Called by objects that are traversing the nodes of the tree implicitely
078     * defined by the contents of a Java class. I.e., the hierarchy of methods,
079     * fields, attributes, etc. spawns a tree of objects.
080     *
081     * @param v Visitor object
082     */
083    @Override
084    public void accept( final Visitor v ) {
085        v.visitInnerClass(this);
086    }
087
088
089    /**
090     * Dump inner class attribute to file stream in binary format.
091     *
092     * @param file Output file stream
093     * @throws IOException
094     */
095    public void dump( final DataOutputStream file ) throws IOException {
096        file.writeShort(innerClassIndex);
097        file.writeShort(outerClassIndex);
098        file.writeShort(innerNameIndex);
099        file.writeShort(innerAccessFlags);
100    }
101
102
103    /**
104     * @return access flags of inner class.
105     */
106    public int getInnerAccessFlags() {
107        return innerAccessFlags;
108    }
109
110
111    /**
112     * @return class index of inner class.
113     */
114    public int getInnerClassIndex() {
115        return innerClassIndex;
116    }
117
118
119    /**
120     * @return name index of inner class.
121     */
122    public int getInnerNameIndex() {
123        return innerNameIndex;
124    }
125
126
127    /**
128     * @return class index of outer class.
129     */
130    public int getOuterClassIndex() {
131        return outerClassIndex;
132    }
133
134
135    /**
136     * @param innerAccessFlags access flags for this inner class
137     */
138    public void setInnerAccessFlags( final int innerAccessFlags ) {
139        this.innerAccessFlags = innerAccessFlags;
140    }
141
142
143    /**
144     * @param innerClassIndex index into the constant pool for this class
145     */
146    public void setInnerClassIndex( final int innerClassIndex ) {
147        this.innerClassIndex = innerClassIndex;
148    }
149
150
151    /**
152     * @param innerNameIndex index into the constant pool for this class's name
153     */
154    public void setInnerNameIndex( final int innerNameIndex ) { // TODO unused
155        this.innerNameIndex = innerNameIndex;
156    }
157
158
159    /**
160     * @param outerClassIndex index into the constant pool for the owning class
161     */
162    public void setOuterClassIndex( final int outerClassIndex ) { // TODO unused
163        this.outerClassIndex = outerClassIndex;
164    }
165
166
167    /**
168     * @return String representation.
169     */
170    @Override
171    public String toString() {
172        return "InnerClass(" + innerClassIndex + ", " + outerClassIndex + ", "
173                + innerNameIndex + ", " + innerAccessFlags + ")";
174    }
175
176
177    /**
178     * @return Resolved string representation
179     */
180    public String toString( final ConstantPool constantPool ) {
181        String outer_class_name;
182        String inner_name;
183        String inner_class_name = constantPool.getConstantString(innerClassIndex,
184                Const.CONSTANT_Class);
185        inner_class_name = Utility.compactClassName(inner_class_name, false);
186        if (outerClassIndex != 0) {
187            outer_class_name = constantPool.getConstantString(outerClassIndex,
188                    Const.CONSTANT_Class);
189            outer_class_name = " of class " + Utility.compactClassName(outer_class_name, false);
190        } else {
191            outer_class_name = "";
192        }
193        if (innerNameIndex != 0) {
194            inner_name = ((ConstantUtf8) constantPool.getConstant(innerNameIndex,
195                    Const.CONSTANT_Utf8)).getBytes();
196        } else {
197            inner_name = "(anonymous)";
198        }
199        String access = Utility.accessToString(innerAccessFlags, true);
200        access = access.isEmpty() ? "" : (access + " ");
201        return "  " + access + inner_name + "=class " + inner_class_name + outer_class_name;
202    }
203
204
205    /**
206     * @return deep copy of this object
207     */
208    public InnerClass copy() {
209        try {
210            return (InnerClass) clone();
211        } catch (final CloneNotSupportedException e) {
212            // TODO should this throw?
213        }
214        return null;
215    }
216}