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; 025import java.util.Arrays; 026 027import org.apache.bcel.Const; 028import org.apache.bcel.util.Args; 029 030/** 031 * This class is derived from <em>Attribute</em> and represents the list of modules required, exported, opened or 032 * provided by a module. There may be at most one Module attribute in a ClassFile structure. 033 * 034 * @see Attribute 035 * @since 6.4.0 036 */ 037public final class Module extends Attribute { 038 039 /** 040 * The module file name extension. 041 * 042 * @since 6.7.0 043 */ 044 public static final String EXTENSION = ".jmod"; 045 046 private static String getClassNameAtIndex(final ConstantPool cp, final int index, final boolean compactClassName) { 047 final String className = cp.getConstantString(index, Const.CONSTANT_Class); 048 if (compactClassName) { 049 return Utility.compactClassName(className, false); 050 } 051 return className; 052 } 053 private final int moduleNameIndex; 054 private final int moduleFlags; 055 056 private final int moduleVersionIndex; 057 private ModuleRequires[] requiresTable; 058 private ModuleExports[] exportsTable; 059 private ModuleOpens[] opensTable; 060 private final int usesCount; 061 private final int[] usesIndex; 062 063 private ModuleProvides[] providesTable; 064 065 /** 066 * Constructs object from input stream. 067 * 068 * @param nameIndex Index in constant pool. 069 * @param length Content length in bytes. 070 * @param dataInput Input stream. 071 * @param constantPool Array of constants. 072 * @throws IOException Thrown if an I/O error occurs. 073 */ 074 Module(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException { 075 super(Const.ATTR_MODULE, nameIndex, length, constantPool); 076 077 moduleNameIndex = dataInput.readUnsignedShort(); 078 moduleFlags = dataInput.readUnsignedShort(); 079 moduleVersionIndex = dataInput.readUnsignedShort(); 080 081 final int requiresCount = dataInput.readUnsignedShort(); 082 requiresTable = new ModuleRequires[requiresCount]; 083 for (int i = 0; i < requiresCount; i++) { 084 requiresTable[i] = new ModuleRequires(dataInput); 085 } 086 087 final int exportsCount = dataInput.readUnsignedShort(); 088 exportsTable = new ModuleExports[exportsCount]; 089 for (int i = 0; i < exportsCount; i++) { 090 exportsTable[i] = new ModuleExports(dataInput); 091 } 092 093 final int opensCount = dataInput.readUnsignedShort(); 094 opensTable = new ModuleOpens[opensCount]; 095 for (int i = 0; i < opensCount; i++) { 096 opensTable[i] = new ModuleOpens(dataInput); 097 } 098 099 usesIndex = ClassParser.readU2U2Table(dataInput); 100 usesCount = usesIndex.length; 101 102 final int providesCount = dataInput.readUnsignedShort(); 103 providesTable = new ModuleProvides[providesCount]; 104 for (int i = 0; i < providesCount; i++) { 105 providesTable[i] = new ModuleProvides(dataInput); 106 } 107 } 108 109 /** 110 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 111 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 112 * 113 * @param v Visitor object. 114 */ 115 @Override 116 public void accept(final Visitor v) { 117 v.visitModule(this); 118 } 119 120 /** 121 * @return deep copy of this attribute. 122 */ 123 @Override 124 public Attribute copy(final ConstantPool constantPool) { 125 final Module c = (Module) clone(); 126 127 c.requiresTable = new ModuleRequires[requiresTable.length]; 128 Arrays.setAll(c.requiresTable, i -> requiresTable[i].copy()); 129 130 c.exportsTable = new ModuleExports[exportsTable.length]; 131 Arrays.setAll(c.exportsTable, i -> exportsTable[i].copy()); 132 133 c.opensTable = new ModuleOpens[opensTable.length]; 134 Arrays.setAll(c.opensTable, i -> opensTable[i].copy()); 135 136 c.providesTable = new ModuleProvides[providesTable.length]; 137 Arrays.setAll(c.providesTable, i -> providesTable[i].copy()); 138 139 c.setConstantPool(constantPool); 140 return c; 141 } 142 143 /** 144 * Dumps Module attribute to file stream in binary format. 145 * 146 * @param file Output file stream. 147 * @throws IOException Thrown if an I/O error occurs. 148 */ 149 @Override 150 public void dump(final DataOutputStream file) throws IOException { 151 super.dump(file); 152 153 file.writeShort(moduleNameIndex); 154 file.writeShort(moduleFlags); 155 file.writeShort(moduleVersionIndex); 156 157 file.writeShort(Args.requireU2(requiresTable.length, "requiresTable.length")); 158 for (final ModuleRequires entry : requiresTable) { 159 entry.dump(file); 160 } 161 162 file.writeShort(Args.requireU2(exportsTable.length, "exportsTable.length")); 163 for (final ModuleExports entry : exportsTable) { 164 entry.dump(file); 165 } 166 167 file.writeShort(Args.requireU2(opensTable.length, "opensTable.length")); 168 for (final ModuleOpens entry : opensTable) { 169 entry.dump(file); 170 } 171 172 file.writeShort(Args.requireU2(usesIndex.length, "usesIndex.length")); 173 for (final int entry : usesIndex) { 174 file.writeShort(entry); 175 } 176 177 file.writeShort(Args.requireU2(providesTable.length, "providesTable.length")); 178 for (final ModuleProvides entry : providesTable) { 179 entry.dump(file); 180 } 181 } 182 183 /** 184 * Gets the table of exported interfaces. 185 * 186 * @return table of exported interfaces. 187 * @see ModuleExports 188 */ 189 public ModuleExports[] getExportsTable() { 190 return exportsTable; 191 } 192 193 /** 194 * Gets flags for this module. 195 * 196 * @return module flags. 197 * @since 6.10.0 198 */ 199 public int getModuleFlags() { 200 return moduleFlags; 201 } 202 203 /** 204 * Gets module name. 205 * 206 * @param cp Array of constants. 207 * @return module name. 208 * @since 6.10.0 209 */ 210 public String getModuleName(final ConstantPool cp) { 211 return cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module); 212 } 213 214 /** 215 * Gets the table of open interfaces. 216 * 217 * @return table of provided interfaces. 218 * @see ModuleOpens 219 */ 220 public ModuleOpens[] getOpensTable() { 221 return opensTable; 222 } 223 224 /** 225 * Gets the table of provided interfaces. 226 * 227 * @return table of provided interfaces. 228 * @see ModuleProvides 229 */ 230 public ModuleProvides[] getProvidesTable() { 231 return providesTable; 232 } 233 234 /** 235 * Gets the table of required modules. 236 * 237 * @return table of required modules. 238 * @see ModuleRequires 239 */ 240 public ModuleRequires[] getRequiresTable() { 241 return requiresTable; 242 } 243 244 /** 245 * Gets the array of class names for this module's uses. 246 * 247 * @param constantPool Array of constants usually obtained from the ClassFile object. 248 * @param compactClassName false for original constant pool value, true to replace '/' with '.'. 249 * @return array of used class names. 250 * @since 6.10.0 251 */ 252 public String[] getUsedClassNames(final ConstantPool constantPool, final boolean compactClassName) { 253 final String[] usedClassNames = new String[usesCount]; 254 for (int i = 0; i < usesCount; i++) { 255 usedClassNames[i] = getClassNameAtIndex(constantPool, usesIndex[i], compactClassName); 256 } 257 return usedClassNames; 258 } 259 260 /** 261 * Gets version for this module. 262 * 263 * @param cp Array of constants. 264 * @return version from constant pool, "0" if version index is 0. 265 * @since 6.10.0 266 */ 267 public String getVersion(final ConstantPool cp) { 268 return moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8); 269 } 270 271 /** 272 * @return String representation, that is, a list of packages. 273 */ 274 @Override 275 public String toString() { 276 final ConstantPool cp = super.getConstantPool(); 277 final StringBuilder buf = new StringBuilder(); 278 buf.append("Module:\n"); 279 buf.append(" name: ").append(Utility.pathToPackage(getModuleName(cp))).append("\n"); 280 buf.append(" flags: ").append(String.format("%04x", moduleFlags)).append("\n"); 281 final String version = getVersion(cp); 282 buf.append(" version: ").append(version).append("\n"); 283 284 buf.append(" requires(").append(requiresTable.length).append("):\n"); 285 for (final ModuleRequires module : requiresTable) { 286 buf.append(" ").append(module.toString(cp)).append("\n"); 287 } 288 289 buf.append(" exports(").append(exportsTable.length).append("):\n"); 290 for (final ModuleExports module : exportsTable) { 291 buf.append(" ").append(module.toString(cp)).append("\n"); 292 } 293 294 buf.append(" opens(").append(opensTable.length).append("):\n"); 295 for (final ModuleOpens module : opensTable) { 296 buf.append(" ").append(module.toString(cp)).append("\n"); 297 } 298 299 buf.append(" uses(").append(usesIndex.length).append("):\n"); 300 for (final int index : usesIndex) { 301 final String className = getClassNameAtIndex(cp, index, true); 302 buf.append(" ").append(className).append("\n"); 303 } 304 305 buf.append(" provides(").append(providesTable.length).append("):\n"); 306 for (final ModuleProvides module : providesTable) { 307 buf.append(" ").append(module.toString(cp)).append("\n"); 308 } 309 310 return buf.substring(0, buf.length() - 1); // remove the last newline 311 } 312}