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;
024
025import org.apache.bcel.Const;
026import org.apache.bcel.util.Args;
027
028/**
029 * Record component info from a record. Instances from this class maps
030 * every component from a given record.
031 *
032 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se14/preview/specs/records-jvms.html#jvms-4.7.30">
033 *      The Java Virtual Machine Specification, Java SE 14 Edition, Records (preview)</a>
034 * @since 6.9.0
035 */
036public class RecordComponentInfo implements Node {
037
038    private final int index;
039    private final int descriptorIndex;
040    private final Attribute[] attributes;
041    private final ConstantPool constantPool;
042
043    /**
044     * Constructs record component information from a data input using standard Code headers for nested attributes.
045     *
046     * @param input        Input stream.
047     * @param constantPool The constant pool.
048     * @throws IOException Thrown if an I/O error occurs.
049     * @throws ClassFormatException Thrown if a component attribute is malformed.
050     */
051    public RecordComponentInfo(final DataInput input, final ConstantPool constantPool) throws IOException {
052        this(input, constantPool, false);
053    }
054
055    /**
056     * Constructs record component information from a data input using the selected Code header layout for nested attributes.
057     *
058     * <p>
059     * The header selection is forwarded when reading component attributes; it does not change the record component layout.
060     * </p>
061     *
062     * @param input Input stream.
063     * @param constantPool The constant pool.
064     * @param compactCodeHeader Whether to read any Code attributes encountered with compact four-byte headers ({@code true})
065     *        or standard eight-byte headers ({@code false}).
066     * @throws IOException Thrown if an I/O error occurs.
067     * @throws ClassFormatException Thrown if a component attribute is malformed.
068     * @see Attribute#readAttribute(DataInput, ConstantPool, boolean)
069     */
070    RecordComponentInfo(final DataInput input, final ConstantPool constantPool, final boolean compactCodeHeader) throws IOException {
071        this.index = input.readUnsignedShort();
072        this.descriptorIndex = input.readUnsignedShort();
073        final int attributesCount = input.readUnsignedShort();
074        this.attributes = new Attribute[attributesCount];
075        for (int j = 0; j < attributesCount; j++) {
076            attributes[j] = Attribute.readAttribute(input, constantPool, compactCodeHeader);
077        }
078        this.constantPool = constantPool;
079    }
080
081    @Override
082    public void accept(final Visitor v) {
083        v.visitRecordComponent(this);
084    }
085
086    /**
087     * Dumps contents into a file stream in binary format.
088     *
089     * @param file Output file stream.
090     * @throws IOException Thrown if an I/O error occurs.
091     */
092    public void dump(final DataOutputStream file) throws IOException {
093        file.writeShort(index);
094        file.writeShort(descriptorIndex);
095        file.writeShort(Args.requireU2(attributes.length, "attributes.length"));
096        for (final Attribute attribute : attributes) {
097            attribute.dump(file);
098        }
099    }
100
101    /**
102     * Gets the attribute for the given tag if present, or null if absent.
103     *
104     * @param <T> The attribute type.
105     * @param tag The attribute tag.
106     * @return Attribute for given tag, null if not found.
107     * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values.
108     * @since 6.13.0
109     */
110    @SuppressWarnings("unchecked")
111    public final <T extends Attribute> T getAttribute(final byte tag) {
112        for (final Attribute attribute : getAttributes()) {
113            if (attribute.getTag() == tag) {
114                return (T) attribute;
115            }
116        }
117        return null;
118    }
119
120    /**
121     * Gets all attributes.
122     *
123     * @return all attributes.
124     */
125    public Attribute[] getAttributes() {
126        return attributes;
127    }
128
129    /**
130     * Gets the constant pool.
131     *
132     * @return Constant pool.
133     */
134    public ConstantPool getConstantPool() {
135        return constantPool;
136    }
137
138    /**
139     * Gets the description index.
140     *
141     * @return index in constant pool of this record component descriptor.
142     */
143    public int getDescriptorIndex() {
144        return descriptorIndex;
145    }
146
147    /**
148     * Gets the name index.
149     *
150     * @return index in constant pool of this record component name.
151     */
152    public int getIndex() {
153        return index;
154    }
155
156    /**
157     * Converts this instance to a String suitable for debugging.
158     *
159     * @return A String suitable for debugging.
160     */
161    @Override
162    public String toString() {
163        final StringBuilder buf = new StringBuilder();
164        buf.append("RecordComponentInfo(");
165        buf.append(constantPool.getConstantString(index, Const.CONSTANT_Utf8));
166        buf.append(",");
167        buf.append(constantPool.getConstantString(descriptorIndex, Const.CONSTANT_Utf8));
168        buf.append(",");
169        buf.append(attributes.length);
170        buf.append("):\n");
171        for (final Attribute attribute : attributes) {
172            buf.append("  ").append(attribute.toString()).append("\n");
173        }
174        return buf.substring(0, buf.length() - 1); // remove the last newline
175    }
176
177}