1. /*
  2. * @(#)BasicDirectoryModel.java 1.15 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import java.io.File;
  9. import java.util.Vector;
  10. import javax.swing.*;
  11. import javax.swing.filechooser.*;
  12. import javax.swing.event.*;
  13. import java.beans.*;
  14. /**
  15. * Basic implementation of a file list.
  16. *
  17. * @version %i% %g%
  18. * @author Jeff Dinkins
  19. */
  20. public class BasicDirectoryModel extends AbstractListModel implements PropertyChangeListener {
  21. private JFileChooser filechooser = null;
  22. private Vector fileCache = null;
  23. private LoadFilesThread loadThread = null;
  24. private Vector files = null;
  25. private Vector directories = null;
  26. private int fetchID = 0;
  27. public BasicDirectoryModel(JFileChooser filechooser) {
  28. this.filechooser = filechooser;
  29. validateFileCache();
  30. }
  31. public void propertyChange(PropertyChangeEvent e) {
  32. String prop = e.getPropertyName();
  33. if(prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY ||
  34. prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY ||
  35. prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY ||
  36. prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY ||
  37. prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
  38. invalidateFileCache();
  39. validateFileCache();
  40. }
  41. }
  42. public void invalidateFileCache() {
  43. files = null;
  44. directories = null;
  45. fileCache = null;
  46. }
  47. public Vector getDirectories() {
  48. if(directories != null) {
  49. return directories;
  50. }
  51. Vector fls = getFiles();
  52. return directories;
  53. }
  54. public Vector getFiles() {
  55. if(files != null) {
  56. return files;
  57. }
  58. files = new Vector();
  59. directories = new Vector();
  60. directories.addElement(filechooser.getFileSystemView().createFileObject(
  61. filechooser.getCurrentDirectory(), "..")
  62. );
  63. for(int i = 0; i < fileCache.size(); i++) {
  64. File f = (File) fileCache.elementAt(i);
  65. if(filechooser.isTraversable(f)) {
  66. directories.addElement(f);
  67. } else {
  68. files.addElement(f);
  69. }
  70. }
  71. return files;
  72. }
  73. public void validateFileCache() {
  74. File currentDirectory = filechooser.getCurrentDirectory();
  75. if(currentDirectory == null) {
  76. invalidateFileCache();
  77. return;
  78. }
  79. if(loadThread != null) {
  80. // interrupt
  81. loadThread.interrupt();
  82. }
  83. fetchID++;
  84. // PENDING(jeff) pick the size more sensibly
  85. invalidateFileCache();
  86. fileCache = new Vector(50);
  87. loadThread = new LoadFilesThread(currentDirectory, fetchID);
  88. loadThread.start(); }
  89. // PENDING(jeff) - this is inefficient - should sent out
  90. // incremental adjustment values instead of saying that the
  91. // whole list has changed.
  92. public void fireContentsChanged() {
  93. // System.out.println("BasicDirectoryModel: firecontentschanged");
  94. files = null;
  95. directories = null;
  96. fireContentsChanged(this, 0, fileCache.size()-1);
  97. }
  98. public int getSize() {
  99. if(fileCache != null) {
  100. return fileCache.size();
  101. } else {
  102. return 0;
  103. }
  104. }
  105. public boolean contains(Object o) {
  106. if(fileCache != null) {
  107. return fileCache.contains(o);
  108. } else {
  109. return false;
  110. }
  111. }
  112. public int indexOf(Object o) {
  113. if(fileCache != null) {
  114. return fileCache.indexOf(o);
  115. } else {
  116. return 0;
  117. }
  118. }
  119. public Object getElementAt(int index) {
  120. if(fileCache != null) {
  121. return fileCache.elementAt(index);
  122. } else {
  123. return null;
  124. }
  125. }
  126. // PENDING(jeff) - implement
  127. public void intervalAdded(ListDataEvent e) {
  128. }
  129. // PENDING(jeff) - implement
  130. public void intervalRemoved(ListDataEvent e) {
  131. }
  132. protected void sort(Vector v){
  133. quickSort(v, 0, v.size()-1);
  134. }
  135. // Liberated from the 1.1 SortDemo
  136. //
  137. // This is a generic version of C.A.R Hoare's Quick Sort
  138. // algorithm. This will handle arrays that are already
  139. // sorted, and arrays with duplicate keys.<BR>
  140. //
  141. // If you think of a one dimensional array as going from
  142. // the lowest index on the left to the highest index on the right
  143. // then the parameters to this function are lowest index or
  144. // left and highest index or right. The first time you call
  145. // this function it will be with the parameters 0, a.length - 1.
  146. //
  147. // @param a an integer array
  148. // @param lo0 left boundary of array partition
  149. // @param hi0 right boundary of array partition
  150. private void quickSort(Vector v, int lo0, int hi0) {
  151. int lo = lo0;
  152. int hi = hi0;
  153. File mid;
  154. if (hi0 > lo0) {
  155. // Arbitrarily establishing partition element as the midpoint of
  156. // the array.
  157. mid = (File) v.elementAt((lo0 + hi0) / 2);
  158. // loop through the array until indices cross
  159. while(lo <= hi) {
  160. // find the first element that is greater than or equal to
  161. // the partition element starting from the left Index.
  162. //
  163. // Nasty to have to cast here. Would it be quicker
  164. // to copy the vectors into arrays and sort the arrays?
  165. while((lo < hi0) && lt((File)v.elementAt(lo), mid)) {
  166. ++lo;
  167. }
  168. // find an element that is smaller than or equal to
  169. // the partition element starting from the right Index.
  170. while((hi > lo0) && lt(mid, (File)v.elementAt(hi))) {
  171. --hi;
  172. }
  173. // if the indexes have not crossed, swap
  174. if(lo <= hi) {
  175. swap(v, lo, hi);
  176. ++lo;
  177. --hi;
  178. }
  179. }
  180. // If the right index has not reached the left side of array
  181. // must now sort the left partition.
  182. if(lo0 < hi) {
  183. quickSort(v, lo0, hi);
  184. }
  185. // If the left index has not reached the right side of array
  186. // must now sort the right partition.
  187. if(lo < hi0) {
  188. quickSort(v, lo, hi0);
  189. }
  190. }
  191. }
  192. private void swap(Vector a, int i, int j) {
  193. Object T = a.elementAt(i);
  194. a.setElementAt(a.elementAt(j), i);
  195. a.setElementAt(T, j);
  196. }
  197. protected boolean lt(File a, File b) {
  198. return a.getName().compareTo(b.getName()) < 0;
  199. }
  200. class LoadFilesThread extends Thread {
  201. File currentDirectory = null;
  202. int fid;
  203. public LoadFilesThread(File currentDirectory, int fid) {
  204. super("Basic L&F File Loading Thread");
  205. this.currentDirectory = currentDirectory;
  206. this.fid = fid;
  207. }
  208. public void run() {
  209. Vector runnables = new Vector(10);
  210. FileSystemView fileSystem = filechooser.getFileSystemView();
  211. File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());
  212. Vector acceptsList = new Vector();
  213. // run through the file list, add directories and selectable files to fileCache
  214. for (int i = 0; i < list.length; i++) {
  215. if(filechooser.accept(list[i])) {
  216. acceptsList.addElement(list[i]);
  217. }
  218. }
  219. // First sort alphabetically by filename
  220. sort(acceptsList);
  221. Vector directories = new Vector(10);
  222. Vector files = new Vector();
  223. // run through list grabbing directories in chunks of ten
  224. for(int i = 0; i < acceptsList.size(); i++) {
  225. File f = (File) acceptsList.elementAt(i);
  226. boolean isTraversable = filechooser.isTraversable(f);
  227. if(isTraversable) {
  228. directories.addElement(f);
  229. } else if(!isTraversable && filechooser.isFileSelectionEnabled()) {
  230. files.addElement(f);
  231. }
  232. if((directories.size() == 10) || (i == acceptsList.size()-1)) {
  233. DoChangeContents runnable = new DoChangeContents(directories, fid);
  234. runnables.addElement(runnable);
  235. SwingUtilities.invokeLater(runnable);
  236. directories = new Vector(10);
  237. }
  238. if(isInterrupted()) {
  239. // interrupted, cancel all runnables
  240. cancelRunnables(runnables);
  241. return;
  242. }
  243. }
  244. // PENDING(jeff) - run through the files in blocks instead of
  245. // sending them along as one big chunk
  246. DoChangeContents runnable = new DoChangeContents(files, fid);
  247. runnables.addElement(runnable);
  248. SwingUtilities.invokeLater(runnable);
  249. if(isInterrupted()) {
  250. // interrupted, blow out
  251. cancelRunnables(runnables);
  252. return;
  253. }
  254. }
  255. public void cancelRunnables(Vector runnables) {
  256. for(int i = 0; i < runnables.size(); i++) {
  257. ((DoChangeContents)runnables.elementAt(i)).cancel();
  258. }
  259. }
  260. }
  261. class DoChangeContents implements Runnable {
  262. private Vector files;
  263. private boolean doFire = true;
  264. private Object lock = new Object();
  265. private int fid;
  266. public DoChangeContents(Vector files, int fid) {
  267. this.files = files;
  268. this.fid = fid;
  269. }
  270. synchronized void cancel() {
  271. synchronized(lock) {
  272. doFire = false;
  273. }
  274. }
  275. public void run() {
  276. if(fetchID == fid) {
  277. synchronized(lock) {
  278. if(doFire) {
  279. for(int i = 0; i < files.size(); i++) {
  280. fileCache.addElement(files.elementAt(i));
  281. }
  282. }
  283. fireContentsChanged();
  284. }
  285. }
  286. }
  287. }
  288. }