org.lambdamatic.analyzer.ast.InsnCursor.java Source code

Java tutorial

Introduction

Here is the source code for org.lambdamatic.analyzer.ast.InsnCursor.java

Source

/*******************************************************************************
 * Copyright (c) 2015 Red Hat. All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License v1.0 which accompanies this
 * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: Red Hat - Initial Contribution
 *******************************************************************************/

package org.lambdamatic.analyzer.ast;

import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;

import org.objectweb.asm.Label;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;

/**
 * A cursor to navigate on an {@link InsnList}.
 * 
 */
class InsnCursor {

    /**
     * the {@link InsnList} on which this {@link InsnCursor} operates.
     */
    private final InsnList instructions;

    /**
     * the underlying {@link ListIterator} on the {@link InsnList}.
     */
    private final ListIterator<AbstractInsnNode> iterator;

    /** The current instruction. */
    private AbstractInsnNode currentInstruction = null;

    /**
     * the current position of the cursor in the {@link InsnList}.
     */
    private int currentPosition = -1;

    /** the labels to locate specific instructions. */
    private final Map<String, AbstractInsnNode> labels;

    /**
     * Constructor
     * 
     * @param instructions the {@link InsnList} to iterate on.
     * @param labels the labels to locate specific instructions.
     */
    @SuppressWarnings("unchecked")
    public InsnCursor(final InsnList instructions, final Map<String, AbstractInsnNode> labels) {
        this.instructions = instructions;
        this.iterator = instructions.iterator();
        this.labels = labels;
    }

    /**
     * Returns {@code true} if the underlying list iterator has more {@link AbstractInsnNode}s when
     * traversing the {@link InsnList} in the reverse direction. (In other words, returns {@code true}
     * if {@link #previous} would return an {@link AbstractInsnNode} rather than throwing an
     * exception.)
     *
     * @return {@code true} if the underlying list iterator has more {@link AbstractInsnNode}s when
     *         traversing the {@link InsnList} in the reverse direction
     */
    public boolean hasPrevious() {
        return this.iterator.hasPrevious();
    }

    /**
     * Returns the previous {@link AbstractInsnNode} in the underlying list and moves the cursor
     * position backwards. This method may be called repeatedly to iterate through the
     * {@link InsnList} backwards, or intermixed with calls to {@link #next} to go back and forth.
     * (Note that alternating calls to {@code next} and {@code previous} will return the same
     * {@link AbstractInsnNode} repeatedly.)
     *
     * @return the previous element in the {@link InsnList}
     * @throws NoSuchElementException if the underlying iterator has no previous
     *         {@link AbstractInsnNode}
     */
    public AbstractInsnNode getPrevious() {
        this.currentInstruction = this.iterator.previous();
        this.currentPosition--;
        return this.currentInstruction;
    }

    /**
     * Moves to the previous {@link AbstractInsnNode} in the underlying list and moves the cursor
     * position backwards. This method may be called repeatedly to iterate through the
     * {@link InsnList} backwards, or intermixed with calls to {@link #next} to go back and forth.
     * (Note that alternating calls to {@code next} and {@code previous} will return the same
     * {@link AbstractInsnNode} repeatedly.)
     *
     * @return {@code this}, by convenience.
     * @throws NoSuchElementException if the underlying iterator has no previous
     *         {@link AbstractInsnNode}
     */
    public InsnCursor previous() {
        getPrevious();
        return this;
    }

    /**
     * Returns {@code true} if the underlying list iterator has more {@link AbstractInsnNode}s when
     * traversing the list in the forward direction. (In other words, returns {@code true} if
     * {@link #next} would return an {@link AbstractInsnNode} rather than throwing an exception.)
     *
     * @return {@code true} if the underlying list iterator has more {@link AbstractInsnNode}s when
     *         traversing the list in the forward direction
     */
    public boolean hasNext() {
        return this.iterator.hasNext();
    }

    /**
     * Returns the next {@link AbstractInsnNode} in the list and advances the cursor position. This
     * method may be called repeatedly to iterate through the {@link InsnList}, or intermixed with
     * calls to {@link #previous} to go back and forth. (Note that alternating calls to {@code next}
     * and {@code previous} will return the same {@link AbstractInsnNode} repeatedly.)
     *
     * @return the next {@link AbstractInsnNode} in the list
     * @throws NoSuchElementException if the iteration has no next element
     */
    public AbstractInsnNode getNext() {
        this.currentInstruction = this.iterator.next();
        this.currentPosition++;
        return this.currentInstruction;
    }

    /**
     * Moves to the next {@link AbstractInsnNode} in the list and advances the cursor position. This
     * method may be called repeatedly to iterate through the {@link InsnList}, or intermixed with
     * calls to {@link #previous} to go back and forth. (Note that alternating calls to {@code next}
     * and {@code previous} will return the same {@link AbstractInsnNode} repeatedly.)
     *
     * @return {@code this}, by convenience.
     * @throws NoSuchElementException if the iteration has no next element
     */
    public InsnCursor next() {
        if (hasNext()) {
            getNext();
        } else {
            this.currentInstruction = null;
        }
        return this;
    }

    /**
     * Moves to the first {@link AbstractInsnNode} in the list having the given {@code label}.
     *
     * @param label the target label to reach
     * @return {@code this}, by convenience.
     * @throws NoSuchElementException if the iteration has no next element
     */
    public InsnCursor move(final Label label) {
        final AbstractInsnNode targetInstruction = this.labels.get(label.toString());
        if (this.instructions.indexOf(targetInstruction) > this.currentPosition) {
            while (this.currentInstruction != targetInstruction) {
                next();
            }
        } else {
            while (this.currentInstruction != targetInstruction) {
                previous();
            }
        }
        return this;
    }

    /**
     * Returns {@code true} if the current instruction exists, {@code false} otherwise. A
     * {@code false} result means that either the cursor did not start yet, or it reached the end of
     * the underlying {@link InsnList}.
     * 
     * @return {@code true} if the current instruction exists, {@code false} otherwise.
     */
    public boolean hasCurrent() {
        return this.currentInstruction != null;
    }

    /**
     * Returns the current {@link AbstractInsnNode} or {@code null} if the cursor was not moved
     * forward yet.
     * 
     * @return the current {@link AbstractInsnNode} in the list
     */
    public AbstractInsnNode getCurrent() {
        return this.currentInstruction;
    }

    /**
     * Duplicates this InsnCursor, returning a copy at the same position on the underlying
     * {@link InsnList}.
     * 
     * @return a copy of this cursor.
     */
    public InsnCursor duplicate() {
        final InsnCursor duplicateCursor = new InsnCursor(this.instructions, this.labels);
        while (duplicateCursor.currentPosition < this.currentPosition && duplicateCursor.hasNext()) {
            duplicateCursor.next();
        }
        return duplicateCursor;
    }

}