-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Simplifies the extraction of elements of type Reshuffled using iterators
- Loading branch information
Showing
4 changed files
with
401 additions
and
18 deletions.
There are no files selected for viewing
126 changes: 126 additions & 0 deletions
126
.../src/test/java/com/github/javaparser/printer/lexicalpreservation/PeekingIteratorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package com.github.javaparser.printer.lexicalpreservation; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.NoSuchElementException; | ||
|
||
import org.junit.jupiter.api.*; | ||
|
||
class PeekingIteratorTest { | ||
|
||
private static final List<String> EMPTY_LIST = new ArrayList(); | ||
|
||
private static List<String> NON_EMPTY_LIST ; | ||
|
||
private PeekingIterator<String> peekingIterator; | ||
|
||
@BeforeAll | ||
static void setUpBeforeClass() throws Exception { | ||
} | ||
|
||
@AfterAll | ||
static void tearDownAfterClass() throws Exception { | ||
} | ||
|
||
@BeforeEach | ||
void setUp() throws Exception { | ||
NON_EMPTY_LIST = Arrays.asList("A", "B", "C"); | ||
} | ||
|
||
@AfterEach | ||
void tearDown() throws Exception { | ||
} | ||
|
||
@Test | ||
void testHasNext() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertFalse(peekingIterator.hasNext()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertTrue(peekingIterator.hasNext()); | ||
} | ||
|
||
@Test | ||
void testPeek() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertEquals(null, peekingIterator.peek()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertEquals("A", peekingIterator.peek()); | ||
assertEquals("A", peekingIterator.next()); | ||
} | ||
|
||
@Test | ||
void testElement() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertEquals(null, peekingIterator.peek()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertEquals("A", peekingIterator.peek()); | ||
assertEquals(1, peekingIterator.nextIndex()); | ||
} | ||
|
||
@Test | ||
void testNext() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertThrows(NoSuchElementException.class, () -> peekingIterator.next()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertEquals("A", peekingIterator.next()); | ||
} | ||
|
||
@Test | ||
void testRemove() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertThrows(IllegalStateException.class, () -> peekingIterator.remove()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertThrows(IllegalStateException.class, () -> peekingIterator.remove()); | ||
String result = peekingIterator.next(); | ||
assertThrows(UnsupportedOperationException.class, () -> peekingIterator.remove()); | ||
} | ||
|
||
@Test | ||
void testHasPrevious() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertFalse(peekingIterator.hasPrevious()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertFalse(peekingIterator.hasPrevious()); | ||
String result = peekingIterator.next(); | ||
assertTrue(peekingIterator.hasPrevious()); | ||
} | ||
|
||
@Test | ||
void testPrevious() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertThrows(NoSuchElementException.class, () -> peekingIterator.previous()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertThrows(NoSuchElementException.class, () -> peekingIterator.previous()); | ||
} | ||
|
||
@Test | ||
void testNextIndex() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertEquals(0, peekingIterator.nextIndex()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertEquals(0, peekingIterator.nextIndex()); | ||
} | ||
|
||
@Test | ||
void testPreviousIndex() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertEquals(-1, peekingIterator.previousIndex()); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertEquals(-1, peekingIterator.previousIndex()); | ||
} | ||
|
||
@Test | ||
void testSet() { | ||
peekingIterator = new PeekingIterator(EMPTY_LIST.listIterator()); | ||
assertThrows(IllegalStateException.class, () -> peekingIterator.set("D")); | ||
peekingIterator = new PeekingIterator(NON_EMPTY_LIST.listIterator()); | ||
assertThrows(IllegalStateException.class, () -> peekingIterator.set("D")); | ||
peekingIterator.next(); | ||
peekingIterator.set("D"); | ||
assertEquals(3, NON_EMPTY_LIST.size()); | ||
} | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
...re/src/main/java/com/github/javaparser/printer/lexicalpreservation/LookaheadIterator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.github.javaparser.printer.lexicalpreservation; | ||
|
||
import java.util.NoSuchElementException; | ||
|
||
public interface LookaheadIterator<E> { | ||
|
||
/** | ||
* Returns the next element in iteration without advancing the underlying iterator. | ||
* If the iterator is already exhausted, null will be returned. | ||
* <p> | ||
* Note: this method does not throw a {@link NoSuchElementException} if the iterator | ||
* is already exhausted. If you want such a behavior, use {@link #element()} instead. | ||
* <p> | ||
* The rationale behind this is to follow the {@link java.util.Queue} interface | ||
* which uses the same terminology. | ||
* | ||
* @return the next element from the iterator | ||
*/ | ||
public E peek(); | ||
|
||
/** | ||
* Returns the next element in iteration without advancing the underlying iterator. | ||
* If the iterator is already exhausted, null will be returned. | ||
* | ||
* @return the next element from the iterator | ||
* @throws NoSuchElementException if the iterator is already exhausted according to {@link #hasNext()} | ||
*/ | ||
public E element(); | ||
|
||
} |
223 changes: 223 additions & 0 deletions
223
...core/src/main/java/com/github/javaparser/printer/lexicalpreservation/PeekingIterator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
/* | ||
* Copyright (C) 2011, 2013-2023 The JavaParser Team. | ||
* | ||
* This file is part of JavaParser. | ||
* | ||
* JavaParser can be used either under the terms of | ||
* a) the GNU Lesser General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* b) the terms of the Apache License | ||
* | ||
* You should have received a copy of both licenses in LICENCE.LGPL and | ||
* LICENCE.APACHE. Please refer to those files for details. | ||
* | ||
* JavaParser is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Lesser General Public License for more details. | ||
*/ | ||
package com.github.javaparser.printer.lexicalpreservation; | ||
|
||
import java.util.List; | ||
import java.util.ListIterator; | ||
import java.util.NoSuchElementException; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Decorates an iterator to support one-element lookahead while iterating. | ||
* <p> | ||
* The decorator supports the removal operation, but an {@link IllegalStateException} | ||
* will be thrown if {@link #remove()}, {@link #add()}, {@link #set()}} is called directly after a call to | ||
* {@link #peek()} or {@link #element()}. | ||
* | ||
* @param <E> the type of elements returned by this iterator. | ||
* @since 4.0 | ||
*/ | ||
public class PeekingIterator<E> implements ListIterator<E>, LookaheadIterator<E> { | ||
|
||
/** The iterator being decorated. */ | ||
private final ListIterator<E> iterator; | ||
|
||
/** Indicates that the decorated iterator is exhausted. */ | ||
private boolean exhausted; | ||
|
||
/** Indicates if the lookahead slot is filled. */ | ||
private boolean slotFilled; | ||
|
||
/** The current slot for lookahead. */ | ||
private E slot; | ||
|
||
/** | ||
* Decorates the specified iterator to support one-element lookahead. | ||
* <p> | ||
* If the iterator is already a {@link PeekingIterator} it is returned directly. | ||
* | ||
* @param <E> the element type | ||
* @param iterator the iterator to decorate | ||
* @return a new peeking iterator | ||
* @throws NullPointerException if the iterator is null | ||
*/ | ||
public <E> PeekingIterator<E> peekingIterator(final ListIterator<E> iterator) { | ||
Objects.requireNonNull(iterator, "iterator"); | ||
if (iterator instanceof PeekingIterator<?>) { | ||
final PeekingIterator<E> it = (PeekingIterator<E>) iterator; | ||
return it; | ||
} | ||
return new PeekingIterator<>(iterator); | ||
} | ||
|
||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param iterator the iterator to decorate | ||
*/ | ||
public PeekingIterator(final ListIterator<E> iterator) { | ||
this.iterator = iterator; | ||
} | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param list the provider of the iterator to decorate | ||
*/ | ||
public PeekingIterator(final List<E> list) { | ||
this.iterator = list.listIterator(); | ||
} | ||
|
||
private void fill() { | ||
if (exhausted || slotFilled) { | ||
return; | ||
} | ||
if (iterator.hasNext()) { | ||
slot = iterator.next(); | ||
slotFilled = true; | ||
} else { | ||
exhausted = true; | ||
slot = null; | ||
slotFilled = false; | ||
} | ||
} | ||
|
||
@Override | ||
public boolean hasNext() { | ||
if (exhausted) { | ||
return false; | ||
} | ||
return slotFilled || iterator.hasNext(); | ||
} | ||
|
||
/** | ||
* Returns the next element in iteration without advancing the underlying iterator. | ||
* If the iterator is already exhausted, null will be returned. | ||
* <p> | ||
* Note: this method does not throw a {@link NoSuchElementException} if the iterator | ||
* is already exhausted. If you want such a behavior, use {@link #element()} instead. | ||
* <p> | ||
* The rationale behind this is to follow the {@link java.util.Queue} interface | ||
* which uses the same terminology. | ||
* | ||
* @return the next element from the iterator | ||
*/ | ||
@Override | ||
public E peek() { | ||
fill(); | ||
return exhausted ? null : slot; | ||
} | ||
|
||
/** | ||
* Returns the next element in iteration without advancing the underlying iterator. | ||
* If the iterator is already exhausted, null will be returned. | ||
* | ||
* @return the next element from the iterator | ||
* @throws NoSuchElementException if the iterator is already exhausted according to {@link #hasNext()} | ||
*/ | ||
@Override | ||
public E element() { | ||
fill(); | ||
if (exhausted) { | ||
throw new NoSuchElementException(); | ||
} | ||
return slot; | ||
} | ||
|
||
@Override | ||
public E next() { | ||
if (!hasNext()) { | ||
throw new NoSuchElementException(); | ||
} | ||
final E x = slotFilled ? slot : iterator.next(); | ||
// reset the lookahead slot | ||
slot = null; | ||
slotFilled = false; | ||
return x; | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws IllegalStateException if {@link #peek()} or {@link #element()} has been called | ||
* prior to the call to {@link #remove()} | ||
*/ | ||
@Override | ||
public void remove() { | ||
if (slotFilled) { | ||
throw new IllegalStateException("peek() or element() called before remove()"); | ||
} | ||
iterator.remove(); | ||
} | ||
|
||
|
||
@Override | ||
public boolean hasPrevious() { | ||
return iterator.hasPrevious(); | ||
} | ||
|
||
|
||
@Override | ||
public E previous() { | ||
return iterator.previous(); | ||
} | ||
|
||
|
||
@Override | ||
public int nextIndex() { | ||
return iterator.nextIndex(); | ||
} | ||
|
||
/* | ||
* Returns the index of the element that would be returned by the last call to next. | ||
* Returns list size - 1 if the listiterator is at the end of the list. | ||
* Returns -1 if the listiterator is at the beginning of the list. | ||
*/ | ||
public int currentIndex() { | ||
if (!hasPrevious()) return previousIndex(); | ||
return nextIndex() - 1; | ||
} | ||
|
||
|
||
@Override | ||
public int previousIndex() { | ||
return iterator.previousIndex(); | ||
} | ||
|
||
|
||
@Override | ||
public void set(E e) { | ||
if (slotFilled) { | ||
throw new IllegalStateException("peek() or element() called before set()"); | ||
} | ||
iterator.set(e); | ||
} | ||
|
||
|
||
@Override | ||
public void add(E e) { | ||
if (slotFilled) { | ||
throw new IllegalStateException("peek() or element() called before add()"); | ||
} | ||
iterator.add(e); | ||
} | ||
|
||
} |
Oops, something went wrong.