Example usage for java.util ListIterator previous

List of usage examples for java.util ListIterator previous

Introduction

In this page you can find the example usage for java.util ListIterator previous.

Prototype

E previous();

Source Link

Document

Returns the previous element in the list and moves the cursor position backwards.

Usage

From source file:org.matonto.catalog.impl.SimpleCatalogManager.java

@Override
public Set<Conflict> getConflicts(Resource leftId, Resource rightId) throws MatOntoException {
    if (resourceExists(leftId, Commit.TYPE) && resourceExists(rightId, Commit.TYPE)) {
        try (RepositoryConnection conn = repository.getConnection()) {
            LinkedList<Value> leftList = new LinkedList<>();
            LinkedList<Value> rightList = new LinkedList<>();

            getCommitChainIterator(leftId, conn).forEachRemaining(leftList::add);
            getCommitChainIterator(rightId, conn).forEachRemaining(rightList::add);

            ListIterator<Value> leftIterator = leftList.listIterator();
            ListIterator<Value> rightIterator = rightList.listIterator();

            Value originalEnd = null;//from   ww  w .j a v a2  s.co m
            while (leftIterator.hasNext() && rightIterator.hasNext()) {
                Value currentId = leftIterator.next();
                if (!currentId.equals(rightIterator.next())) {
                    leftIterator.previous();
                    rightIterator.previous();
                    break;
                } else {
                    originalEnd = currentId;
                }
            }
            if (originalEnd == null) {
                throw new MatOntoException("There is no common parent between the provided Commits.");
            }

            Model left = createModelFromIterator(leftIterator, conn);
            Model right = createModelFromIterator(rightIterator, conn);

            Model duplicates = mf.createModel(left);
            duplicates.retainAll(right);

            left.removeAll(duplicates);
            right.removeAll(duplicates);

            Resource deletionContext = vf.createIRI(DELETION_CONTEXT);

            Model leftDeletions = mf.createModel(left.filter(null, null, null, deletionContext));
            Model rightDeletions = mf.createModel(right.filter(null, null, null, deletionContext));

            left.removeAll(leftDeletions);
            right.removeAll(rightDeletions);

            Set<Conflict> result = new HashSet<>();

            Model original = getCompiledResource((Resource) originalEnd).get();
            IRI rdfType = vf.createIRI(RDF.TYPE.stringValue());

            leftDeletions.forEach(statement -> {
                Resource subject = statement.getSubject();
                IRI predicate = statement.getPredicate();
                if (predicate.equals(rdfType) || right.contains(subject, predicate, null)) {
                    result.add(createConflict(subject, predicate, original, left, leftDeletions, right,
                            rightDeletions));
                    Stream.of(left, right, rightDeletions)
                            .forEach(item -> item.remove(subject, predicate, null));
                }
            });

            rightDeletions.forEach(statement -> {
                Resource subject = statement.getSubject();
                IRI predicate = statement.getPredicate();
                if (predicate.equals(rdfType) || left.contains(subject, predicate, null)) {
                    result.add(createConflict(subject, predicate, original, left, leftDeletions, right,
                            rightDeletions));
                    Stream.of(left, leftDeletions, right)
                            .forEach(item -> item.remove(subject, predicate, null));
                }
            });

            left.forEach(statement -> {
                Resource subject = statement.getSubject();
                IRI predicate = statement.getPredicate();
                if (right.contains(subject, predicate, null)) {
                    result.add(createConflict(subject, predicate, original, left, leftDeletions, right,
                            rightDeletions));
                    Stream.of(leftDeletions, right, rightDeletions)
                            .forEach(item -> item.remove(subject, predicate, null));
                }
            });

            return result;
        } catch (RepositoryException e) {
            throw new MatOntoException("Error in repository connection.", e);
        }
    }
    throw new MatOntoException("One or both of the commit IRIs could not be found in the Repository.");
}

From source file:org.eclipse.jubula.rc.swt.driver.RobotSwtImpl.java

/**
 * @param options options//w  ww.j  av a2  s. co  m
 * @param alreadyDown set of pressed keys
 * @param i ListIterator
 */
private void releaseKeys(final InterceptorOptions options, final Set alreadyDown, final ListIterator i) {

    // Release all keys in reverse order.
    final Set alreadyUp = new HashSet();
    try {
        while (i.hasPrevious()) {
            IRobotEventConfirmer confirmer = m_interceptor.intercept(options);
            m_queuer.invokeAndWait(this.getClass().getName() + ".type", //$NON-NLS-1$
                    new IRunnable() {
                        public Object run() { // SYNCH THREAD START
                            AbstractKeyTyper keyTyper = (AbstractKeyTyper) i.previous();
                            if (log.isDebugEnabled()) {
                                log.debug("trying to release: " + keyTyper); //$NON-NLS-1$
                            }
                            if (alreadyDown.contains(keyTyper) && !alreadyUp.contains(keyTyper)) {

                                if (log.isDebugEnabled()) {
                                    log.debug("releasing: " + keyTyper); //$NON-NLS-1$
                                }

                                post(keyTyper.createKeyUpEvent());
                                alreadyUp.add(keyTyper);
                            }
                            return null;
                        }
                    });
            confirmer.waitToConfirm(null, new KeySwtEventMatcher(SWT.KeyUp));
        }
    } catch (RobotException e) {
        log.error("error releasing keys", e); //$NON-NLS-1$
        if (!i.hasPrevious()) {
            throw e;
        }
    }
}

From source file:org.apache.fop.layoutmgr.inline.TextLayoutManager.java

/** {@inheritDoc} */
public boolean applyChanges(final List oldList, int depth) {

    // make sure the LM appears unfinished in between this call
    // and the next call to getChangedKnuthElements()
    setFinished(false);// w  w w  . jav a  2 s .  c  om

    if (oldList.isEmpty()) {
        return false;
    }

    // Find the first and last positions in oldList that point to an AreaInfo
    // (i.e. getLeafPos() != -1)
    LeafPosition startPos = null;
    LeafPosition endPos = null;
    ListIterator oldListIter;
    for (oldListIter = oldList.listIterator(); oldListIter.hasNext();) {
        Position pos = ((KnuthElement) oldListIter.next()).getPosition();
        startPos = (LeafPosition) pos.getPosition(depth);
        if (startPos != null && startPos.getLeafPos() != -1) {
            break;
        }
    }
    for (oldListIter = oldList.listIterator(oldList.size()); oldListIter.hasPrevious();) {
        Position pos = ((KnuthElement) oldListIter.previous()).getPosition();
        endPos = (LeafPosition) pos.getPosition(depth);
        if (endPos != null && endPos.getLeafPos() != -1) {
            break;
        }
    }

    // set start/end index, taking into account any offset due to
    // changes applied to previous paragraphs
    returnedIndices[0] = (startPos != null ? startPos.getLeafPos() : -1) + changeOffset;
    returnedIndices[1] = (endPos != null ? endPos.getLeafPos() : -1) + changeOffset;

    int areaInfosAdded = 0;
    int areaInfosRemoved = 0;

    if (!changeList.isEmpty()) {
        int oldIndex = -1;
        int changeIndex;
        PendingChange currChange;
        ListIterator changeListIterator = changeList.listIterator();
        while (changeListIterator.hasNext()) {
            currChange = (PendingChange) changeListIterator.next();
            if (currChange.index == oldIndex) {
                areaInfosAdded++;
                changeIndex = currChange.index + areaInfosAdded - areaInfosRemoved;
            } else {
                areaInfosRemoved++;
                areaInfosAdded++;
                oldIndex = currChange.index;
                changeIndex = currChange.index + areaInfosAdded - areaInfosRemoved;
                removeAreaInfo(changeIndex);
            }
            addAreaInfo(changeIndex, currChange.areaInfo);
        }
        changeList.clear();
    }

    // increase the end index for getChangedKnuthElements()
    returnedIndices[1] += (areaInfosAdded - areaInfosRemoved);
    // increase offset to use for subsequent paragraphs
    changeOffset += (areaInfosAdded - areaInfosRemoved);

    return hasChanged;
}

From source file:org.apache.fop.layoutmgr.inline.LineLayoutManager.java

private void processUpdates(Paragraph par, List updateList) {
    // create iterator for the updateList
    ListIterator updateListIterator = updateList.listIterator();
    Update currUpdate;/* ww  w. j a  v a 2 s .co  m*/
    int elementsAdded = 0;

    while (updateListIterator.hasNext()) {
        // ask the LMs to apply the changes and return
        // the new KnuthElements to replace the old ones
        currUpdate = (Update) updateListIterator.next();
        int fromIndex = currUpdate.firstIndex;
        int toIndex;
        if (updateListIterator.hasNext()) {
            Update nextUpdate = (Update) updateListIterator.next();
            toIndex = nextUpdate.firstIndex;
            updateListIterator.previous();
        } else {
            // maybe this is not always correct!
            toIndex = par.size() - par.ignoreAtEnd - elementsAdded;
        }

        // applyChanges() returns true if the LM modifies its data,
        // so it must return new KnuthElements to replace the old ones
        if (currUpdate.inlineLM.applyChanges(par.subList(fromIndex + elementsAdded, toIndex + elementsAdded))) {
            // insert the new KnuthElements
            List newElements = currUpdate.inlineLM.getChangedKnuthElements(
                    par.subList(fromIndex + elementsAdded, toIndex + elementsAdded),
                    /*flaggedPenalty,*/ effectiveAlignment);
            // remove the old elements
            par.subList(fromIndex + elementsAdded, toIndex + elementsAdded).clear();
            // insert the new elements
            par.addAll(fromIndex + elementsAdded, newElements);
            elementsAdded += newElements.size() - (toIndex - fromIndex);
        }
    }
    updateList.clear();
}

From source file:com.edgenius.wiki.render.macro.AlignMacro.java

@Override
protected void replaceHTML(HTMLNode node, ListIterator<HTMLNode> iter, RenderContext context) {
    if (node.getPair() == null) {
        log.warn("Unexpect case: No close tag for " + this.getClass().getName());
        return;//w  w  w.ja  v  a  2  s. co m
    }
    if (node.getStyle() != null && node.getStyle().get(NameConstants.TEXT_ALIGN) != null
            && (node.getAttributes() == null
                    || StringUtils.isBlank(node.getAttributes().get(NameConstants.AID)))) {
        String align = node.getStyle().get(NameConstants.TEXT_ALIGN);
        HTMLNode alignNode = null;

        //what ever the NODE is, such as <td style="align:center;colspan=4"> etc, the align attribute need be remove
        node.removeStyle("text-align", null);
        if ("div".equalsIgnoreCase(node.getTagName()) || "span".equalsIgnoreCase(node.getTagName())
                || "p".equalsIgnoreCase(node.getTagName())) {
            alignNode = node;
        } else {
            //maybe <td> tag,  it can not be simply reset -- otherwise, table will lost one cell.So here will add a paired <span> node
            //!!! don't insert <div> pair as it may broken TableMacro.isSimpleTableSupport()
            //enclose by current node 
            //?? need handle default left?
            if ("center".equalsIgnoreCase(align) || "right".equalsIgnoreCase(align)) {
                //build a proxy tag, their tagname does not matter as it will reset following code
                alignNode = new HTMLNode("<span>", false);
                HTMLNode closeAlignNode = new HTMLNode("</span>", false);
                alignNode.setPair(closeAlignNode);

                iter.add(alignNode);

                //example, go to </td> tag 
                moveIteratorCursorTo(node.getPair(), iter, true);
                //this is that node before </td>, need confirm it is not <td> (basically, it is impossible a <div> insert after <td>) 
                HTMLNode nd = iter.previous();
                if (nd != node) {
                    iter.add(closeAlignNode);
                    //move iterator back to node
                    moveIteratorCursorTo(node, iter, false);
                }
            }
        }

        if (alignNode != null) {
            if ("center".equalsIgnoreCase(align)) {
                alignNode.reset("{align:align=center}", true);
                alignNode.getPair().reset("{align}", true);

            } else if ("right".equalsIgnoreCase(align)) {
                alignNode.reset("{align:align=right}", true);
                alignNode.getPair().reset("{align}", true);
            }
        }
    }

}

From source file:org.apache.fop.layoutmgr.inline.LineLayoutManager.java

/**
 * Add a line with inline content/* w w  w .  j  a  v  a  2 s .co m*/
 * @param context the context for adding areas
 * @param lbp the position for which the line is generated
 * @param isLastPosition true if this is the last position of this LM
 */
private void addInlineArea(LayoutContext context, LineBreakPosition lbp, boolean isLastPosition) {

    KnuthSequence seq = knuthParagraphs.get(lbp.parIndex);
    int startElementIndex = lbp.startIndex;
    int endElementIndex = lbp.getLeafPos();

    LineArea lineArea = new LineArea((lbp.getLeafPos() < seq.size() - 1 ? textAlignment : textAlignmentLast),
            lbp.difference, lbp.availableStretch, lbp.availableShrink);
    if (lbp.startIndent != 0) {
        lineArea.addTrait(Trait.START_INDENT, lbp.startIndent);
    }
    if (lbp.endIndent != 0) {
        lineArea.addTrait(Trait.END_INDENT, new Integer(lbp.endIndent));
    }
    lineArea.setBPD(lbp.lineHeight);
    lineArea.setIPD(lbp.lineWidth);
    lineArea.setBidiLevel(bidiLevel);
    lineArea.addTrait(Trait.SPACE_BEFORE, lbp.spaceBefore);
    lineArea.addTrait(Trait.SPACE_AFTER, lbp.spaceAfter);
    alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);

    if (seq instanceof Paragraph) {
        Paragraph currPar = (Paragraph) seq;
        // ignore the first elements added by the LineLayoutManager
        startElementIndex += (startElementIndex == 0) ? currPar.ignoreAtStart : 0;

        // if this is the last line area that for this paragraph,
        // ignore the last elements added by the LineLayoutManager and
        // subtract the last-line-end-indent from the area ipd
        if (endElementIndex == (currPar.size() - 1)) {
            endElementIndex -= currPar.ignoreAtEnd;
            lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
        }
    }

    // ignore the last element in the line if it is a KnuthGlue object
    ListIterator seqIterator = seq.listIterator(endElementIndex);
    KnuthElement lastElement = (KnuthElement) seqIterator.next();
    // the TLM which created the last KnuthElement in this line
    LayoutManager lastLM = lastElement.getLayoutManager();
    if (lastElement.isGlue()) {
        // Remove trailing spaces if allowed so
        if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED || whiteSpaceTreament == EN_IGNORE
                || whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) {
            endElementIndex--;
            // this returns the same KnuthElement
            seqIterator.previous();
            if (seqIterator.hasPrevious()) {
                lastLM = ((KnuthElement) seqIterator.previous()).getLayoutManager();
            }
        }
    }

    // Remove leading spaces if allowed so
    if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED || whiteSpaceTreament == EN_IGNORE
            || whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) {
        // ignore KnuthGlue and KnuthPenalty objects
        // at the beginning of the line
        seqIterator = seq.listIterator(startElementIndex);
        while (seqIterator.hasNext() && !((KnuthElement) seqIterator.next()).isBox()) {
            startElementIndex++;
        }
    }
    // Add the inline areas to lineArea
    PositionIterator inlinePosIter = new KnuthPossPosIter(seq, startElementIndex, endElementIndex + 1);

    LayoutContext lc = new LayoutContext(0);
    lc.setAlignmentContext(alignmentContext);
    lc.setSpaceAdjust(lbp.dAdjust);
    lc.setIPDAdjust(lbp.ipdAdjust);
    lc.setLeadingSpace(new SpaceSpecifier(true));
    lc.setTrailingSpace(new SpaceSpecifier(false));
    lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);

    setCurrentArea(lineArea);
    setChildContext(lc);
    LayoutManager childLM;
    while ((childLM = inlinePosIter.getNextChildLM()) != null) {
        lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
        childLM.addAreas(inlinePosIter, lc);
        lc.setLeadingSpace(lc.getTrailingSpace());
        lc.setTrailingSpace(new SpaceSpecifier(false));
    }

    // if display-align is distribute, add space after
    if (context.getSpaceAfter() > 0 && (!context.isLastArea() || !isLastPosition)) {
        lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
    }
    lineArea.finish();
    if (lineArea.getBidiLevel() >= 0) {
        BidiResolver.reorder(lineArea);
    }
    parentLayoutManager.addChildArea(lineArea);
}

From source file:org.apache.fop.layoutmgr.SpaceResolver.java

/**
 * Resolves unresolved elements applying the space resolution rules defined in 4.3.1.
 * @param elems the element list/*from  ww  w . j a  v  a 2  s .c  o  m*/
 */
public static void resolveElementList(List elems) {
    if (LOG.isTraceEnabled()) {
        LOG.trace(elems);
    }
    boolean first = true;
    boolean last = false;
    boolean skipNextElement = false;
    List unresolvedFirst = new java.util.ArrayList();
    List unresolvedSecond = new java.util.ArrayList();
    List currentGroup;
    ListIterator iter = elems.listIterator();
    while (iter.hasNext()) {
        ListElement el = (ListElement) iter.next();
        if (el.isUnresolvedElement()) {
            if (LOG.isTraceEnabled()) {
                LOG.trace("unresolved found: " + el + " " + first + "/" + last);
            }
            BreakElement breakPoss = null;
            //Clear temp lists
            unresolvedFirst.clear();
            unresolvedSecond.clear();
            //Collect groups
            if (el instanceof BreakElement) {
                breakPoss = (BreakElement) el;
                currentGroup = unresolvedSecond;
            } else {
                currentGroup = unresolvedFirst;
                currentGroup.add(el);
            }
            iter.remove();
            last = true;
            skipNextElement = true;
            while (iter.hasNext()) {
                el = (ListElement) iter.next();
                if (el instanceof BreakElement && breakPoss != null) {
                    skipNextElement = false;
                    last = false;
                    break;
                } else if (currentGroup == unresolvedFirst && (el instanceof BreakElement)) {
                    breakPoss = (BreakElement) el;
                    iter.remove();
                    currentGroup = unresolvedSecond;
                } else if (el.isUnresolvedElement()) {
                    currentGroup.add(el);
                    iter.remove();
                } else {
                    last = false;
                    break;
                }
            }
            //last = !iter.hasNext();
            if (breakPoss == null && unresolvedSecond.isEmpty() && !last) {
                LOG.trace("Swap first and second parts in no-break condition," + " second part is empty.");
                //The first list is reversed, so swap if this shouldn't happen
                List swapList = unresolvedSecond;
                unresolvedSecond = unresolvedFirst;
                unresolvedFirst = swapList;
            }

            LOG.debug("----start space resolution (first=" + first + ", last=" + last + ")...");
            SpaceResolver resolver = new SpaceResolver(unresolvedFirst, breakPoss, unresolvedSecond, first,
                    last);
            if (!last) {
                iter.previous();
            }
            resolver.generate(iter);
            if (!last && skipNextElement) {
                iter.next();
            }
            LOG.debug("----end space resolution.");
        }
        first = false;
    }
}

From source file:org.rifidi.emulator.reader.thingmagic.commandobjects.SetCommand.java

private void setUpStart(ListIterator<String> tokenIterator) throws CommandCreationException {
    /*/*from  w  ww  .j a v a  2s . c o m*/
     * we are expecting a "turn on" command
     */
    String token;
    do {

        token = tokenIterator.next();
        if (!token.matches(WHITE_SPACE)) {
            throw new CommandCreationException("Error 0100:     syntax error at '" + token + "'");
        }

        if (!tmsr.getCursorCommandRegistry().containsKey(token)) {
            throw new CommandCreationException("Error 0100:   Cursor does not exist");
        }

        cursorList.add(tmsr.getCursorCommandRegistry().get(token));

        token = tokenIterator.next();

    } while (token.matches(COMMA_WITH_WS));

    if (!token.matches(EQUALS_WITH_WS)) {
        throw new CommandCreationException("Error 0100:     syntax error at '" + token + "'");
    }

    token = tokenIterator.next();

    if (!token.equalsIgnoreCase("ON")) {
        throw new CommandCreationException("Error 0100:     syntax error at '" + token + "'");
    }

    token = tokenIterator.next();
    if (token.matches(COMMA_WITH_WS)) {

        token = tokenIterator.next();
        if (!token.equals("repeat")) {
            throw new CommandCreationException("Error 0100:     syntax error at '" + token + "'");
        }

        token = tokenIterator.next();

        if (token.matches(EQUALS_WITH_WS)) {
            throw new CommandCreationException("Error 0100:     syntax error at '" + token + "'");
        }

        token = tokenIterator.next();
        try {
            repeat = Long.parseLong(token);
        } catch (NumberFormatException e) {
            throw new CommandCreationException("Error 0100:     syntax error at '" + token + "'");
        }
    } else {
        /*
         * rewind one token.
         */
        tokenIterator.previous();
    }

    commandSwitch = ESetSubCommand.START;
}

From source file:net.sourceforge.fenixedu.domain.DegreeCurricularPlan.java

public ExecutionDegree getMostRecentExecutionDegree() {
    if (getExecutionDegreesSet().isEmpty()) {
        return null;
    }//from w ww  .  ja v a2  s.c o  m

    final ExecutionYear currentYear = ExecutionYear.readCurrentExecutionYear();
    ExecutionDegree result = getExecutionDegreeByYear(currentYear);
    if (result != null) {
        return result;
    }

    final List<ExecutionDegree> sorted = new ArrayList<ExecutionDegree>(getExecutionDegreesSet());
    Collections.sort(sorted, ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_YEAR);

    final ExecutionDegree first = sorted.iterator().next();
    if (sorted.size() == 1) {
        return first;
    }

    if (first.getExecutionYear().isAfter(currentYear)) {
        return first;
    } else {
        final ListIterator<ExecutionDegree> iter = sorted.listIterator(sorted.size());
        while (iter.hasPrevious()) {
            final ExecutionDegree executionDegree = iter.previous();
            if (executionDegree.getExecutionYear().isBeforeOrEquals(currentYear)) {
                return executionDegree;
            }
        }
    }

    return null;
}

From source file:org.trnltk.experiment.morphology.ambiguity.DataDiffUtil.java

/**
 * Reorder and merge like edit sections.  Merge equalities.
 * Any edit section can move as long as it doesn't cross an equality.
 *
 * @param diffs LinkedList of Diff objects.
 *//*from  w  w w . j a va  2  s .c om*/
public void diff_cleanupMerge(LinkedList<Diff<T>> diffs) {
    diffs.add(new Diff<T>(Operation.EQUAL, new ArrayList<T>())); // Add a dummy entry at the end.
    ListIterator<Diff<T>> pointer = diffs.listIterator();
    int count_delete = 0;
    int count_insert = 0;
    List<T> text_delete = new ArrayList<T>();
    List<T> text_insert = new ArrayList<T>();
    Diff thisDiff = pointer.next();
    Diff prevEqual = null;
    int commonlength;
    while (thisDiff != null) {
        switch (thisDiff.operation) {
        case INSERT:
            count_insert++;
            text_insert = ListUtils.union(text_insert, thisDiff.text);
            prevEqual = null;
            break;
        case DELETE:
            count_delete++;
            text_delete = ListUtils.union(text_delete, thisDiff.text);
            prevEqual = null;
            break;
        case EQUAL:
            if (count_delete + count_insert > 1) {
                boolean both_types = count_delete != 0 && count_insert != 0;
                // Delete the offending records.
                pointer.previous(); // Reverse direction.
                while (count_delete-- > 0) {
                    pointer.previous();
                    pointer.remove();
                }
                while (count_insert-- > 0) {
                    pointer.previous();
                    pointer.remove();
                }
                if (both_types) {
                    // Factor out any common prefixies.
                    commonlength = diff_commonPrefix(text_insert, text_delete);
                    if (commonlength != 0) {
                        if (pointer.hasPrevious()) {
                            thisDiff = pointer.previous();
                            assert thisDiff.operation == Operation.EQUAL : "Previous diff should have been an equality.";
                            thisDiff.text = ListUtils.union(thisDiff.text,
                                    text_insert.subList(0, commonlength));
                            pointer.next();
                        } else {
                            pointer.add(new Diff(Operation.EQUAL, text_insert.subList(0, commonlength)));
                        }
                        text_insert = text_insert.subList(commonlength, text_insert.size());
                        text_delete = text_delete.subList(commonlength, text_delete.size());
                    }
                    // Factor out any common suffixies.
                    commonlength = diff_commonSuffix(text_insert, text_delete);
                    if (commonlength != 0) {
                        thisDiff = pointer.next();
                        thisDiff.text = ListUtils.union(
                                text_insert.subList(text_insert.size() - commonlength, text_insert.size()),
                                thisDiff.text);
                        text_insert = text_insert.subList(0, text_insert.size() - commonlength);
                        text_delete = text_delete.subList(0, text_delete.size() - commonlength);
                        pointer.previous();
                    }
                }
                // Insert the merged records.
                if (text_delete.size() != 0) {
                    pointer.add(new Diff(Operation.DELETE, text_delete));
                }
                if (text_insert.size() != 0) {
                    pointer.add(new Diff(Operation.INSERT, text_insert));
                }
                // Step forward to the equality.
                thisDiff = pointer.hasNext() ? pointer.next() : null;
            } else if (prevEqual != null) {
                // Merge this equality with the previous one.
                prevEqual.text = ListUtils.union(prevEqual.text, thisDiff.text);
                pointer.remove();
                thisDiff = pointer.previous();
                pointer.next(); // Forward direction
            }
            count_insert = 0;
            count_delete = 0;
            text_delete = new ArrayList<T>();
            text_insert = new ArrayList<T>();
            prevEqual = thisDiff;
            break;
        }
        thisDiff = pointer.hasNext() ? pointer.next() : null;
    }
    if (diffs.getLast().text.size() == 0) {
        diffs.removeLast(); // Remove the dummy entry at the end.
    }

    /*
     * Second pass: look for single edits surrounded on both sides by equalities
     * which can be shifted sideways to eliminate an equality.
     * e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
     */
    boolean changes = false;
    // Create a new iterator at the start.
    // (As opposed to walking the current one back.)
    pointer = diffs.listIterator();
    Diff<T> prevDiff = pointer.hasNext() ? pointer.next() : null;
    thisDiff = pointer.hasNext() ? pointer.next() : null;
    Diff nextDiff = pointer.hasNext() ? pointer.next() : null;
    // Intentionally ignore the first and last element (don't need checking).
    while (nextDiff != null) {
        if (prevDiff.operation == Operation.EQUAL && nextDiff.operation == Operation.EQUAL) {
            // This is a single edit surrounded by equalities.
            if (endsWith(thisDiff.text, prevDiff.text)) {
                // Shift the edit over the previous equality.
                thisDiff.text = ListUtils.union(prevDiff.text,
                        thisDiff.text.subList(0, thisDiff.text.size() - prevDiff.text.size()));
                nextDiff.text = ListUtils.union(prevDiff.text, nextDiff.text);
                pointer.previous(); // Walk past nextDiff.
                pointer.previous(); // Walk past thisDiff.
                pointer.previous(); // Walk past prevDiff.
                pointer.remove(); // Delete prevDiff.
                pointer.next(); // Walk past thisDiff.
                thisDiff = pointer.next(); // Walk past nextDiff.
                nextDiff = pointer.hasNext() ? pointer.next() : null;
                changes = true;
            } else if (startsWith(thisDiff.text, nextDiff.text)) {
                // Shift the edit over the next equality.
                prevDiff.text = ListUtils.union(prevDiff.text, nextDiff.text);
                thisDiff.text = ListUtils.union(
                        thisDiff.text.subList(nextDiff.text.size(), thisDiff.text.size()), nextDiff.text);
                pointer.remove(); // Delete nextDiff.
                nextDiff = pointer.hasNext() ? pointer.next() : null;
                changes = true;
            }
        }
        prevDiff = thisDiff;
        thisDiff = nextDiff;
        nextDiff = pointer.hasNext() ? pointer.next() : null;
    }
    // If shifts were made, the diff needs reordering and another shift sweep.
    if (changes) {
        diff_cleanupMerge(diffs);
    }
}