001    /**
002     *      jline - Java console input library
003     *      Copyright (c) 2002,2003 Marc Prud'hommeaux mwp1@cornell.edu
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     *      <p>TODO</p>
043     *      <ul>
044     *      <li>Handle files with spaces in them</li>
045     *      <li>Have an option for file type color highlighting</li>
046     *      </ul>
047     *
048     *  @author  <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
049     */
050    public class FileNameCompletor
051            implements Completor
052    {
053            public int complete (final String buf, final int cursor,
054                    final List candidates)
055            {
056                    String buffer = buf == null ? "" : buf;
057    
058                    String translated = buffer;
059    
060                    // special character: ~ maps to the user's home directory
061                    if (translated.startsWith ("~" + File.separator))
062                    {
063                            translated = System.getProperty ("user.home")
064                                    + translated.substring (1);
065                    }
066                    else if (translated.startsWith ("~"))
067                    {
068                            translated = new File (System.getProperty ("user.home"))
069                                    .getParentFile ().getAbsolutePath ();
070                    }
071                    else if (!(translated.startsWith (File.separator)))
072                    {
073                            translated = new File ("").getAbsolutePath ()
074                                    + File.separator + translated;
075                    }
076    
077                    File f = new File (translated);
078    
079                    final File dir;
080                    
081                    if (translated.endsWith (File.separator))
082                            dir = f;
083                    else
084                            dir = f.getParentFile ();
085                    
086                    final File [] entries = dir == null ? new File [0] : dir.listFiles ();
087    
088                    try
089                    {
090                            return matchFiles (buffer, translated, entries, candidates);
091                    }
092                    finally
093                    {
094                            // we want to output a sorted list of files
095                            sortFileNames (candidates);
096                    }
097            }
098    
099    
100            protected void sortFileNames (final List fileNames)
101            {
102                    Collections.sort (fileNames);
103            }
104    
105    
106            /**
107             *  Match the specified <i>buffer</i> to the array of <i>entries</i>
108             *  and enter the matches into the list of <i>candidates</i>. This method
109             *  can be overridden in a subclass that wants to do more
110             *  sophisticated file name completion.
111             *
112             *  @param      buffer          the untranslated buffer 
113             *  @param      translated      the buffer with common characters replaced      
114             *  @param      entries         the list of files to match      
115             *  @param      candidates      the list of candidates to populate      
116             *
117             *  @return  the offset of the match
118             */
119            public int matchFiles (String buffer, String translated,
120                    File [] entries, List candidates)
121            {
122                    if (entries == null)
123                            return -1;
124    
125                    int matches = 0;
126    
127                    // first pass: just count the matches
128                    for (int i = 0; i < entries.length; i++)
129                    {
130                            if (entries [i].getAbsolutePath ().startsWith (translated))
131                            {
132                                    matches++;
133                            }
134                    }
135    
136                    // green - executable
137                    // blue - directory
138                    // red - compressed
139                    // cyan - symlink
140                    for (int i = 0; i < entries.length; i++)
141                    {
142                            if (entries [i].getAbsolutePath ().startsWith (translated))
143                            {
144                                    String name = entries [i].getName ()
145                                            + (matches == 1 && entries [i].isDirectory ()
146                                                    ? File.separator : " ");
147    
148                                    /*
149                                    if (entries [i].isDirectory ())
150                                    {
151                                            name = new ANSIBuffer ().blue (name).toString ();
152                                    }
153                                    */
154    
155                                    candidates.add (name);
156                            }
157                    }
158    
159    
160                    final int index = buffer.lastIndexOf (File.separator);
161                    return index + File.separator.length ();
162            }
163    }
164