    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;
    024import java.util.Arrays;
    025import java.util.Iterator;
    026import java.util.stream.Stream;
    027
    028import org.apache.bcel.Const;
    029import org.apache.bcel.util.Args;
    030
    031/**
    032 * This class is derived from <em>Attribute</em> and denotes that this class is an Inner class of another. to the source
    033 * file of this class. It is instantiated from the <em>Attribute.readAttribute()</em> method.
    034 *
    035 * @see Attribute
    036 */
    037public final class InnerClasses extends Attribute implements Iterable<InnerClass> {
    038
    039    /**
    040     * Empty array.
    041     */
    042    private static final InnerClass[] EMPTY_ARRAY = {};
    043
    044    private InnerClass[] innerClasses;
    045
    046    /**
    047     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
    048     * physical copy.
    049     *
    050     * @param c Source to copy.
    051     */
    052    public InnerClasses(final InnerClasses c) {
    053        this(c.getNameIndex(), c.getLength(), c.getInnerClasses(), c.getConstantPool());
    054    }
    055
    056    /**
    057     * Constructs object from input stream.
    058     *
    059     * @param nameIndex Index in constant pool to CONSTANT_Utf8.
    060     * @param length Content length in bytes.
    061     * @param input Input stream.
    062     * @param constantPool Array of constants.
    063     * @throws IOException if an I/O error occurs.
    064     */
    065    InnerClasses(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
    066        this(nameIndex, length, (InnerClass[]) null, constantPool);
    067        final int classCount = input.readUnsignedShort();
    068        innerClasses = new InnerClass[classCount];
    069        for (int i = 0; i < classCount; i++) {
    070            innerClasses[i] = new InnerClass(input);
    071        }
    072    }
    073
    074    /**
    075     * Constructs an InnerClasses attribute.
    076     *
    077     * @param nameIndex Index in constant pool to CONSTANT_Utf8.
    078     * @param length Content length in bytes.
    079     * @param innerClasses array of inner classes attributes.
    080     * @param constantPool Array of constants.
    081     */
    082    public InnerClasses(final int nameIndex, final int length, final InnerClass[] innerClasses, final ConstantPool constantPool) {
    083        super(Const.ATTR_INNER_CLASSES, nameIndex, length, constantPool);
    084        this.innerClasses = innerClasses != null ? innerClasses : EMPTY_ARRAY;
    085        Args.requireU2(this.innerClasses.length, "innerClasses.length");
    086    }
    087
    088    /**
    089     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    090     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    091     *
    092     * @param v Visitor object.
    093     */
    094    @Override
    095    public void accept(final Visitor v) {
    096        v.visitInnerClasses(this);
    097    }
    098
    099    /**
    100     * Creates a deep copy of this attribute.
    101     *
    102     * @return deep copy of this attribute.
    103     */
    104    @Override
    105    public Attribute copy(final ConstantPool constantPool) {
    106        // TODO this could be recoded to use a lower level constructor after creating a copy of the inner classes
    107        final InnerClasses c = (InnerClasses) clone();
    108        c.innerClasses = new InnerClass[innerClasses.length];
    109        Arrays.setAll(c.innerClasses, i -> innerClasses[i].copy());
    110        c.setConstantPool(constantPool);
    111        return c;
    112    }
    113
    114    /**
    115     * Dumps source file attribute to file stream in binary format.
    116     *
    117     * @param file Output file stream.
    118     * @throws IOException if an I/O error occurs.
    119     */
    120    @Override
    121    public void dump(final DataOutputStream file) throws IOException {
    122        super.dump(file);
    123        file.writeShort(innerClasses.length);
    124        for (final InnerClass innerClass : innerClasses) {
    125            innerClass.dump(file);
    126        }
    127    }
    128
    129    /**
    130     * Gets the array of inner class records.
    131     *
    132     * @return array of inner class "records".
    133     */
    134    public InnerClass[] getInnerClasses() {
    135        return innerClasses;
    136    }
    137
    138    @Override
    139    public Iterator<InnerClass> iterator() {
    140        return Stream.of(innerClasses).iterator();
    141    }
    142
    143    /**
    144     * Sets the array of inner classes.
    145     *
    146     * @param innerClasses the array of inner classes.
    147     */
    148    public void setInnerClasses(final InnerClass[] innerClasses) {
    149        this.innerClasses = innerClasses != null ? innerClasses : EMPTY_ARRAY;
    150    }
    151
    152    /**
    153     * @return String representation.
    154     */
    155    @Override
    156    public String toString() {
    157        final StringBuilder buf = new StringBuilder();
    158        buf.append("InnerClasses(");
    159        buf.append(innerClasses.length);
    160        buf.append("):\n");
    161        for (final InnerClass innerClass : innerClasses) {
    162            buf.append(innerClass.toString(super.getConstantPool())).append("\n");
    163        }
    164        return buf.substring(0, buf.length() - 1); // remove the last newline
    165    }
    166}