source: gli/trunk/src/org/greenstone/gatherer/file/FileNode.java@ 16838

Last change on this file since 16838 was 16838, checked in by ak19, 16 years ago

Moved the displayString and toString() added recently in CollectionTreeNode into the superclass FileNode class, since WorkspaceTreeNode may also want to attempt to display filenames with proper encoding (if UTF8)

  • Property svn:keywords set to Author Date Id Revision
File size: 9.7 KB
Line 
1package org.greenstone.gatherer.file;
2
3import java.io.*;
4import java.util.ArrayList;
5import java.util.Enumeration;
6import javax.swing.*;
7import javax.swing.filechooser.*;
8import javax.swing.tree.*;
9import org.greenstone.gatherer.DebugStream;
10import org.greenstone.gatherer.util.ArrayTools;
11
12
13public abstract class FileNode
14 implements MutableTreeNode
15{
16 protected boolean allows_children = true;
17 protected ArrayList child_nodes = null;
18 protected ArrayList child_nodes_unfiltered = null;
19 protected File file = null;
20 protected FileSystemModel model = null;
21 protected MutableTreeNode parent = null;
22 /** The string that is displayed as the filename. Attempts to be in the correct encoding. */
23 protected String displayFileName = null;
24
25
26 public FileNode(File file)
27 {
28 this.file = file;
29
30 // Files cannot have children
31 if (file != null && file.isFile()) {
32 // Cache this result to prevent unceasing missing disk messages being thrown if the
33 // removable media was, um, removed after directory mapped
34 this.allows_children = false;
35 }
36 displayFileName = calcDisplayString();
37 }
38
39
40 /** This method returns a string representation of the filenodes in the Collection
41 * Tree, that can then be displayed in the tree.
42 * We'll initially assume that the filenames are utf8 encoded and so convert the
43 * filename into utf8 for proper presentation in the Collection tree pane.
44 * If the filenames are not utf8, then the conversion would have introduced funny
45 * characters. Therefore, when converting to utf8, if the converted filename
46 * contains the special character '\ufffd', then we know the conversion did not work
47 * and we return the original string which may or may not be properly presented by
48 * default.
49 * See http://java.sun.com/j2se/1.4.2/docs/api/java/nio/charset/CharsetDecoder.html
50 * which says "How a decoding error is handled depends upon the action requested for
51 * that type of error, which is described by an instance of the CodingErrorAction class.
52 * The possible error actions are to ignore the erroneous input, report the error to
53 * the invoker via the returned CoderResult object, or replace the erroneous input with
54 * the current value of the replacement string. The replacement has the initial value
55 * "\uFFFD"; its value may be changed via the replaceWith method."
56 * The following made me think that String(byte[], String charsetName) constructor may
57 * use the replacement value \uFFFD.
58 * http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_20512969.html
59 * mentions the following which made me think of this:
60 * convertedStr = convertedStr.replace('\ufffd', ' ');
61 */
62 protected String calcDisplayString() {
63 String filename = file.getName();
64 try{
65 String utf8filename = new String(filename.getBytes(), "UTF8");
66 if(utf8filename.indexOf('\ufffd') == -1) {
67 return utf8filename;
68 } else { // contains the character indicating that it's invalid utf8
69 // return the original string
70 return filename;
71 }
72 } catch(java.io.UnsupportedEncodingException e) {
73 return filename;
74 }
75 }
76
77 /** Returns the children of the node as an Enumeration. */
78 public Enumeration children()
79 {
80 return new FileEnumeration();
81 }
82
83
84 /** Returns true if the receiver allows children. */
85 public boolean getAllowsChildren()
86 {
87 return allows_children;
88 }
89
90
91 /** Returns the child TreeNode at index childIndex. */
92 public TreeNode getChildAt(int index)
93 {
94 return (TreeNode) child_nodes.get(index);
95 }
96
97
98 /** Returns the number of children TreeNodes the receiver contains. */
99 public int getChildCount()
100 {
101 map();
102
103 // Use the number of (filtered) child nodes
104 if (child_nodes != null) {
105 return child_nodes.size();
106 }
107
108 return 0;
109 }
110
111
112 /** Returns the index of node in the receivers children. */
113 public int getIndex(TreeNode node)
114 {
115 if (child_nodes != null) {
116 return child_nodes.indexOf(node);
117 }
118
119 return -1;
120 }
121
122
123 /** Returns the parent TreeNode of the receiver. */
124 public TreeNode getParent()
125 {
126 return parent;
127 }
128
129
130 /** Adds child to the receiver at index. */
131 public void insert(MutableTreeNode child, int index)
132 {
133 DebugStream.println("Insert " + child + " in " + this + " at index " + index + " [Model: " + model + "]");
134 if (child == null) {
135 return;
136 }
137
138 try {
139 FileNode child_node = (FileNode) child;
140 child_nodes.add(index, child_node);
141 child_node.model = model;
142 child_node.parent = this;
143 }
144 catch (Exception exception) {
145 DebugStream.printStackTrace(exception);
146 }
147 }
148
149
150 /** Returns true if the receiver is a leaf. */
151 public boolean isLeaf()
152 {
153 return (allows_children == false);
154 }
155
156
157 /** Removes the child at index from the receiver. */
158 public void remove(int index)
159 {
160 if (index >= 0 && index < child_nodes.size()) {
161 child_nodes.remove(index);
162 }
163 }
164
165
166 /** Removes node from the receiver. */
167 public void remove(MutableTreeNode node)
168 {
169 remove(getIndex(node));
170 }
171
172
173 /** Removes the receiver from its parent. */
174 public void removeFromParent()
175 {
176 parent.remove(this);
177 parent = null;
178 }
179
180
181 /** Resets the user object of the receiver to object. */
182 public void setUserObject(Object object) {
183 try {
184 file = (File) object;
185 }
186 catch (Exception exception) {
187 DebugStream.printStackTrace(exception);
188 }
189 }
190
191
192 // -------------------------------------------------------------------------------
193
194
195 public void add(MutableTreeNode child)
196 {
197 insert(child, child_nodes.size());
198 }
199
200
201 protected abstract FileNode addChildNode(File file);
202
203
204 /** Compare two FileNodes for equality. */
205 public boolean equals(FileNode node)
206 {
207 if (node == null) {
208 // Definitely not a match
209 return false;
210 }
211
212 if (file != null) {
213 return file.equals(node.getFile());
214 }
215 else {
216 return toString().equals(node.toString());
217 }
218 }
219
220
221 /** Retrieve the file node at the given index, regardless of filters set. */
222 public FileNode getChildAtUnfiltered(int index)
223 {
224 if (index >= 0 && index < size()) {
225 return (FileNode) child_nodes_unfiltered.get(index);
226 }
227 return null;
228 }
229
230
231 public File getFile() {
232 return file;
233 }
234
235
236 /** Retrieves the tree path from the root node to this node. */
237 public TreeNode[] getPath() {
238 int count = 0;
239 TreeNode current = this;
240 while(current != null) {
241 count++;
242 current = current.getParent();
243 }
244 TreeNode[] path = new TreeNode[count];
245 current = this;
246 while(current != null) {
247 path[count - 1] = current;
248 count--;
249 current = current.getParent();
250 }
251 return path;
252 }
253
254
255 public boolean isFileSystemRoot() {
256 if (file != null) {
257 return FileSystemView.getFileSystemView().isFileSystemRoot(file);
258 }
259 else {
260 return false;
261 }
262 }
263
264
265 /** Overridden if necessary by subclasses. */
266 public boolean isInLoadedCollection()
267 {
268 return false;
269 }
270
271
272 /** Overridden if necessary by subclasses. */
273 public boolean isReadOnly()
274 {
275 return false;
276 }
277
278
279 public void map()
280 {
281 // If this node has already been mapped, don't bother doing it again
282 if (child_nodes != null) {
283 return;
284 }
285 child_nodes = new ArrayList();
286
287 // General case, only map if children are allowed
288 if (file != null && getAllowsChildren()) {
289 File[] files = file.listFiles();
290 if (files != null && files.length > 0) {
291 // Sort the child files
292 ArrayTools.sort(files);
293
294 // Now add them to child_nodes_unfiltered
295 child_nodes_unfiltered = new ArrayList();
296 for (int i = 0; i < files.length; i++) {
297 FileNode child_node = this.addChildNode(files[i]);
298 child_nodes_unfiltered.add(child_node);
299 }
300
301 // Apply the filters set in the model
302 FileFilter[] filters = model.getFilters();
303 for (int i = 0; filters != null && i < filters.length; i++) {
304 files = ArrayTools.filter(files, filters[i].filter, filters[i].exclude);
305 }
306
307 // Add the files left after filtering to child_nodes
308 for (int i = 0, j = 0; (i < child_nodes_unfiltered.size() && j < files.length); i++) {
309 // Use the FileNode object in child_nodes_unfiltered rather than creating another
310 FileNode file_node = (FileNode) child_nodes_unfiltered.get(i);
311 if (file_node.getFile().equals(files[j])) {
312 child_nodes.add(file_node);
313 j++;
314 }
315 }
316 }
317
318 model.nodeStructureChanged(this);
319 }
320 }
321
322
323 public void refresh()
324 {
325 unmap();
326 map();
327 }
328
329
330 public void setModel(FileSystemModel model) {
331 this.model = model;
332 }
333
334 public void setParent(MutableTreeNode parent) {
335 this.parent = parent;
336 }
337
338
339 /** Return the total number of child files for the file this node represents, irrespective of filters set. */
340 public int size() {
341 if (child_nodes_unfiltered != null) {
342 return child_nodes_unfiltered.size();
343 }
344 return 0;
345 }
346
347
348 public String toString()
349 {
350 if (isFileSystemRoot()) {
351 return file.getAbsolutePath();
352 }
353 else {
354 if(displayFileName == null) {
355 displayFileName = calcDisplayString();
356 }
357 return displayFileName;
358 }
359 }
360
361
362 /** Unmap this node's children. */
363 public void unmap()
364 {
365 DebugStream.println("Unmapping " + this + "...");
366 child_nodes_unfiltered = null;
367 child_nodes = null;
368 }
369
370
371 private class FileEnumeration
372 implements Enumeration
373 {
374 private int index = 0;
375
376 /** Tests if this enumeration contains more elements. */
377 public boolean hasMoreElements() {
378 return (index < child_nodes.size());
379 }
380
381 /** Returns the next element of this enumeration if this enumeration object has at least one more element to provide. */
382 public Object nextElement() {
383 Object result = null;
384 if (index < child_nodes.size()) {
385 result = child_nodes.get(index);
386 index++;
387 }
388 return result;
389 }
390 }
391}
Note: See TracBrowser for help on using the repository browser.