001 /**
002 * jline - Java console input library
003 * Copyright (c) 2002,2003 Marc Prud'hommeaux marc@apocalypse.org
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either
008 * version 2.1 of the License, or (at your option) any later version.
009 *
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * Lesser General Public License for more details.
014 *
015 * You should have received a copy of the GNU Lesser General Public
016 * License along with this library; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018 */
019 package jline;
020
021 import java.io.*;
022 import java.util.*;
023
024
025 /**
026 * A file name completor takes the buffer and issues a list of
027 * potential completions.
028 *
029 * <p>
030 * This completor tries to behave as similar as possible to
031 * <i>bash</i>'s file name completion (using GNU readline)
032 * with the following exceptions:
033 *
034 * <ul>
035 * <li>Candidates that are directories will end with "/"</li>
036 * <li>Wildcard regular expressions are not evaluated or replaced</li>
037 * <li>The "~" character can be used to represent the user's home,
038 * but it cannot complete to other users' homes, since java does
039 * not provide any way of determining that easily</li>
040 * </ul>
041 *
042 * @author <a href="mailto:marc@apocalypse.org">Marc Prud'hommeaux</a>
043 */
044 public class FileNameCompletor
045 implements Completor
046 {
047 public int complete (String buffer, int cursor, List candidates)
048 {
049 if (buffer == null)
050 buffer = "";
051
052 String translated = buffer;
053
054 // special character: ~ maps to the user's home directory
055 if (translated.startsWith ("~" + File.separator))
056 {
057 translated = System.getProperty ("user.home")
058 + translated.substring (1);
059 }
060 else if (translated.startsWith ("~"))
061 {
062 translated = new File (System.getProperty ("user.home"))
063 .getParentFile ().getAbsolutePath ();
064 }
065 else if (!(translated.startsWith (File.separator)))
066 {
067 translated = new File ("").getAbsolutePath ()
068 + File.separator + translated;
069 }
070
071 File f = new File (translated);
072
073 final File dir;
074
075 if (translated.endsWith (File.separator))
076 dir = f;
077 else
078 dir = f.getParentFile ();
079
080 final File [] entries = dir == null ? new File [0] : dir.listFiles ();
081
082 try
083 {
084 return matchFiles (buffer, translated, entries, candidates);
085 }
086 finally
087 {
088 // we want to output a sorted list of files
089 sortFileNames (candidates);
090 }
091 }
092
093
094 protected void sortFileNames (List fileNames)
095 {
096 Collections.sort (fileNames);
097 }
098
099
100 /**
101 * Match the specified <i>buffer</i> to the array of <i>entries</i>
102 * and enter the matches into the list of <i>candidates</i>. This method
103 * can be overridden in a subclass that wants to do more
104 * sophisticated file name completion.
105 *
106 * @param buffer the untranslated buffer
107 * @param translated the buffer with common characters replaced
108 * @param entries the list of files to match
109 * @param candidates the list of candidates to populate
110 *
111 * @return the offset of the match
112 */
113 public int matchFiles (String buffer, String translated,
114 File [] entries, List candidates)
115 {
116 if (entries == null)
117 return -1;
118
119 int matches = 0;
120
121 // first pass: just count the matches
122 for (int i = 0; i < entries.length; i++)
123 {
124 if (entries [i].getAbsolutePath ().startsWith (translated))
125 {
126 matches++;
127 }
128 }
129
130 // green - executable
131 // blue - directory
132 // red - compressed
133 // cyan - symlink
134 for (int i = 0; i < entries.length; i++)
135 {
136 if (entries [i].getAbsolutePath ().startsWith (translated))
137 {
138 String name = entries [i].getName ()
139 + (matches == 1 && entries [i].isDirectory ()
140 ? File.separator : " ");
141
142 /*
143 if (entries [i].isDirectory ())
144 {
145 name = new ANSIBuffer ().blue (name).toString ();
146 }
147 */
148
149 candidates.add (name);
150 }
151 }
152
153
154 final int index = buffer.lastIndexOf (File.separator);
155 return index + File.separator.length ();
156 }
157 }
158