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.Iterator;
026import java.util.stream.Stream;
027
028import org.apache.bcel.Const;
029import org.apache.bcel.util.Args;
030
031/**
032 * This class represents a BootstrapMethods attribute.
033 *
034 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23"> The class File Format :
035 *      The BootstrapMethods Attribute</a>
036 * @since 6.0
037 */
038public class BootstrapMethods extends Attribute implements Iterable<BootstrapMethod> {
039
040    private BootstrapMethod[] bootstrapMethods; // TODO this could be made final (setter is not used)
041
042    /**
043     * Initialize from another object. Note that both objects use the same references (shallow copy). Use clone() for a
044     * physical copy.
045     *
046     * @param c Source to copy.
047     */
048    public BootstrapMethods(final BootstrapMethods c) {
049        this(c.getNameIndex(), c.getLength(), c.getBootstrapMethods(), c.getConstantPool());
050    }
051
052    /**
053     * Constructs a BootstrapMethods attribute.
054     *
055     * @param nameIndex Index in constant pool to CONSTANT_Utf8.
056     * @param length Content length in bytes.
057     * @param bootstrapMethods array of bootstrap methods.
058     * @param constantPool Array of constants.
059     */
060    public BootstrapMethods(final int nameIndex, final int length, final BootstrapMethod[] bootstrapMethods, final ConstantPool constantPool) {
061        super(Const.ATTR_BOOTSTRAP_METHODS, nameIndex, length, constantPool);
062        setBootstrapMethods(bootstrapMethods);
063    }
064
065    /**
066     * Constructs object from Input stream.
067     *
068     * @param nameIndex Index in constant pool to CONSTANT_Utf8.
069     * @param length Content length in bytes.
070     * @param input Input stream.
071     * @param constantPool Array of constants.
072     * @throws IOException Thrown if an I/O error occurs.
073     */
074    BootstrapMethods(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException {
075        this(nameIndex, length, (BootstrapMethod[]) null, constantPool);
076
077        final int numBootstrapMethods = input.readUnsignedShort();
078        bootstrapMethods = new BootstrapMethod[numBootstrapMethods];
079        for (int i = 0; i < numBootstrapMethods; i++) {
080            bootstrapMethods[i] = new BootstrapMethod(input);
081        }
082    }
083
084    /**
085     * Accepts a visitor.
086     *
087     * @param v Visitor object.
088     */
089    @Override
090    public void accept(final Visitor v) {
091        v.visitBootstrapMethods(this);
092    }
093
094    /**
095     * Creates a deep copy of this attribute.
096     *
097     * @param constantPool The constant pool.
098     * @return deep copy of this attribute.
099     */
100    @Override
101    public BootstrapMethods copy(final ConstantPool constantPool) {
102        final BootstrapMethods c = (BootstrapMethods) clone();
103        c.bootstrapMethods = new BootstrapMethod[bootstrapMethods.length];
104
105        for (int i = 0; i < bootstrapMethods.length; i++) {
106            c.bootstrapMethods[i] = bootstrapMethods[i].copy();
107        }
108        c.setConstantPool(constantPool);
109        return c;
110    }
111
112    /**
113     * Dumps bootstrap methods attribute to file stream in binary format.
114     *
115     * @param file Output file stream.
116     * @throws IOException Thrown if an I/O error occurs.
117     */
118    @Override
119    public final void dump(final DataOutputStream file) throws IOException {
120        super.dump(file);
121
122        file.writeShort(Args.requireU2(bootstrapMethods.length, "bootstrapMethods.length"));
123        for (final BootstrapMethod bootstrapMethod : bootstrapMethods) {
124            bootstrapMethod.dump(file);
125        }
126    }
127
128    /**
129     * Gets the array of bootstrap method records.
130     *
131     * @return array of bootstrap method records.
132     */
133    public final BootstrapMethod[] getBootstrapMethods() {
134        return bootstrapMethods;
135    }
136
137    @Override
138    public Iterator<BootstrapMethod> iterator() {
139        return Stream.of(bootstrapMethods).iterator();
140    }
141
142    /**
143     * Sets the bootstrap methods.
144     *
145     * @param bootstrapMethods The array of bootstrap methods.
146     */
147    public final void setBootstrapMethods(final BootstrapMethod[] bootstrapMethods) {
148        this.bootstrapMethods = bootstrapMethods != null ? bootstrapMethods : BootstrapMethod.EMPTY_ARRAY;
149    }
150
151    /**
152     * @return String representation.
153     */
154    @Override
155    public final String toString() {
156        final StringBuilder buf = new StringBuilder();
157        buf.append("BootstrapMethods(");
158        buf.append(bootstrapMethods.length);
159        buf.append("):");
160        for (int i = 0; i < bootstrapMethods.length; i++) {
161            buf.append("\n");
162            final int start = buf.length();
163            buf.append("  ").append(i).append(": ");
164            final int indentCount = buf.length() - start;
165            final String[] lines = bootstrapMethods[i].toString(super.getConstantPool()).split("\\r?\\n");
166            buf.append(lines[0]);
167            for (int j = 1; j < lines.length; j++) {
168                buf.append("\n").append("          ", 0, indentCount).append(lines[j]);
169            }
170        }
171        return buf.toString();
172    }
173}