    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     * @param nameIndex Index in constant pool to CONSTANT_Utf8
    076     * @param length Content length in bytes
    077     * @param innerClasses array of inner classes attributes
    078     * @param constantPool Array of constants
    079     */
    080    public InnerClasses(final int nameIndex, final int length, final InnerClass[] innerClasses, final ConstantPool constantPool) {
    081        super(Const.ATTR_INNER_CLASSES, nameIndex, length, constantPool);
    082        this.innerClasses = innerClasses != null ? innerClasses : EMPTY_ARRAY;
    083        Args.requireU2(this.innerClasses.length, "innerClasses.length");
    084    }
    085
    086    /**
    087     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
    088     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
    089     *
    090     * @param v Visitor object
    091     */
    092    @Override
    093    public void accept(final Visitor v) {
    094        v.visitInnerClasses(this);
    095    }
    096
    097    /**
    098     * @return deep copy of this attribute
    099     */
    100    @Override
    101    public Attribute copy(final ConstantPool constantPool) {
    102        // TODO this could be recoded to use a lower level constructor after creating a copy of the inner classes
    103        final InnerClasses c = (InnerClasses) clone();
    104        c.innerClasses = new InnerClass[innerClasses.length];
    105        Arrays.setAll(c.innerClasses, i -> innerClasses[i].copy());
    106        c.setConstantPool(constantPool);
    107        return c;
    108    }
    109
    110    /**
    111     * Dump source file attribute to file stream in binary format.
    112     *
    113     * @param file Output file stream
    114     * @throws IOException if an I/O error occurs.
    115     */
    116    @Override
    117    public void dump(final DataOutputStream file) throws IOException {
    118        super.dump(file);
    119        file.writeShort(innerClasses.length);
    120        for (final InnerClass innerClass : innerClasses) {
    121            innerClass.dump(file);
    122        }
    123    }
    124
    125    /**
    126     * @return array of inner class "records"
    127     */
    128    public InnerClass[] getInnerClasses() {
    129        return innerClasses;
    130    }
    131
    132    @Override
    133    public Iterator<InnerClass> iterator() {
    134        return Stream.of(innerClasses).iterator();
    135    }
    136
    137    /**
    138     * @param innerClasses the array of inner classes
    139     */
    140    public void setInnerClasses(final InnerClass[] innerClasses) {
    141        this.innerClasses = innerClasses != null ? innerClasses : EMPTY_ARRAY;
    142    }
    143
    144    /**
    145     * @return String representation.
    146     */
    147    @Override
    148    public String toString() {
    149        final StringBuilder buf = new StringBuilder();
    150        buf.append("InnerClasses(");
    151        buf.append(innerClasses.length);
    152        buf.append("):\n");
    153        for (final InnerClass innerClass : innerClasses) {
    154            buf.append(innerClass.toString(super.getConstantPool())).append("\n");
    155        }
    156        return buf.substring(0, buf.length() - 1); // remove the last newline
    157    }
    158}