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 */
017package org.apache.bcel.classfile;
018
019import java.io.DataInput;
020import java.io.DataOutputStream;
021import java.io.IOException;
022
023import org.apache.bcel.Const;
024
025/**
026 * This class represents a inner class attribute, i.e., the class indices of the inner and outer classes, the name and
027 * the attributes of the inner class.
028 *
029 * @see InnerClasses
030 */
031public final class InnerClass implements Cloneable, Node {
032
033    private int innerClassIndex;
034    private int outerClassIndex;
035    private int innerNameIndex;
036    private int innerAccessFlags;
037
038    /**
039     * Construct object from file stream.
040     *
041     * @param file Input stream
042     * @throws IOException if an I/O error occurs.
043     */
044    InnerClass(final DataInput file) throws IOException {
045        this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort());
046    }
047
048    /**
049     * Initialize from another object.
050     */
051    public InnerClass(final InnerClass c) {
052        this(c.getInnerClassIndex(), c.getOuterClassIndex(), c.getInnerNameIndex(), c.getInnerAccessFlags());
053    }
054
055    /**
056     * @param innerClassIndex Class index in constant pool of inner class
057     * @param outerClassIndex Class index in constant pool of outer class
058     * @param innerNameIndex Name index in constant pool of inner class
059     * @param innerAccessFlags Access flags of inner class
060     */
061    public InnerClass(final int innerClassIndex, final int outerClassIndex, final int innerNameIndex, final int innerAccessFlags) {
062        this.innerClassIndex = innerClassIndex;
063        this.outerClassIndex = outerClassIndex;
064        this.innerNameIndex = innerNameIndex;
065        this.innerAccessFlags = innerAccessFlags;
066    }
067
068    /**
069     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
070     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
071     *
072     * @param v Visitor object
073     */
074    @Override
075    public void accept(final Visitor v) {
076        v.visitInnerClass(this);
077    }
078
079    /**
080     * @return deep copy of this object
081     */
082    public InnerClass copy() {
083        try {
084            return (InnerClass) clone();
085        } catch (final CloneNotSupportedException e) {
086            // TODO should this throw?
087        }
088        return null;
089    }
090
091    /**
092     * Dump inner class attribute to file stream in binary format.
093     *
094     * @param file Output file stream
095     * @throws IOException if an I/O error occurs.
096     */
097    public void dump(final DataOutputStream file) throws IOException {
098        file.writeShort(innerClassIndex);
099        file.writeShort(outerClassIndex);
100        file.writeShort(innerNameIndex);
101        file.writeShort(innerAccessFlags);
102    }
103
104    /**
105     * @return access flags of inner class.
106     */
107    public int getInnerAccessFlags() {
108        return innerAccessFlags;
109    }
110
111    /**
112     * @return class index of inner class.
113     */
114    public int getInnerClassIndex() {
115        return innerClassIndex;
116    }
117
118    /**
119     * @return name index of inner class.
120     */
121    public int getInnerNameIndex() {
122        return innerNameIndex;
123    }
124
125    /**
126     * @return class index of outer class.
127     */
128    public int getOuterClassIndex() {
129        return outerClassIndex;
130    }
131
132    /**
133     * @param innerAccessFlags access flags for this inner class
134     */
135    public void setInnerAccessFlags(final int innerAccessFlags) {
136        this.innerAccessFlags = innerAccessFlags;
137    }
138
139    /**
140     * @param innerClassIndex index into the constant pool for this class
141     */
142    public void setInnerClassIndex(final int innerClassIndex) {
143        this.innerClassIndex = innerClassIndex;
144    }
145
146    /**
147     * @param innerNameIndex index into the constant pool for this class's name
148     */
149    public void setInnerNameIndex(final int innerNameIndex) { // TODO unused
150        this.innerNameIndex = innerNameIndex;
151    }
152
153    /**
154     * @param outerClassIndex index into the constant pool for the owning class
155     */
156    public void setOuterClassIndex(final int outerClassIndex) { // TODO unused
157        this.outerClassIndex = outerClassIndex;
158    }
159
160    /**
161     * @return String representation.
162     */
163    @Override
164    public String toString() {
165        return "InnerClass(" + innerClassIndex + ", " + outerClassIndex + ", " + innerNameIndex + ", " + innerAccessFlags + ")";
166    }
167
168    /**
169     * @return Resolved string representation
170     */
171    public String toString(final ConstantPool constantPool) {
172        String outerClassName;
173        String innerName;
174        String innerClassName = constantPool.getConstantString(innerClassIndex, Const.CONSTANT_Class);
175        innerClassName = Utility.compactClassName(innerClassName, false);
176        if (outerClassIndex != 0) {
177            outerClassName = constantPool.getConstantString(outerClassIndex, Const.CONSTANT_Class);
178            outerClassName = " of class " + Utility.compactClassName(outerClassName, false);
179        } else {
180            outerClassName = "";
181        }
182        if (innerNameIndex != 0) {
183            innerName = constantPool.getConstantUtf8(innerNameIndex).getBytes();
184        } else {
185            innerName = "(anonymous)";
186        }
187        String access = Utility.accessToString(innerAccessFlags, true);
188        access = access.isEmpty() ? "" : access + " ";
189        return "  " + access + innerName + "=class " + innerClassName + outerClassName;
190    }
191}