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