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