alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (AquaFileSystemModel.java)

This example Java source code file (AquaFileSystemModel.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

aquafilesystemmodel, dochangecontents, file, filechooser, gui, javabean, jfilechooser, listdataevent, loadfilesthread, object, quicksortdates, quicksortnames, runnable, sort_ascending_changed, sortablefile, string, swing, table, util, vector

The AquaFileSystemModel.java Java example source code

/*
 * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.apple.laf;

import java.beans.*;
import java.io.File;
import java.util.*;

import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.filechooser.FileSystemView;
import javax.swing.table.AbstractTableModel;

/**
 * NavServices-like implementation of a file Table
 *
 * Some of it came from BasicDirectoryModel
 */
class AquaFileSystemModel extends AbstractTableModel implements PropertyChangeListener {
    private final JTable fFileList;
    private LoadFilesThread loadThread = null;
    private Vector<File> files = null;

    JFileChooser filechooser = null;
    Vector<SortableFile> fileCache = null;
    Object fileCacheLock;

    Vector<File> directories = null;
    int fetchID = 0;

    private final boolean fSortAscending[] = {true, true};
    // private boolean fSortAscending = true;
    private boolean fSortNames = true;
    private final String[] fColumnNames;
    public final static String SORT_BY_CHANGED = "sortByChanged";
    public final static String SORT_ASCENDING_CHANGED = "sortAscendingChanged";

    public AquaFileSystemModel(final JFileChooser filechooser, final JTable filelist, final String[] colNames) {
        fileCacheLock = new Object();
        this.filechooser = filechooser;
        fFileList = filelist;
        fColumnNames = colNames;
        validateFileCache();
        updateSelectionMode();
    }

    void updateSelectionMode() {
        // Save dialog lists can't be multi select, because all we're selecting is the next folder to open
        final boolean b = filechooser.isMultiSelectionEnabled() && filechooser.getDialogType() != JFileChooser.SAVE_DIALOG;
        fFileList.setSelectionMode(b ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION);
    }

    public void propertyChange(final PropertyChangeEvent e) {
        final String prop = e.getPropertyName();
        if (prop == JFileChooser.DIRECTORY_CHANGED_PROPERTY || prop == JFileChooser.FILE_VIEW_CHANGED_PROPERTY || prop == JFileChooser.FILE_FILTER_CHANGED_PROPERTY || prop == JFileChooser.FILE_HIDING_CHANGED_PROPERTY) {
            invalidateFileCache();
            validateFileCache();
        } else if (prop.equals(JFileChooser.MULTI_SELECTION_ENABLED_CHANGED_PROPERTY)) {
            updateSelectionMode();
        } else if (prop == JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY) {
            invalidateFileCache();
            validateFileCache();
        }
        if (prop == SORT_BY_CHANGED) {// $ Ought to just resort
            fSortNames = (((Integer)e.getNewValue()).intValue() == 0);
            invalidateFileCache();
            validateFileCache();
            fFileList.repaint();
        }
        if (prop == SORT_ASCENDING_CHANGED) {
            final int sortColumn = (fSortNames ? 0 : 1);
            fSortAscending[sortColumn] = ((Boolean)e.getNewValue()).booleanValue();
            invalidateFileCache();
            validateFileCache();
            fFileList.repaint();
        }
    }

    public void invalidateFileCache() {
        files = null;
        directories = null;

        synchronized(fileCacheLock) {
            if (fileCache != null) {
                final int lastRow = fileCache.size();
                fileCache = null;
                fireTableRowsDeleted(0, lastRow);
            }
        }
    }

    public Vector<File> getDirectories() {
        if (directories != null) { return directories; }
        return directories;
    }

    public Vector<File> getFiles() {
        if (files != null) { return files; }
        files = new Vector<File>();
        directories = new Vector<File>();
        directories.addElement(filechooser.getFileSystemView().createFileObject(filechooser.getCurrentDirectory(), ".."));

        synchronized(fileCacheLock) {
            for (int i = 0; i < fileCache.size(); i++) {
                final SortableFile sf = fileCache.elementAt(i);
                final File f = sf.fFile;
                if (filechooser.isTraversable(f)) {
                    directories.addElement(f);
                } else {
                    files.addElement(f);
                }
            }
        }

        return files;
    }

    public void runWhenDone(final Runnable runnable){
         synchronized (fileCacheLock) {
             if (loadThread != null) {
                 if (loadThread.isAlive()) {
                     loadThread.queuedTasks.add(runnable);
                     return;
                 }
             }

             SwingUtilities.invokeLater(runnable);
         }
     }

    public void validateFileCache() {
        final File currentDirectory = filechooser.getCurrentDirectory();

        if (currentDirectory == null) {
            invalidateFileCache();
            return;
        }

        if (loadThread != null) {
            // interrupt
            loadThread.interrupt();
        }

        fetchID++;

        // PENDING(jeff) pick the size more sensibly
        invalidateFileCache();
        synchronized(fileCacheLock) {
            fileCache = new Vector<SortableFile>(50);
        }

        loadThread = new LoadFilesThread(currentDirectory, fetchID);
        loadThread.start();
    }

    public int getColumnCount() {
        return 2;
    }

    public String getColumnName(final int col) {
        return fColumnNames[col];
    }

    public Class<? extends Object> getColumnClass(final int col) {
        if (col == 0) return File.class;
        return Date.class;
    }

    public int getRowCount() {
        synchronized(fileCacheLock) {
            if (fileCache != null) {
                return fileCache.size();
            }
            return 0;
        }
    }

    // SAK: Part of fix for 3168263. The fileCache contains
    // SortableFiles, so when finding a file in the list we need to
    // first create a sortable file.
    public boolean contains(final File o) {
        synchronized(fileCacheLock) {
            if (fileCache != null) {
                return fileCache.contains(new SortableFile(o));
            }
            return false;
        }
    }

    public int indexOf(final File o) {
        synchronized(fileCacheLock) {
            if (fileCache != null) {
                final boolean isAscending = fSortNames ? fSortAscending[0] : fSortAscending[1];
                final int row = fileCache.indexOf(new SortableFile(o));
                return isAscending ? row : fileCache.size() - row - 1;
            }
            return 0;
        }
    }

    // AbstractListModel interface
    public Object getElementAt(final int row) {
        return getValueAt(row, 0);
    }

    // AbstractTableModel interface

    public Object getValueAt(int row, final int col) {
        if (row < 0 || col < 0) return null;
        final boolean isAscending = fSortNames ? fSortAscending[0] : fSortAscending[1];
        synchronized(fileCacheLock) {
            if (fileCache != null) {
                if (!isAscending) row = fileCache.size() - row - 1;
                return fileCache.elementAt(row).getValueAt(col);
            }
            return null;
        }
    }

    // PENDING(jeff) - implement
    public void intervalAdded(final ListDataEvent e) {
    }

    // PENDING(jeff) - implement
    public void intervalRemoved(final ListDataEvent e) {
    }

    protected void sort(final Vector<Object> v) {
        if (fSortNames) sSortNames.quickSort(v, 0, v.size() - 1);
        else sSortDates.quickSort(v, 0, v.size() - 1);
    }

    // Liberated from the 1.1 SortDemo
    //
    // This is a generic version of C.A.R Hoare's Quick Sort
    // algorithm. This will handle arrays that are already
    // sorted, and arrays with duplicate keys.<BR>
    //
    // If you think of a one dimensional array as going from
    // the lowest index on the left to the highest index on the right
    // then the parameters to this function are lowest index or
    // left and highest index or right. The first time you call
    // this function it will be with the parameters 0, a.length - 1.
    //
    // @param a an integer array
    // @param lo0 left boundary of array partition
    // @param hi0 right boundary of array partition
    abstract class QuickSort {
        final void quickSort(final Vector<Object> v, final int lo0, final int hi0) {
            int lo = lo0;
            int hi = hi0;
            SortableFile mid;

            if (hi0 > lo0) {
                // Arbitrarily establishing partition element as the midpoint of
                // the array.
                mid = (SortableFile)v.elementAt((lo0 + hi0) / 2);

                // loop through the array until indices cross
                while (lo <= hi) {
                    // find the first element that is greater than or equal to
                    // the partition element starting from the left Index.
                    //
                    // Nasty to have to cast here. Would it be quicker
                    // to copy the vectors into arrays and sort the arrays?
                    while ((lo < hi0) && lt((SortableFile)v.elementAt(lo), mid)) {
                        ++lo;
                    }

                    // find an element that is smaller than or equal to
                    // the partition element starting from the right Index.
                    while ((hi > lo0) && lt(mid, (SortableFile)v.elementAt(hi))) {
                        --hi;
                    }

                    // if the indexes have not crossed, swap
                    if (lo <= hi) {
                        swap(v, lo, hi);
                        ++lo;
                        --hi;
                    }
                }

                // If the right index has not reached the left side of array
                // must now sort the left partition.
                if (lo0 < hi) {
                    quickSort(v, lo0, hi);
                }

                // If the left index has not reached the right side of array
                // must now sort the right partition.
                if (lo < hi0) {
                    quickSort(v, lo, hi0);
                }

            }
        }

        private final void swap(final Vector<Object> a, final int i, final int j) {
            final Object T = a.elementAt(i);
            a.setElementAt(a.elementAt(j), i);
            a.setElementAt(T, j);
        }

        protected abstract boolean lt(SortableFile a, SortableFile b);
    }

    class QuickSortNames extends QuickSort {
        protected boolean lt(final SortableFile a, final SortableFile b) {
            final String aLower = a.fName.toLowerCase();
            final String bLower = b.fName.toLowerCase();
            return aLower.compareTo(bLower) < 0;
        }
    }

    class QuickSortDates extends QuickSort {
        protected boolean lt(final SortableFile a, final SortableFile b) {
            return a.fDateValue < b.fDateValue;
        }
    }

    // for speed in sorting, displaying
    class SortableFile /* extends FileView */{
        File fFile;
        String fName;
        long fDateValue;
        Date fDate;

        SortableFile(final File f) {
            fFile = f;
            fName = fFile.getName();
            fDateValue = fFile.lastModified();
            fDate = new Date(fDateValue);
        }

        public Object getValueAt(final int col) {
            if (col == 0) return fFile;
            return fDate;
        }

        public boolean equals(final Object other) {
            final SortableFile otherFile = (SortableFile)other;
            return otherFile.fFile.equals(fFile);
        }
    }

    class LoadFilesThread extends Thread {
        Vector<Runnable> queuedTasks = new Vector();
        File currentDirectory = null;
        int fid;

        public LoadFilesThread(final File currentDirectory, final int fid) {
            super("Aqua L&F File Loading Thread");
            this.currentDirectory = currentDirectory;
            this.fid = fid;
        }

        public void run() {
            final Vector<DoChangeContents> runnables = new Vector(10);
            final FileSystemView fileSystem = filechooser.getFileSystemView();

            final File[] list = fileSystem.getFiles(currentDirectory, filechooser.isFileHidingEnabled());

            final Vector<Object> acceptsList = new Vector();

            for (final File element : list) {
                // Return all files to the file chooser. The UI will disable or enable
                // the file name if the current filter approves.
                acceptsList.addElement(new SortableFile(element));
            }

            // Sort based on settings.
            sort(acceptsList);

            // Don't separate directories from files
            Vector<SortableFile> chunk = new Vector(10);
            final int listSize = acceptsList.size();
            // run through list grabbing file/dirs in chunks of ten
            for (int i = 0; i < listSize;) {
                SortableFile f;
                for (int j = 0; j < 10 && i < listSize; j++, i++) {
                    f = (SortableFile)acceptsList.elementAt(i);
                    chunk.addElement(f);
                }
                final DoChangeContents runnable = new DoChangeContents(chunk, fid);
                runnables.addElement(runnable);
                SwingUtilities.invokeLater(runnable);
                chunk = new Vector<SortableFile>(10);
                if (isInterrupted()) {
                    // interrupted, cancel all runnables
                    cancelRunnables(runnables);
                    return;
                }
            }

            synchronized (fileCacheLock) {
                for (final Runnable r : queuedTasks) {
                    SwingUtilities.invokeLater(r);
                }
            }
        }

        public void cancelRunnables(final Vector<DoChangeContents> runnables) {
            for (int i = 0; i < runnables.size(); i++) {
                runnables.elementAt(i).cancel();
            }
        }
    }

    class DoChangeContents implements Runnable {
        private Vector<SortableFile> contentFiles;
        private boolean doFire = true;
        private final Object lock = new Object();
        private final int fid;

        public DoChangeContents(final Vector<SortableFile> files, final int fid) {
            this.contentFiles = files;
            this.fid = fid;
        }

        synchronized void cancel() {
            synchronized(lock) {
                doFire = false;
            }
        }

        public void run() {
            if (fetchID == fid) {
                synchronized(lock) {
                    if (doFire) {
                        synchronized(fileCacheLock) {
                            if (fileCache != null) {
                                for (int i = 0; i < contentFiles.size(); i++) {
                                    fileCache.addElement(contentFiles.elementAt(i));
                                    fireTableRowsInserted(i, i);
                                }
                            }
                        }
                    }
                    contentFiles = null;
                    directories = null;
                }
            }
        }
    }

    final QuickSortNames sSortNames = new QuickSortNames();
    final QuickSortDates sSortDates = new QuickSortDates();
}

Other Java examples (source code examples)

Here is a short list of links related to this Java AquaFileSystemModel.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.