/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.containers;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.Functions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Iterator that accumulates transformations and filters keeping its instance.
* So JBIterable#filter() and JBIterable#transform() preserve the underlying iterator API.
*
* <h3>Supported contracts:</h3>
* <ul>
* <li>Classic iterator: hasNext() / next()</li>
* <li>Cursor: advance() / current()</li>
* <li>One-time iterable: cursor()</li>
* </ul>
*
* Implementors should provide nextImpl() method which can call stop()/skip().
*
* @see JBIterable#transform(Function)
* @see JBIterable#filter(Condition)
* @see TreeTraversal.TracingIt
*
* @author gregsh
*
* @noinspection unchecked, AssignmentToForLoopParameter
*/
public abstract class JBIterator<E> implements Iterator<E> {
private static final Object NONE = new String("#none");
private static final Object STOP = new String("#stop");
private static final Object SKIP = new String("#skip");
@NotNull
public static <E extends JBIterator<?>> JBIterable<E> cursor(@NotNull E iterator) {
return JBIterable.generate(iterator, Functions.<E, E>identity()).takeWhile(ADVANCE);
}
@NotNull
public static <E> JBIterator<E> from(@NotNull final Iterator<E> it) {
return it instanceof JBIterator ? (JBIterator<E>)it : wrap(it);
}
@NotNull
static <E> JBIterator<E> wrap(@NotNull final Iterator<E> it) {
return new JBIterator<E>() {
@Override
protected E nextImpl() {
return it.hasNext() ? it.next() : stop();
}
};
}
private Object myCurrent = NONE;
private Object myNext = NONE;
private Op myFirstOp = new Op(null);
private Op myLastOp = myFirstOp;
/**
* Returns the next element if any; otherwise calls stop() or skip().
*/
protected abstract E nextImpl();
/**
* Called right after the new current value is set.
*/
protected void currentChanged() { }
/**
* Notifies the iterator that there's no more elements.
*/
@Nullable
protected final E stop() {
myNext = STOP;
return null;
}
/**
* Notifies the iterator to skip and re-invoke nextImpl().
*/
@Nullable
protected final E skip() {
myNext = SKIP;
return null;
}
@Override
public final boolean hasNext() {
peekNext();
return myNext != STOP;
}
@Nullable
@Override
public final E next() {
advance();
return current();
}
/**
* Proceeds to the next element if any and returns true; otherwise false.
*/
public final boolean advance() {
myCurrent = NONE;
peekNext();
if (myNext == STOP) return false;
myCurrent = myNext;
myNext = NONE;
currentChanged();
return true;
}
/**
* Returns the current element if any; otherwise throws exception.
*/
@Nullable
public final E current() {
if (myCurrent == NONE) throw new NoSuchElementException();
return (E)myCurrent;
}
private void peekNext() {
if (myNext != NONE) return;
Object o = NONE;
for (Op op = myFirstOp; op != null; op = op == null ? myFirstOp : op.nextOp) {
o = op.impl == null ? nextImpl() : op.apply(o);
if (myNext == SKIP) {
o = myNext = NONE;
op = null;
}
if (myNext == STOP) return;
}
myNext = o;
}
@NotNull
public final <T> JBIterator<T> transform(@NotNull Function<? super E, T> function) {
return addOp(true, new TransformOp<E, T>(function));
}
@NotNull
public final JBIterator<E> filter(@NotNull Condition<? super E> condition) {
return addOp(true, new FilterOp<E>(condition));
}
@NotNull
public final JBIterator<E> take(int count) {
// add first so that the underlying iterator stay on 'count' position
return addOp(myLastOp.impl != null, new WhileOp<E>(new CountDown<E>(count)));
}
@NotNull
public final JBIterator<E> takeWhile(@NotNull Condition<? super E> condition) {
return addOp(true, new WhileOp<E>(condition));
}
@NotNull
public final JBIterator<E> skip(int count) {
return skipWhile(new CountDown<E>(count));
}
@NotNull
public final JBIterator<E> skipWhile(@NotNull final Condition<? super E> condition) {
return addOp(true, new SkipOp<E>(condition));
}
@NotNull
private <T> T addOp(boolean last, @NotNull Op op) {
if (last) {
myLastOp.nextOp = op;
myLastOp = myLastOp.nextOp;
}
else {
op.nextOp = myFirstOp;
myFirstOp = op;
}
return (T)this;
}
@Override
public final void remove() {
throw new UnsupportedOperationException();
}
@NotNull
public final List<E> toList() {
return Collections.unmodifiableList(ContainerUtil.newArrayList(JBIterable.once(this)));
}
@Override
public String toString() {
JBIterable<Op> ops = operationsImpl();
return "{cur=" + myCurrent + "; next=" + myNext + (ops.isEmpty() ? "" : "; ops[" + ops.size() + "]=" + ops) + "}";
}
@NotNull
public final JBIterable<Function<Object, Object>> getTransformations() {
return (JBIterable<Function<Object, Object>>)(JBIterable)operationsImpl().transform(new Function<Op, Object>() {
@Override
public Object fun(Op op) {
return op.impl;
}
}).filter(Function.class);
}
@NotNull
private JBIterable<Op> operationsImpl() {
return JBIterable.generate(myFirstOp.nextOp, new Function<Op, Op>() {
@Override
public Op fun(Op op) {
return op.nextOp;
}
});
}
static String toShortString(@NotNull Object o) {
String fqn = o.getClass().getName();
return StringUtil.replace(o.toString(), fqn, StringUtil.getShortName(fqn, '.'));
}
private static final Condition<JBIterator<?>> ADVANCE = new Condition<JBIterator<?>>() {
@Override
public boolean value(JBIterator<?> it) {
return it.advance();
}
};
private static class Op<T> {
final T impl;
Op nextOp;
public Op(T impl) {
this.impl = impl;
}
Object apply(Object o) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return impl == null ? "" : toShortString(impl);
}
}
private static class CountDown<A> implements Condition<A> {
int cur;
public CountDown(int count) {
cur = count;
}
@Override
public boolean value(A a) {
return cur > 0 && cur-- != 0;
}
}
private static class TransformOp<E, T> extends Op<Function<? super E, T>> {
TransformOp(Function<? super E, T> function) {
super(function);
}
@Override
public Object apply(Object o) {
return impl.fun((E)o);
}
}
private class FilterOp<E> extends Op<Condition<? super E>> {
FilterOp(Condition<? super E> condition) {
super(condition);
}
@Override
public Object apply(Object o) {
return impl.value((E)o) ? o : skip();
}
}
private class WhileOp<E> extends Op<Condition<? super E>> {
WhileOp(Condition<? super E> condition) {
super(condition);
}
@Override
public Object apply(Object o) {
return impl.value((E)o) ? o : stop();
}
}
private class SkipOp<E> extends Op<Condition<? super E>> {
boolean active;
SkipOp(Condition<? super E> condition) {
super(condition);
active = true;
}
@Override
public Object apply(Object o) {
if (active && impl.value((E)o)) return skip();
active = false;
return o;
}
}
}