Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/com/apple/laf/AquaFileSystemModel.java
38831 views
/*1* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.apple.laf;2627import java.beans.*;28import java.io.File;29import java.util.*;3031import javax.swing.*;32import javax.swing.event.ListDataEvent;33import javax.swing.filechooser.FileSystemView;34import javax.swing.table.AbstractTableModel;3536/**37* NavServices-like implementation of a file Table38*39* Some of it came from BasicDirectoryModel40*/41class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeListener {42private final JTable fFileList;43private LoadFilesThread loadThread = null;44private Vector<File> files = null;4546JFileChooser filechooser = null;47Vector<SortableFile> fileCache = null;48Object fileCacheLock;4950Vector<File> directories = null;51int fetchID = 0;5253private final boolean fSortAscending[] = {true, true};54// private boolean fSortAscending = true;55private boolean fSortNames = true;56private final String[] fColumnNames;57public final static String SORT_BY_CHANGED = "sortByChanged";58public final static String SORT_ASCENDING_CHANGED = "sortAscendingChanged";5960public AquaFileSystemModel(final JFileChooser filechooser, final JTable filelist, final String[] colNames) {61fileCacheLock = new Object();62this.filechooser = filechooser;63fFileList = filelist;64fColumnNames = colNames;65validateFileCache();66updateSelectionMode();67}6869void updateSelectionMode() {70// Save dialog lists can't be multi select, because all we're selecting is the next folder to open71final boolean b = filechooser.isMultiSelectionEnabled() && filechooser.getDialogType() != JFileChooser.SAVE_DIALOG;72fFileList.setSelectionMode(b ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION);73}7475public void propertyChange(final PropertyChangeEvent e) {76final String prop = e.getPropertyName();77if (prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY || prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY || prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY || prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY) {78invalidateFileCache();79validateFileCache();80} else if (prop.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {81updateSelectionMode();82} else if (prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {83invalidateFileCache();84validateFileCache();85}86if (prop == SORT_BY_CHANGED) {// $ Ought to just resort87fSortNames = (((Integer)e.getNewValue()).intValue() == 0);88invalidateFileCache();89validateFileCache();90fFileList.repaint();91}92if (prop == SORT_ASCENDING_CHANGED) {93final int sortColumn = (fSortNames ? 0 : 1);94fSortAscending[sortColumn] = ((Boolean)e.getNewValue()).booleanValue();95invalidateFileCache();96validateFileCache();97fFileList.repaint();98}99}100101public void invalidateFileCache() {102files = null;103directories = null;104105synchronized(fileCacheLock) {106if (fileCache != null) {107final int lastRow = fileCache.size();108fileCache = null;109fireTableRowsDeleted(0, lastRow);110}111}112}113114public Vector<File> getDirectories() {115if (directories != null) { return directories; }116return directories;117}118119public Vector<File> getFiles() {120if (files != null) { return files; }121files = new Vector<File>();122directories = new Vector<File>();123directories.addElement(filechooser.getFileSystemView().createFileObject(filechooser.getCurrentDirectory(), ".."));124125synchronized(fileCacheLock) {126for (int i = 0; i < fileCache.size(); i++) {127final SortableFile sf = fileCache.elementAt(i);128final File f = sf.fFile;129if (filechooser.isTraversable(f)) {130directories.addElement(f);131} else {132files.addElement(f);133}134}135}136137return files;138}139140public void runWhenDone(final Runnable runnable){141synchronized (fileCacheLock) {142if (loadThread != null) {143if (loadThread.isAlive()) {144loadThread.queuedTasks.add(runnable);145return;146}147}148149SwingUtilities.invokeLater(runnable);150}151}152153public void validateFileCache() {154final File currentDirectory = filechooser.getCurrentDirectory();155156if (currentDirectory == null) {157invalidateFileCache();158return;159}160161if (loadThread != null) {162// interrupt163loadThread.interrupt();164}165166fetchID++;167168// PENDING(jeff) pick the size more sensibly169invalidateFileCache();170synchronized(fileCacheLock) {171fileCache = new Vector<SortableFile>(50);172}173174loadThread = new LoadFilesThread(currentDirectory, fetchID);175loadThread.start();176}177178public int getColumnCount() {179return 2;180}181182public String getColumnName(final int col) {183return fColumnNames[col];184}185186public Class<? extends Object> getColumnClass(final int col) {187if (col == 0) return File.class;188return Date.class;189}190191public int getRowCount() {192synchronized(fileCacheLock) {193if (fileCache != null) {194return fileCache.size();195}196return 0;197}198}199200// SAK: Part of fix for 3168263. The fileCache contains201// SortableFiles, so when finding a file in the list we need to202// first create a sortable file.203public boolean contains(final File o) {204synchronized(fileCacheLock) {205if (fileCache != null) {206return fileCache.contains(new SortableFile(o));207}208return false;209}210}211212public int indexOf(final File o) {213synchronized(fileCacheLock) {214if (fileCache != null) {215final boolean isAscending = fSortNames ? fSortAscending[0] : fSortAscending[1];216final int row = fileCache.indexOf(new SortableFile(o));217return isAscending ? row : fileCache.size() - row - 1;218}219return 0;220}221}222223// AbstractListModel interface224public Object getElementAt(final int row) {225return getValueAt(row, 0);226}227228// AbstractTableModel interface229230public Object getValueAt(int row, final int col) {231if (row < 0 || col < 0) return null;232final boolean isAscending = fSortNames ? fSortAscending[0] : fSortAscending[1];233synchronized(fileCacheLock) {234if (fileCache != null) {235if (!isAscending) row = fileCache.size() - row - 1;236return fileCache.elementAt(row).getValueAt(col);237}238return null;239}240}241242// PENDING(jeff) - implement243public void intervalAdded(final ListDataEvent e) {244}245246// PENDING(jeff) - implement247public void intervalRemoved(final ListDataEvent e) {248}249250protected void sort(final Vector<Object> v) {251if (fSortNames) sSortNames.quickSort(v, 0, v.size() - 1);252else sSortDates.quickSort(v, 0, v.size() - 1);253}254255// Liberated from the 1.1 SortDemo256//257// This is a generic version of C.A.R Hoare's Quick Sort258// algorithm. This will handle arrays that are already259// sorted, and arrays with duplicate keys.<BR>260//261// If you think of a one dimensional array as going from262// the lowest index on the left to the highest index on the right263// then the parameters to this function are lowest index or264// left and highest index or right. The first time you call265// this function it will be with the parameters 0, a.length - 1.266//267// @param a an integer array268// @param lo0 left boundary of array partition269// @param hi0 right boundary of array partition270abstract class QuickSort {271final void quickSort(final Vector<Object> v, final int lo0, final int hi0) {272int lo = lo0;273int hi = hi0;274SortableFile mid;275276if (hi0 > lo0) {277// Arbitrarily establishing partition element as the midpoint of278// the array.279mid = (SortableFile)v.elementAt((lo0 + hi0) / 2);280281// loop through the array until indices cross282while (lo <= hi) {283// find the first element that is greater than or equal to284// the partition element starting from the left Index.285//286// Nasty to have to cast here. Would it be quicker287// to copy the vectors into arrays and sort the arrays?288while ((lo < hi0) && lt((SortableFile)v.elementAt(lo), mid)) {289++lo;290}291292// find an element that is smaller than or equal to293// the partition element starting from the right Index.294while ((hi > lo0) && lt(mid, (SortableFile)v.elementAt(hi))) {295--hi;296}297298// if the indexes have not crossed, swap299if (lo <= hi) {300swap(v, lo, hi);301++lo;302--hi;303}304}305306// If the right index has not reached the left side of array307// must now sort the left partition.308if (lo0 < hi) {309quickSort(v, lo0, hi);310}311312// If the left index has not reached the right side of array313// must now sort the right partition.314if (lo < hi0) {315quickSort(v, lo, hi0);316}317318}319}320321private final void swap(final Vector<Object> a, final int i, final int j) {322final Object T = a.elementAt(i);323a.setElementAt(a.elementAt(j), i);324a.setElementAt(T, j);325}326327protected abstract boolean lt(SortableFile a, SortableFile b);328}329330class QuickSortNames extends QuickSort {331protected boolean lt(final SortableFile a, final SortableFile b) {332final String aLower = a.fName.toLowerCase();333final String bLower = b.fName.toLowerCase();334return aLower.compareTo(bLower) < 0;335}336}337338class QuickSortDates extends QuickSort {339protected boolean lt(final SortableFile a, final SortableFile b) {340return a.fDateValue < b.fDateValue;341}342}343344// for speed in sorting, displaying345class SortableFile /* extends FileView */{346File fFile;347String fName;348long fDateValue;349Date fDate;350351SortableFile(final File f) {352fFile = f;353fName = fFile.getName();354fDateValue = fFile.lastModified();355fDate = new Date(fDateValue);356}357358public Object getValueAt(final int col) {359if (col == 0) return fFile;360return fDate;361}362363public boolean equals(final Object other) {364final SortableFile otherFile = (SortableFile)other;365return otherFile.fFile.equals(fFile);366}367}368369class LoadFilesThread extends Thread {370Vector<Runnable> queuedTasks = new Vector<Runnable>();371File currentDirectory = null;372int fid;373374public LoadFilesThread(final File currentDirectory, final int fid) {375super("Aqua L&F File Loading Thread");376this.currentDirectory = currentDirectory;377this.fid = fid;378}379380public void run() {381final Vector<DoChangeContents> runnables = new Vector<DoChangeContents>(10);382final FileSystemView fileSystem = filechooser.getFileSystemView();383384final File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());385386final Vector<Object> acceptsList = new Vector<Object>();387388for (final File element : list) {389// Return all files to the file chooser. The UI will disable or enable390// the file name if the current filter approves.391acceptsList.addElement(new SortableFile(element));392}393394// Sort based on settings.395sort(acceptsList);396397// Don't separate directories from files398Vector<SortableFile> chunk = new Vector<SortableFile>(10);399final int listSize = acceptsList.size();400// run through list grabbing file/dirs in chunks of ten401for (int i = 0; i < listSize;) {402SortableFile f;403for (int j = 0; j < 10 && i < listSize; j++, i++) {404f = (SortableFile)acceptsList.elementAt(i);405chunk.addElement(f);406}407final DoChangeContents runnable = new DoChangeContents(chunk, fid);408runnables.addElement(runnable);409SwingUtilities.invokeLater(runnable);410chunk = new Vector<SortableFile>(10);411if (isInterrupted()) {412// interrupted, cancel all runnables413cancelRunnables(runnables);414return;415}416}417418synchronized (fileCacheLock) {419for (final Runnable r : queuedTasks) {420SwingUtilities.invokeLater(r);421}422}423}424425public void cancelRunnables(final Vector<DoChangeContents> runnables) {426for (int i = 0; i < runnables.size(); i++) {427runnables.elementAt(i).cancel();428}429}430}431432class DoChangeContents implements Runnable {433private Vector<SortableFile> contentFiles;434private boolean doFire = true;435private final Object lock = new Object();436private final int fid;437438public DoChangeContents(final Vector<SortableFile> files, final int fid) {439this.contentFiles = files;440this.fid = fid;441}442443synchronized void cancel() {444synchronized(lock) {445doFire = false;446}447}448449public void run() {450if (fetchID == fid) {451synchronized(lock) {452if (doFire) {453synchronized(fileCacheLock) {454if (fileCache != null) {455for (int i = 0; i < contentFiles.size(); i++) {456fileCache.addElement(contentFiles.elementAt(i));457fireTableRowsInserted(i, i);458}459}460}461}462contentFiles = null;463directories = null;464}465}466}467}468469final QuickSortNames sSortNames = new QuickSortNames();470final QuickSortDates sSortDates = new QuickSortDates();471}472473474