001    /*
002     * $Id: FileSystemModel.java 2713 2008-02-15 15:08:23Z kleopatra $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    
022    package org.jdesktop.swingx.treetable;
023    
024    import java.io.File;
025    import java.util.Arrays;
026    import java.util.Date;
027    
028    /**
029     * A tree table model to simulate a file system.
030     * <p>
031     * This tree table model implementation extends {@code AbstractTreeTableModel}.
032     * The file system metaphor demonstrates that it is often easier to directly
033     * implement tree structures directly instead of using intermediaries, such as
034     * {@code TreeTableNode}.
035     * <p>
036     * A comparison of this class with {@code SimpleFileSystemModel}, shows that
037     * extending {@code AbstractTreeTableModel} is often easier than creating a model
038     * from scratch.
039     * <p>
040     * A "full" version of this model might allow editing of file names, the
041     * deletion of files, and the movement of files. This simple implementation does
042     * not intend to tackle such problems, but this implementation may be extended
043     * to handle such details.
044     * 
045     * @author Ramesh Gupta
046     * @author Karl Schaefer
047     */
048    public class FileSystemModel extends AbstractTreeTableModel {
049        // The the returned file length for directories.
050        private static final Long DIRECTORY = 0L;
051    
052        /**
053         * Creates a file system model using the root directory as the model root.
054         */
055        public FileSystemModel() {
056            this(new File(File.separator));
057        }
058    
059        /**
060         * Creates a file system model using the specified {@code root}.
061         * 
062         * @param root
063         *            the root for this model; this may be different than the root
064         *            directory for a file system.
065         */
066        public FileSystemModel(File root) {
067            super(root);
068        }
069    
070        private boolean isValidFileNode(Object file) {
071            boolean result = false;
072            
073            if (file instanceof File) {
074                File f = (File) file;
075                
076                while (!result && f != null) {
077                    result = f.equals(root);
078                    
079                    f = f.getParentFile();
080                }
081            }
082            
083            return result;
084        }
085        
086        /**
087         * {@inheritDoc}
088         */
089        public File getChild(Object parent, int index) {
090            if (!isValidFileNode(parent)) {
091                throw new IllegalArgumentException("parent is not a file governed by this model");
092            }
093            
094            File parentFile = (File) parent;
095            String[] children = parentFile.list();
096            
097            if (children != null) {
098                return new File(parentFile, children[index]);
099            }
100            
101            return null;
102        }
103    
104        /**
105         * {@inheritDoc}
106         */
107        public int getChildCount(Object parent) {
108            if (parent instanceof File) {
109                String[] children = ((File) parent).list();
110                
111                if (children != null) {
112                    return children.length;
113                }
114            }
115    
116            return 0;
117        }
118    
119        /**
120         * {@inheritDoc}
121         */
122        @Override
123        public Class<?> getColumnClass(int column) {
124            switch (column) {
125            case 0:
126                return String.class;
127            case 1:
128                return Long.class;
129            case 2:
130                return Boolean.class;
131            case 3:
132                return Date.class;
133            default:
134                return super.getColumnClass(column);
135            }
136        }
137    
138        public int getColumnCount() {
139            return 4;
140        }
141    
142        @Override
143        public String getColumnName(int column) {
144            switch (column) {
145            case 0:
146                return "Name";
147            case 1:
148                return "Size";
149            case 2:
150                return "Directory";
151            case 3:
152                return "Modification Date";
153            default:
154                return super.getColumnName(column);
155            }
156        }
157    
158        public Object getValueAt(Object node, int column) {
159            if (node instanceof File) {
160                File file = (File) node;
161                switch (column) {
162                case 0:
163                    return file.getName();
164                case 1:
165                    return isLeaf(node) ? file.length() : DIRECTORY;
166                case 2:
167                    return file.isDirectory();
168                case 3:
169                    return new Date(file.lastModified());
170                }
171            }
172    
173            return null;
174        }
175    
176        /**
177         * {@inheritDoc}
178         */
179        public int getIndexOfChild(Object parent, Object child) {
180            if (parent instanceof File && child instanceof File) {
181                File parentFile = (File) parent;
182                File[] files = parentFile.listFiles();
183                
184                Arrays.sort(files);
185                
186                for (int i = 0, len = files.length; i < len; i++) {
187                    if (files[i].equals(child)) {
188                        return i;
189                    }
190                }
191            }
192            
193            return -1;
194        }
195    
196        /**
197         * {@inheritDoc}
198         */
199        @Override
200        public File getRoot() {
201            return (File) root;
202        }
203    
204        /**
205         * Sets the root for this tree table model. This method will notify
206         * listeners that a change has taken place.
207         * 
208         * @param root
209         *            the new root node to set
210         */
211        public void setRoot(File root) {
212            this.root = root;
213            
214            modelSupport.fireNewRoot();
215        }
216    
217        /**
218         * {@inheritDoc}
219         */
220        @Override
221        public boolean isLeaf(Object node) {
222            if (node instanceof File) {
223                //do not use isFile(); some system files return false
224                return ((File) node).list() == null;
225            }
226            
227            return true;
228        }
229    }