--- /dev/null 2015-04-26 06:51:08.003313989 -0700 +++ new/jdk/src/jdk.internal.le/share/classes/jdk/internal/jline/console/history/MemoryHistory.java 2015-06-25 08:33:13.689391663 -0700 @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2002-2012, the original author or authors. + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * http://www.opensource.org/licenses/bsd-license.php + */ +package jdk.internal.jline.console.history; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import static jdk.internal.jline.internal.Preconditions.checkNotNull; + +/** + * Non-persistent {@link History}. + * + * @author Marc Prud'hommeaux + * @author Jason Dillon + * @since 2.3 + */ +public class MemoryHistory + implements History +{ + public static final int DEFAULT_MAX_SIZE = 500; + + private final LinkedList items = new LinkedList(); + + private int maxSize = DEFAULT_MAX_SIZE; + + private boolean ignoreDuplicates = true; + + private boolean autoTrim = false; + + // NOTE: These are all ideas from looking at the Bash man page: + + // TODO: Add ignore space? (lines starting with a space are ignored) + + // TODO: Add ignore patterns? + + // TODO: Add history timestamp? + + // TODO: Add erase dups? + + private int offset = 0; + + private int index = 0; + + public void setMaxSize(final int maxSize) { + this.maxSize = maxSize; + maybeResize(); + } + + public int getMaxSize() { + return maxSize; + } + + public boolean isIgnoreDuplicates() { + return ignoreDuplicates; + } + + public void setIgnoreDuplicates(final boolean flag) { + this.ignoreDuplicates = flag; + } + + public boolean isAutoTrim() { + return autoTrim; + } + + public void setAutoTrim(final boolean flag) { + this.autoTrim = flag; + } + + public int size() { + return items.size(); + } + + public boolean isEmpty() { + return items.isEmpty(); + } + + public int index() { + return offset + index; + } + + public void clear() { + items.clear(); + offset = 0; + index = 0; + } + + public CharSequence get(final int index) { + return items.get(index - offset); + } + + public void set(int index, CharSequence item) { + items.set(index - offset, item); + } + + public void add(CharSequence item) { + checkNotNull(item); + + if (isAutoTrim()) { + item = String.valueOf(item).trim(); + } + + if (isIgnoreDuplicates()) { + if (!items.isEmpty() && item.equals(items.getLast())) { + return; + } + } + + internalAdd(item); + } + + public CharSequence remove(int i) { + return items.remove(i); + } + + public CharSequence removeFirst() { + return items.removeFirst(); + } + + public CharSequence removeLast() { + return items.removeLast(); + } + + protected void internalAdd(CharSequence item) { + items.add(item); + + maybeResize(); + } + + public void replace(final CharSequence item) { + items.removeLast(); + add(item); + } + + private void maybeResize() { + while (size() > getMaxSize()) { + items.removeFirst(); + offset++; + } + + index = size(); + } + + public ListIterator entries(final int index) { + return new EntriesIterator(index - offset); + } + + public ListIterator entries() { + return entries(offset); + } + + public Iterator iterator() { + return entries(); + } + + private static class EntryImpl + implements Entry + { + private final int index; + + private final CharSequence value; + + public EntryImpl(int index, CharSequence value) { + this.index = index; + this.value = value; + } + + public int index() { + return index; + } + + public CharSequence value() { + return value; + } + + @Override + public String toString() { + return String.format("%d: %s", index, value); + } + } + + private class EntriesIterator + implements ListIterator + { + private final ListIterator source; + + private EntriesIterator(final int index) { + source = items.listIterator(index); + } + + public Entry next() { + if (!source.hasNext()) { + throw new NoSuchElementException(); + } + return new EntryImpl(offset + source.nextIndex(), source.next()); + } + + public Entry previous() { + if (!source.hasPrevious()) { + throw new NoSuchElementException(); + } + return new EntryImpl(offset + source.previousIndex(), source.previous()); + } + + public int nextIndex() { + return offset + source.nextIndex(); + } + + public int previousIndex() { + return offset + source.previousIndex(); + } + + public boolean hasNext() { + return source.hasNext(); + } + + public boolean hasPrevious() { + return source.hasPrevious(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public void set(final Entry entry) { + throw new UnsupportedOperationException(); + } + + public void add(final Entry entry) { + throw new UnsupportedOperationException(); + } + } + + // + // Navigation + // + + /** + * This moves the history to the last entry. This entry is one position + * before the moveToEnd() position. + * + * @return Returns false if there were no history entries or the history + * index was already at the last entry. + */ + public boolean moveToLast() { + int lastEntry = size() - 1; + if (lastEntry >= 0 && lastEntry != index) { + index = size() - 1; + return true; + } + + return false; + } + + /** + * Move to the specified index in the history + * @param index + * @return + */ + public boolean moveTo(int index) { + index -= offset; + if (index >= 0 && index < size() ) { + this.index = index; + return true; + } + return false; + } + + /** + * Moves the history index to the first entry. + * + * @return Return false if there are no entries in the history or if the + * history is already at the beginning. + */ + public boolean moveToFirst() { + if (size() > 0 && index != 0) { + index = 0; + return true; + } + + return false; + } + + /** + * Move to the end of the history buffer. This will be a blank entry, after + * all of the other entries. + */ + public void moveToEnd() { + index = size(); + } + + /** + * Return the content of the current buffer. + */ + public CharSequence current() { + if (index >= size()) { + return ""; + } + + return items.get(index); + } + + /** + * Move the pointer to the previous element in the buffer. + * + * @return true if we successfully went to the previous element + */ + public boolean previous() { + if (index <= 0) { + return false; + } + + index--; + + return true; + } + + /** + * Move the pointer to the next element in the buffer. + * + * @return true if we successfully went to the next element + */ + public boolean next() { + if (index >= size()) { + return false; + } + + index++; + + return true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Entry e : this) { + sb.append(e.toString() + "\n"); + } + return sb.toString(); + } +}