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