package org.incha.ui;
import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.incha.core.BuildPath;
import org.incha.core.JavaProjectsModel;
import org.incha.core.JavaProject;
import org.incha.utils.CollectionUtils;
public class ProjectsView extends JPanel {
private static final long serialVersionUID = -7945996533262500375L;
/**
* The class path node.
*/
public static final String CLASSPATH = "classpath";
/**
* The sources node.
*/
public static final String SOURCES = "sources";
/**
* List of actions for right mouse clicking on project
*/
private List<ProjectsViewMouseListener> mouseListeners = new LinkedList<ProjectsViewMouseListener>();
/**
* The model.
*/
private final JavaProjectsModel model;
/**
* Project tree.
*/
private final JTree tree = new JTree(new DefaultTreeModel(
new DefaultMutableTreeNode(null)));
/**
* The build path listener.
*/
private final PropertyChangeListener buildPathListener = new PropertyChangeListener() {
@SuppressWarnings("unchecked")
@Override
public void propertyChange(final PropertyChangeEvent evt) {
buildPathChanged((JavaProject) evt.getSource(), evt.getPropertyName(),
(List<File>) evt.getOldValue(),
(List<File>) evt.getNewValue());
}
};
/**
* @param model the model.
*/
public ProjectsView(final JavaProjectsModel model) {
super(new BorderLayout());
this.model = model;
//add listeners to model
model.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
modelChanged(evt);
}
});
//add projects tree
((DefaultMutableTreeNode) tree.getModel().getRoot()).setUserObject(model);
tree.setRootVisible(false);
tree.setEditable(false);
tree.setCellRenderer(new ProjectsTreeCellRenderer());
add(new JScrollPane(tree), BorderLayout.CENTER);
tree.addMouseListener(new MouseAdapter() {
/* (non-Javadoc)
* @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
*/
@Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
e.consume();
mousePressedOnTree(e.getX(), e.getY(), ProjectsViewMouseEvent.LEFT_MOUSE_PRESSED);
}
}
/* (non-Javadoc)
* @see java.awt.event.MouseAdapter#mouseClicked(java.awt.event.MouseEvent)
*/
@Override
public void mouseClicked(final MouseEvent e) {
if (!SwingUtilities.isRightMouseButton(e)) {
mousePressedOnTree(e.getX(), e.getY(), ProjectsViewMouseEvent.MOUSE_CLICKED);
}
}
});
synchronizeModel();
}
/**
* Synchronizes model by view.
*/
private void synchronizeModel() {
for (final JavaProject p : model.getProjects()) {
handleProjectAdded(p);
}
}
/**
* @param evt property change event.
*/
@SuppressWarnings("unchecked")
protected void modelChanged(final PropertyChangeEvent evt) {
if (JavaProjectsModel.PROJECTS.equals(evt.getPropertyName())) {
CollectionUtils.synchronize((List<JavaProject>) evt.getOldValue(),
(List<JavaProject>) evt.getNewValue(),
new CollectionUtils.SynchHandler<JavaProject>() {
@Override
public void itemAdded(final JavaProject item) {
handleProjectAdded(item);
};
@Override
public void itemDeleted(final JavaProject item) {
handleProjectDeleted(item);
};
});
}
}
/**
* @param project the project.
* @param propertyName property name.
* @param oldValue old build path value.
* @param newValue new build path value
*/
protected void buildPathChanged(final JavaProject project, final String propertyName,
final List<File> oldValue, final List<File> newValue) {
final DefaultMutableTreeNode projectNode = getProjectNode(project);
final DefaultMutableTreeNode root = getBuildPathNode(projectNode, propertyName);
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
final List<File> newFiles = new LinkedList<File>(newValue);
final List<File> oldFiles = new LinkedList<File>(oldValue);
//process added files
//handle added files
Iterator<File> iter = newFiles.iterator();
while (iter.hasNext()) {
final File f = iter.next();
if (!oldFiles.remove(f)) {
model.insertNodeInto(new DefaultMutableTreeNode(f),
root, model.getChildCount(root));
}
iter.remove();
}
//handle deleted files
iter = oldFiles.iterator();
while(iter.hasNext()) {
final DefaultMutableTreeNode fileNode = getFileNode(root, iter.next());
model.removeNodeFromParent(fileNode);
}
}
/**
* @param project deleted project.
*/
protected void handleProjectDeleted(final JavaProject project) {
//remove property change listener from project
project.getBuildPath().removePropertyChangeListener(buildPathListener);
//remove project node from view
final DefaultMutableTreeNode node = getProjectNode(project);
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.removeNodeFromParent(node);
}
/**
* @param project the project.
* @return the node containing given project.
*/
private DefaultMutableTreeNode getProjectNode(final JavaProject project) {
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
final int count = model.getChildCount(model.getRoot());
for (int i = 0; i < count; i++) {
final DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(
model.getRoot(), i);
if (project.equals(child.getUserObject())) {
return child;
}
}
return null;
}
/**
* @param project added project.
*/
protected void handleProjectAdded(final JavaProject project) {
//add change listener to project
project.getBuildPath().addPropertyChangeListener(buildPathListener);
//create project node.
final DefaultMutableTreeNode projectNode = new DefaultMutableTreeNode(project);
//create class path source node.
final DefaultMutableTreeNode src = new DefaultMutableTreeNode(SOURCES);
projectNode.add(src);
//add source items to source node
for (final File f : project.getBuildPath().getSources()) {
src.add(new DefaultMutableTreeNode(f));
}
//create class path node.
final DefaultMutableTreeNode cp = new DefaultMutableTreeNode(CLASSPATH);
projectNode.add(cp);
//add class path items to source node
for (final File f : project.getBuildPath().getClassPath()) {
cp.add(new DefaultMutableTreeNode(f));
}
//add project node to view
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.insertNodeInto(projectNode, (DefaultMutableTreeNode) model.getRoot(),
model.getChildCount(model.getRoot()));
//expand root if required
final TreePath path = new TreePath(tree.getModel().getRoot());
if (!tree.isExpanded(path)) {
tree.expandPath(path);
}
}
/**
* @param root the files container node (sources/claspath).
* @param file
* @return the node for given file.
*/
private DefaultMutableTreeNode getFileNode(final DefaultMutableTreeNode root,
final File file) {
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
final int count = model.getChildCount(root);
for (int i = 0; i < count; i++) {
final DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(
root, i);
if (file.equals(child.getUserObject())) {
return child;
}
}
return null;
}
/**
* @param projectNode the project node.
* @param propertyName the property node.
* @return build path node for given property (class path node or source node)
*/
private DefaultMutableTreeNode getBuildPathNode(
final DefaultMutableTreeNode projectNode, final String propertyName) {
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
final int count = model.getChildCount(projectNode);
final String label = BuildPath.SOURCES.equals(propertyName) ? SOURCES : CLASSPATH;
for (int i = 0; i < count; i++) {
final DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(
projectNode, i);
if (label.equals(child.getUserObject())) {
return child;
}
}
return null;
}
/**
* @param x x mouse coordinate.
* @param y y mouse coordinate.
* @param actionId the action ID.
*/
protected void mousePressedOnTree(final int x, final int y, final int actionId) {
final TreePath path = tree.getPathForLocation(x, y);
if (path != null) {
//create array of user objects
final Object[] userObjects = new Object[path.getPathCount()];
for (int i = 0; i < userObjects.length; i++) {
userObjects[i] = ((DefaultMutableTreeNode) path.getPathComponent(i)).getUserObject();
}
final ProjectsViewMouseEvent e = new ProjectsViewMouseEvent(userObjects, actionId, x, y);
for (final ProjectsViewMouseListener l : this.mouseListeners) {
l.handle(e);
}
}
}
/**
* @param l action listener.
*/
public void addProjectsViewMouseListener(final ProjectsViewMouseListener l) {
mouseListeners.add(l);
}
/**
* @param l action listener.
*/
public void removeProjectsViewMouseListener(final ProjectsViewMouseListener l) {
mouseListeners.remove(l);
}
}