2 * jSite2 - FileManager.java -
3 * Copyright © 2008 David Roden
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.
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.
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.
20 package net.pterodactylus.jsite.gui;
22 import java.awt.BorderLayout;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
31 import javax.swing.JDialog;
32 import javax.swing.JFrame;
33 import javax.swing.JPanel;
34 import javax.swing.JScrollPane;
35 import javax.swing.JTree;
36 import javax.swing.event.TreeModelListener;
37 import javax.swing.tree.TreeModel;
38 import javax.swing.tree.TreePath;
40 import net.pterodactylus.jsite.i18n.I18n;
41 import net.pterodactylus.jsite.i18n.I18nable;
42 import net.pterodactylus.jsite.project.Entry;
43 import net.pterodactylus.jsite.project.Project;
44 import net.pterodactylus.util.data.Node;
45 import net.pterodactylus.util.data.Tree;
46 import net.pterodactylus.util.logging.Logging;
47 import net.pterodactylus.util.swing.SwingUtils;
50 * Manages physical and virtual files in a project.
52 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
54 public class FileManager extends JDialog implements I18nable {
57 private static final Logger logger = Logging.getLogger(FileManager.class.getName());
59 /** The project whose files to manage. */
60 private final Project project;
62 /** The tree model for the project files. */
63 private final FileTreeModel fileTreeModel;
65 /** The tree that shows the files. */
66 private JTree fileTree;
69 * Creates a new file manager.
74 * The project whose files to manage
76 public FileManager(JFrame parent, Project project) {
77 super(parent, I18n.get("fileManager.title", project.getName()), true);
78 logger.log(Level.FINEST, "project: " + project);
79 this.project = project;
80 fileTreeModel = new FileTreeModel();
82 SwingUtils.repackCentered(this);
90 * Initializes all components.
92 private void initComponents() {
93 JPanel contentPanel = new JPanel(new BorderLayout(12, 12));
95 fileTree = new JTree(fileTreeModel);
96 fileTree.setShowsRootHandles(false);
97 contentPanel.add(new JScrollPane(fileTree), BorderLayout.CENTER);
99 setContentPane(contentPanel);
103 // INTERFACE I18nable
109 public void updateI18n() {
110 setTitle(I18n.get("fileManager.title", project.getName()));
114 * Model for the tree of files.
116 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
118 private class FileTreeModel implements TreeModel, PropertyChangeListener {
120 /** Tree model listeners. */
121 private final List<TreeModelListener> treeModelListeners = new ArrayList<TreeModelListener>();
123 /** The tree of files. */
124 private final Tree<FileTreePath> fileTreePathTree = new Tree<FileTreePath>();
127 * Creates a new file tree model.
140 public void addTreeModelListener(TreeModelListener treeModelListener) {
141 treeModelListeners.add(treeModelListener);
147 public void removeTreeModelListener(TreeModelListener treeModelListener) {
148 treeModelListeners.remove(treeModelListener);
158 @SuppressWarnings("synthetic-access")
159 public Object getChild(Object parent, int index) {
160 logger.log(Level.FINEST, "getChild(" + parent + ", " + index + ")");
161 Node<FileTreePath> parentNode = findNode(parent);
162 if (parentNode != null) {
163 return parentNode.getChild(index).getElement();
171 @SuppressWarnings("synthetic-access")
172 public int getChildCount(Object parent) {
173 logger.log(Level.FINEST, "getChildCount(" + parent + ")");
174 Node<FileTreePath> parentNode = findNode(parent);
175 if (parentNode != null) {
176 logger.log(Level.FINEST, "getChildCount(" + parent + "): " + parentNode.size());
177 return parentNode.size();
185 @SuppressWarnings("synthetic-access")
186 public int getIndexOfChild(Object parent, Object child) {
187 logger.log(Level.FINEST, "getIndexOfChild(" + parent + ", " + child + ")");
188 Node<FileTreePath> parentNode = findNode(parent);
189 if (parentNode != null) {
190 return parentNode.getIndexOfChild((FileTreePath) child);
198 @SuppressWarnings("synthetic-access")
199 public Object getRoot() {
200 logger.log(Level.FINEST, "getRoot()");
201 return fileTreePathTree.getRootNode().getChild(0).getElement();
207 @SuppressWarnings("synthetic-access")
208 public boolean isLeaf(Object node) {
209 logger.log(Level.FINEST, "isLeaf(" + node + ")");
210 Node<FileTreePath> parentNode = findNode(node);
211 if (parentNode != null) {
212 return parentNode.size() == 0;
224 public void valueForPathChanged(TreePath path, Object newValue) {
225 /* TODO - implement */
233 * Finds the node for the given object. This method is quite necessary
234 * because the element for the root node of the JTree is
238 * The element whose node to return
239 * @return The node, or <code>null</code> if no node could be found
241 private Node<FileTreePath> findNode(Object node) {
243 return fileTreePathTree.getRootNode().getChild(0);
245 return fileTreePathTree.getRootNode().getChild(0).findChild((FileTreePath) node);
249 * Builds the tree from the project’s file entries.
251 @SuppressWarnings("synthetic-access")
252 private void buildTree() {
253 Tree<String> pathTree = new Tree<String>();
254 Node<String> pathRootNode = pathTree.getRootNode().addChild(File.separator);
255 logger.log(Level.FINEST, "project: " + project);
256 buildTree(pathRootNode, project.getBasePathEntries());
257 buildTree(pathRootNode, project.getVirtualEntries());
258 /* now convert to a tree suitable for the JTree. */
259 Node<FileTreePath> fileTreePathRootNode = fileTreePathTree.getRootNode();
260 fileTreePathRootNode.removeAllChildren();
261 convertTree(File.separator, pathRootNode, fileTreePathRootNode.addChild(new FileTreePath(File.separator, project.getName())));
262 /* TODO - now add entries to all file tree path tree nodes. */
266 * Traverses the tree of path nodes and converts all paths to
267 * {@link FileTreePath} objects, suitable for the JTree.
269 * @param completePath
270 * The base path of the current root node
271 * @param pathRootNode
272 * The root node of the path tree
273 * @param fileTreePathRootNode
274 * The root node of the file tree path tree.
276 private void convertTree(String completePath, Node<String> pathRootNode, Node<FileTreePath> fileTreePathRootNode) {
277 for (Node<String> pathChild: pathRootNode) {
278 String currentFilePath = completePath + pathChild.getElement();
279 Node<FileTreePath> newNode = fileTreePathRootNode.addChild(new FileTreePath(currentFilePath));
280 convertTree(currentFilePath, pathChild, newNode);
282 fileTreePathRootNode.sortChildren();
286 * Builds a tree matching the directory structure of the given entries.
288 * @param pathRootNode
289 * The root node of the tree
293 private void buildTree(Node<String> pathRootNode, List<Entry> entries) {
294 for (Entry basePathEntry: entries) {
295 String entryName = basePathEntry.getName();
296 String[] directories = entryName.split("\\" + File.separator);
297 Node<String> currentPathNode = pathRootNode;
298 for (String directory: directories) {
299 if (!currentPathNode.hasChild(directory)) {
300 currentPathNode = currentPathNode.addChild(directory);
302 currentPathNode = currentPathNode.getChild(directory);
309 // INTERFACE PropertyChangeListener
315 public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
316 if (propertyChangeEvent.getSource() instanceof Project) {
317 if (propertyChangeEvent.getPropertyName().equals(Project.PROPERTY_BASE_PATH_ENTRIES)) {
326 * Container that is used to back the {@link FileTreeModel}. Each
327 * FileTreePath contains a complete path name, a filename, and the
328 * associated {@link Entry}, if any.
330 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
332 private static class FileTreePath implements Comparable<FileTreePath> {
334 /** The complete file path. */
335 private final String filePath;
337 /** The file name. */
338 private final String fileName;
340 /** The file entry, if any. */
341 private Entry fileEntry;
344 * Creates a new file tree path with an auto-detected file name. The
345 * file name is everything after the last separator in the complete
346 * path, or the complete path itself if it does not contain any
350 * The complete file path
352 public FileTreePath(String filePath) {
353 this(filePath, null);
357 * Creates a new file tree path with the given file path and file name.
360 * The complete file path
364 public FileTreePath(String filePath, String fileName) {
365 this.filePath = filePath;
366 if (fileName == null) {
367 if (filePath.indexOf(File.separatorChar) != -1) {
368 this.fileName = filePath.substring(filePath.lastIndexOf(File.separatorChar) + 1);
370 this.fileName = filePath;
373 this.fileName = fileName;
378 * Returns the complete file path.
380 * @return The file path
382 public String getFilePath() {
387 * Returns the file name, i.e. everything after the last
388 * {@link File#separatorChar}.
390 * @return The file name
392 public String getFileName() {
397 * Returns the file entry associated with this path, if any.
399 * @return The file entry associated with this path, or
400 * <code>null</code> if this path denotes a directory
402 public Entry getFileEntry() {
407 * Sets the entry associated with this path.
412 public void setFileEntry(Entry fileEntry) {
413 this.fileEntry = fileEntry;
420 public boolean equals(Object object) {
421 if ((object == null) || !(object instanceof FileTreePath)) {
424 FileTreePath fileTreePath = (FileTreePath) object;
425 return fileTreePath.filePath.equals(filePath);
432 public int hashCode() {
433 return filePath.hashCode();
440 public String toString() {
445 // INTERFACE Comparable
451 public int compareTo(FileTreePath otherFileTreePath) {
452 return filePath.compareTo(otherFileTreePath.filePath);