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