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 input 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 input, final ConstantPool constantPool) throws IOException {
074        super(Const.ATTR_MODULE, nameIndex, length, constantPool);
075
076        moduleNameIndex = input.readUnsignedShort();
077        moduleFlags = input.readUnsignedShort();
078        moduleVersionIndex = input.readUnsignedShort();
079
080        final int requiresCount = input.readUnsignedShort();
081        requiresTable = new ModuleRequires[requiresCount];
082        for (int i = 0; i < requiresCount; i++) {
083            requiresTable[i] = new ModuleRequires(input);
084        }
085
086        final int exportsCount = input.readUnsignedShort();
087        exportsTable = new ModuleExports[exportsCount];
088        for (int i = 0; i < exportsCount; i++) {
089            exportsTable[i] = new ModuleExports(input);
090        }
091
092        final int opensCount = input.readUnsignedShort();
093        opensTable = new ModuleOpens[opensCount];
094        for (int i = 0; i < opensCount; i++) {
095            opensTable[i] = new ModuleOpens(input);
096        }
097
098        usesCount = input.readUnsignedShort();
099        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}