package de.uni_passau.fim.infosun.prophet.plugin.plugins.codeViewerPlugin.fileTree;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
/**
* A <code>TreeModel</code> that only allows <code>FileTreeNode</code> instances to be used. This is the
* <code>TreeModel</code> used by <code>FileTree</code>.
*/
public class FileTreeModel implements TreeModel {
private FileTreeNode root;
/**
* Constructs a new <code>FileTreeModel</code> with the given root node.
*
* @param root
* the root node for the tree
*/
public FileTreeModel(FileTreeNode root) {
this.root = root;
}
@Override
public FileTreeNode getRoot() {
return root;
}
@Override
public FileTreeNode getChild(Object parent, int index) {
if (parent instanceof FileTreeNode) {
return ((FileTreeNode) parent).getChild(index);
} else {
return null;
}
}
@Override
public int getChildCount(Object parent) {
if (parent instanceof FileTreeNode) {
return ((FileTreeNode) parent).getChildCount();
} else {
return 0;
}
}
@Override
public boolean isLeaf(Object node) {
return node instanceof FileTreeNode && ((FileTreeNode) node).isFile();
}
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
// not used by this model
}
@Override
public int getIndexOfChild(Object parent, Object child) {
if (!(parent instanceof FileTreeNode && child instanceof FileTreeNode)) {
return -1;
} else {
return ((FileTreeNode) parent).getIndexOfChild((FileTreeNode) child);
}
}
@Override
public void addTreeModelListener(TreeModelListener l) {
// no events fired by this model
}
@Override
public void removeTreeModelListener(TreeModelListener l) {
// no events fired by this model
}
/**
* Builds a <code>FileTreeNode[]</code> representing the path to take through the tree to arrive at a
* <code>FileTreeNode</code> representing the given <code>File</code>. Returns <code>null</code> if there is no such
* node in the tree.
*
* @param file the file to build the path to
* @return the path
*/
public FileTreeNode[] buildPath(File file) {
if (root == null || !isChild(root.getFile(), file)) {
return null;
}
List<FileTreeNode> path = new ArrayList<>();
FileTreeNode searchNode = root;
do {
path.add(searchNode);
searchNode = searchNode.getChildren().stream().filter(node -> isChild(node.getFile(), file)).findFirst().orElse(null);
} while (searchNode != null);
if (!path.isEmpty() && isSameFile(path.get(path.size() - 1).getFile(), file)) {
return path.toArray(new FileTreeNode[path.size()]);
} else {
return null;
}
}
/**
* Checks whether two <code>File</code> instances represent the same file. Will return <code>false</code> if
* there is an <code>IOException</code> trying to determine this.
*
* @param first the first <code>File</code>
* @param second the second <code>File</code>
* @return true iff the two <code>File</code> represent the same file
*/
private boolean isSameFile(File first, File second) {
try {
return Files.isSameFile(first.toPath(), second.toPath());
} catch (IOException e) {
return false;
}
}
/**
* Checks whether the given <code>file</code> is a child (in the file system directory structure) of the
* <code>File</code> <code>parent</code>.
*
* @param parent the parent <code>File</code> to check against
* @param file the file to check
* @return true iff <code>file</code> is a child of <code>parent</code>
*/
private boolean isChild(File parent, File file) {
return file.toPath().toAbsolutePath().startsWith(parent.toPath().toAbsolutePath());
}
}