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     */
007    package jline;
008    
009    import java.io.*;
010    import java.util.*;
011    
012    /**
013     * A command history buffer.
014     *
015     * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
016     */
017    public 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    }