001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017
018package org.apache.bcel.classfile;
019
020import java.io.DataInput;
021import java.io.DataOutputStream;
022import java.io.IOException;
023
024import org.apache.bcel.Const;
025import org.apache.bcel.util.Args;
026
027/**
028 * Extends {@link Attribute} and records the classes and
029 * interfaces that are authorized to claim membership in the nest hosted by the
030 * current class or interface. There may be at most one Record attribute in a
031 * ClassFile structure.
032 *
033 * @see Attribute
034 * @since 6.9.0
035 */
036public final class Record extends Attribute {
037
038    private static final RecordComponentInfo[] EMPTY_RCI_ARRAY = {};
039
040    private static RecordComponentInfo[] readComponents(final DataInput input, final ConstantPool constantPool)
041            throws IOException {
042        final int classCount = input.readUnsignedShort();
043        final RecordComponentInfo[] components = new RecordComponentInfo[classCount];
044        for (int i = 0; i < classCount; i++) {
045            components[i] = new RecordComponentInfo(input, constantPool);
046        }
047        return components;
048    }
049
050    private RecordComponentInfo[] components;
051
052    /**
053     * Constructs object from input stream.
054     *
055     * @param nameIndex    Index in constant pool
056     * @param length       Content length in bytes
057     * @param input        Input stream
058     * @param constantPool Array of constants
059     * @throws IOException if an I/O error occurs.
060     */
061    Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
062            throws IOException {
063        this(nameIndex, length, readComponents(input, constantPool), constantPool);
064    }
065
066    /**
067     * Constructs a new instance using components.
068     *
069     * @param nameIndex    Index in constant pool
070     * @param length       Content length in bytes
071     * @param classes      Array of Record Component Info elements
072     * @param constantPool Array of constants
073     */
074    public Record(final int nameIndex, final int length, final RecordComponentInfo[] classes,
075            final ConstantPool constantPool) {
076        super(Const.ATTR_RECORD, nameIndex, length, constantPool);
077        this.components = classes != null ? classes : EMPTY_RCI_ARRAY;
078        Args.requireU2(this.components.length, "attributes.length");
079    }
080
081    /**
082     * Called by objects that are traversing the nodes of the tree implicitly
083     * defined by the contents of a Java class. For example, the hierarchy of methods,
084     * fields, attributes, etc. spawns a tree of objects.
085     *
086     * @param v Visitor object
087     */
088    @Override
089    public void accept(final Visitor v) {
090        v.visitRecord(this);
091    }
092
093    /**
094     * Copies this instance and its components.
095     *
096     * @return a deep copy of this instance and its components.
097     */
098    @Override
099    public Attribute copy(final ConstantPool constantPool) {
100        final Record c = (Record) clone();
101        if (components.length > 0) {
102            c.components = components.clone();
103        }
104        c.setConstantPool(constantPool);
105        return c;
106    }
107
108    /**
109     * Dumps this instance into a file stream in binary format.
110     *
111     * @param file output stream.
112     * @throws IOException if an I/O error occurs.
113     */
114    @Override
115    public void dump(final DataOutputStream file) throws IOException {
116        super.dump(file);
117        file.writeShort(components.length);
118        for (final RecordComponentInfo component : components) {
119            component.dump(file);
120        }
121    }
122
123    /**
124     * Gets all the record components.
125     *
126     * @return array of Record Component Info elements.
127     */
128    public RecordComponentInfo[] getComponents() {
129        return components;
130    }
131
132    /**
133     * Converts this instance to a String suitable for debugging.
134     *
135     * @return String a String suitable for debugging.
136     */
137    @Override
138    public String toString() {
139        final StringBuilder buf = new StringBuilder();
140        buf.append("Record(");
141        buf.append(components.length);
142        buf.append("):\n");
143        for (final RecordComponentInfo component : components) {
144            buf.append("  ").append(component.toString()).append("\n");
145        }
146        return buf.substring(0, buf.length() - 1); // remove the last newline
147    }
148
149}