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, final boolean compactCodeHeader)
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, compactCodeHeader);
048        }
049        return components;
050    }
051
052    private RecordComponentInfo[] components;
053
054    /**
055     * Constructs a Record attribute from a data input using standard Code headers for nested attributes.
056     *
057     * @param nameIndex    Constant pool index of the attribute name.
058     * @param length       Content length in bytes.
059     * @param input        Input stream.
060     * @param constantPool The constant pool.
061     * @throws IOException Thrown if an I/O error occurs.
062     * @throws ClassFormatException Thrown if the attribute or one of its components is malformed.
063     */
064    Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
065            throws IOException {
066        this(nameIndex, length, input, constantPool, false);
067    }
068
069    /**
070     * Constructs a Record attribute from a data input using the selected Code header layout for nested attributes.
071     *
072     * <p>
073     * The header selection is forwarded when reading component attributes; it does not change the Record attribute or record component layout.
074     * </p>
075     *
076     * @param nameIndex Constant pool index of the attribute name.
077     * @param length Content length in bytes.
078     * @param input Input stream.
079     * @param constantPool The constant pool.
080     * @param compactCodeHeader Whether to read any Code attributes encountered with compact four-byte headers ({@code true})
081     *        or standard eight-byte headers ({@code false}).
082     * @throws IOException Thrown if an I/O error occurs.
083     * @throws ClassFormatException Thrown if the attribute or one of its components is malformed.
084     * @see Attribute#readAttribute(DataInput, ConstantPool, boolean)
085     */
086    Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool, final boolean compactCodeHeader)
087            throws IOException {
088        this(nameIndex, length, readComponents(input, constantPool, compactCodeHeader), constantPool);
089    }
090
091    /**
092     * Constructs a new instance using components.
093     *
094     * @param nameIndex    Index in constant pool.
095     * @param length       Content length in bytes.
096     * @param classes      Array of Record Component Info elements.
097     * @param constantPool Array of constants.
098     */
099    public Record(final int nameIndex, final int length, final RecordComponentInfo[] classes,
100            final ConstantPool constantPool) {
101        super(Const.ATTR_RECORD, nameIndex, length, constantPool);
102        this.components = classes != null ? classes : EMPTY_RCI_ARRAY;
103        Args.requireU2(this.components.length, "attributes.length");
104    }
105
106    /**
107     * Called by objects that are traversing the nodes of the tree implicitly
108     * defined by the contents of a Java class. For example, the hierarchy of methods,
109     * fields, attributes, and other elements forms a tree of objects.
110     *
111     * @param v Visitor object.
112     */
113    @Override
114    public void accept(final Visitor v) {
115        v.visitRecord(this);
116    }
117
118    /**
119     * Copies this instance and its components.
120     *
121     * @return A deep copy of this instance and its components.
122     */
123    @Override
124    public Attribute copy(final ConstantPool constantPool) {
125        final Record c = (Record) clone();
126        if (components.length > 0) {
127            c.components = components.clone();
128        }
129        c.setConstantPool(constantPool);
130        return c;
131    }
132
133    /**
134     * Dumps this instance into a file stream in binary format.
135     *
136     * @param file output stream.
137     * @throws IOException Thrown if an I/O error occurs.
138     */
139    @Override
140    public void dump(final DataOutputStream file) throws IOException {
141        super.dump(file);
142        file.writeShort(Args.requireU2(components.length, "components.length"));
143        for (final RecordComponentInfo component : components) {
144            component.dump(file);
145        }
146    }
147
148    /**
149     * Gets all the record components.
150     *
151     * @return array of Record Component Info elements.
152     */
153    public RecordComponentInfo[] getComponents() {
154        return components;
155    }
156
157    /**
158     * Converts this instance to a String suitable for debugging.
159     *
160     * @return String a String suitable for debugging.
161     */
162    @Override
163    public String toString() {
164        final StringBuilder buf = new StringBuilder();
165        buf.append("Record(");
166        buf.append(components.length);
167        buf.append("):\n");
168        for (final RecordComponentInfo component : components) {
169            buf.append("  ").append(component.toString()).append("\n");
170        }
171        return buf.substring(0, buf.length() - 1); // remove the last newline
172    }
173
174}