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) 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); 048 } 049 return components; 050 } 051 052 private RecordComponentInfo[] components; 053 054 /** 055 * Constructs object from input stream. 056 * 057 * @param nameIndex Index in constant pool 058 * @param length Content length in bytes 059 * @param input Input stream 060 * @param constantPool Array of constants 061 * @throws IOException if an I/O error occurs. 062 */ 063 Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) 064 throws IOException { 065 this(nameIndex, length, readComponents(input, constantPool), constantPool); 066 } 067 068 /** 069 * Constructs a new instance using components. 070 * 071 * @param nameIndex Index in constant pool 072 * @param length Content length in bytes 073 * @param classes Array of Record Component Info elements 074 * @param constantPool Array of constants 075 */ 076 public Record(final int nameIndex, final int length, final RecordComponentInfo[] classes, 077 final ConstantPool constantPool) { 078 super(Const.ATTR_RECORD, nameIndex, length, constantPool); 079 this.components = classes != null ? classes : EMPTY_RCI_ARRAY; 080 Args.requireU2(this.components.length, "attributes.length"); 081 } 082 083 /** 084 * Called by objects that are traversing the nodes of the tree implicitly 085 * defined by the contents of a Java class. For example, the hierarchy of methods, 086 * fields, attributes, etc. spawns a tree of objects. 087 * 088 * @param v Visitor object 089 */ 090 @Override 091 public void accept(final Visitor v) { 092 v.visitRecord(this); 093 } 094 095 /** 096 * Copies this instance and its components. 097 * 098 * @return a deep copy of this instance and its components. 099 */ 100 @Override 101 public Attribute copy(final ConstantPool constantPool) { 102 final Record c = (Record) clone(); 103 if (components.length > 0) { 104 c.components = components.clone(); 105 } 106 c.setConstantPool(constantPool); 107 return c; 108 } 109 110 /** 111 * Dumps this instance into a file stream in binary format. 112 * 113 * @param file output stream. 114 * @throws IOException if an I/O error occurs. 115 */ 116 @Override 117 public void dump(final DataOutputStream file) throws IOException { 118 super.dump(file); 119 file.writeShort(components.length); 120 for (final RecordComponentInfo component : components) { 121 component.dump(file); 122 } 123 } 124 125 /** 126 * Gets all the record components. 127 * 128 * @return array of Record Component Info elements. 129 */ 130 public RecordComponentInfo[] getComponents() { 131 return components; 132 } 133 134 /** 135 * Converts this instance to a String suitable for debugging. 136 * 137 * @return String a String suitable for debugging. 138 */ 139 @Override 140 public String toString() { 141 final StringBuilder buf = new StringBuilder(); 142 buf.append("Record("); 143 buf.append(components.length); 144 buf.append("):\n"); 145 for (final RecordComponentInfo component : components) { 146 buf.append(" ").append(component.toString()).append("\n"); 147 } 148 return buf.substring(0, buf.length() - 1); // remove the last newline 149 } 150 151}