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 input 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 input, final ConstantPool constantPool) throws IOException {
74          super(Const.ATTR_MODULE, nameIndex, length, constantPool);
75  
76          moduleNameIndex = input.readUnsignedShort();
77          moduleFlags = input.readUnsignedShort();
78          moduleVersionIndex = input.readUnsignedShort();
79  
80          final int requiresCount = input.readUnsignedShort();
81          requiresTable = new ModuleRequires[requiresCount];
82          for (int i = 0; i < requiresCount; i++) {
83              requiresTable[i] = new ModuleRequires(input);
84          }
85  
86          final int exportsCount = input.readUnsignedShort();
87          exportsTable = new ModuleExports[exportsCount];
88          for (int i = 0; i < exportsCount; i++) {
89              exportsTable[i] = new ModuleExports(input);
90          }
91  
92          final int opensCount = input.readUnsignedShort();
93          opensTable = new ModuleOpens[opensCount];
94          for (int i = 0; i < opensCount; i++) {
95              opensTable[i] = new ModuleOpens(input);
96          }
97  
98          usesCount = input.readUnsignedShort();
99          usesIndex = new int[usesCount];
100         for (int i = 0; i < usesCount; i++) {
101             usesIndex[i] = input.readUnsignedShort();
102         }
103 
104         final int providesCount = input.readUnsignedShort();
105         providesTable = new ModuleProvides[providesCount];
106         for (int i = 0; i < providesCount; i++) {
107             providesTable[i] = new ModuleProvides(input);
108         }
109     }
110 
111     /**
112      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
113      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
114      *
115      * @param v Visitor object
116      */
117     @Override
118     public void accept(final Visitor v) {
119         v.visitModule(this);
120     }
121 
122     /**
123      * @return deep copy of this attribute
124      */
125     @Override
126     public Attribute copy(final ConstantPool constantPool) {
127         final Module c = (Module) clone();
128 
129         c.requiresTable = new ModuleRequires[requiresTable.length];
130         Arrays.setAll(c.requiresTable, i -> requiresTable[i].copy());
131 
132         c.exportsTable = new ModuleExports[exportsTable.length];
133         Arrays.setAll(c.exportsTable, i -> exportsTable[i].copy());
134 
135         c.opensTable = new ModuleOpens[opensTable.length];
136         Arrays.setAll(c.opensTable, i -> opensTable[i].copy());
137 
138         c.providesTable = new ModuleProvides[providesTable.length];
139         Arrays.setAll(c.providesTable, i -> providesTable[i].copy());
140 
141         c.setConstantPool(constantPool);
142         return c;
143     }
144 
145     /**
146      * Dump Module attribute to file stream in binary format.
147      *
148      * @param file Output file stream
149      * @throws IOException if an I/O error occurs.
150      */
151     @Override
152     public void dump(final DataOutputStream file) throws IOException {
153         super.dump(file);
154 
155         file.writeShort(moduleNameIndex);
156         file.writeShort(moduleFlags);
157         file.writeShort(moduleVersionIndex);
158 
159         file.writeShort(requiresTable.length);
160         for (final ModuleRequires entry : requiresTable) {
161             entry.dump(file);
162         }
163 
164         file.writeShort(exportsTable.length);
165         for (final ModuleExports entry : exportsTable) {
166             entry.dump(file);
167         }
168 
169         file.writeShort(opensTable.length);
170         for (final ModuleOpens entry : opensTable) {
171             entry.dump(file);
172         }
173 
174         file.writeShort(usesIndex.length);
175         for (final int entry : usesIndex) {
176             file.writeShort(entry);
177         }
178 
179         file.writeShort(providesTable.length);
180         for (final ModuleProvides entry : providesTable) {
181             entry.dump(file);
182         }
183     }
184 
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      * @return module flags
196      * @since 6.10.0
197      */
198     public int getModuleFlags() {
199         return moduleFlags;
200     }
201 
202     /**
203      * Gets module name.
204      * @param cp Array of constants
205      * @return module name
206      * @since 6.10.0
207      */
208     public String getModuleName(final ConstantPool cp) {
209         return cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module);
210     }
211 
212     /**
213      * @return table of provided interfaces
214      * @see ModuleOpens
215      */
216     public ModuleOpens[] getOpensTable() {
217         return opensTable;
218     }
219 
220     /**
221      * @return table of provided interfaces
222      * @see ModuleProvides
223      */
224     public ModuleProvides[] getProvidesTable() {
225         return providesTable;
226     }
227 
228     /**
229      * @return table of required modules
230      * @see ModuleRequires
231      */
232     public ModuleRequires[] getRequiresTable() {
233         return requiresTable;
234     }
235 
236     /**
237      * Gets the array of class names for this module's uses.
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      * @param cp Array of constants
254      * @return version from constant pool, "0" if version index is 0
255      * @since 6.10.0
256      */
257     public String getVersion(final ConstantPool cp) {
258         return moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8);
259     }
260 
261     /**
262      * @return String representation, i.e., a list of packages.
263      */
264     @Override
265     public String toString() {
266         final ConstantPool cp = super.getConstantPool();
267         final StringBuilder buf = new StringBuilder();
268         buf.append("Module:\n");
269         buf.append("  name:    ").append(Utility.pathToPackage(getModuleName(cp))).append("\n");
270         buf.append("  flags:   ").append(String.format("%04x", moduleFlags)).append("\n");
271         final String version = getVersion(cp);
272         buf.append("  version: ").append(version).append("\n");
273 
274         buf.append("  requires(").append(requiresTable.length).append("):\n");
275         for (final ModuleRequires module : requiresTable) {
276             buf.append("    ").append(module.toString(cp)).append("\n");
277         }
278 
279         buf.append("  exports(").append(exportsTable.length).append("):\n");
280         for (final ModuleExports module : exportsTable) {
281             buf.append("    ").append(module.toString(cp)).append("\n");
282         }
283 
284         buf.append("  opens(").append(opensTable.length).append("):\n");
285         for (final ModuleOpens module : opensTable) {
286             buf.append("    ").append(module.toString(cp)).append("\n");
287         }
288 
289         buf.append("  uses(").append(usesIndex.length).append("):\n");
290         for (final int index : usesIndex) {
291             final String className = getClassNameAtIndex(cp, index, true);
292             buf.append("    ").append(className).append("\n");
293         }
294 
295         buf.append("  provides(").append(providesTable.length).append("):\n");
296         for (final ModuleProvides module : providesTable) {
297             buf.append("    ").append(module.toString(cp)).append("\n");
298         }
299 
300         return buf.substring(0, buf.length() - 1); // remove the last newline
301     }
302 }