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 inner_class_index;
036    private int outer_class_index;
037    private int inner_name_index;
038    private int inner_access_flags;
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 inner_class_index Class index in constant pool of inner class
063     * @param outer_class_index Class index in constant pool of outer class
064     * @param inner_name_index  Name index in constant pool of inner class
065     * @param inner_access_flags Access flags of inner class
066     */
067    public InnerClass(final int inner_class_index, final int outer_class_index, final int inner_name_index,
068            final int inner_access_flags) {
069        this.inner_class_index = inner_class_index;
070        this.outer_class_index = outer_class_index;
071        this.inner_name_index = inner_name_index;
072        this.inner_access_flags = inner_access_flags;
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(inner_class_index);
097        file.writeShort(outer_class_index);
098        file.writeShort(inner_name_index);
099        file.writeShort(inner_access_flags);
100    }
101
102
103    /**
104     * @return access flags of inner class.
105     */
106    public int getInnerAccessFlags() {
107        return inner_access_flags;
108    }
109
110
111    /**
112     * @return class index of inner class.
113     */
114    public int getInnerClassIndex() {
115        return inner_class_index;
116    }
117
118
119    /**
120     * @return name index of inner class.
121     */
122    public int getInnerNameIndex() {
123        return inner_name_index;
124    }
125
126
127    /**
128     * @return class index of outer class.
129     */
130    public int getOuterClassIndex() {
131        return outer_class_index;
132    }
133
134
135    /**
136     * @param inner_access_flags access flags for this inner class
137     */
138    public void setInnerAccessFlags( final int inner_access_flags ) {
139        this.inner_access_flags = inner_access_flags;
140    }
141
142
143    /**
144     * @param inner_class_index index into the constant pool for this class
145     */
146    public void setInnerClassIndex( final int inner_class_index ) {
147        this.inner_class_index = inner_class_index;
148    }
149
150
151    /**
152     * @param inner_name_index index into the constant pool for this class's name
153     */
154    public void setInnerNameIndex( final int inner_name_index ) { // TODO unused
155        this.inner_name_index = inner_name_index;
156    }
157
158
159    /**
160     * @param outer_class_index index into the constant pool for the owning class
161     */
162    public void setOuterClassIndex( final int outer_class_index ) { // TODO unused
163        this.outer_class_index = outer_class_index;
164    }
165
166
167    /**
168     * @return String representation.
169     */
170    @Override
171    public String toString() {
172        return "InnerClass(" + inner_class_index + ", " + outer_class_index + ", "
173                + inner_name_index + ", " + inner_access_flags + ")";
174    }
175
176
177    /**
178     * @return Resolved string representation
179     */
180    public String toString( final ConstantPool constant_pool ) {
181        String outer_class_name;
182        String inner_name;
183        String inner_class_name = constant_pool.getConstantString(inner_class_index,
184                Const.CONSTANT_Class);
185        inner_class_name = Utility.compactClassName(inner_class_name, false);
186        if (outer_class_index != 0) {
187            outer_class_name = constant_pool.getConstantString(outer_class_index,
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 (inner_name_index != 0) {
194            inner_name = ((ConstantUtf8) constant_pool.getConstant(inner_name_index,
195                    Const.CONSTANT_Utf8)).getBytes();
196        } else {
197            inner_name = "(anonymous)";
198        }
199        String access = Utility.accessToString(inner_access_flags, 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}