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 a new instance from an input stream.
045     *
046     * @param input        Input stream.
047     * @param constantPool Array of constants.
048     * @throws IOException Thrown if an I/O error occurs.
049     */
050    public RecordComponentInfo(final DataInput input, final ConstantPool constantPool) throws IOException {
051        this.index = input.readUnsignedShort();
052        this.descriptorIndex = input.readUnsignedShort();
053        final int attributesCount = input.readUnsignedShort();
054        this.attributes = new Attribute[attributesCount];
055        for (int j = 0; j < attributesCount; j++) {
056            attributes[j] = Attribute.readAttribute(input, constantPool);
057        }
058        this.constantPool = constantPool;
059    }
060
061    @Override
062    public void accept(final Visitor v) {
063        v.visitRecordComponent(this);
064    }
065
066    /**
067     * Dumps contents into a file stream in binary format.
068     *
069     * @param file Output file stream.
070     * @throws IOException Thrown if an I/O error occurs.
071     */
072    public void dump(final DataOutputStream file) throws IOException {
073        file.writeShort(index);
074        file.writeShort(descriptorIndex);
075        file.writeShort(Args.requireU2(attributes.length, "attributes.length"));
076        for (final Attribute attribute : attributes) {
077            attribute.dump(file);
078        }
079    }
080
081    /**
082     * Gets the attribute for the given tag if present, or null if absent.
083     *
084     * @param <T> The attribute type.
085     * @param tag The attribute tag.
086     * @return Attribute for given tag, null if not found.
087     * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values.
088     * @since 6.13.0
089     */
090    @SuppressWarnings("unchecked")
091    public final <T extends Attribute> T getAttribute(final byte tag) {
092        for (final Attribute attribute : getAttributes()) {
093            if (attribute.getTag() == tag) {
094                return (T) attribute;
095            }
096        }
097        return null;
098    }
099
100    /**
101     * Gets all attributes.
102     *
103     * @return all attributes.
104     */
105    public Attribute[] getAttributes() {
106        return attributes;
107    }
108
109    /**
110     * Gets the constant pool.
111     *
112     * @return Constant pool.
113     */
114    public ConstantPool getConstantPool() {
115        return constantPool;
116    }
117
118    /**
119     * Gets the description index.
120     *
121     * @return index in constant pool of this record component descriptor.
122     */
123    public int getDescriptorIndex() {
124        return descriptorIndex;
125    }
126
127    /**
128     * Gets the name index.
129     *
130     * @return index in constant pool of this record component name.
131     */
132    public int getIndex() {
133        return index;
134    }
135
136    /**
137     * Converts this instance to a String suitable for debugging.
138     *
139     * @return A String suitable for debugging.
140     */
141    @Override
142    public String toString() {
143        final StringBuilder buf = new StringBuilder();
144        buf.append("RecordComponentInfo(");
145        buf.append(constantPool.getConstantString(index, Const.CONSTANT_Utf8));
146        buf.append(",");
147        buf.append(constantPool.getConstantString(descriptorIndex, Const.CONSTANT_Utf8));
148        buf.append(",");
149        buf.append(attributes.length);
150        buf.append("):\n");
151        for (final Attribute attribute : attributes) {
152            buf.append("  ").append(attribute.toString()).append("\n");
153        }
154        return buf.substring(0, buf.length() - 1); // remove the last newline
155    }
156
157}