// Path: common/src/main/java/eu/mikroskeem/shuriken/common/collections/CollectionUtilities.java
// @Nullable
// public static <T> T firstOrNull(@NotNull Iterator<T> iterator) {
//     return iterator.hasNext() ? iterator.next() : null;
// }


import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static eu.mikroskeem.shuriken.common.collections.CollectionUtilities.firstOrNull;
package eu.mikroskeem.shuriken.instrumentation.bytecode;




/**
 * "Safer" bytecode manipulation utilities
 *
 * @author Mark Vainomaa
 */
public final class BytecodeManipulation {
    /**
     * Tries to find an instructions from instruction list
     *
     * @param instructions Instructions list
     * @param instructionType Instruction type to find
     * @param predicate Instruction test predicate
     * @param <T> Instruction type
     * @return Found instructions or empty list, if didn't find anything
     */
    @NotNull
    public static <T extends AbstractInsnNode> List<T> findInstructions(@NotNull InsnList instructions,
                                                                        @NotNull Class<T> instructionType,
                                                                        @NotNull Predicate<T> predicate) {
        return Arrays.stream(instructions.toArray())
                .filter(i -> i.getClass() == instructionType)
                .map(instructionType::cast)
                .filter(predicate)
                .collect(Collectors.toList());
    }

    /**
     * Tries to find an instruction from instruction list
     *
     * @param instructions Instruction list
     * @param instructionType Instruction type
     * @param predicate Instruction test predicate
     * @param <T> Instruction type
     * @return First target instruction, or null if not found
     */
    @Nullable
    public static <T extends AbstractInsnNode> T findInstruction(@NotNull InsnList instructions,
                                                                 @NotNull Class<T> instructionType,
                                                                 @NotNull Predicate<T> predicate) {
// Path: common/src/main/java/eu/mikroskeem/shuriken/common/collections/CollectionUtilities.java
// @Nullable
// public static <T> T firstOrNull(@NotNull Iterator<T> iterator) {
//     return iterator.hasNext() ? iterator.next() : null;
// }


// Path: instrumentation/src/main/java/eu/mikroskeem/shuriken/instrumentation/bytecode/BytecodeManipulation.java
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static eu.mikroskeem.shuriken.common.collections.CollectionUtilities.firstOrNull;

package eu.mikroskeem.shuriken.instrumentation.bytecode;




/**
 * "Safer" bytecode manipulation utilities
 *
 * @author Mark Vainomaa
 */
public final class BytecodeManipulation {
    /**
     * Tries to find an instructions from instruction list
     *
     * @param instructions Instructions list
     * @param instructionType Instruction type to find
     * @param predicate Instruction test predicate
     * @param <T> Instruction type
     * @return Found instructions or empty list, if didn't find anything
     */
    @NotNull
    public static <T extends AbstractInsnNode> List<T> findInstructions(@NotNull InsnList instructions,
                                                                        @NotNull Class<T> instructionType,
                                                                        @NotNull Predicate<T> predicate) {
        return Arrays.stream(instructions.toArray())
                .filter(i -> i.getClass() == instructionType)
                .map(instructionType::cast)
                .filter(predicate)
                .collect(Collectors.toList());
    }

    /**
     * Tries to find an instruction from instruction list
     *
     * @param instructions Instruction list
     * @param instructionType Instruction type
     * @param predicate Instruction test predicate
     * @param <T> Instruction type
     * @return First target instruction, or null if not found
     */
    @Nullable
    public static <T extends AbstractInsnNode> T findInstruction(@NotNull InsnList instructions,
                                                                 @NotNull Class<T> instructionType,
                                                                 @NotNull Predicate<T> predicate) {
        return firstOrNull(findInstructions(instructions, instructionType, predicate));