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