001/*
002 * Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved.
003 *
004 * This software is distributable under the BSD license. See the terms of the
005 * BSD license in the documentation provided with this software.
006 */
007package jline;
008
009import java.io.*;
010import java.util.*;
011
012/**
013 * A command history buffer.
014 *
015 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
016 */
017public class History {
018
019    private List history = new ArrayList();
020    private PrintWriter output = null;
021    private int maxSize = 500;
022    private int currentIndex = 0;
023
024    /**
025     * Construstor: initialize a blank history.
026     */
027    public History() {
028    }
029
030    /**
031     * Construstor: initialize History object the the specified {@link File} for
032     * storage.
033     */
034    public History(final File historyFile) throws IOException {
035        setHistoryFile(historyFile);
036    }
037
038    public void setHistoryFile(final File historyFile) throws IOException {
039        if (historyFile.isFile()) {
040            load(new FileInputStream(historyFile));
041        }
042
043        setOutput(new PrintWriter(new FileWriter(historyFile), true));
044        flushBuffer();
045    }
046
047    /**
048     * Load the history buffer from the specified InputStream.
049     */
050    public void load(final InputStream in) throws IOException {
051        load(new InputStreamReader(in));
052    }
053
054    /**
055     * Load the history buffer from the specified Reader.
056     */
057    public void load(final Reader reader) throws IOException {
058        BufferedReader breader = new BufferedReader(reader);
059        List lines = new ArrayList();
060        String line;
061
062        while ((line = breader.readLine()) != null) {
063            lines.add(line);
064        }
065
066        for (Iterator i = lines.iterator(); i.hasNext();) {
067            addToHistory((String) i.next());
068        }
069    }
070
071    public int size() {
072        return history.size();
073    }
074
075    /**
076     * Clear the history buffer
077     */
078    public void clear() {
079        history.clear();
080        currentIndex = 0;
081    }
082
083    /**
084     * Add the specified buffer to the end of the history. The pointer is set to
085     * the end of the history buffer.
086     */
087    public void addToHistory(final String buffer) {
088        // don't append duplicates to the end of the buffer
089        if ((history.size() != 0) && buffer.equals(history.get(history.size() - 1))) {
090            return;
091        }
092
093        history.add(buffer);
094
095        while (history.size() > getMaxSize()) {
096            history.remove(0);
097        }
098
099        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}