    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}