View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package org.apache.bcel.classfile;
19  
20  import java.io.DataInput;
21  import java.io.DataOutputStream;
22  import java.io.IOException;
23  import java.util.Arrays;
24  
25  import org.apache.bcel.Const;
26  
27  /**
28   * This class is derived from <em>Attribute</em> and represents the list of modules required, exported, opened or
29   * provided by a module. There may be at most one Module attribute in a ClassFile structure.
30   *
31   * @see Attribute
32   * @since 6.4.0
33   */
34  public final class Module extends Attribute {
35  
36      /**
37       * The module file name extension.
38       *
39       * @since 6.7.0
40       */
41      public static final String EXTENSION = ".jmod";
42  
43      private static String getClassNameAtIndex(final ConstantPool cp, final int index, final boolean compactClassName) {
44          final String className = cp.getConstantString(index, Const.CONSTANT_Class);
45          if (compactClassName) {
46              return Utility.compactClassName(className, false);
47          }
48          return className;
49      }
50      private final int moduleNameIndex;
51      private final int moduleFlags;
52  
53      private final int moduleVersionIndex;
54      private ModuleRequires[] requiresTable;
55      private ModuleExports[] exportsTable;
56      private ModuleOpens[] opensTable;
57      private final int usesCount;
58      private final int[] usesIndex;
59  
60      private ModuleProvides[] providesTable;
61  
62      /**
63       * Constructs object from input stream.
64       *
65       * @param nameIndex Index in constant pool
66       * @param length Content length in bytes
67       * @param input Input stream
68       * @param constantPool Array of constants
69       * @throws IOException if an I/O error occurs.
70       */
71      Module(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
72          super(Const.ATTR_MODULE, nameIndex, length, constantPool);
73  
74          moduleNameIndex = input.readUnsignedShort();
75          moduleFlags = input.readUnsignedShort();
76          moduleVersionIndex = input.readUnsignedShort();
77  
78          final int requiresCount = input.readUnsignedShort();
79          requiresTable = new ModuleRequires[requiresCount];
80          for (int i = 0; i < requiresCount; i++) {
81              requiresTable[i] = new ModuleRequires(input);
82          }
83  
84          final int exportsCount = input.readUnsignedShort();
85          exportsTable = new ModuleExports[exportsCount];
86          for (int i = 0; i < exportsCount; i++) {
87              exportsTable[i] = new ModuleExports(input);
88          }
89  
90          final int opensCount = input.readUnsignedShort();
91          opensTable = new ModuleOpens[opensCount];
92          for (int i = 0; i < opensCount; i++) {
93              opensTable[i] = new ModuleOpens(input);
94          }
95  
96          usesCount = input.readUnsignedShort();
97          usesIndex = new int[usesCount];
98          for (int i = 0; i < usesCount; i++) {
99              usesIndex[i] = input.readUnsignedShort();
100         }
101 
102         final int providesCount = input.readUnsignedShort();
103         providesTable = new ModuleProvides[providesCount];
104         for (int i = 0; i < providesCount; i++) {
105             providesTable[i] = new ModuleProvides(input);
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      * Dump Module attribute to file stream in binary format.
145      *
146      * @param file Output file stream
147      * @throws IOException 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(requiresTable.length);
158         for (final ModuleRequires entry : requiresTable) {
159             entry.dump(file);
160         }
161 
162         file.writeShort(exportsTable.length);
163         for (final ModuleExports entry : exportsTable) {
164             entry.dump(file);
165         }
166 
167         file.writeShort(opensTable.length);
168         for (final ModuleOpens entry : opensTable) {
169             entry.dump(file);
170         }
171 
172         file.writeShort(usesIndex.length);
173         for (final int entry : usesIndex) {
174             file.writeShort(entry);
175         }
176 
177         file.writeShort(providesTable.length);
178         for (final ModuleProvides entry : providesTable) {
179             entry.dump(file);
180         }
181     }
182 
183     /**
184      * @return table of exported interfaces
185      * @see ModuleExports
186      */
187     public ModuleExports[] getExportsTable() {
188         return exportsTable;
189     }
190 
191     /**
192      * Gets flags for this module.
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      * @param cp Array of constants
203      * @return module name
204      * @since 6.10.0
205      */
206     public String getModuleName(final ConstantPool cp) {
207         return cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module);
208     }
209 
210     /**
211      * @return table of provided interfaces
212      * @see ModuleOpens
213      */
214     public ModuleOpens[] getOpensTable() {
215         return opensTable;
216     }
217 
218     /**
219      * @return table of provided interfaces
220      * @see ModuleProvides
221      */
222     public ModuleProvides[] getProvidesTable() {
223         return providesTable;
224     }
225 
226     /**
227      * @return table of required modules
228      * @see ModuleRequires
229      */
230     public ModuleRequires[] getRequiresTable() {
231         return requiresTable;
232     }
233 
234     /**
235      * Gets the array of class names for this module's uses.
236      * @param constantPool Array of constants usually obtained from the ClassFile object
237      * @param compactClassName false for original constant pool value, true to replace '/' with '.'
238      * @return array of used class names
239      * @since 6.10.0
240      */
241     public String[] getUsedClassNames(final ConstantPool constantPool, final boolean compactClassName) {
242         final String[] usedClassNames = new String[usesCount];
243         for (int i = 0; i < usesCount; i++) {
244             usedClassNames[i] = getClassNameAtIndex(constantPool, usesIndex[i], compactClassName);
245         }
246         return usedClassNames;
247     }
248 
249     /**
250      * Gets version for this module.
251      * @param cp Array of constants
252      * @return version from constant pool, "0" if version index is 0
253      * @since 6.10.0
254      */
255     public String getVersion(final ConstantPool cp) {
256         return moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8);
257     }
258 
259     /**
260      * @return String representation, i.e., a list of packages.
261      */
262     @Override
263     public String toString() {
264         final ConstantPool cp = super.getConstantPool();
265         final StringBuilder buf = new StringBuilder();
266         buf.append("Module:\n");
267         buf.append("  name:    ").append(Utility.pathToPackage(getModuleName(cp))).append("\n");
268         buf.append("  flags:   ").append(String.format("%04x", moduleFlags)).append("\n");
269         final String version = getVersion(cp);
270         buf.append("  version: ").append(version).append("\n");
271 
272         buf.append("  requires(").append(requiresTable.length).append("):\n");
273         for (final ModuleRequires module : requiresTable) {
274             buf.append("    ").append(module.toString(cp)).append("\n");
275         }
276 
277         buf.append("  exports(").append(exportsTable.length).append("):\n");
278         for (final ModuleExports module : exportsTable) {
279             buf.append("    ").append(module.toString(cp)).append("\n");
280         }
281 
282         buf.append("  opens(").append(opensTable.length).append("):\n");
283         for (final ModuleOpens module : opensTable) {
284             buf.append("    ").append(module.toString(cp)).append("\n");
285         }
286 
287         buf.append("  uses(").append(usesIndex.length).append("):\n");
288         for (final int index : usesIndex) {
289             final String className = getClassNameAtIndex(cp, index, true);
290             buf.append("    ").append(className).append("\n");
291         }
292 
293         buf.append("  provides(").append(providesTable.length).append("):\n");
294         for (final ModuleProvides module : providesTable) {
295             buf.append("    ").append(module.toString(cp)).append("\n");
296         }
297 
298         return buf.substring(0, buf.length() - 1); // remove the last newline
299     }
300 }