source: gli/branches/rtl-gli/src/org/greenstone/gatherer/file/FileNode.java@ 18354

Last change on this file since 18354 was 18354, checked in by kjdon, 15 years ago

updated the rtl-gli branch with files from trunk. Result of a merge 14807:18318

  • Property svn:keywords set to Author Date Id Revision
File size: 9.8 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.isDirectory()) { //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 displayFileName = calcDisplayString();
36 }
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; //return file.getName();
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.