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, final boolean compactCodeHeader) 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, compactCodeHeader); 048 } 049 return components; 050 } 051 052 private RecordComponentInfo[] components; 053 054 /** 055 * Constructs a Record attribute from a data input using standard Code headers for nested attributes. 056 * 057 * @param nameIndex Constant pool index of the attribute name. 058 * @param length Content length in bytes. 059 * @param input Input stream. 060 * @param constantPool The constant pool. 061 * @throws IOException Thrown if an I/O error occurs. 062 * @throws ClassFormatException Thrown if the attribute or one of its components is malformed. 063 */ 064 Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) 065 throws IOException { 066 this(nameIndex, length, input, constantPool, false); 067 } 068 069 /** 070 * Constructs a Record attribute from a data input using the selected Code header layout for nested attributes. 071 * 072 * <p> 073 * The header selection is forwarded when reading component attributes; it does not change the Record attribute or record component layout. 074 * </p> 075 * 076 * @param nameIndex Constant pool index of the attribute name. 077 * @param length Content length in bytes. 078 * @param input Input stream. 079 * @param constantPool The constant pool. 080 * @param compactCodeHeader Whether to read any Code attributes encountered with compact four-byte headers ({@code true}) 081 * or standard eight-byte headers ({@code false}). 082 * @throws IOException Thrown if an I/O error occurs. 083 * @throws ClassFormatException Thrown if the attribute or one of its components is malformed. 084 * @see Attribute#readAttribute(DataInput, ConstantPool, boolean) 085 */ 086 Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool, final boolean compactCodeHeader) 087 throws IOException { 088 this(nameIndex, length, readComponents(input, constantPool, compactCodeHeader), constantPool); 089 } 090 091 /** 092 * Constructs a new instance using components. 093 * 094 * @param nameIndex Index in constant pool. 095 * @param length Content length in bytes. 096 * @param classes Array of Record Component Info elements. 097 * @param constantPool Array of constants. 098 */ 099 public Record(final int nameIndex, final int length, final RecordComponentInfo[] classes, 100 final ConstantPool constantPool) { 101 super(Const.ATTR_RECORD, nameIndex, length, constantPool); 102 this.components = classes != null ? classes : EMPTY_RCI_ARRAY; 103 Args.requireU2(this.components.length, "attributes.length"); 104 } 105 106 /** 107 * Called by objects that are traversing the nodes of the tree implicitly 108 * defined by the contents of a Java class. For example, the hierarchy of methods, 109 * fields, attributes, and other elements forms a tree of objects. 110 * 111 * @param v Visitor object. 112 */ 113 @Override 114 public void accept(final Visitor v) { 115 v.visitRecord(this); 116 } 117 118 /** 119 * Copies this instance and its components. 120 * 121 * @return A deep copy of this instance and its components. 122 */ 123 @Override 124 public Attribute copy(final ConstantPool constantPool) { 125 final Record c = (Record) clone(); 126 if (components.length > 0) { 127 c.components = components.clone(); 128 } 129 c.setConstantPool(constantPool); 130 return c; 131 } 132 133 /** 134 * Dumps this instance into a file stream in binary format. 135 * 136 * @param file output stream. 137 * @throws IOException Thrown if an I/O error occurs. 138 */ 139 @Override 140 public void dump(final DataOutputStream file) throws IOException { 141 super.dump(file); 142 file.writeShort(Args.requireU2(components.length, "components.length")); 143 for (final RecordComponentInfo component : components) { 144 component.dump(file); 145 } 146 } 147 148 /** 149 * Gets all the record components. 150 * 151 * @return array of Record Component Info elements. 152 */ 153 public RecordComponentInfo[] getComponents() { 154 return components; 155 } 156 157 /** 158 * Converts this instance to a String suitable for debugging. 159 * 160 * @return String a String suitable for debugging. 161 */ 162 @Override 163 public String toString() { 164 final StringBuilder buf = new StringBuilder(); 165 buf.append("Record("); 166 buf.append(components.length); 167 buf.append("):\n"); 168 for (final RecordComponentInfo component : components) { 169 buf.append(" ").append(component.toString()).append("\n"); 170 } 171 return buf.substring(0, buf.length() - 1); // remove the last newline 172 } 173 174}