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