1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.xnap.gui.component;
20
21 import java.io.File;
22 import java.io.FileFilter;
23
24 import javax.swing.DefaultComboBoxModel;
25
26 import org.apache.log4j.Logger;
27 import org.xnap.util.FileHelper;
28
29 /***
30 * This class provides completion for path prefixes on the local file system.
31 */
32 public class FileCompletionModel extends DefaultComboBoxModel
33 implements CompletionModel
34 {
35
36
37
38
39 private boolean completeOnlyDirectories = false;
40
41 private FileFilter dirFilter = new DirectoryFileFilter();
42
43 private static Logger logger = Logger.getLogger(FileCompletionModel.class);
44
45
46
47 /***
48 * Constructs a new file completion model.
49 *
50 * @param completeOnlyDirectories if true only subdirectories are
51 * suggested as possible completions, otherwise files are taken into
52 * account too
53 */
54 public FileCompletionModel(boolean completeOnlyDirectories)
55 {
56 this.completeOnlyDirectories = completeOnlyDirectories;
57 }
58
59 /***
60 * Constructs a new file completion model.
61 *
62 * The default setting is to complete subdirectories and files.
63 */
64 public FileCompletionModel()
65 {
66 this(false);
67 }
68
69
70
71 /***
72 * Returns true if there are files in the local file system having the
73 * given prefix.
74 *
75 * First we test if the given prefix denotes a file which exists and is a
76 * directory. If that is the case all the files contained in that
77 * directory are added to the model.
78 *
79 * Regardless of the above results the parent directory if there is one is
80 * searched for files matching the prefix. Thus we get all possible
81 * completions that make sense.
82 */
83 public boolean complete(String prefix)
84 {
85 removeAllElements();
86
87 File file = new File(prefix);
88 if (file.exists() && file.isDirectory()) {
89 File[] files = file.listFiles(dirFilter);
90 for (int i = 0; files != null && i < files.length; i++) {
91 addElement(files[i].getAbsolutePath());
92 }
93 }
94
95
96
97 File[] files = file.getParentFile().listFiles
98 (new PrefixFileFilter(file.getName()));
99 for (int i = 0; files != null && i < files.length; i++) {
100 addElement(files[i].getAbsolutePath());
101 }
102 }
103 return getSize() > 0;
104 }
105
106 /***
107 * This method's behaviour is a matter of taste.
108 *
109 * It first checks if the given prefix is an existing directory. If that
110 * is the case the commmon unique prefix of all files found in the
111 * directory is returned.
112 *
113 * Only if the above fails the parent directory is searched for the common
114 * unique prefix.
115 */
116 public String completeUniquePrefix(String prefix)
117 {
118 File file = new File(prefix);
119 if (file.exists() && file.isDirectory()) {
120 File[] files = file.listFiles(dirFilter);
121 StringBuffer sb = new StringBuffer
122 (FileHelper.appendSeparator(file.getAbsolutePath()));
123 for (int i = 0; matches(files, i); i++) {
124 sb.append(files[0].getName().charAt(i));
125 }
126 return sb.toString();
127 }
128 else if (file.getParentFile() != null) {
129 File[] files = file.getParentFile().listFiles
130 (new PrefixFileFilter(file.getName()));
131 StringBuffer sb = new StringBuffer
132 (FileHelper.appendSeparator
133 (file.getParentFile().getAbsolutePath()));
134 for (int i = 0; matches(files, i); i++) {
135 sb.append(files[0].getName().charAt(i));
136 }
137 return sb.toString();
138 }
139 return prefix;
140 }
141
142 /***
143 * Returns true if all files in the given array have the same character at
144 * position <code>index</code>.
145 *
146 * @param files the array of files, can be null or empty
147 * @param index the position in the filenames that is tested for equality
148 * @return false if the array is null or empty, true if all filenames of
149 * the files in the array have the same character at position
150 * <code>index</code>
151 */
152 private boolean matches(File[] files, int index)
153 {
154 if (files == null || files.length == 0) {
155 return false;
156 }
157 if (files.length == 1) {
158 return index < files[0].getName().length();
159 }
160 for (int i = 0; i < files.length - 1; i++) {
161 if (files[i].getName().length() == index
162 || files[i+1].getName().length() == index) {
163 return false;
164 }
165 else if (files[i].getName().charAt(index)
166 != files[i+1].getName().charAt(index)) {
167 return false;
168 }
169 }
170 return true;
171 }
172
173 /***
174 * Does nothing, since we don't do any data keeping of our own.
175 */
176 public void insert(Object object)
177 {
178
179 }
180
181 /***
182 * Does nothing, since we don't do any data keeping of our own.
183 */
184 public void remove(Object object)
185 {
186
187 }
188
189 /***
190 * This class checks in its {@link DirectoryFileFilter#accept(File)}
191 * method if the given file is a directory in case the constructor {@link
192 * FileCompletionModel#FileCompletionModel(boolean)
193 * FileCompletionModel(true)} was used.
194 */
195 private class DirectoryFileFilter implements FileFilter
196 {
197 public boolean accept(File pathname)
198 {
199 return completeOnlyDirectories ? pathname.isDirectory() : true;
200 }
201 }
202
203 private class PrefixFileFilter extends DirectoryFileFilter
204 {
205 private String prefix;
206
207 public PrefixFileFilter(String prefix)
208 {
209 this.prefix = prefix;
210 }
211
212 public boolean accept(File pathname)
213 {
214 return super.accept(pathname)
215 && pathname.getName().startsWith(prefix);
216 }
217 }
218 }