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