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}