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