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  
29  /**
30   * This class is derived from <em>Attribute</em> and represents the list of modules required, exported, opened or
31   * provided by a module. There may be at most one Module attribute in a ClassFile structure.
32   *
33   * @see Attribute
34   * @since 6.4.0
35   */
36  public final class Module extends Attribute {
37  
38      /**
39       * The module file name extension.
40       *
41       * @since 6.7.0
42       */
43      public static final String EXTENSION = ".jmod";
44  
45      private static String getClassNameAtIndex(final ConstantPool cp, final int index, final boolean compactClassName) {
46          final String className = cp.getConstantString(index, Const.CONSTANT_Class);
47          if (compactClassName) {
48              return Utility.compactClassName(className, false);
49          }
50          return className;
51      }
52      private final int moduleNameIndex;
53      private final int moduleFlags;
54  
55      private final int moduleVersionIndex;
56      private ModuleRequires[] requiresTable;
57      private ModuleExports[] exportsTable;
58      private ModuleOpens[] opensTable;
59      private final int usesCount;
60      private final int[] usesIndex;
61  
62      private ModuleProvides[] providesTable;
63  
64      /**
65       * Constructs object from input stream.
66       *
67       * @param nameIndex Index in constant pool.
68       * @param length Content length in bytes.
69       * @param dataInput Input stream.
70       * @param constantPool Array of constants.
71       * @throws IOException if an I/O error occurs.
72       */
73      Module(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException {
74          super(Const.ATTR_MODULE, nameIndex, length, constantPool);
75  
76          moduleNameIndex = dataInput.readUnsignedShort();
77          moduleFlags = dataInput.readUnsignedShort();
78          moduleVersionIndex = dataInput.readUnsignedShort();
79  
80          final int requiresCount = dataInput.readUnsignedShort();
81          requiresTable = new ModuleRequires[requiresCount];
82          for (int i = 0; i < requiresCount; i++) {
83              requiresTable[i] = new ModuleRequires(dataInput);
84          }
85  
86          final int exportsCount = dataInput.readUnsignedShort();
87          exportsTable = new ModuleExports[exportsCount];
88          for (int i = 0; i < exportsCount; i++) {
89              exportsTable[i] = new ModuleExports(dataInput);
90          }
91  
92          final int opensCount = dataInput.readUnsignedShort();
93          opensTable = new ModuleOpens[opensCount];
94          for (int i = 0; i < opensCount; i++) {
95              opensTable[i] = new ModuleOpens(dataInput);
96          }
97  
98          usesIndex = ClassParser.readU2U2Table(dataInput);
99          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 }