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    private final int moduleNameIndex;
037    private final int moduleFlags;
038    private final int moduleVersionIndex;
039
040    private ModuleRequires[] requiresTable;
041    private ModuleExports[] exportsTable;
042    private ModuleOpens[] opensTable;
043    private final int usesCount;
044    private final int[] usesIndex;
045    private ModuleProvides[] providesTable;
046
047    /**
048     * Construct object from input stream.
049     *
050     * @param nameIndex Index in constant pool
051     * @param length Content length in bytes
052     * @param input Input stream
053     * @param constantPool Array of constants
054     * @throws IOException if an I/O error occurs.
055     */
056    Module(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
057        super(Const.ATTR_MODULE, nameIndex, length, constantPool);
058
059        moduleNameIndex = input.readUnsignedShort();
060        moduleFlags = input.readUnsignedShort();
061        moduleVersionIndex = input.readUnsignedShort();
062
063        final int requiresCount = input.readUnsignedShort();
064        requiresTable = new ModuleRequires[requiresCount];
065        for (int i = 0; i < requiresCount; i++) {
066            requiresTable[i] = new ModuleRequires(input);
067        }
068
069        final int exportsCount = input.readUnsignedShort();
070        exportsTable = new ModuleExports[exportsCount];
071        for (int i = 0; i < exportsCount; i++) {
072            exportsTable[i] = new ModuleExports(input);
073        }
074
075        final int opensCount = input.readUnsignedShort();
076        opensTable = new ModuleOpens[opensCount];
077        for (int i = 0; i < opensCount; i++) {
078            opensTable[i] = new ModuleOpens(input);
079        }
080
081        usesCount = input.readUnsignedShort();
082        usesIndex = new int[usesCount];
083        for (int i = 0; i < usesCount; i++) {
084            usesIndex[i] = input.readUnsignedShort();
085        }
086
087        final int providesCount = input.readUnsignedShort();
088        providesTable = new ModuleProvides[providesCount];
089        for (int i = 0; i < providesCount; i++) {
090            providesTable[i] = new ModuleProvides(input);
091        }
092    }
093
094    /**
095     * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
096     * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
097     *
098     * @param v Visitor object
099     */
100    @Override
101    public void accept(final Visitor v) {
102        v.visitModule(this);
103    }
104
105    // TODO add more getters and setters?
106
107    /**
108     * @return deep copy of this attribute
109     */
110    @Override
111    public Attribute copy(final ConstantPool constantPool) {
112        final Module c = (Module) clone();
113
114        c.requiresTable = new ModuleRequires[requiresTable.length];
115        Arrays.setAll(c.requiresTable, i -> requiresTable[i].copy());
116
117        c.exportsTable = new ModuleExports[exportsTable.length];
118        Arrays.setAll(c.exportsTable, i -> exportsTable[i].copy());
119
120        c.opensTable = new ModuleOpens[opensTable.length];
121        Arrays.setAll(c.opensTable, i -> opensTable[i].copy());
122
123        c.providesTable = new ModuleProvides[providesTable.length];
124        Arrays.setAll(c.providesTable, i -> providesTable[i].copy());
125
126        c.setConstantPool(constantPool);
127        return c;
128    }
129
130    /**
131     * Dump Module attribute to file stream in binary format.
132     *
133     * @param file Output file stream
134     * @throws IOException if an I/O error occurs.
135     */
136    @Override
137    public void dump(final DataOutputStream file) throws IOException {
138        super.dump(file);
139
140        file.writeShort(moduleNameIndex);
141        file.writeShort(moduleFlags);
142        file.writeShort(moduleVersionIndex);
143
144        file.writeShort(requiresTable.length);
145        for (final ModuleRequires entry : requiresTable) {
146            entry.dump(file);
147        }
148
149        file.writeShort(exportsTable.length);
150        for (final ModuleExports entry : exportsTable) {
151            entry.dump(file);
152        }
153
154        file.writeShort(opensTable.length);
155        for (final ModuleOpens entry : opensTable) {
156            entry.dump(file);
157        }
158
159        file.writeShort(usesIndex.length);
160        for (final int entry : usesIndex) {
161            file.writeShort(entry);
162        }
163
164        file.writeShort(providesTable.length);
165        for (final ModuleProvides entry : providesTable) {
166            entry.dump(file);
167        }
168    }
169
170    /**
171     * @return table of exported interfaces
172     * @see ModuleExports
173     */
174    public ModuleExports[] getExportsTable() {
175        return exportsTable;
176    }
177
178    /**
179     * @return table of provided interfaces
180     * @see ModuleOpens
181     */
182    public ModuleOpens[] getOpensTable() {
183        return opensTable;
184    }
185
186    /**
187     * @return table of provided interfaces
188     * @see ModuleProvides
189     */
190    public ModuleProvides[] getProvidesTable() {
191        return providesTable;
192    }
193
194    /**
195     * @return table of required modules
196     * @see ModuleRequires
197     */
198    public ModuleRequires[] getRequiresTable() {
199        return requiresTable;
200    }
201
202    /**
203     * @return String representation, i.e., a list of packages.
204     */
205    @Override
206    public String toString() {
207        final ConstantPool cp = super.getConstantPool();
208        final StringBuilder buf = new StringBuilder();
209        buf.append("Module:\n");
210        buf.append("  name:    ").append(Utility.pathToPackage(cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module))).append("\n");
211        buf.append("  flags:   ").append(String.format("%04x", moduleFlags)).append("\n");
212        final String version = moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8);
213        buf.append("  version: ").append(version).append("\n");
214
215        buf.append("  requires(").append(requiresTable.length).append("):\n");
216        for (final ModuleRequires module : requiresTable) {
217            buf.append("    ").append(module.toString(cp)).append("\n");
218        }
219
220        buf.append("  exports(").append(exportsTable.length).append("):\n");
221        for (final ModuleExports module : exportsTable) {
222            buf.append("    ").append(module.toString(cp)).append("\n");
223        }
224
225        buf.append("  opens(").append(opensTable.length).append("):\n");
226        for (final ModuleOpens module : opensTable) {
227            buf.append("    ").append(module.toString(cp)).append("\n");
228        }
229
230        buf.append("  uses(").append(usesIndex.length).append("):\n");
231        for (final int index : usesIndex) {
232            final String className = cp.getConstantString(index, Const.CONSTANT_Class);
233            buf.append("    ").append(Utility.compactClassName(className, false)).append("\n");
234        }
235
236        buf.append("  provides(").append(providesTable.length).append("):\n");
237        for (final ModuleProvides module : providesTable) {
238            buf.append("    ").append(module.toString(cp)).append("\n");
239        }
240
241        return buf.substring(0, buf.length() - 1); // remove the last newline
242    }
243}