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 }