1/*
2 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
3 *
4 * This software is distributable under the BSD license. See the terms of the
5 * BSD license in the documentation provided with this software.
6 */
7package jline;
8
9import java.io.*;
10import java.util.*;
11
12/**
13 * A command history buffer.
14 *
15 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
16 */
17public class History {
18
19    private List history = new ArrayList();
20    private PrintWriter output = null;
21    private int maxSize = 500;
22    private int currentIndex = 0;
23
24    /**
25     * Construstor: initialize a blank history.
26     */
27    public History() {
28    }
29
30    /**
31     * Construstor: initialize History object the the specified {@link File} for
32     * storage.
33     */
34    public History(final File historyFile) throws IOException {
35        setHistoryFile(historyFile);
36    }
37
38    public void setHistoryFile(final File historyFile) throws IOException {
39        if (historyFile.isFile()) {
40            load(new FileInputStream(historyFile));
41        }
42
43        setOutput(new PrintWriter(new FileWriter(historyFile), true));
44        flushBuffer();
45    }
46
47    /**
48     * Load the history buffer from the specified InputStream.
49     */
50    public void load(final InputStream in) throws IOException {
51        load(new InputStreamReader(in));
52    }
53
54    /**
55     * Load the history buffer from the specified Reader.
56     */
57    public void load(final Reader reader) throws IOException {
58        BufferedReader breader = new BufferedReader(reader);
59        List lines = new ArrayList();
60        String line;
61
62        while ((line = breader.readLine()) != null) {
63            lines.add(line);
64        }
65
66        for (Iterator i = lines.iterator(); i.hasNext();) {
67            addToHistory((String) i.next());
68        }
69    }
70
71    public int size() {
72        return history.size();
73    }
74
75    /**
76     * Clear the history buffer
77     */
78    public void clear() {
79        history.clear();
80        currentIndex = 0;
81    }
82
83    /**
84     * Add the specified buffer to the end of the history. The pointer is set to
85     * the end of the history buffer.
86     */
87    public void addToHistory(final String buffer) {
88        // don't append duplicates to the end of the buffer
89        if ((history.size() != 0) && buffer.equals(history.get(history.size() - 1))) {
90            return;
91        }
92
93        history.add(buffer);
94
95        while (history.size() > getMaxSize()) {
96            history.remove(0);
97        }
98
99        currentIndex = history.size();
100
101        if (getOutput() != null) {
102            getOutput().println(buffer);
103            getOutput().flush();
104        }
105    }
106
107    /**
108     * Flush the entire history buffer to the output PrintWriter.
109     */
110    public void flushBuffer() throws IOException {
111        if (getOutput() != null) {
112            for (Iterator i = history.iterator(); i.hasNext(); getOutput().println((String) i.next())) {
113                ;
114            }
115
116            getOutput().flush();
117        }
118    }
119
120    /**
121     * This moves the history to the last entry. This entry is one position
122     * before the moveToEnd() position.
123     *
124     * @return Returns false if there were no history entries or the history
125     *         index was already at the last entry.
126     */
127    public boolean moveToLastEntry() {
128        int lastEntry = history.size() - 1;
129        if (lastEntry >= 0 && lastEntry != currentIndex) {
130            currentIndex = history.size() - 1;
131            return true;
132        }
133
134        return false;
135    }
136
137    /**
138     * Move to the end of the history buffer. This will be a blank entry, after
139     * all of the other entries.
140     */
141    public void moveToEnd() {
142        currentIndex = history.size();
143    }
144
145    /**
146     * Set the maximum size that the history buffer will store.
147     */
148    public void setMaxSize(final int maxSize) {
149        this.maxSize = maxSize;
150    }
151
152    /**
153     * Get the maximum size that the history buffer will store.
154     */
155    public int getMaxSize() {
156        return this.maxSize;
157    }
158
159    /**
160     * The output to which all history elements will be written (or null of
161     * history is not saved to a buffer).
162     */
163    public void setOutput(final PrintWriter output) {
164        this.output = output;
165    }
166
167    /**
168     * Returns the PrintWriter that is used to store history elements.
169     */
170    public PrintWriter getOutput() {
171        return this.output;
172    }
173
174    /**
175     * Returns the current history index.
176     */
177    public int getCurrentIndex() {
178        return this.currentIndex;
179    }
180
181    /**
182     * Return the content of the current buffer.
183     */
184    public String current() {
185        if (currentIndex >= history.size()) {
186            return "";
187        }
188
189        return (String) history.get(currentIndex);
190    }
191
192    /**
193     * Move the pointer to the previous element in the buffer.
194     *
195     * @return true if we successfully went to the previous element
196     */
197    public boolean previous() {
198        if (currentIndex <= 0) {
199            return false;
200        }
201
202        currentIndex--;
203
204        return true;
205    }
206
207    /**
208     * Move the pointer to the next element in the buffer.
209     *
210     * @return true if we successfully went to the next element
211     */
212    public boolean next() {
213        if (currentIndex >= history.size()) {
214            return false;
215        }
216
217        currentIndex++;
218
219        return true;
220    }
221
222    /**
223     * Returns an immutable list of the history buffer.
224     */
225    public List getHistoryList() {
226        return Collections.unmodifiableList(history);
227    }
228
229    /**
230     * Returns the standard {@link AbstractCollection#toString} representation
231     * of the history list.
232     */
233    public String toString() {
234        return history.toString();
235    }
236
237    /**
238     * Moves the history index to the first entry.
239     *
240     * @return Return false if there are no entries in the history or if the
241     *         history is already at the beginning.
242     */
243    public boolean moveToFirstEntry() {
244        if (history.size() > 0 && currentIndex != 0) {
245            currentIndex = 0;
246            return true;
247        }
248
249        return false;
250    }
251
252    /**
253     * Search backward in history from a given position.
254     *
255     * @param searchTerm substring to search for.
256     * @param startIndex the index from which on to search
257     * @return index where this substring has been found, or -1 else.
258     */
259    public int searchBackwards(String searchTerm, int startIndex) {
260        for (int i = startIndex - 1; i >= 0; i--) {
261            if (i >= size())
262                continue;
263            if (getHistory(i).indexOf(searchTerm) != -1) {
264                return i;
265            }
266        }
267        return -1;
268    }
269
270    /**
271     * Search backwards in history from the current position.
272     *
273     * @param searchTerm substring to search for.
274     * @return index where the substring has been found, or -1 else.
275     */
276    public int searchBackwards(String s) {
277        return searchBackwards(s, getCurrentIndex());
278    }
279
280    /**
281     * Get the history string for the given index.
282     *
283     * @param index
284     * @return
285     */
286    public String getHistory(int index) {
287        return (String) history.get(index);
288    }
289
290    /**
291     * Set current index to given number.
292     *
293     * @param index
294     */
295    public void setCurrentIndex(int index) {
296        if (index >= 0 && index < history.size())
297            currentIndex = index;
298    }
299}
300