View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.bcel.classfile;
21  
22  import java.io.DataInput;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  import java.util.Arrays;
26  
27  import org.apache.bcel.Const;
28  import org.apache.bcel.util.Args;
29  
30  /**
31   * This class is derived from <em>Attribute</em> and represents the list of modules required, exported, opened or
32   * provided by a module. There may be at most one Module attribute in a ClassFile structure.
33   *
34   * @see Attribute
35   * @since 6.4.0
36   */
37  public final class Module extends Attribute {
38  
39      /**
40       * The module file name extension.
41       *
42       * @since 6.7.0
43       */
44      public static final String EXTENSION = ".jmod";
45  
46      private static String getClassNameAtIndex(final ConstantPool cp, final int index, final boolean compactClassName) {
47          final String className = cp.getConstantString(index, Const.CONSTANT_Class);
48          if (compactClassName) {
49              return Utility.compactClassName(className, false);
50          }
51          return className;
52      }
53      private final int moduleNameIndex;
54      private final int moduleFlags;
55  
56      private final int moduleVersionIndex;
57      private ModuleRequires[] requiresTable;
58      private ModuleExports[] exportsTable;
59      private ModuleOpens[] opensTable;
60      private final int usesCount;
61      private final int[] usesIndex;
62  
63      private ModuleProvides[] providesTable;
64  
65      /**
66       * Constructs object from input stream.
67       *
68       * @param nameIndex Index in constant pool.
69       * @param length Content length in bytes.
70       * @param dataInput Input stream.
71       * @param constantPool Array of constants.
72       * @throws IOException Thrown if an I/O error occurs.
73       */
74      Module(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException {
75          super(Const.ATTR_MODULE, nameIndex, length, constantPool);
76  
77          moduleNameIndex = dataInput.readUnsignedShort();
78          moduleFlags = dataInput.readUnsignedShort();
79          moduleVersionIndex = dataInput.readUnsignedShort();
80  
81          final int requiresCount = dataInput.readUnsignedShort();
82          requiresTable = new ModuleRequires[requiresCount];
83          for (int i = 0; i < requiresCount; i++) {
84              requiresTable[i] = new ModuleRequires(dataInput);
85          }
86  
87          final int exportsCount = dataInput.readUnsignedShort();
88          exportsTable = new ModuleExports[exportsCount];
89          for (int i = 0; i < exportsCount; i++) {
90              exportsTable[i] = new ModuleExports(dataInput);
91          }
92  
93          final int opensCount = dataInput.readUnsignedShort();
94          opensTable = new ModuleOpens[opensCount];
95          for (int i = 0; i < opensCount; i++) {
96              opensTable[i] = new ModuleOpens(dataInput);
97          }
98  
99          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 }