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, i.e., 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     * @param innerClassIndex Class index in constant pool of inner class
061     * @param outerClassIndex Class index in constant pool of outer class
062     * @param innerNameIndex Name index in constant pool of inner class
063     * @param innerAccessFlags Access flags of inner class
064     */
065    public InnerClass(final int innerClassIndex, final int outerClassIndex, final int innerNameIndex, final int innerAccessFlags) {
066        this.innerClassIndex = innerClassIndex;
067        this.outerClassIndex = outerClassIndex;
068        this.innerNameIndex = innerNameIndex;
069        this.innerAccessFlags = innerAccessFlags;
070    }
071
072    /**
073     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
074     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
075     *
076     * @param v Visitor object
077     */
078    @Override
079    public void accept(final Visitor v) {
080        v.visitInnerClass(this);
081    }
082
083    /**
084     * @return deep copy of this object
085     */
086    public InnerClass copy() {
087        try {
088            return (InnerClass) clone();
089        } catch (final CloneNotSupportedException e) {
090            // TODO should this throw?
091        }
092        return null;
093    }
094
095    /**
096     * Dump inner class attribute to file stream in binary format.
097     *
098     * @param file Output file stream
099     * @throws IOException if an I/O error occurs.
100     */
101    public void dump(final DataOutputStream file) throws IOException {
102        file.writeShort(innerClassIndex);
103        file.writeShort(outerClassIndex);
104        file.writeShort(innerNameIndex);
105        file.writeShort(innerAccessFlags);
106    }
107
108    /**
109     * @return access flags of inner class.
110     */
111    public int getInnerAccessFlags() {
112        return innerAccessFlags;
113    }
114
115    /**
116     * @return class index of inner class.
117     */
118    public int getInnerClassIndex() {
119        return innerClassIndex;
120    }
121
122    /**
123     * @return name index of inner class.
124     */
125    public int getInnerNameIndex() {
126        return innerNameIndex;
127    }
128
129    /**
130     * @return class index of outer class.
131     */
132    public int getOuterClassIndex() {
133        return outerClassIndex;
134    }
135
136    /**
137     * @param innerAccessFlags access flags for this inner class
138     */
139    public void setInnerAccessFlags(final int innerAccessFlags) {
140        this.innerAccessFlags = innerAccessFlags;
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     * @param innerNameIndex index into the constant pool for this class's name
152     */
153    public void setInnerNameIndex(final int innerNameIndex) { // TODO unused
154        this.innerNameIndex = innerNameIndex;
155    }
156
157    /**
158     * @param outerClassIndex index into the constant pool for the owning class
159     */
160    public void setOuterClassIndex(final int outerClassIndex) { // TODO unused
161        this.outerClassIndex = outerClassIndex;
162    }
163
164    /**
165     * @return String representation.
166     */
167    @Override
168    public String toString() {
169        return "InnerClass(" + innerClassIndex + ", " + outerClassIndex + ", " + innerNameIndex + ", " + innerAccessFlags + ")";
170    }
171
172    /**
173     * @return Resolved string representation
174     */
175    public String toString(final ConstantPool constantPool) {
176        String outerClassName;
177        String innerClassName = constantPool.getConstantString(innerClassIndex, Const.CONSTANT_Class);
178        innerClassName = Utility.compactClassName(innerClassName, false);
179        if (outerClassIndex != 0) {
180            outerClassName = constantPool.getConstantString(outerClassIndex, Const.CONSTANT_Class);
181            outerClassName = " of class " + Utility.compactClassName(outerClassName, false);
182        } else {
183            outerClassName = "";
184        }
185        final String innerName;
186        if (innerNameIndex != 0) {
187            innerName = constantPool.getConstantUtf8(innerNameIndex).getBytes();
188        } else {
189            innerName = "(anonymous)";
190        }
191        String access = Utility.accessToString(innerAccessFlags, true);
192        access = access.isEmpty() ? "" : access + " ";
193        return "  " + access + innerName + "=class " + innerClassName + outerClassName;
194    }
195}