View Javadoc
1   package wjhk.jupload2.gui.filepanel.treeview;
2   
3   import java.io.File;
4   import java.util.Date;
5   
6   import javax.swing.table.TableModel;
7   import javax.swing.tree.TreePath;
8   
9   import wjhk.jupload2.exception.JUploadExceptionStopAddingFiles;
10  import wjhk.jupload2.gui.filepanel.FilePanel;
11  import wjhk.jupload2.gui.filepanel.FilePanelFlatDataModel2;
12  import wjhk.jupload2.policies.UploadPolicy;
13  
14  /**
15   * This code is taken from the tutorial written by Jörn Hameister, <A
16   * HREF="http://www.hameister.org/JavaSwingTreeTable.html">available here</A>.<BR/>
17   * <BR/>
18   * In the class MyDataModel the concrete data model view is defined. Ie, the columns are defined including data type.
19   * The class also contains the unimplemented methods of the interface TreeModel . It should be noted the method
20   * isCellEditable . This has the value true return so that the Listener on a treeExpanded or treeCollapsed can respond.
21   * One could in this method only for the first column ( column ) the value true and otherwise return the value false .
22   * 
23   * @author Jörn Hameister
24   */
25  public class FileDataTreeViewModel extends MyAbstractTreeTableModel<TreeFileDataNode> {
26  
27      /**
28       * The uploadPolicy contains all current parameter, including the FileDataParam
29       */
30      UploadPolicy uploadPolicy = null;
31  
32      /**
33       * The column names, as displayed on the applet. They are not real final values, as they are translated: we need to
34       * have an uploadPolicy for this translation, and the uploadPolicy is 'given' to the constructor.
35       */
36      final static String COL_NAME = "colName";
37  
38      final static String COL_SIZE = "colSize";
39  
40      final static String COL_DIRECTORY = "colDirectory";
41  
42      final static String COL_MODIFIED = "colModified";
43  
44      /** The localized column name, for the file name */
45      String colName = null;
46  
47      /** The localized column name, for the file size */
48      String colSize = null;
49  
50      /** The localized column name, for the file directory */
51      String colDirectory = null;
52  
53      /** The localized column name, for the file last modification date */
54      String colModified = null;
55  
56      String[] columnNames = null;
57  
58      /**
59       * This array indicates, for each column, the percentage of the available width it should use. It's initialized in
60       * the constructor of this class.
61       * 
62       * @see #getColumnSize(int)
63       */
64      int[] columnSizePercentage = null;
65  
66      /**
67       * Indicates whether each column is editable or not. Only the check box should be editable.
68       */
69      boolean[] columnEditable = null;
70  
71      Class<?> columnClasses[] = null;
72  
73      public FileDataTreeViewModel(UploadPolicy uploadPolicy, FilePanelFlatDataModel2 flatModel) {
74          // The models are unknown now. They will be set later.
75          super(uploadPolicy, new RootNode(uploadPolicy, null, flatModel));
76  
77          this.uploadPolicy = uploadPolicy;
78  
79          // Initialization for column name
80          colName = this.uploadPolicy.getLocalizedString(COL_NAME);
81          colSize = this.uploadPolicy.getLocalizedString(COL_SIZE);
82          colDirectory = this.uploadPolicy.getLocalizedString(COL_DIRECTORY);
83          colModified = this.uploadPolicy.getLocalizedString(COL_MODIFIED);
84          this.columnNames = new String[] {
85                  colName, colSize, colDirectory, colModified, ""
86          };
87  
88          // Initial size (before percentage) was: 150, 75, 199, 130, 75
89          this.columnSizePercentage = new int[] {
90                  50, 17, 15, 15, 3
91          };
92  
93          this.columnClasses = new Class[] {
94                  MyTreeTableModel.class, Long.class, String.class, Date.class, Boolean.class
95          };
96  
97          // Initial size (before percentage) was: 150, 75, 199, 130, 75
98          this.columnEditable = new boolean[] {
99                  true, false, false, false, true
100         };
101     }
102 
103     /** {@inheritDoc} */
104     public int getColumnCount() {
105         return columnNames.length;
106     }
107 
108     /** {@inheritDoc} */
109     public String getColumnName(int column) {
110         return columnNames[column];
111     }
112 
113     /** {@inheritDoc} */
114     public int getColumnSizePercentage(int col) {
115         return this.columnSizePercentage[col];
116     }
117 
118     /** {@inheritDoc} */
119     public Class<?> getColumnClass(int column) {
120         return columnClasses[column];
121     }
122 
123     /** {@inheritDoc} */
124     public Object getValueAt(TreeFileDataNode node, int column) {
125         if (node != null) {
126             String columnName = getColumnName(column);
127             // Don't know if it will be useful, but the switch below allows the
128             // column to be in any order.
129             if (columnName.equals(colName)) {
130                 return node.getFileName();
131             } else if (columnName.equals(colSize)) {
132                 return Long.valueOf(node.getFileLength());
133             } else if (columnName.equals(colDirectory)) {
134                 return node.getDirectory();
135             } else if (columnName.equals(colModified)) {
136                 return node.getLastModified();
137             } else if (columnName.equals("")) {
138                 return node.getUploadFlag();
139             } else {
140                 this.uploadPolicy.displayErr("Unknown column in " + this.getClass().getName() + ": " + columnName);
141                 return null;
142             }
143         } else {
144             return null;
145         }
146     }
147 
148     /** @see TableModel#isCellEditable(int, int) */
149     public boolean isCellEditable(TreeFileDataNode node, int column) {
150         return columnEditable[column];
151     }
152 
153     /** @see TableModel#setValueAt(Object, int, int) */
154     public void setValueAt(Object aValue, TreeFileDataNode node, int col) {
155         if (!columnEditable[col]) {
156             this.uploadPolicy.displayWarn(this.getClass().getName() + ".setValueAt: no action");
157         } else {
158             if (!(aValue instanceof Boolean)) {
159                 this.uploadPolicy.displayErr("Internal error in " + this.getClass().getName()
160                         + ": aValue should be a Boolean but is a " + aValue.getClass().getName());
161             } else {
162                 node.setUploadFlag((Boolean) aValue);
163             }
164         }
165     }
166 
167     /**
168      * Returns the {@link TreePath} for the given {@link File}. This TreePath is basically the array of
169      * {@link TreeFileDataNode}, representing the given file. the root path if the filename for the {@link RootNode},
170      * that is: the empty String: ""
171      * 
172      * @param fileRoot The File we want the path for. It may be the root, a folder, leaf...
173      * @return The TreePath for this file, or null if this file doesn't exist in the existing hierarchy. public TreePath
174      *         getTreePath(TreeFileDataNode fileData) { TreeFileDataNode parent = (TreeFileDataNode)
175      *         fileData.getParent(); if (parent == null) { TreeFileDataNode[] path = (TreeFileDataNode[])
176      *         (Array.newInstance(TreeFileDataNode.class, 2)); path[0] = absoluteRoot; path[1] = ((FolderNode)
177      *         absoluteRoot).getChild(fileData.getFileName()); return new TreePath(path); } else { TreePath
178      *         treePathParent = getTreePath(parent); if (treePathParent == null) { // The parent is not in the file
179      *         hierarchy, so this file is not either. return null; } else { FolderNode folderNodeParent = (FolderNode)
180      *         treePathParent.getLastPathComponent(); TreeFileDataNode tfdn =
181      *         folderNodeParent.getChild(fileData.getFileName()); if (tfdn == null) { // This file doesn't exist in the
182      *         hierarchy. return null; } else { return treePathParent.pathByAddingChild(tfdn); } } } }
183      */
184 
185     /**
186      * Returns the {@link TreePath} for the given {@link File}. This TreePath is basically the array of
187      * {@link TreeFileDataNode}, representing the given file. the root path if the filename for the {@link RootNode},
188      * that is: the empty String: ""
189      * 
190      * @param fileRoot The File we want the path for. It may be the root, a folder, leaf... If the given file is null,
191      *            the returned {@link TreePath} is a one level {@link TreePath}, containing only the absolute root of
192      *            the tree view.
193      * @param createIntermediateNode If true, all missing intermediate nodes are created. If false, no node are created.
194      *            In this last case, the returned {@link TreePath} is null, if one or mode nodes are missing.
195      * @return The TreePath for this file, or null if this file doesn't exist in the existing hierarchy.
196      * @throws JUploadExceptionStopAddingFiles
197      */
198     public TreePath getTreePathFromFile(File file, boolean createIntermediateNode)
199             throws JUploadExceptionStopAddingFiles {
200         if (this.uploadPolicy.getFileListViewMode() != FilePanel.FileListViewMode.FLAT
201                 && this.uploadPolicy.getFileListViewMode() != FilePanel.FileListViewMode.TREE_VIEW) {
202             throw new IllegalStateException(this.getClass().getName()
203                     + ".getTreePathFromFile(File) may not be called when the ListViewMode is in "
204                     + this.uploadPolicy.getFileListViewMode() + " mode");
205         }
206         if (file == null) {
207             return new TreePath(absoluteRoot);
208         } else {
209             TreePath parentTreePath = getTreePathFromFile(file.getParentFile(), createIntermediateNode);
210             if (parentTreePath == null) {
211                 // At least one node is missing. We can't build the TreePath
212                 return null;
213             } else {
214                 // The parent TreePath should finish by a folder ... as it's our parent !
215                 FolderNode parentNode = (FolderNode) parentTreePath.getLastPathComponent();
216                 TreeFileDataNode node = parentNode.getChild(file);
217 
218                 // If file is not a child of the node for the parent file, we may have to create a new Node.
219                 if (node == null && createIntermediateNode) {
220                     node = parentNode.addChild(file);
221                 }
222 
223                 // If there is no node for our file ... there is no valid TreePath
224                 if (node == null) {
225                     return null;
226                 } else {
227                     return parentTreePath.pathByAddingChild(node);
228                 }
229             }/*
230               * File parent = file.getParentFile(); if (parent == null) { TreeFileDataNode[] path = (TreeFileDataNode[])
231               * (Array.newInstance(TreeFileDataNode.class, 2)); path[0] = absoluteRoot; path[1] = ((FolderNode)
232               * absoluteRoot).getChild(file); // if the root doesn't return new TreePath(path); } else { TreePath
233               * treePathParent = getTreePathFromFile(parent, createIntermediateNode); if (treePathParent == null) { //
234               * The parent is not in the file hierarchy, so this file is not either. return null; } else { FolderNode
235               * folderNodeParent = (FolderNode) treePathParent.getLastPathComponent(); TreeFileDataNode tfdn =
236               * folderNodeParent.getChild(file); if (tfdn == null) { // This file doesn't exist in the hierarchy. return
237               * null; } else { return treePathParent.pathByAddingChild(tfdn); } } }
238               */
239         }// if (file == null)
240     }
241 
242     /**
243      * Returns the node corresponding to the given treePath, or null if this node is'nt found, when crawling the
244      * hierarchy behind {@link #absoluteRoot}.
245      * 
246      * @param treePath A {@link TreePath} made of String. If it's made of String, each represents a filename. If it's a
247      *            {@link TreePath} made of TreeFileDataNode, this method works, but it's much quicker to just use
248      *            {@link TreePath#getLastPathComponent()} instead of this method.
249      * @return TreeFileDataNode getNode(TreePath treePath) { TreePath parent = treePath.getParentPath(); if (parent ==
250      *         null) { return absoluteRoot; } else { TreeFileDataNode parentNode = getNode(parent); return
251      *         (TreeFileDataNode) parentNode.getChild((String) treePath.getLastPathComponent()); } }
252      */
253 
254     /**
255      * This method returns
256      * 
257      * @param file
258      * @return
259      * @throws JUploadExceptionStopAddingFiles public FolderNode getFolderNodeForNewFile(File file) throws
260      *             JUploadExceptionStopAddingFiles { return getFolderNodeForNewFile(file.getParentFile(), null); }
261      */
262 
263     /**
264      * This method searches in the hierarchy of files and folder to upload, the node which contains this file. If this
265      * node doesn't exist, it is first created and attached into the hierarchy.
266      * 
267      * @param o A File or Folder. If o is a file, it will just be added. If o is a folder, it will be added, including
268      *            all its content (child, grand-child...)
269      * @return The number of files added to the hierarchy. Folders are not counted.
270      * @throws JUploadExceptionStopAddingFiles When an exception occurs
271      * @throws NullPointerException When o is null
272      * @throws IllegalArgumentException When o is not a {@link File}
273      * @see MyAbstractTreeTableModel
274      */
275     public int attachObject(Object o) throws NullPointerException, IllegalArgumentException,
276             JUploadExceptionStopAddingFiles {
277         if (o == null) {
278             throw new NullPointerException(this.getClass().getName() + " has been called with a null parameter");
279         } else if (!(o instanceof File)) {
280             throw new IllegalArgumentException(this.getClass().getName() + " has been called with a "
281                     + o.getClass().getName() + ". It must be a File");
282         }
283         File file = (File) o;
284 
285         FolderNode parentNode = null;
286         switch (uploadPolicy.getFileListViewMode()) {
287             case FLAT:
288             case TREE_VIEW:
289                 // We're mapped to the file system, we need to find the parent for this file.
290                 if (file.getParentFile() != null) {
291                     parentNode = (FolderNode) getTreePathFromFile(file.getParentFile(), true).getLastPathComponent();
292                 } else {
293                     parentNode = (FolderNode) absoluteRoot;
294                 }
295                 break;
296             case INDEPENDENT_TREE_VIEW:
297                 // Otherwise, it's just a new child of the root.
298                 parentNode = (FolderNode) absoluteRoot;
299         }
300 
301         if (parentNode == null) {
302             throw new IllegalArgumentException("Root not found for file " + file.getAbsolutePath());
303         }
304 
305         return parentNode.addChildAndDescendants(file);
306     }
307 
308     /** {@inheritDoc} */
309     public TreePath getTreePathForObject(Object o) {
310         if (o != null && !(o instanceof File)) {
311             throw new IllegalArgumentException(this.getClass().getName() + " has been called with a "
312                     + o.getClass().getName() + ". It must be a File");
313         }
314         try {
315             return getTreePathFromFile((File) o, false);
316         } catch (JUploadExceptionStopAddingFiles e) {
317             throw new IllegalStateException(e.getMessage(), e);
318         }
319     }
320 
321 }