001/*
002 * $Id: SimpleFileSystemModel.java 3927 2011-02-22 16:34:11Z 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
022package org.jdesktop.swingx.treetable;
023
024import java.io.File;
025import java.util.Date;
026
027import javax.swing.event.EventListenerList;
028import javax.swing.event.TreeModelListener;
029import javax.swing.tree.TreePath;
030
031/**
032 * A tree table model to simulate a file system.
033 * <p>
034 * This tree table model implementation does not extends
035 * {@code AbstractTreeTableModel}. The file system metaphor demonstrates that
036 * it is often easier to directly implement tree structures directly instead of
037 * using intermediaries, such as {@code TreeNode}.
038 * <p>
039 * It would be possible to create this same class by extending
040 * {@code AbstractTreeTableModel}, however the number of methods that you would
041 * need to override almost precludes that means of implementation.
042 * <p>
043 * A "full" version of this model might allow editing of file names, the
044 * deletion of files, and the movement of files. This simple implementation does
045 * not intend to tackle such problems, but this implementation may be extended
046 * to handle such details.
047 * 
048 * @author Ramesh Gupta
049 * @author Karl Schaefer
050 */
051public class SimpleFileSystemModel implements TreeTableModel {
052    protected EventListenerList listenerList;
053
054    // the returned file length for directories
055    private static final Long ZERO = Long.valueOf(0);
056
057    private File root;
058
059    /**
060     * Creates a file system model, using the root directory as the model root.
061     */
062    public SimpleFileSystemModel() {
063        this(new File(File.separator));
064    }
065
066    /**
067     * Creates a file system model, using the specified {@code root} as the
068     * model root.
069     */
070    public SimpleFileSystemModel(File root) {
071        this.root = root;
072        this.listenerList = new EventListenerList();
073    }
074
075    /**
076     * {@inheritDoc}
077     */
078    @Override
079    public File getChild(Object parent, int index) {
080        if (parent instanceof File) {
081            File parentFile = (File) parent;
082            File[] files = parentFile.listFiles();
083
084            if (files != null) {
085                return files[index];
086            }
087        }
088
089        return null;
090    }
091
092    /**
093     * {@inheritDoc}
094     */
095    @Override
096    public int getChildCount(Object parent) {
097        if (parent instanceof File) {
098            String[] children = ((File) parent).list();
099            
100            if (children != null) {
101                return children.length;
102            }
103        }
104
105        return 0;
106    }
107
108    /**
109     * {@inheritDoc}
110     */
111    @Override
112    public Class<?> getColumnClass(int column) {
113        switch(column) {
114        case 0:
115            return String.class;
116        case 1:
117            return Long.class;
118        case 2:
119            return Boolean.class;
120        case 3:
121            return Date.class;
122        default:
123            return Object.class;
124        }
125    }
126
127    /**
128     * {@inheritDoc}
129     */
130    @Override
131    public int getColumnCount() {
132        return 4;
133    }
134
135    /**
136     * {@inheritDoc}
137     */
138    @Override
139    public String getColumnName(int column) {
140        switch (column) {
141        case 0:
142            return "Name";
143        case 1:
144            return "Size";
145        case 2:
146            return "Directory";
147        case 3:
148            return "Modification Date";
149        default:
150            return "Column " + column;
151        }
152    }
153
154    /**
155     * {@inheritDoc}
156     */
157    @Override
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 file.isFile() ? file.length() : ZERO;
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    @Override
180    public int getHierarchicalColumn() {
181        return 0;
182    }
183
184    /**
185     * {@inheritDoc}
186     */
187    @Override
188    public boolean isCellEditable(Object node, int column) {
189        return false;
190    }
191
192    /**
193     * {@inheritDoc}
194     */
195    @Override
196    public void setValueAt(Object value, Object node, int column) {
197        //does nothing
198    }
199
200    /**
201     * {@inheritDoc}
202     */
203    @Override
204    public void addTreeModelListener(TreeModelListener l) {
205        listenerList.add(TreeModelListener.class, l);
206    }
207
208    /**
209     * {@inheritDoc}
210     */
211    @Override
212    public int getIndexOfChild(Object parent, Object child) {
213        if (parent instanceof File && child instanceof File) {
214            File parentFile = (File) parent;
215            File[] files = parentFile.listFiles();
216            
217            for (int i = 0, len = files.length; i < len; i++) {
218                if (files[i].equals(child)) {
219                    return i;
220                }
221            }
222        }
223        
224        return -1;
225    }
226
227    /**
228     * {@inheritDoc}
229     */
230    @Override
231    public File getRoot() {
232        return root;
233    }
234
235    /**
236     * {@inheritDoc}
237     */
238    @Override
239    public boolean isLeaf(Object node) {
240        if (node instanceof File) {
241            //do not use isFile(); some system files return false
242            return ((File) node).list() == null;
243        }
244        
245        return true;
246    }
247
248    /**
249     * {@inheritDoc}
250     */
251    @Override
252    public void removeTreeModelListener(TreeModelListener l) {
253        listenerList.remove(TreeModelListener.class, l);
254    }
255
256    /**
257     * {@inheritDoc}
258     */
259    @Override
260    public void valueForPathChanged(TreePath path, Object newValue) {
261        //does nothing
262    }
263    
264    /**
265     * Gets a an array of all the listeners attached to this model.
266     * 
267     * @return an array of listeners; this array is guaranteed to be
268     * non-{@code null}
269     */
270    public TreeModelListener[] getTreeModelListeners() {
271        return listenerList.getListeners(TreeModelListener.class);
272    }
273}