001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017
018package org.apache.bcel.classfile;
019
020import java.io.DataInput;
021import java.io.DataOutputStream;
022import java.io.IOException;
023import java.util.Arrays;
024
025import org.apache.bcel.Const;
026
027/**
028 * This class is derived from <em>Attribute</em> and represents the list of modules required, exported, opened or
029 * provided by a module. There may be at most one Module attribute in a ClassFile structure.
030 *
031 * @see Attribute
032 * @since 6.4.0
033 */
034public final class Module extends Attribute {
035
036    /**
037     * The module file name extension.
038     *
039     * @since 6.7.0
040     */
041    public static final String EXTENSION = ".jmod";
042
043    private final int moduleNameIndex;
044    private final int moduleFlags;
045    private final int moduleVersionIndex;
046
047    private ModuleRequires[] requiresTable;
048    private ModuleExports[] exportsTable;
049    private ModuleOpens[] opensTable;
050    private final int usesCount;
051    private final int[] usesIndex;
052    private ModuleProvides[] providesTable;
053
054    /**
055     * Constructs object from input stream.
056     *
057     * @param nameIndex Index in constant pool
058     * @param length Content length in bytes
059     * @param input Input stream
060     * @param constantPool Array of constants
061     * @throws IOException if an I/O error occurs.
062     */
063    Module(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
064        super(Const.ATTR_MODULE, nameIndex, length, constantPool);
065
066        moduleNameIndex = input.readUnsignedShort();
067        moduleFlags = input.readUnsignedShort();
068        moduleVersionIndex = input.readUnsignedShort();
069
070        final int requiresCount = input.readUnsignedShort();
071        requiresTable = new ModuleRequires[requiresCount];
072        for (int i = 0; i < requiresCount; i++) {
073            requiresTable[i] = new ModuleRequires(input);
074        }
075
076        final int exportsCount = input.readUnsignedShort();
077        exportsTable = new ModuleExports[exportsCount];
078        for (int i = 0; i < exportsCount; i++) {
079            exportsTable[i] = new ModuleExports(input);
080        }
081
082        final int opensCount = input.readUnsignedShort();
083        opensTable = new ModuleOpens[opensCount];
084        for (int i = 0; i < opensCount; i++) {
085            opensTable[i] = new ModuleOpens(input);
086        }
087
088        usesCount = input.readUnsignedShort();
089        usesIndex = new int[usesCount];
090        for (int i = 0; i < usesCount; i++) {
091            usesIndex[i] = input.readUnsignedShort();
092        }
093
094        final int providesCount = input.readUnsignedShort();
095        providesTable = new ModuleProvides[providesCount];
096        for (int i = 0; i < providesCount; i++) {
097            providesTable[i] = new ModuleProvides(input);
098        }
099    }
100
101    /**
102     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
103     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
104     *
105     * @param v Visitor object
106     */
107    @Override
108    public void accept(final Visitor v) {
109        v.visitModule(this);
110    }
111
112    // TODO add more getters and setters?
113
114    /**
115     * @return deep copy of this attribute
116     */
117    @Override
118    public Attribute copy(final ConstantPool constantPool) {
119        final Module c = (Module) clone();
120
121        c.requiresTable = new ModuleRequires[requiresTable.length];
122        Arrays.setAll(c.requiresTable, i -> requiresTable[i].copy());
123
124        c.exportsTable = new ModuleExports[exportsTable.length];
125        Arrays.setAll(c.exportsTable, i -> exportsTable[i].copy());
126
127        c.opensTable = new ModuleOpens[opensTable.length];
128        Arrays.setAll(c.opensTable, i -> opensTable[i].copy());
129
130        c.providesTable = new ModuleProvides[providesTable.length];
131        Arrays.setAll(c.providesTable, i -> providesTable[i].copy());
132
133        c.setConstantPool(constantPool);
134        return c;
135    }
136
137    /**
138     * Dump Module attribute to file stream in binary format.
139     *
140     * @param file Output file stream
141     * @throws IOException if an I/O error occurs.
142     */
143    @Override
144    public void dump(final DataOutputStream file) throws IOException {
145        super.dump(file);
146
147        file.writeShort(moduleNameIndex);
148        file.writeShort(moduleFlags);
149        file.writeShort(moduleVersionIndex);
150
151        file.writeShort(requiresTable.length);
152        for (final ModuleRequires entry : requiresTable) {
153            entry.dump(file);
154        }
155
156        file.writeShort(exportsTable.length);
157        for (final ModuleExports entry : exportsTable) {
158            entry.dump(file);
159        }
160
161        file.writeShort(opensTable.length);
162        for (final ModuleOpens entry : opensTable) {
163            entry.dump(file);
164        }
165
166        file.writeShort(usesIndex.length);
167        for (final int entry : usesIndex) {
168            file.writeShort(entry);
169        }
170
171        file.writeShort(providesTable.length);
172        for (final ModuleProvides entry : providesTable) {
173            entry.dump(file);
174        }
175    }
176
177    /**
178     * @return table of exported interfaces
179     * @see ModuleExports
180     */
181    public ModuleExports[] getExportsTable() {
182        return exportsTable;
183    }
184
185    /**
186     * @return table of provided interfaces
187     * @see ModuleOpens
188     */
189    public ModuleOpens[] getOpensTable() {
190        return opensTable;
191    }
192
193    /**
194     * @return table of provided interfaces
195     * @see ModuleProvides
196     */
197    public ModuleProvides[] getProvidesTable() {
198        return providesTable;
199    }
200
201    /**
202     * @return table of required modules
203     * @see ModuleRequires
204     */
205    public ModuleRequires[] getRequiresTable() {
206        return requiresTable;
207    }
208
209    /**
210     * @return String representation, i.e., a list of packages.
211     */
212    @Override
213    public String toString() {
214        final ConstantPool cp = super.getConstantPool();
215        final StringBuilder buf = new StringBuilder();
216        buf.append("Module:\n");
217        buf.append("  name:    ").append(Utility.pathToPackage(cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module))).append("\n");
218        buf.append("  flags:   ").append(String.format("%04x", moduleFlags)).append("\n");
219        final String version = moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8);
220        buf.append("  version: ").append(version).append("\n");
221
222        buf.append("  requires(").append(requiresTable.length).append("):\n");
223        for (final ModuleRequires module : requiresTable) {
224            buf.append("    ").append(module.toString(cp)).append("\n");
225        }
226
227        buf.append("  exports(").append(exportsTable.length).append("):\n");
228        for (final ModuleExports module : exportsTable) {
229            buf.append("    ").append(module.toString(cp)).append("\n");
230        }
231
232        buf.append("  opens(").append(opensTable.length).append("):\n");
233        for (final ModuleOpens module : opensTable) {
234            buf.append("    ").append(module.toString(cp)).append("\n");
235        }
236
237        buf.append("  uses(").append(usesIndex.length).append("):\n");
238        for (final int index : usesIndex) {
239            final String className = cp.getConstantString(index, Const.CONSTANT_Class);
240            buf.append("    ").append(Utility.compactClassName(className, false)).append("\n");
241        }
242
243        buf.append("  provides(").append(providesTable.length).append("):\n");
244        for (final ModuleProvides module : providesTable) {
245            buf.append("    ").append(module.toString(cp)).append("\n");
246        }
247
248        return buf.substring(0, buf.length() - 1); // remove the last newline
249    }
250}