Skip to content

Commit

Permalink
PathContainer#clearAndShowPath, History#peek(int)
Browse files Browse the repository at this point in the history
  • Loading branch information
rjrjr committed Sep 9, 2015
1 parent 7ca3c0d commit da26647
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 35 deletions.
29 changes: 26 additions & 3 deletions flow-path/src/main/java/flow/path/PathContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
*/
public abstract class PathContainer {

private static final ViewState NULL_VIEW_STATE = new NullViewState();

/**
* Provides information about the current or most recent Traversal handled by the container.
*/
Expand Down Expand Up @@ -68,9 +70,22 @@ protected PathContainer(int tagKey) {

public final void executeTraversal(PathContainerView view, Flow.Traversal traversal,
final Flow.TraversalCallback callback) {
final View oldChild = view.getCurrentChild();
Path path = traversal.destination.top();
ViewState viewState = traversal.destination.currentViewState();
doShowPath(view, traversal.destination.<Path>top(), traversal.direction, viewState, callback);
}

/**
* Replace the current view and show the given path. Allows display of {@link Path}s other
* than in response to Flow dispatches.
*/
public void setPath(PathContainerView view, Path path, Flow.Direction direction,
Flow.TraversalCallback callback) {
doShowPath(view, path, direction, NULL_VIEW_STATE, callback);
}

private void doShowPath(PathContainerView view, Path path, Flow.Direction direction,
ViewState viewState, Flow.TraversalCallback callback) {
final View oldChild = view.getCurrentChild();
Path oldPath;
ViewGroup containerView = view.getContainerView();
TraversalState traversalState = ensureTag(containerView);
Expand All @@ -86,7 +101,7 @@ public final void executeTraversal(PathContainerView view, Flow.Traversal traver
}

traversalState.setNextEntry(path, viewState);
performTraversal(containerView, traversalState, traversal.direction, callback);
performTraversal(containerView, traversalState, direction, callback);
}

protected abstract void performTraversal(ViewGroup container, TraversalState traversalState,
Expand All @@ -100,4 +115,12 @@ private TraversalState ensureTag(ViewGroup container) {
}
return traversalState;
}

private static final class NullViewState implements ViewState {
@Override public void save(View view) {
}

@Override public void restore(View view) {
}
}
}
84 changes: 52 additions & 32 deletions flow/src/main/java/flow/History.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
import android.os.Parcelable;
import android.util.SparseArray;
import android.view.View;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

import static flow.Preconditions.checkArgument;
Expand All @@ -43,7 +43,7 @@ public interface Filter {
public static History from(Parcelable parcelable, StateParceler parceler) {
Bundle bundle = (Bundle) parcelable; // TODO(loganj): assert/throw
ArrayList<Bundle> entryBundles = bundle.getParcelableArrayList("ENTRIES");
Deque<Entry> entries = new ArrayDeque<>(entryBundles.size());
List<Entry> entries = new ArrayList<>(entryBundles.size());
for (Bundle entryBundle : entryBundles) {
Object object = parceler.unwrap(entryBundle.getParcelable("OBJECT"));
Entry entry = new Entry(object);
Expand All @@ -53,14 +53,14 @@ public static History from(Parcelable parcelable, StateParceler parceler) {
return new History(entries);
}

private final Deque<Entry> history;
private final List<Entry> history;

/** Get a {@link Parcelable} of this history using the supplied {@link StateParceler}. */
public Parcelable getParcelable(StateParceler parceler) {
Bundle historyBundle = new Bundle();
ArrayList<Bundle> entryBundles = new ArrayList<>(history.size());
for (Entry entry : history) {
entryBundles.add(entry.getBundle(parceler));
entryBundles.add(entry.getBundle(parceler));
}
historyBundle.putParcelableArrayList("ENTRIES", entryBundles);
return historyBundle;
Expand All @@ -77,9 +77,9 @@ public Parcelable getParcelable(StateParceler parceler) {
public Parcelable getParcelable(StateParceler parceler, Filter filter) {
Bundle historyBundle = new Bundle();
ArrayList<Bundle> entryBundles = new ArrayList<>(history.size());
Iterator<Entry> it = history.descendingIterator();
while (it.hasNext()) {
Entry entry = it.next();
ListIterator<Entry> it = history.listIterator();
while (it.hasPrevious()) {
Entry entry = it.previous();
if (filter.apply(entry.state)) {
entryBundles.add(entry.getBundle(parceler));
}
Expand All @@ -101,18 +101,17 @@ public static History single(Object object) {
return emptyBuilder().push(object).build();
}

private History(Deque<Entry> history) {
private History(List<Entry> history) {
checkArgument(history != null && !history.isEmpty(), "History may not be empty");
this.history = history;
}

@SuppressWarnings("UnusedDeclaration")
@Override public Iterator<Object> iterator() {
public <T> Iterator<T> reverseIterator() {
return new ReadIterator<>(history.iterator());
}

public <T> Iterator<T> reverseIterator() {
return new ReadIterator<>(history.descendingIterator());
@SuppressWarnings("UnusedDeclaration") @Override public Iterator<Object> iterator() {
return new ReadIterator<>(new ReverseIterator<>(history));
}

public int size() {
Expand All @@ -121,20 +120,26 @@ public int size() {

public <T> T top() {
//noinspection unchecked
return (T) history.peek().state;
return (T) peek(0);
}

public <T> T peek(int index) {
//noinspection unchecked
return history.isEmpty() ? null : (T) history.get(history.size() - index - 1).state;
}

public ViewState currentViewState() {
return history.peek();
return history.isEmpty() ? null : history.get(history.size() - 1);
}

/**
* Get a builder to modify a copy of this history.
*
* The builder returned will retain all internal information related to the states in the history,
* The builder returned will retain all internal information related to the states in the
* history,
* including any associated View state. It is safe to remove states from the builder and push
* them back on; nothing will be lost in those operations.
* */
*/
public Builder buildUpon() {
return new Builder(history);
}
Expand Down Expand Up @@ -170,32 +175,28 @@ Bundle getBundle(StateParceler parceler) {
return bundle;
}

@Override
public boolean equals(Object o) {
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entry entry = (Entry) o;
return (state.equals(entry.state));
}

@Override
public int hashCode() {
@Override public int hashCode() {
return state.hashCode();
}
}

public static final class Builder {
private final Deque<Entry> history;
private final List<Entry> history;
private final Map<Object, Entry> entryMemory = new LinkedHashMap<>();

private Builder(Collection<Entry> history) {
this.history = new ArrayDeque<>(history);
this.history = new ArrayList<>(history);
}

public Builder clear() {
while (!history.isEmpty()) {
pop();
}
history.clear();
return this;
}

Expand All @@ -204,12 +205,12 @@ public Builder push(Object object) {
if (entry == null) {
entry = new Entry(object);
}
history.push(entry);
history.add(entry);
entryMemory.remove(object);
return this;
}

public Builder addAll(Collection<Object> c) {
public Builder addAll(Collection<?> c) {
for (Object object : c) {
push(object);
}
Expand All @@ -218,20 +219,19 @@ public Builder addAll(Collection<Object> c) {
}

public Object peek() {
final Entry peek = history.peek();
return peek == null ? null : peek.state;
return history.isEmpty() ? null : history.get(history.size() - 1).state;
}

public boolean isEmpty() {
return peek() == null;
return history.isEmpty();
}

/** @throws IllegalStateException if empty */
public Object pop() {
if (isEmpty()) {
throw new IllegalStateException("Cannot pop from an empty builder");
}
Entry entry = history.pop();
Entry entry = history.remove(history.size() - 1);
entryMemory.put(entry.state, entry);
return entry.state;
}
Expand All @@ -241,6 +241,26 @@ public History build() {
}
}

public static class ReverseIterator<T> implements Iterator<T> {
private final ListIterator<T> wrapped;

public ReverseIterator(List<T> list) {
wrapped = list.listIterator(list.size());
}

@Override public boolean hasNext() {
return wrapped.hasPrevious();
}

@Override public T next() {
return wrapped.previous();
}

@Override public void remove() {
wrapped.remove();
}
}

private static class ReadIterator<T> implements Iterator<T> {
private final Iterator<Entry> iterator;

Expand Down
8 changes: 8 additions & 0 deletions flow/src/test/java/flow/FlowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package flow;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -310,4 +311,11 @@ public int hashCode() {
builder.pop();
assertThat(builder.isEmpty()).isTrue();
}

@Test public void historyIndexAccess() {
History history = History.emptyBuilder().addAll(asList("able", "baker", "charlie")).build();
assertThat(history.peek(0)).isEqualTo("charlie");
assertThat(history.peek(1)).isEqualTo("baker");
assertThat(history.peek(2)).isEqualTo("able");
}
}

0 comments on commit da26647

Please sign in to comment.