1
2
3
4
5
6
7
8
9 package jline.console.history;
10
11 import java.util.Iterator;
12 import java.util.LinkedList;
13 import java.util.ListIterator;
14 import java.util.NoSuchElementException;
15
16 import static jline.internal.Preconditions.checkNotNull;
17
18
19
20
21
22
23
24
25 public class MemoryHistory
26 implements History
27 {
28 public static final int DEFAULT_MAX_SIZE = 500;
29
30 private final LinkedList<CharSequence> items = new LinkedList<CharSequence>();
31
32 private int maxSize = DEFAULT_MAX_SIZE;
33
34 private boolean ignoreDuplicates = true;
35
36 private boolean autoTrim = false;
37
38
39
40
41
42
43
44
45
46
47
48 private int offset = 0;
49
50 private int index = 0;
51
52 public void setMaxSize(final int maxSize) {
53 this.maxSize = maxSize;
54 maybeResize();
55 }
56
57 public int getMaxSize() {
58 return maxSize;
59 }
60
61 public boolean isIgnoreDuplicates() {
62 return ignoreDuplicates;
63 }
64
65 public void setIgnoreDuplicates(final boolean flag) {
66 this.ignoreDuplicates = flag;
67 }
68
69 public boolean isAutoTrim() {
70 return autoTrim;
71 }
72
73 public void setAutoTrim(final boolean flag) {
74 this.autoTrim = flag;
75 }
76
77 public int size() {
78 return items.size();
79 }
80
81 public boolean isEmpty() {
82 return items.isEmpty();
83 }
84
85 public int index() {
86 return offset + index;
87 }
88
89 public void clear() {
90 items.clear();
91 offset = 0;
92 index = 0;
93 }
94
95 public CharSequence get(final int index) {
96 return items.get(index - offset);
97 }
98
99 public void set(int index, CharSequence item) {
100 items.set(index - offset, item);
101 }
102
103 public void add(CharSequence item) {
104 checkNotNull(item);
105
106 if (isAutoTrim()) {
107 item = String.valueOf(item).trim();
108 }
109
110 if (isIgnoreDuplicates()) {
111 if (!items.isEmpty() && item.equals(items.getLast())) {
112 return;
113 }
114 }
115
116 internalAdd(item);
117 }
118
119 public CharSequence remove(int i) {
120 return items.remove(i);
121 }
122
123 public CharSequence removeFirst() {
124 return items.removeFirst();
125 }
126
127 public CharSequence removeLast() {
128 return items.removeLast();
129 }
130
131 protected void internalAdd(CharSequence item) {
132 items.add(item);
133
134 maybeResize();
135 }
136
137 public void replace(final CharSequence item) {
138 items.removeLast();
139 add(item);
140 }
141
142 private void maybeResize() {
143 while (size() > getMaxSize()) {
144 items.removeFirst();
145 offset++;
146 }
147
148 index = size();
149 }
150
151 public ListIterator<Entry> entries(final int index) {
152 return new EntriesIterator(index - offset);
153 }
154
155 public ListIterator<Entry> entries() {
156 return entries(offset);
157 }
158
159 public Iterator<Entry> iterator() {
160 return entries();
161 }
162
163 private static class EntryImpl
164 implements Entry
165 {
166 private final int index;
167
168 private final CharSequence value;
169
170 public EntryImpl(int index, CharSequence value) {
171 this.index = index;
172 this.value = value;
173 }
174
175 public int index() {
176 return index;
177 }
178
179 public CharSequence value() {
180 return value;
181 }
182
183 @Override
184 public String toString() {
185 return String.format("%d: %s", index, value);
186 }
187 }
188
189 private class EntriesIterator
190 implements ListIterator<Entry>
191 {
192 private final ListIterator<CharSequence> source;
193
194 private EntriesIterator(final int index) {
195 source = items.listIterator(index);
196 }
197
198 public Entry next() {
199 if (!source.hasNext()) {
200 throw new NoSuchElementException();
201 }
202 return new EntryImpl(offset + source.nextIndex(), source.next());
203 }
204
205 public Entry previous() {
206 if (!source.hasPrevious()) {
207 throw new NoSuchElementException();
208 }
209 return new EntryImpl(offset + source.previousIndex(), source.previous());
210 }
211
212 public int nextIndex() {
213 return offset + source.nextIndex();
214 }
215
216 public int previousIndex() {
217 return offset + source.previousIndex();
218 }
219
220 public boolean hasNext() {
221 return source.hasNext();
222 }
223
224 public boolean hasPrevious() {
225 return source.hasPrevious();
226 }
227
228 public void remove() {
229 throw new UnsupportedOperationException();
230 }
231
232 public void set(final Entry entry) {
233 throw new UnsupportedOperationException();
234 }
235
236 public void add(final Entry entry) {
237 throw new UnsupportedOperationException();
238 }
239 }
240
241
242
243
244
245
246
247
248
249
250
251
252 public boolean moveToLast() {
253 int lastEntry = size() - 1;
254 if (lastEntry >= 0 && lastEntry != index) {
255 index = size() - 1;
256 return true;
257 }
258
259 return false;
260 }
261
262
263
264
265
266
267 public boolean moveTo(int index) {
268 index -= offset;
269 if (index >= 0 && index < size() ) {
270 this.index = index;
271 return true;
272 }
273 return false;
274 }
275
276
277
278
279
280
281
282 public boolean moveToFirst() {
283 if (size() > 0 && index != 0) {
284 index = 0;
285 return true;
286 }
287
288 return false;
289 }
290
291
292
293
294
295 public void moveToEnd() {
296 index = size();
297 }
298
299
300
301
302 public CharSequence current() {
303 if (index >= size()) {
304 return "";
305 }
306
307 return items.get(index);
308 }
309
310
311
312
313
314
315 public boolean previous() {
316 if (index <= 0) {
317 return false;
318 }
319
320 index--;
321
322 return true;
323 }
324
325
326
327
328
329
330 public boolean next() {
331 if (index >= size()) {
332 return false;
333 }
334
335 index++;
336
337 return true;
338 }
339
340 @Override
341 public String toString() {
342 StringBuilder sb = new StringBuilder();
343 for (Entry e : this) {
344 sb.append(e.toString() + "\n");
345 }
346 return sb.toString();
347 }
348 }