improve file manager
[jSite2.git] / src / net / pterodactylus / jsite / gui / FileManager.java
1 /*
2  * jSite2 - FileManager.java -
3  * Copyright © 2008 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 package net.pterodactylus.jsite.gui;
21
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.FlowLayout;
25 import java.awt.GridBagConstraints;
26 import java.awt.GridBagLayout;
27 import java.awt.Insets;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.beans.PropertyChangeEvent;
31 import java.beans.PropertyChangeListener;
32 import java.io.File;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
37
38 import javax.swing.BorderFactory;
39 import javax.swing.JButton;
40 import javax.swing.JCheckBox;
41 import javax.swing.JComboBox;
42 import javax.swing.JDialog;
43 import javax.swing.JFrame;
44 import javax.swing.JPanel;
45 import javax.swing.JScrollPane;
46 import javax.swing.JTree;
47 import javax.swing.event.TreeModelListener;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.tree.TreeModel;
51 import javax.swing.tree.TreePath;
52
53 import net.pterodactylus.jsite.i18n.I18n;
54 import net.pterodactylus.jsite.i18n.I18nable;
55 import net.pterodactylus.jsite.i18n.gui.I18nAction;
56 import net.pterodactylus.jsite.i18n.gui.I18nLabel;
57 import net.pterodactylus.jsite.project.Entry;
58 import net.pterodactylus.jsite.project.Project;
59 import net.pterodactylus.util.data.Node;
60 import net.pterodactylus.util.data.Tree;
61 import net.pterodactylus.util.io.MimeTypes;
62 import net.pterodactylus.util.logging.Logging;
63 import net.pterodactylus.util.swing.SwingUtils;
64
65 /**
66  * Manages physical and virtual files in a project.
67  * 
68  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
69  */
70 public class FileManager extends JDialog implements I18nable, ActionListener, TreeSelectionListener {
71
72         /** Logger. */
73         private static final Logger logger = Logging.getLogger(FileManager.class.getName());
74
75         /** The project whose files to manage. */
76         private final Project project;
77
78         /** The tree model for the project files. */
79         private final FileTreeModel fileTreeModel;
80
81         /** The “close” action. */
82         private I18nAction closeAction;
83
84         /** The “project files” label. */
85         private I18nLabel projectFilesLabel;
86
87         /** The tree that shows the files. */
88         private JTree fileTree;
89
90         /** The “insert” action. */
91         private I18nAction insertAction;
92
93         /** The “insert” checkbox. */
94         private JCheckBox insertCheckBox;
95
96         /** The “use custom mime type” action. */
97         private I18nAction useCustomMimeTypeAction;
98
99         /** The “use custom mime type” checkbox. */
100         private JCheckBox useCustomMimeTypeCheckBox;
101
102         /** The “mime type” combo box. */
103         private JComboBox mimeTypeComboBox;
104
105         /**
106          * Creates a new file manager.
107          * 
108          * @param parent
109          *            The parent frame
110          * @param project
111          *            The project whose files to manage
112          */
113         public FileManager(JFrame parent, Project project) {
114                 super(parent, I18n.get("fileManager.title", project.getName()), true);
115                 logger.log(Level.FINEST, "project: " + project);
116                 this.project = project;
117                 fileTreeModel = new FileTreeModel();
118                 initActions();
119                 initComponents();
120                 pack();
121                 SwingUtils.center(this);
122         }
123
124         //
125         // PRIVATE METHODS
126         //
127
128         /**
129          * Initializes all actions.
130          */
131         private void initActions() {
132                 closeAction = new I18nAction("fileManager.button.close") {
133
134                         /**
135                          * {@inheritDoc}
136                          */
137                         public void actionPerformed(ActionEvent e) {
138                                 setVisible(false);
139                         }
140                 };
141                 insertAction = new I18nAction("fileManager.checkbox.insertFile") {
142
143                         /**
144                          * {@inheritDoc}
145                          */
146                         @SuppressWarnings("synthetic-access")
147                         public void actionPerformed(ActionEvent actionEvent) {
148                                 useCustomMimeTypeAction.setEnabled(insertCheckBox.isSelected());
149                                 mimeTypeComboBox.setEnabled(insertCheckBox.isSelected());
150                                 for (Entry entry: getSelectedEntries()) {
151                                         entry.setInsert(insertCheckBox.isSelected());
152                                 }
153                         }
154                 };
155                 insertAction.setEnabled(false);
156                 useCustomMimeTypeAction = new I18nAction("fileManager.checkbox.useCustomMimeType") {
157
158                         /**
159                          * {@inheritDoc}
160                          */
161                         @SuppressWarnings("synthetic-access")
162                         public void actionPerformed(ActionEvent actionEvent) {
163                                 mimeTypeComboBox.setEnabled(useCustomMimeTypeCheckBox.isSelected());
164                                 if (!useCustomMimeTypeCheckBox.isSelected()) {
165                                         for (Entry entry: getSelectedEntries()) {
166                                                 entry.setContentType(null);
167                                         }
168                                 }
169                         }
170                 };
171                 useCustomMimeTypeAction.setEnabled(false);
172         }
173
174         /**
175          * Initializes all components.
176          */
177         private void initComponents() {
178                 JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
179                 contentPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
180
181                 contentPanel.add(createFileManagerPanel(), BorderLayout.CENTER);
182                 contentPanel.add(createButtonPanel(), BorderLayout.PAGE_END);
183
184                 setContentPane(contentPanel);
185         }
186
187         /**
188          * Creates the main panel with the file tree and the file properties.
189          * 
190          * @return The mail panel
191          */
192         private Component createFileManagerPanel() {
193                 JPanel fileManagerPanel = new JPanel(new BorderLayout(12, 12));
194
195                 JPanel fileTreePanel = new JPanel(new BorderLayout(12, 12));
196                 fileManagerPanel.add(fileTreePanel, BorderLayout.LINE_START);
197
198                 fileTree = new JTree(fileTreeModel);
199                 fileTree.setShowsRootHandles(false);
200                 fileTree.addTreeSelectionListener(this);
201                 fileTreePanel.add(new JScrollPane(fileTree), BorderLayout.CENTER);
202
203                 projectFilesLabel = new I18nLabel("fileManager.label.projectFiles", fileTree);
204                 JPanel projectFilesLabelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
205                 fileTreePanel.add(projectFilesLabelPanel, BorderLayout.NORTH);
206                 projectFilesLabelPanel.add(projectFilesLabel);
207
208                 JPanel propertiesPanel = new JPanel(new GridBagLayout());
209                 fileManagerPanel.add(propertiesPanel, BorderLayout.CENTER);
210                 propertiesPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder(12, 12, 12, 12)));
211
212                 insertCheckBox = new JCheckBox(insertAction);
213                 propertiesPanel.add(insertCheckBox, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
214
215                 useCustomMimeTypeCheckBox = new JCheckBox(useCustomMimeTypeAction);
216                 List<String> allMimeTypes = MimeTypes.getAllMimeTypes();
217                 mimeTypeComboBox = new JComboBox(allMimeTypes.toArray(new String[0]));
218                 mimeTypeComboBox.setEnabled(false);
219                 mimeTypeComboBox.addActionListener(this);
220                 propertiesPanel.add(useCustomMimeTypeCheckBox, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 0, 0, 0), 0, 0));
221                 propertiesPanel.add(mimeTypeComboBox, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 6, 0, 0), 0, 0));
222
223                 propertiesPanel.add(new JPanel(), new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
224
225                 return fileManagerPanel;
226         }
227
228         /**
229          * Creates the button panel.
230          * 
231          * @return The button panel
232          */
233         private Component createButtonPanel() {
234                 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
235
236                 buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
237                 JButton closeButton = new JButton(closeAction);
238                 buttonPanel.add(closeButton);
239
240                 getRootPane().setDefaultButton(closeButton);
241                 return buttonPanel;
242         }
243
244         /**
245          * Returns a list of all selected entries.
246          * 
247          * @return The selected entries
248          */
249         private List<Entry> getSelectedEntries() {
250                 TreePath[] selectedPaths = fileTree.getSelectionPaths();
251                 List<Entry> entries = new ArrayList<Entry>();
252                 for (TreePath selectedPath: selectedPaths) {
253                         Entry entry = ((FileTreePath) selectedPath.getLastPathComponent()).getFileEntry();
254                         if (entry != null) {
255                                 entries.add(entry);
256                         }
257                 }
258                 return entries;
259         }
260
261         //
262         // INTERFACE I18nable
263         //
264
265         /**
266          * {@inheritDoc}
267          */
268         public void updateI18n() {
269                 setTitle(I18n.get("fileManager.title", project.getName()));
270         }
271
272         //
273         // INTERFACE TreeSelectionListener
274         //
275
276         /**
277          * {@inheritDoc}
278          */
279         public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
280                 TreePath[] selectedPaths = fileTree.getSelectionPaths();
281                 if (selectedPaths.length == 1) {
282                         Entry fileEntry = ((FileTreePath) selectedPaths[0].getLastPathComponent()).getFileEntry();
283                         if (fileEntry == null) {
284                                 /* some directory node selected. */
285                                 insertAction.setEnabled(false);
286                                 insertCheckBox.setSelected(false);
287                                 useCustomMimeTypeAction.setEnabled(false);
288                                 useCustomMimeTypeCheckBox.setSelected(false);
289                                 mimeTypeComboBox.setEnabled(false);
290                         } else {
291                                 String contentType = fileEntry.getContentType();
292                                 insertAction.setEnabled(true);
293                                 insertCheckBox.setSelected(fileEntry.isInsert());
294                                 useCustomMimeTypeAction.setEnabled(fileEntry.isInsert());
295                                 useCustomMimeTypeCheckBox.setSelected(contentType != null);
296                                 mimeTypeComboBox.setEnabled(contentType != null);
297                                 mimeTypeComboBox.setSelectedItem(contentType);
298                         }
299                 }
300         }
301
302         //
303         // INTERFACE ActionListener
304         //
305
306         /**
307          * {@inheritDoc}
308          */
309         public void actionPerformed(ActionEvent actionEvent) {
310                 if (actionEvent.getSource() == mimeTypeComboBox) {
311                         String contentType = (String) mimeTypeComboBox.getSelectedItem();
312                         for (Entry entry: getSelectedEntries()) {
313                                 entry.setContentType(contentType);
314                         }
315                         useCustomMimeTypeCheckBox.setSelected(contentType != null);
316                 }
317         }
318
319         /**
320          * Model for the tree of files.
321          * 
322          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
323          */
324         private class FileTreeModel implements TreeModel, PropertyChangeListener {
325
326                 /** Tree model listeners. */
327                 private final List<TreeModelListener> treeModelListeners = new ArrayList<TreeModelListener>();
328
329                 /** The tree of files. */
330                 private final Tree<FileTreePath> fileTreePathTree = new Tree<FileTreePath>();
331
332                 /**
333                  * Creates a new file tree model.
334                  */
335                 FileTreeModel() {
336                         buildTree();
337                 }
338
339                 //
340                 // EVENT MANAGEMENT
341                 //
342
343                 /**
344                  * {@inheritDoc}
345                  */
346                 public void addTreeModelListener(TreeModelListener treeModelListener) {
347                         treeModelListeners.add(treeModelListener);
348                 }
349
350                 /**
351                  * {@inheritDoc}
352                  */
353                 public void removeTreeModelListener(TreeModelListener treeModelListener) {
354                         treeModelListeners.remove(treeModelListener);
355                 }
356
357                 //
358                 // ACCESSORS
359                 //
360
361                 /**
362                  * {@inheritDoc}
363                  */
364                 @SuppressWarnings("synthetic-access")
365                 public Object getChild(Object parent, int index) {
366                         logger.log(Level.FINEST, "getChild(" + parent + ", " + index + ")");
367                         Node<FileTreePath> parentNode = findNode(parent);
368                         if (parentNode != null) {
369                                 return parentNode.getChild(index).getElement();
370                         }
371                         return null;
372                 }
373
374                 /**
375                  * {@inheritDoc}
376                  */
377                 @SuppressWarnings("synthetic-access")
378                 public int getChildCount(Object parent) {
379                         logger.log(Level.FINEST, "getChildCount(" + parent + ")");
380                         Node<FileTreePath> parentNode = findNode(parent);
381                         if (parentNode != null) {
382                                 logger.log(Level.FINEST, "getChildCount(" + parent + "): " + parentNode.size());
383                                 return parentNode.size();
384                         }
385                         return -1;
386                 }
387
388                 /**
389                  * {@inheritDoc}
390                  */
391                 @SuppressWarnings("synthetic-access")
392                 public int getIndexOfChild(Object parent, Object child) {
393                         logger.log(Level.FINEST, "getIndexOfChild(" + parent + ", " + child + ")");
394                         Node<FileTreePath> parentNode = findNode(parent);
395                         if (parentNode != null) {
396                                 return parentNode.getIndexOfChild((FileTreePath) child);
397                         }
398                         return -1;
399                 }
400
401                 /**
402                  * {@inheritDoc}
403                  */
404                 @SuppressWarnings("synthetic-access")
405                 public Object getRoot() {
406                         logger.log(Level.FINEST, "getRoot()");
407                         return fileTreePathTree.getRootNode().getChild(0).getElement();
408                 }
409
410                 /**
411                  * {@inheritDoc}
412                  */
413                 @SuppressWarnings("synthetic-access")
414                 public boolean isLeaf(Object node) {
415                         logger.log(Level.FINEST, "isLeaf(" + node + ")");
416                         Node<FileTreePath> parentNode = findNode(node);
417                         if (parentNode != null) {
418                                 return parentNode.size() == 0;
419                         }
420                         return true;
421                 }
422
423                 //
424                 // ACTIONS
425                 //
426
427                 /**
428                  * {@inheritDoc}
429                  */
430                 public void valueForPathChanged(TreePath path, Object newValue) {
431                         /* TODO - implement */
432                 }
433
434                 //
435                 // PRIVATE METHODS
436                 //
437
438                 /**
439                  * Finds the node for the given object. This method is quite necessary
440                  * because the element for the root node of the JTree is
441                  * <code>null</code>
442                  * 
443                  * @param node
444                  *            The element whose node to return
445                  * @return The node, or <code>null</code> if no node could be found
446                  */
447                 private Node<FileTreePath> findNode(Object node) {
448                         if (node == null) {
449                                 return fileTreePathTree.getRootNode().getChild(0);
450                         }
451                         return fileTreePathTree.getRootNode().getChild(0).findChild((FileTreePath) node);
452                 }
453
454                 /**
455                  * Builds the tree from the project’s file entries.
456                  */
457                 @SuppressWarnings("synthetic-access")
458                 private void buildTree() {
459                         Tree<String> pathTree = new Tree<String>();
460                         Node<String> pathRootNode = pathTree.getRootNode().addChild(File.separator);
461                         logger.log(Level.FINEST, "project: " + project);
462                         buildTree(pathRootNode, project.getBasePathEntries());
463                         buildTree(pathRootNode, project.getVirtualEntries());
464                         /* now convert to a tree suitable for the JTree. */
465                         Node<FileTreePath> fileTreePathRootNode = fileTreePathTree.getRootNode();
466                         fileTreePathRootNode.removeAllChildren();
467                         convertTree(File.separator, pathRootNode, fileTreePathRootNode.addChild(new FileTreePath(File.separator, project.getName())));
468                         /* TODO - now add entries to all file tree path tree nodes. */
469                         addEntries(fileTreePathRootNode.getChild(0), project.getVirtualEntries());
470                         addEntries(fileTreePathRootNode.getChild(0), project.getBasePathEntries());
471                 }
472
473                 /**
474                  * Traverses the tree and assigned {@link Entry}s to every file tree
475                  * path whose name matchtes the name of an Entry.
476                  * 
477                  * @param fileTreePathRootNode
478                  *            The root node of the tree to walk
479                  * @param entries
480                  *            The list of entries
481                  */
482                 private void addEntries(Node<FileTreePath> fileTreePathRootNode, List<Entry> entries) {
483                         for (Entry entry: entries) {
484                                 String completeEntryName = File.separatorChar + entry.getName();
485                                 FileTreePath fileTreePath = getFileTreePath(fileTreePathRootNode, completeEntryName);
486                                 if (fileTreePath != null) {
487                                         fileTreePath.setFileEntry(entry);
488                                 }
489                         }
490                 }
491
492                 /**
493                  * Find the {@link FileTreePath} below the given node that has the given
494                  * file path.
495                  * 
496                  * @param fileTreePathNode
497                  *            The node to start searching at
498                  * @param filePath
499                  *            The path to search
500                  * @return The file tree path with the matching file path, or
501                  *         <code>null</code> if these is no such file tree path
502                  */
503                 private FileTreePath getFileTreePath(Node<FileTreePath> fileTreePathNode, String filePath) {
504                         for (Node<FileTreePath> child: fileTreePathNode) {
505                                 if (child.getElement().getFilePath().equals(filePath)) {
506                                         return child.getElement();
507                                 }
508                                 FileTreePath fileTreePath = getFileTreePath(child, filePath);
509                                 if (fileTreePath != null) {
510                                         return fileTreePath;
511                                 }
512                         }
513                         return null;
514                 }
515
516                 /**
517                  * Traverses the tree of path nodes and converts all paths to
518                  * {@link FileTreePath} objects, suitable for the JTree.
519                  * 
520                  * @param completePath
521                  *            The base path of the current root node
522                  * @param pathRootNode
523                  *            The root node of the path tree
524                  * @param fileTreePathRootNode
525                  *            The root node of the file tree path tree.
526                  */
527                 private void convertTree(String completePath, Node<String> pathRootNode, Node<FileTreePath> fileTreePathRootNode) {
528                         for (Node<String> pathChild: pathRootNode) {
529                                 String currentFilePath = completePath + pathChild.getElement();
530                                 Node<FileTreePath> newNode = fileTreePathRootNode.addChild(new FileTreePath(currentFilePath));
531                                 convertTree(currentFilePath, pathChild, newNode);
532                         }
533                         fileTreePathRootNode.sortChildren();
534                 }
535
536                 /**
537                  * Builds a tree matching the directory structure of the given entries.
538                  * 
539                  * @param pathRootNode
540                  *            The root node of the tree
541                  * @param entries
542                  *            The entries
543                  */
544                 private void buildTree(Node<String> pathRootNode, List<Entry> entries) {
545                         for (Entry basePathEntry: entries) {
546                                 String entryName = basePathEntry.getName();
547                                 String[] directories = entryName.split("\\" + File.separator);
548                                 Node<String> currentPathNode = pathRootNode;
549                                 for (String directory: directories) {
550                                         if (!currentPathNode.hasChild(directory)) {
551                                                 currentPathNode = currentPathNode.addChild(directory);
552                                         } else {
553                                                 currentPathNode = currentPathNode.getChild(directory);
554                                         }
555                                 }
556                         }
557                 }
558
559                 //
560                 // INTERFACE PropertyChangeListener
561                 //
562
563                 /**
564                  * {@inheritDoc}
565                  */
566                 public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
567                         if (propertyChangeEvent.getSource() instanceof Project) {
568                                 if (propertyChangeEvent.getPropertyName().equals(Project.PROPERTY_BASE_PATH_ENTRIES)) {
569                                         buildTree();
570                                 }
571                         }
572                 }
573
574         }
575
576         /**
577          * Container that is used to back the {@link FileTreeModel}. Each
578          * FileTreePath contains a complete path name, a filename, and the
579          * associated {@link Entry}, if any.
580          * 
581          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
582          */
583         private static class FileTreePath implements Comparable<FileTreePath> {
584
585                 /** The complete file path. */
586                 private final String filePath;
587
588                 /** The file name. */
589                 private final String fileName;
590
591                 /** The file entry, if any. */
592                 private Entry fileEntry;
593
594                 /**
595                  * Creates a new file tree path with an auto-detected file name. The
596                  * file name is everything after the last separator in the complete
597                  * path, or the complete path itself if it does not contain any
598                  * separators.
599                  * 
600                  * @param filePath
601                  *            The complete file path
602                  */
603                 public FileTreePath(String filePath) {
604                         this(filePath, null);
605                 }
606
607                 /**
608                  * Creates a new file tree path with the given file path and file name.
609                  * 
610                  * @param filePath
611                  *            The complete file path
612                  * @param fileName
613                  *            The file name
614                  */
615                 public FileTreePath(String filePath, String fileName) {
616                         this.filePath = filePath;
617                         if (fileName == null) {
618                                 if (filePath.indexOf(File.separatorChar) != -1) {
619                                         this.fileName = filePath.substring(filePath.lastIndexOf(File.separatorChar) + 1);
620                                 } else {
621                                         this.fileName = filePath;
622                                 }
623                         } else {
624                                 this.fileName = fileName;
625                         }
626                 }
627
628                 /**
629                  * Returns the complete file path.
630                  * 
631                  * @return The file path
632                  */
633                 public String getFilePath() {
634                         return filePath;
635                 }
636
637                 /**
638                  * Returns the file name, i.e. everything after the last
639                  * {@link File#separatorChar}.
640                  * 
641                  * @return The file name
642                  */
643                 public String getFileName() {
644                         return fileName;
645                 }
646
647                 /**
648                  * Returns the file entry associated with this path, if any.
649                  * 
650                  * @return The file entry associated with this path, or
651                  *         <code>null</code> if this path denotes a directory
652                  */
653                 public Entry getFileEntry() {
654                         return fileEntry;
655                 }
656
657                 /**
658                  * Sets the entry associated with this path.
659                  * 
660                  * @param fileEntry
661                  *            The entry
662                  */
663                 public void setFileEntry(Entry fileEntry) {
664                         this.fileEntry = fileEntry;
665                 }
666
667                 /**
668                  * {@inheritDoc}
669                  */
670                 @Override
671                 public boolean equals(Object object) {
672                         if ((object == null) || !(object instanceof FileTreePath)) {
673                                 return false;
674                         }
675                         FileTreePath fileTreePath = (FileTreePath) object;
676                         return fileTreePath.filePath.equals(filePath);
677                 }
678
679                 /**
680                  * {@inheritDoc}
681                  */
682                 @Override
683                 public int hashCode() {
684                         return filePath.hashCode();
685                 }
686
687                 /**
688                  * {@inheritDoc}
689                  */
690                 @Override
691                 public String toString() {
692                         return fileName;
693                 }
694
695                 //
696                 // INTERFACE Comparable
697                 //
698
699                 /**
700                  * {@inheritDoc}
701                  */
702                 public int compareTo(FileTreePath otherFileTreePath) {
703                         return filePath.compareTo(otherFileTreePath.filePath);
704                 }
705
706         }
707
708 }