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}