1 | package org.greenstone.gatherer.file;
|
---|
2 |
|
---|
3 | import java.io.*;
|
---|
4 | import java.util.ArrayList;
|
---|
5 | import java.util.Enumeration;
|
---|
6 | import javax.swing.*;
|
---|
7 | import javax.swing.filechooser.*;
|
---|
8 | import javax.swing.tree.*;
|
---|
9 | import org.greenstone.gatherer.Dictionary;
|
---|
10 | import org.greenstone.gatherer.Gatherer;
|
---|
11 | import org.greenstone.gatherer.collection.BasicCollectionConfiguration;
|
---|
12 | import org.greenstone.gatherer.collection.Collection;
|
---|
13 | import org.greenstone.gatherer.file.FileFilter;
|
---|
14 | import org.greenstone.gatherer.file.FileSystemModel;
|
---|
15 | import org.greenstone.gatherer.util.ArrayTools;
|
---|
16 | import org.greenstone.gatherer.util.StaticStrings;
|
---|
17 | import org.greenstone.gatherer.util.Utility;
|
---|
18 |
|
---|
19 | public class FileNode
|
---|
20 | implements MutableTreeNode {
|
---|
21 |
|
---|
22 | static final private int FALSE = 0;
|
---|
23 | static final private int TRUE = 1;
|
---|
24 | static final private int UNKNOWN = 2;
|
---|
25 |
|
---|
26 | private ArrayList children;
|
---|
27 | private ArrayList raw_children;
|
---|
28 | private boolean children_readonly = true;
|
---|
29 | private boolean current_collection = false;
|
---|
30 | private boolean readonly = true;
|
---|
31 | private File file;
|
---|
32 | private FileSystemModel model;
|
---|
33 | private int allows_children = UNKNOWN;
|
---|
34 | private MutableTreeNode parent;
|
---|
35 | private String title;
|
---|
36 | private boolean gs3_site = false;
|
---|
37 |
|
---|
38 | public FileNode(File file) {
|
---|
39 | // System.err.println("New FileNode(" + file.getAbsolutePath() + ")");
|
---|
40 | this.file = file;
|
---|
41 | }
|
---|
42 |
|
---|
43 | public FileNode(File file, boolean readonly) {
|
---|
44 | this(file);
|
---|
45 | this.children_readonly = readonly;
|
---|
46 | this.readonly = readonly;
|
---|
47 | }
|
---|
48 |
|
---|
49 | public FileNode(File file, FileSystemModel model) {
|
---|
50 | this(file);
|
---|
51 | this.model = model;
|
---|
52 | }
|
---|
53 |
|
---|
54 | public FileNode(File file, FileSystemModel model, boolean readonly) {
|
---|
55 | this(file, readonly);
|
---|
56 | this.model = model;
|
---|
57 | }
|
---|
58 |
|
---|
59 | public FileNode(File file, FileSystemModel model, String title) {
|
---|
60 | this(file, model);
|
---|
61 | this.title = title;
|
---|
62 | }
|
---|
63 |
|
---|
64 | public FileNode(File file, String title) {
|
---|
65 | this(file);
|
---|
66 | this.title = title;
|
---|
67 | }
|
---|
68 |
|
---|
69 | public FileNode(File file, String title, boolean readonly) {
|
---|
70 | this(file, readonly);
|
---|
71 | this.title = title;
|
---|
72 | }
|
---|
73 |
|
---|
74 | public FileNode(File file, FileSystemModel model, String title, boolean readonly) {
|
---|
75 | this(file, model, readonly);
|
---|
76 | this.title = title;
|
---|
77 | }
|
---|
78 |
|
---|
79 | /** The special 'dummy' root node, that is not based on a particular file, but instead holds several special directory mappings. */
|
---|
80 | public FileNode(String title) {
|
---|
81 | this.children = new ArrayList();
|
---|
82 | this.title = title;
|
---|
83 | }
|
---|
84 |
|
---|
85 | /** Returns the children of the receiver as an Enumeration. */
|
---|
86 | public Enumeration children() {
|
---|
87 | return new FileEnumeration();
|
---|
88 | }
|
---|
89 |
|
---|
90 | /** Compare two filenodes for equality. */
|
---|
91 | public boolean equals(FileNode node) {
|
---|
92 | boolean result = false;
|
---|
93 | if(node != null) {
|
---|
94 | if(file != null) {
|
---|
95 | result = (file.equals(node.getFile()));
|
---|
96 | }
|
---|
97 | else {
|
---|
98 | result = toString().equals(node.toString());
|
---|
99 | }
|
---|
100 | }
|
---|
101 | return result;
|
---|
102 | }
|
---|
103 |
|
---|
104 | /** Retrieve the filenode at the given index, regardless of filters set.
|
---|
105 | * @param index int
|
---|
106 | */
|
---|
107 | public FileNode get(int index) {
|
---|
108 | if(0 <= index && index < size() && file != null) {
|
---|
109 | return (FileNode) raw_children.get(index);
|
---|
110 | }
|
---|
111 | return null;
|
---|
112 | }
|
---|
113 |
|
---|
114 | /** Returns true if the receiver allows children. We have to cache the result of this call to prevent unceasing missing disk messages being thrown if the removable media was, um, removed after directory mapped. */
|
---|
115 | public boolean getAllowsChildren() {
|
---|
116 | if(readonly) {
|
---|
117 | if(allows_children == UNKNOWN) {
|
---|
118 | // If the file is non-null but doesn't exist (as is the case for removable media), return true anyway.
|
---|
119 | if(file != null) {
|
---|
120 | if(isFileSystemRoot()) {
|
---|
121 | allows_children = TRUE;
|
---|
122 | }
|
---|
123 | else if(file.exists() && file.isDirectory()) {
|
---|
124 | allows_children = TRUE;
|
---|
125 | }
|
---|
126 | // Any mapped directories always allow children.
|
---|
127 | else if(getParent() != null && getParent().getParent() == null) {
|
---|
128 | allows_children = TRUE;
|
---|
129 | }
|
---|
130 | else {
|
---|
131 | allows_children = FALSE;
|
---|
132 | }
|
---|
133 | }
|
---|
134 | // Allows children is always true for dummy nodes.
|
---|
135 | else {
|
---|
136 | allows_children = TRUE;
|
---|
137 | }
|
---|
138 | }
|
---|
139 | return (allows_children == TRUE);
|
---|
140 | }
|
---|
141 | else {
|
---|
142 | return (file == null || file.isDirectory());
|
---|
143 | }
|
---|
144 | }
|
---|
145 |
|
---|
146 | /** Returns the child TreeNode at index childIndex. */
|
---|
147 | public TreeNode getChildAt(int index) {
|
---|
148 | TreeNode result = null;
|
---|
149 | map();
|
---|
150 | if(0 <= index && index < children.size()) {
|
---|
151 | result = (TreeNode) children.get(index);
|
---|
152 | }
|
---|
153 | else {
|
---|
154 | result = new DefaultMutableTreeNode("Error");
|
---|
155 | }
|
---|
156 | ///ystem.err.println(this + ".getChildAt(" + index + ") = " + result);
|
---|
157 | return result;
|
---|
158 | }
|
---|
159 |
|
---|
160 | /** Returns the number of children TreeNodes the receiver contains. */
|
---|
161 | public int getChildCount() {
|
---|
162 | int size = 0;
|
---|
163 | // We don't automatically map if this is a system root, or we risk the 50,000 Disk not found error messages of death.
|
---|
164 | if(isFileSystemRoot()) {
|
---|
165 | size = 1; // Size is always non-zero for a system root
|
---|
166 | }
|
---|
167 | else {
|
---|
168 | map();
|
---|
169 | }
|
---|
170 | if(children != null) {
|
---|
171 | size = children.size();
|
---|
172 | }
|
---|
173 | ///ystem.err.println(this + ".getChildCount() = " + size);
|
---|
174 | return size;
|
---|
175 | }
|
---|
176 |
|
---|
177 | public File getFile() {
|
---|
178 | return file;
|
---|
179 | }
|
---|
180 |
|
---|
181 | /** Retrieve the icon the system thinks should be assigned to this node. If this node has no file then the icon will be null. */
|
---|
182 | public ImageIcon getIcon() {
|
---|
183 | return null;
|
---|
184 | }
|
---|
185 |
|
---|
186 | /** Returns the index of node in the receivers children. */
|
---|
187 | public int getIndex(TreeNode node) {
|
---|
188 | map();
|
---|
189 | return children.indexOf(node);
|
---|
190 | }
|
---|
191 |
|
---|
192 | /** Returns the parent TreeNode of the receiver. */
|
---|
193 | public TreeNode getParent() {
|
---|
194 | return parent;
|
---|
195 | }
|
---|
196 |
|
---|
197 | /** Retrieves the tree path from the root node to this node. */
|
---|
198 | public TreeNode[] getPath() {
|
---|
199 | int count = 0;
|
---|
200 | TreeNode current = this;
|
---|
201 | while(current != null) {
|
---|
202 | count++;
|
---|
203 | current = current.getParent();
|
---|
204 | }
|
---|
205 | TreeNode[] path = new TreeNode[count];
|
---|
206 | current = this;
|
---|
207 | while(current != null) {
|
---|
208 | path[count - 1] = current;
|
---|
209 | count--;
|
---|
210 | current = current.getParent();
|
---|
211 | }
|
---|
212 | return path;
|
---|
213 | }
|
---|
214 |
|
---|
215 | public void insert(MutableTreeNode child) {
|
---|
216 | insert(child, children.size());
|
---|
217 | }
|
---|
218 |
|
---|
219 | /** Adds child to the receiver at index. */
|
---|
220 | public void insert(MutableTreeNode child, int index) {
|
---|
221 | Gatherer.println("Insert " + child + " in " + this + " at index " + index + " [Model: " + model + "]");
|
---|
222 | if (child == null) {
|
---|
223 | return;
|
---|
224 | }
|
---|
225 | try {
|
---|
226 | children.add(index, child);
|
---|
227 | // Set parent and model.
|
---|
228 | FileNode new_child = (FileNode) child;
|
---|
229 | new_child.setModel(model);
|
---|
230 | new_child.setParent(this);
|
---|
231 | new_child.setReadOnly(readonly);
|
---|
232 | }
|
---|
233 | catch(Exception error) {
|
---|
234 | error.printStackTrace();
|
---|
235 | }
|
---|
236 | }
|
---|
237 |
|
---|
238 | /** Is this file node within the currently open collection? */
|
---|
239 | public boolean isInCurrentCollection() {
|
---|
240 | if(current_collection) {
|
---|
241 | return true;
|
---|
242 | }
|
---|
243 | else {
|
---|
244 | FileNode parent = (FileNode) getParent();
|
---|
245 | if(parent != null) {
|
---|
246 | return parent.isInCurrentCollection();
|
---|
247 | }
|
---|
248 | }
|
---|
249 | return false;
|
---|
250 | }
|
---|
251 |
|
---|
252 | /** Returns true if the receiver is a leaf. */
|
---|
253 | public boolean isLeaf() {
|
---|
254 | return !getAllowsChildren();
|
---|
255 | }
|
---|
256 |
|
---|
257 | public boolean isReadOnly() {
|
---|
258 | return readonly;
|
---|
259 | }
|
---|
260 |
|
---|
261 | public boolean isFileSystemRoot() {
|
---|
262 | boolean result = false;
|
---|
263 | if(file != null) {
|
---|
264 | result = FileSystemView.getFileSystemView().isFileSystemRoot(file);
|
---|
265 | }
|
---|
266 | return result;
|
---|
267 | }
|
---|
268 |
|
---|
269 | public void map() {
|
---|
270 | if(children != null) {
|
---|
271 | return;
|
---|
272 | }
|
---|
273 | children = new ArrayList();
|
---|
274 | // Super Special Case: if the name of this node is the Tree.World string, then we actually map the collections installed in greenstone. The file in this case will actually be the collect directory of greenstone.
|
---|
275 | if(file == null) { // a special mapping folder
|
---|
276 | if (title.equals(Dictionary.get("Tree.World")) && Gatherer.GS3) {
|
---|
277 | // the Greenstone collections folder for GS3 - this contains a
|
---|
278 | // folder for each site
|
---|
279 | File start = new File(Utility.getSitesDir(Gatherer.config.gsdl3_path));
|
---|
280 | File sites[] = start.listFiles();
|
---|
281 | ArrayTools.sort(sites);
|
---|
282 | for (int i=0; sites != null && i < sites.length; i++) {
|
---|
283 | //File site = sites[i];
|
---|
284 | File collect_dir = new File(sites[i], Utility.COL_DIR);
|
---|
285 | if(!collect_dir.exists()) {
|
---|
286 | continue;
|
---|
287 | }
|
---|
288 |
|
---|
289 | FileNode child = new FileNode(sites[i].getName());
|
---|
290 | child.setGS3Site(true);
|
---|
291 | child.unmap();
|
---|
292 | child.setParent(this);
|
---|
293 | child.setModel(model);
|
---|
294 | child.map();
|
---|
295 | children.add(child);
|
---|
296 | }
|
---|
297 |
|
---|
298 | } else if (title.equals(Dictionary.get("Tree.World")) || gs3_site) {
|
---|
299 | // the collect dir for gs2 or a site dir for gs3
|
---|
300 | ///atherer.println("Map the 'Greenstone Collections' node.");
|
---|
301 | // For each of the children directories, which are collections...
|
---|
302 | File start;
|
---|
303 | if (gs3_site) {
|
---|
304 | start = new File(Utility.getCollectDir(Gatherer.config.gsdl3_path, title));
|
---|
305 | } else {
|
---|
306 | start = new File(Utility.getCollectDir(Gatherer.config.gsdl_path));
|
---|
307 | }
|
---|
308 | File cols[] = start.listFiles();
|
---|
309 | ArrayTools.sort(cols);
|
---|
310 | // We add their import directories, except for the model collection
|
---|
311 | for(int i = 0; cols != null && i < cols.length; i++) {
|
---|
312 | if (!cols[i].getName().equals(StaticStrings.MODEL_COLLECTION_NAME)) {
|
---|
313 | //File dirs[] = cols[i].listFiles();
|
---|
314 | //ArrayTools.sort(dirs);
|
---|
315 | File import_dir = new File(cols[i], StaticStrings.IMPORT_FOLDER);
|
---|
316 | if(!import_dir.exists()) {
|
---|
317 | continue;
|
---|
318 | }
|
---|
319 | // we don't care if there is no config file
|
---|
320 | BasicCollectionConfiguration collect_cfg = new BasicCollectionConfiguration(new File(cols[i], Utility.CONFIG_FILE));
|
---|
321 | FileNode collection_root = new FileNode(import_dir, collect_cfg.toString(), true);
|
---|
322 | collection_root.setParent(this);
|
---|
323 | collection_root.setModel(model);
|
---|
324 | // One last piece of magic so we can determine the current collection
|
---|
325 | Collection collection = Gatherer.c_man.getCollection();
|
---|
326 | if(collection != null) {
|
---|
327 | collection_root.setCurrentCollection(cols[i].getName().equals(collection.getName()));
|
---|
328 | }
|
---|
329 | children.add(collection_root);
|
---|
330 | collection_root = null;
|
---|
331 | import_dir = null;
|
---|
332 | //dirs = null;
|
---|
333 | }
|
---|
334 | } // for each coll
|
---|
335 | cols = null;
|
---|
336 | model.nodeStructureChanged(this);
|
---|
337 | }
|
---|
338 | } // if file == null
|
---|
339 | // General case: Only map if there are no children.
|
---|
340 | else { // file !=null
|
---|
341 | if(getAllowsChildren()) {
|
---|
342 | File[] files = file.listFiles();
|
---|
343 | if(files != null && files.length > 0) {
|
---|
344 | // Sort the remaining files.
|
---|
345 | Gatherer.println("Number of files to sort: " + files.length);
|
---|
346 | ArrayTools.sort(files);
|
---|
347 | Gatherer.println("Files sorted.");
|
---|
348 | // Now add them to children.
|
---|
349 | raw_children = new ArrayList();
|
---|
350 | for(int i = 0; i < files.length; i++) {
|
---|
351 | FileNode child = new FileNode(files[i], model, children_readonly);
|
---|
352 | child.setParent(this);
|
---|
353 | raw_children.add(child);
|
---|
354 | }
|
---|
355 |
|
---|
356 | // Apply the filters set in the model.
|
---|
357 | //if(model != null) {
|
---|
358 | FileFilter[] filters = model.getFilters();
|
---|
359 | for(int i = 0; filters != null && i < filters.length; i++) {
|
---|
360 | files = ArrayTools.filter(files, filters[i].filter, filters[i].exclude);
|
---|
361 | }
|
---|
362 | //}
|
---|
363 |
|
---|
364 | // Now add them to children.
|
---|
365 | for(int i = 0; i < files.length; i++) {
|
---|
366 | FileNode child = new FileNode(files[i], model, children_readonly);
|
---|
367 | int index = -1;
|
---|
368 | if((index = raw_children.indexOf(child)) != -1) {
|
---|
369 | children.add(raw_children.get(index));
|
---|
370 | }
|
---|
371 | else {
|
---|
372 | child.setParent(this);
|
---|
373 | children.add(child);
|
---|
374 | }
|
---|
375 | }
|
---|
376 | }
|
---|
377 | model.nodeStructureChanged(this);
|
---|
378 | }
|
---|
379 | }
|
---|
380 |
|
---|
381 | }
|
---|
382 |
|
---|
383 | /** Removes the child at index from the receiver. */
|
---|
384 | public void remove(int index) {
|
---|
385 | if(0 <= index && index < children.size()) {
|
---|
386 | children.remove(index);
|
---|
387 | }
|
---|
388 | }
|
---|
389 |
|
---|
390 | /** Removes node from the receiver. */
|
---|
391 | public void remove(MutableTreeNode node){
|
---|
392 | int index = getIndex(node);
|
---|
393 | if(index != -1) {
|
---|
394 | children.remove(index);
|
---|
395 | }
|
---|
396 | }
|
---|
397 |
|
---|
398 | /** Removes the receiver from its parent. */
|
---|
399 | public void removeFromParent() {
|
---|
400 | parent.remove(this);
|
---|
401 | parent = null;
|
---|
402 | }
|
---|
403 |
|
---|
404 | public void setCurrentCollection(boolean current_collection) {
|
---|
405 | this.current_collection = current_collection;
|
---|
406 | }
|
---|
407 |
|
---|
408 | /* private void setChildrenReadOnly(boolean children_readonly) {
|
---|
409 | this.children_readonly = children_readonly;
|
---|
410 | } */
|
---|
411 |
|
---|
412 | public void setFile(File file) {
|
---|
413 | this.file = file;
|
---|
414 | }
|
---|
415 |
|
---|
416 | public void setGS3Site(boolean is_site) {
|
---|
417 | this.gs3_site = is_site;
|
---|
418 | }
|
---|
419 |
|
---|
420 | public void setModel(FileSystemModel model) {
|
---|
421 | this.model = model;
|
---|
422 | }
|
---|
423 |
|
---|
424 | public void setParent(MutableTreeNode parent) {
|
---|
425 | this.parent = parent;
|
---|
426 | }
|
---|
427 |
|
---|
428 | public void setReadOnly(boolean readonly) {
|
---|
429 | this.readonly = readonly;
|
---|
430 | }
|
---|
431 |
|
---|
432 | /** Resets the user object of the receiver to object. */
|
---|
433 | public void setUserObject(Object object) {
|
---|
434 | try {
|
---|
435 | file = (File) object;
|
---|
436 | title = null;
|
---|
437 | }
|
---|
438 | catch(Exception error) {
|
---|
439 | error.printStackTrace();
|
---|
440 | }
|
---|
441 | }
|
---|
442 |
|
---|
443 | /** Retrieve the total number of child files for the file this node represents, irrespective of filters set
|
---|
444 | * @return int
|
---|
445 | */
|
---|
446 | public int size() {
|
---|
447 | if(raw_children != null) {
|
---|
448 | return raw_children.size();
|
---|
449 | }
|
---|
450 | return 0;
|
---|
451 | }
|
---|
452 |
|
---|
453 | public String toString() {
|
---|
454 | if(title == null) {
|
---|
455 | if(isFileSystemRoot()) {
|
---|
456 | title = file.getAbsolutePath();
|
---|
457 | }
|
---|
458 | else {
|
---|
459 | title = file.getName();
|
---|
460 | }
|
---|
461 | }
|
---|
462 | return title;
|
---|
463 | }
|
---|
464 |
|
---|
465 | /** Unmap this nodes children. */
|
---|
466 | public void unmap() {
|
---|
467 | // You cannot unmap nodes that have no file basis.
|
---|
468 | if(file != null || title.equals(Dictionary.get("Tree.World")) || gs3_site) {
|
---|
469 | ///atherer.println("Unmap: " + this);
|
---|
470 | children = null;
|
---|
471 | raw_children = null;
|
---|
472 | }
|
---|
473 | else {
|
---|
474 | ///ystem.err.println("No file for " + this + " - can't unmap.");
|
---|
475 | }
|
---|
476 | }
|
---|
477 |
|
---|
478 | private class FileEnumeration
|
---|
479 | implements Enumeration {
|
---|
480 | private int index = 0;
|
---|
481 | /** Tests if this enumeration contains more elements. */
|
---|
482 | public boolean hasMoreElements() {
|
---|
483 | return (index < children.size());
|
---|
484 | }
|
---|
485 | /** Returns the next element of this enumeration if this enumeration object has at least one more element to provide. */
|
---|
486 | public Object nextElement() {
|
---|
487 | Object result = null;
|
---|
488 | if(index < children.size()) {
|
---|
489 | result = children.get(index);
|
---|
490 | index++;
|
---|
491 | }
|
---|
492 | return result;
|
---|
493 | }
|
---|
494 | }
|
---|
495 | }
|
---|