001 /** 002 * jline - Java console input library 003 * Copyright (c) 2002-2006, Marc Prud'hommeaux <mwp1@cornell.edu> 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or 007 * without modification, are permitted provided that the following 008 * conditions are met: 009 * 010 * Redistributions of source code must retain the above copyright 011 * notice, this list of conditions and the following disclaimer. 012 * 013 * Redistributions in binary form must reproduce the above copyright 014 * notice, this list of conditions and the following disclaimer 015 * in the documentation and/or other materials provided with 016 * the distribution. 017 * 018 * Neither the name of JLine nor the names of its contributors 019 * may be used to endorse or promote products derived from this 020 * software without specific prior written permission. 021 * 022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 024 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 025 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 026 * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 027 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 028 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 029 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 030 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 031 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 032 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 033 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 034 * OF THE POSSIBILITY OF SUCH DAMAGE. 035 */ 036 package jline; 037 038 import java.io.*; 039 import java.util.*; 040 041 042 /** 043 * A file name completor takes the buffer and issues a list of 044 * potential completions. 045 * 046 * <p> 047 * This completor tries to behave as similar as possible to 048 * <i>bash</i>'s file name completion (using GNU readline) 049 * with the following exceptions: 050 * 051 * <ul> 052 * <li>Candidates that are directories will end with "/"</li> 053 * <li>Wildcard regular expressions are not evaluated or replaced</li> 054 * <li>The "~" character can be used to represent the user's home, 055 * but it cannot complete to other users' homes, since java does 056 * not provide any way of determining that easily</li> 057 * </ul> 058 * 059 * <p>TODO</p> 060 * <ul> 061 * <li>Handle files with spaces in them</li> 062 * <li>Have an option for file type color highlighting</li> 063 * </ul> 064 * 065 * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a> 066 */ 067 public class FileNameCompletor 068 implements Completor 069 { 070 public int complete (final String buf, final int cursor, 071 final List candidates) 072 { 073 String buffer = buf == null ? "" : buf; 074 075 String translated = buffer; 076 077 // special character: ~ maps to the user's home directory 078 if (translated.startsWith ("~" + File.separator)) 079 { 080 translated = System.getProperty ("user.home") 081 + translated.substring (1); 082 } 083 else if (translated.startsWith ("~")) 084 { 085 translated = new File (System.getProperty ("user.home")) 086 .getParentFile ().getAbsolutePath (); 087 } 088 else if (!(translated.startsWith (File.separator))) 089 { 090 translated = new File ("").getAbsolutePath () 091 + File.separator + translated; 092 } 093 094 File f = new File (translated); 095 096 final File dir; 097 098 if (translated.endsWith (File.separator)) 099 dir = f; 100 else 101 dir = f.getParentFile (); 102 103 final File [] entries = dir == null ? new File [0] : dir.listFiles (); 104 105 try 106 { 107 return matchFiles (buffer, translated, entries, candidates); 108 } 109 finally 110 { 111 // we want to output a sorted list of files 112 sortFileNames (candidates); 113 } 114 } 115 116 117 protected void sortFileNames (final List fileNames) 118 { 119 Collections.sort (fileNames); 120 } 121 122 123 /** 124 * Match the specified <i>buffer</i> to the array of <i>entries</i> 125 * and enter the matches into the list of <i>candidates</i>. This method 126 * can be overridden in a subclass that wants to do more 127 * sophisticated file name completion. 128 * 129 * @param buffer the untranslated buffer 130 * @param translated the buffer with common characters replaced 131 * @param entries the list of files to match 132 * @param candidates the list of candidates to populate 133 * 134 * @return the offset of the match 135 */ 136 public int matchFiles (String buffer, String translated, 137 File [] entries, List candidates) 138 { 139 if (entries == null) 140 return -1; 141 142 int matches = 0; 143 144 // first pass: just count the matches 145 for (int i = 0; i < entries.length; i++) 146 { 147 if (entries [i].getAbsolutePath ().startsWith (translated)) 148 { 149 matches++; 150 } 151 } 152 153 // green - executable 154 // blue - directory 155 // red - compressed 156 // cyan - symlink 157 for (int i = 0; i < entries.length; i++) 158 { 159 if (entries [i].getAbsolutePath ().startsWith (translated)) 160 { 161 String name = entries [i].getName () 162 + (matches == 1 && entries [i].isDirectory () 163 ? File.separator : " "); 164 165 /* 166 if (entries [i].isDirectory ()) 167 { 168 name = new ANSIBuffer ().blue (name).toString (); 169 } 170 */ 171 172 candidates.add (name); 173 } 174 } 175 176 177 final int index = buffer.lastIndexOf (File.separator); 178 return index + File.separator.length (); 179 } 180 } 181