001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.bcel.classfile; 019 020import java.io.DataInput; 021import java.io.DataOutputStream; 022import java.io.IOException; 023import java.util.Arrays; 024 025import org.apache.bcel.Const; 026import org.apache.commons.lang3.ArrayUtils; 027 028/** 029 * This class is derived from <em>Attribute</em> and represents the list of packages that are exported or opened by the 030 * Module attribute. There may be at most one ModulePackages attribute in a ClassFile structure. 031 * 032 * @see Attribute 033 */ 034public final class ModulePackages extends Attribute { 035 036 private int[] packageIndexTable; 037 038 /** 039 * Construct object from input stream. 040 * 041 * @param nameIndex Index in constant pool 042 * @param length Content length in bytes 043 * @param input Input stream 044 * @param constantPool Array of constants 045 * @throws IOException if an I/O error occurs. 046 */ 047 ModulePackages(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { 048 this(nameIndex, length, (int[]) null, constantPool); 049 final int packageCount = input.readUnsignedShort(); 050 packageIndexTable = new int[packageCount]; 051 for (int i = 0; i < packageCount; i++) { 052 packageIndexTable[i] = input.readUnsignedShort(); 053 } 054 } 055 056 /** 057 * @param nameIndex Index in constant pool 058 * @param length Content length in bytes 059 * @param packageIndexTable Table of indices in constant pool 060 * @param constantPool Array of constants 061 */ 062 public ModulePackages(final int nameIndex, final int length, final int[] packageIndexTable, final ConstantPool constantPool) { 063 super(Const.ATTR_MODULE_PACKAGES, nameIndex, length, constantPool); 064 this.packageIndexTable = packageIndexTable != null ? packageIndexTable : ArrayUtils.EMPTY_INT_ARRAY; 065 } 066 067 /** 068 * Initialize from another object. Note that both objects use the same references (shallow copy). Use copy() for a 069 * physical copy. 070 */ 071 public ModulePackages(final ModulePackages c) { 072 this(c.getNameIndex(), c.getLength(), c.getPackageIndexTable(), c.getConstantPool()); 073 } 074 075 /** 076 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 077 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 078 * 079 * @param v Visitor object 080 */ 081 @Override 082 public void accept(final Visitor v) { 083 v.visitModulePackages(this); 084 } 085 086 /** 087 * @return deep copy of this attribute 088 */ 089 @Override 090 public Attribute copy(final ConstantPool constantPool) { 091 final ModulePackages c = (ModulePackages) clone(); 092 if (packageIndexTable != null) { 093 c.packageIndexTable = packageIndexTable.clone(); 094 } 095 c.setConstantPool(constantPool); 096 return c; 097 } 098 099 /** 100 * Dump ModulePackages attribute to file stream in binary format. 101 * 102 * @param file Output file stream 103 * @throws IOException if an I/O error occurs. 104 */ 105 @Override 106 public void dump(final DataOutputStream file) throws IOException { 107 super.dump(file); 108 file.writeShort(packageIndexTable.length); 109 for (final int index : packageIndexTable) { 110 file.writeShort(index); 111 } 112 } 113 114 /** 115 * @return Length of package table. 116 */ 117 public int getNumberOfPackages() { 118 return packageIndexTable == null ? 0 : packageIndexTable.length; 119 } 120 121 /** 122 * @return array of indices into constant pool of package names. 123 */ 124 public int[] getPackageIndexTable() { 125 return packageIndexTable; 126 } 127 128 /** 129 * @return string array of package names 130 */ 131 public String[] getPackageNames() { 132 final String[] names = new String[packageIndexTable.length]; 133 Arrays.setAll(names, i -> Utility.pathToPackage(super.getConstantPool().getConstantString(packageIndexTable[i], Const.CONSTANT_Package))); 134 return names; 135 } 136 137 /** 138 * @param packageIndexTable the list of package indexes Also redefines number_of_packages according to table length. 139 */ 140 public void setPackageIndexTable(final int[] packageIndexTable) { 141 this.packageIndexTable = packageIndexTable != null ? packageIndexTable : ArrayUtils.EMPTY_INT_ARRAY; 142 } 143 144 /** 145 * @return String representation, i.e., a list of packages. 146 */ 147 @Override 148 public String toString() { 149 final StringBuilder buf = new StringBuilder(); 150 buf.append("ModulePackages("); 151 buf.append(packageIndexTable.length); 152 buf.append("):\n"); 153 for (final int index : packageIndexTable) { 154 final String packageName = super.getConstantPool().getConstantString(index, Const.CONSTANT_Package); 155 buf.append(" ").append(Utility.compactClassName(packageName, false)).append("\n"); 156 } 157 return buf.substring(0, buf.length() - 1); // remove the last newline 158 } 159}