    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}