    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 */
    019
    020package org.apache.bcel.classfile;
    021
    022import java.io.DataInput;
    023import java.io.DataOutputStream;
    024import java.io.IOException;
    025
    026import org.apache.bcel.Const;
    027import org.apache.bcel.util.Args;
    028
    029/**
    030 * Extends {@link Attribute} and records the classes and
    031 * interfaces that are authorized to claim membership in the nest hosted by the
    032 * current class or interface. There may be at most one Record attribute in a
    033 * ClassFile structure.
    034 *
    035 * @see Attribute
    036 * @since 6.9.0
    037 */
    038public final class Record extends Attribute {
    039
    040    private static final RecordComponentInfo[] EMPTY_RCI_ARRAY = {};
    041
    042    private static RecordComponentInfo[] readComponents(final DataInput input, final ConstantPool constantPool)
    043            throws IOException {
    044        final int classCount = input.readUnsignedShort();
    045        final RecordComponentInfo[] components = new RecordComponentInfo[classCount];
    046        for (int i = 0; i < classCount; i++) {
    047            components[i] = new RecordComponentInfo(input, constantPool);
    048        }
    049        return components;
    050    }
    051
    052    private RecordComponentInfo[] components;
    053
    054    /**
    055     * Constructs object from input stream.
    056     *
    057     * @param nameIndex    Index in constant pool
    058     * @param length       Content length in bytes
    059     * @param input        Input stream
    060     * @param constantPool Array of constants
    061     * @throws IOException if an I/O error occurs.
    062     */
    063    Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
    064            throws IOException {
    065        this(nameIndex, length, readComponents(input, constantPool), constantPool);
    066    }
    067
    068    /**
    069     * Constructs a new instance using components.
    070     *
    071     * @param nameIndex    Index in constant pool
    072     * @param length       Content length in bytes
    073     * @param classes      Array of Record Component Info elements
    074     * @param constantPool Array of constants
    075     */
    076    public Record(final int nameIndex, final int length, final RecordComponentInfo[] classes,
    077            final ConstantPool constantPool) {
    078        super(Const.ATTR_RECORD, nameIndex, length, constantPool);
    079        this.components = classes != null ? classes : EMPTY_RCI_ARRAY;
    080        Args.requireU2(this.components.length, "attributes.length");
    081    }
    082
    083    /**
    084     * Called by objects that are traversing the nodes of the tree implicitly
    085     * defined by the contents of a Java class. For example, the hierarchy of methods,
    086     * fields, attributes, etc. spawns a tree of objects.
    087     *
    088     * @param v Visitor object
    089     */
    090    @Override
    091    public void accept(final Visitor v) {
    092        v.visitRecord(this);
    093    }
    094
    095    /**
    096     * Copies this instance and its components.
    097     *
    098     * @return a deep copy of this instance and its components.
    099     */
    100    @Override
    101    public Attribute copy(final ConstantPool constantPool) {
    102        final Record c = (Record) clone();
    103        if (components.length > 0) {
    104            c.components = components.clone();
    105        }
    106        c.setConstantPool(constantPool);
    107        return c;
    108    }
    109
    110    /**
    111     * Dumps this instance into a file stream in binary format.
    112     *
    113     * @param file output 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(components.length);
    120        for (final RecordComponentInfo component : components) {
    121            component.dump(file);
    122        }
    123    }
    124
    125    /**
    126     * Gets all the record components.
    127     *
    128     * @return array of Record Component Info elements.
    129     */
    130    public RecordComponentInfo[] getComponents() {
    131        return components;
    132    }
    133
    134    /**
    135     * Converts this instance to a String suitable for debugging.
    136     *
    137     * @return String a String suitable for debugging.
    138     */
    139    @Override
    140    public String toString() {
    141        final StringBuilder buf = new StringBuilder();
    142        buf.append("Record(");
    143        buf.append(components.length);
    144        buf.append("):\n");
    145        for (final RecordComponentInfo component : components) {
    146            buf.append("  ").append(component.toString()).append("\n");
    147        }
    148        return buf.substring(0, buf.length() - 1); // remove the last newline
    149    }
    150
    151}