/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.model.ui.views.navigator;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.jboss.tools.common.model.XFilteredTree;
import org.jboss.tools.common.model.XModel;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.filesystems.FileSystemsHelper;
import org.jboss.tools.common.model.project.IModelNature;
import org.jboss.tools.common.model.project.ModelNature;
import org.jboss.tools.common.model.ui.navigator.TreeViewerModelListenerImpl;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.model.util.XModelTreeListenerSWTASync;
public class NavigatorContentProvider implements ITreeContentProvider, IResourceChangeListener {
protected FilteredTreesCache filteredTrees = FilteredTreesCache.getInstance();
protected TreeViewer viewer = null;
protected TreeViewerModelListenerImpl listener;
protected XModelTreeListenerSWTASync syncListener;
protected Set<String> projects = new HashSet<String>();
protected int isLoading = 0;
public NavigatorContentProvider() {}
public void dispose() {
if (viewer != null) {
IWorkspace workspace = getWorkspace(viewer.getInput());
if(workspace != null) {
workspace.removeResourceChangeListener(this);
}
}
if (projects != null) {
projects.clear();
projects = null;
}
if(filteredTrees != null && syncListener != null) {
filteredTrees.removeListener(syncListener);
}
syncListener = null;
}
public void setListener(TreeViewerModelListenerImpl listener) {
this.listener = listener;
syncListener = new XModelTreeListenerSWTASync(listener);
}
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof XModelObject) {
XFilteredTree tree = getFilteredTree(parentElement);
return (tree == null) ? null : getFilteredTree(parentElement).getChildren((XModelObject)parentElement);
}
return null;
}
public Object getParent(Object element) {
if (element instanceof XModelObject) {
XFilteredTree tree = getFilteredTree(element);
return (tree == null) ? null : tree.getParent((XModelObject)element);
}
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof XModelObject) {
XModelObject o = (XModelObject)element;
if(!o.isActive()) return false;
XFilteredTree tree = getFilteredTree(element);
return tree != null && tree.hasChildren((XModelObject)element);
}
return false;
}
// ============================================ IStructuredContentProvider
public Object[] getElements(Object inputElement) {
isLoading++;
try {
this.projects.clear();
TreeMap<String,XModelObject> result = new TreeMap<String,XModelObject>(new C());
if (inputElement instanceof IWorkspaceRoot) {
IWorkspaceRoot workspaceRoot = (IWorkspaceRoot)inputElement;
IProject[] projects = workspaceRoot.getProjects();
for (int i = 0; i < projects.length; i++) {
XModelObject root = addProject(projects[i]);
if (root != null) result.put(projects[i].getName(), root);
}
} else if (inputElement instanceof IProject) {
IProject p = (IProject)inputElement;
XModelObject root = addProject(p);
if (root != null) result.put(p.getName(), root);
}
return result.values().toArray();
} finally {
isLoading--;
}
}
// ============================================ IContentProvider
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
this.viewer = (TreeViewer)viewer;
IWorkspace oldWorkspace = getWorkspace(oldInput);
IWorkspace newWorkspace = getWorkspace(newInput);
if (oldWorkspace != newWorkspace) {
if (oldWorkspace != null)
oldWorkspace.removeResourceChangeListener(this);
if (newWorkspace != null)
newWorkspace.addResourceChangeListener(this);
}
}
private IWorkspace getWorkspace(Object input) {
return (input instanceof IWorkspace) ? (IWorkspace)input
: (input instanceof IContainer) ? ((IContainer)input).getWorkspace()
: null;
}
// ============================================ IResourceChangeListener
public void resourceChanged(final IResourceChangeEvent event) {
if(isLoading > 0) return;
final IResourceDelta delta = event.getDelta();
final IResource resource = event.getResource();
Control ctrl = viewer.getControl();
if (ctrl != null && !ctrl.isDisposed()) {
// Do a sync exec, not an async exec, since the resource delta
// must be traversed in this method. It is destroyed
// when this method returns.
ctrl.getDisplay().syncExec(
new Runnable() {
public void run() {
switch (event.getType()) {
case IResourceChangeEvent.POST_CHANGE :
handlePostChange(delta);
break;
case IResourceChangeEvent.PRE_CLOSE :
handlePreClose(resource);
break;
case IResourceChangeEvent.PRE_DELETE :
handlePreDelete(resource);
break;
}
check();
}
}
);
}
}
private XModelObject addProject(IProject project) {
if (project == null || !project.isOpen()) return null;
IModelNature nature = getModelNature(project);
if(nature == null) return null;
XModelObject root = nature.getModel().getRoot();
//Does not work now that the check above excludes closed projects.
if(!project.isOpen()) {
root = root.copy(0);
}
XFilteredTree tree = getFilteredTree(root);
if(tree == null) return null;
if(listener != null) {
listener.setViewer(viewer);
filteredTrees.addListener(syncListener, nature.getModel());
}
projects.add(project.getLocation().toString());
return tree.getRoot();
}
protected IModelNature getModelNature(IProject project) {
return EclipseResourceUtil.getModelNature(project);
}
protected String getFilteredTreeName(XModel model) {
return getFilteredTreeName();
}
protected String getFilteredTreeName() {
return "FileSystems"; //$NON-NLS-1$
}
XFilteredTree getFilteredTree(Object object) {
if (object instanceof XModelObject) {
XModel model = ((XModelObject)object).getModel();
if(FileSystemsHelper.getFileSystems(model) == null) return null;
String name = getFilteredTreeName(model);
return filteredTrees.getFilteredTree(name, model);
}
return null;
}
private void handlePostChange(IResourceDelta delta) {
Control ctrl = viewer.getControl();
if (ctrl == null || ctrl.isDisposed()) return;
IResourceDelta[] affectedChildren = delta.getAffectedChildren(IResourceDelta.ADDED | IResourceDelta.CHANGED);
if (affectedChildren.length > 0) {
ArrayList<XModelObject> affected = new ArrayList<XModelObject>();
for (int i = 0; i < affectedChildren.length; i++) {
IProject project = (IProject)affectedChildren[i].getResource().getAdapter(IProject.class);
if (affectedChildren[i].getKind() == IResourceDelta.ADDED ||
(affectedChildren[i].getKind() == IResourceDelta.CHANGED &&
((affectedChildren[i].getFlags() & IResourceDelta.OPEN) != 0)) ||
!projects.contains(project.getLocation().toString())) {
if(!ModelNature.checkModelNature(project)) {
continue;
}
XModelObject root = addProject(project);
if (root != null) affected.add(root);
}
}
if (!affected.isEmpty()) {
viewer.add(viewer.getInput(), affected.toArray());
viewer.refresh();
}
}
}
private void handlePreClose(IResource resource) {
handlePreDelete(resource);
}
private void handlePreDelete(IResource resource) {
Control ctrl = viewer.getControl();
if (ctrl == null || ctrl.isDisposed()) return;
IProject project = (IProject)resource.getAdapter(IProject.class);
if(project == null) return;
XModelObject root = getRootByProject(project);
projects.remove(project.getLocation().toString());
if (root != null) {
root.getModel().removeModelTreeListener(syncListener);
filteredTrees.remove(root.getModel());
viewer.remove(root);
}
}
protected XModelObject getRootByProject(IProject project) {
if(project == null || !project.isOpen()) return null;
IModelNature nature = EclipseResourceUtil.getModelNature(project);
if(nature == null) return null;
XFilteredTree filteredTree = getFilteredTree(nature.getModel().getRoot());
return (filteredTree != null) ? filteredTree.getRoot() : null;
}
private void check() {
if(viewer == null) return;
Tree swtTree = viewer.getTree();
if(swtTree == null || swtTree.isDisposed()) return;
TreeItem[] is = swtTree.getItems();
if(is == null || is.length == 0) return;
for (int i = 0; i < is.length; i++) {
XModelObject o = (XModelObject)is[i].getData();
if(o == null) continue;
IProject p = EclipseResourceUtil.getProject(o);
IModelNature nature = EclipseResourceUtil.getModelNature(p);
if(nature == null) {
o.getModel().removeModelTreeListener(syncListener);
viewer.remove(o);
if(p!=null && p.exists() && p.getLocation()!=null)
projects.remove(p.getLocation().toString());
} else {
String classname = o.getModel().getMetaData().getMapping("FilteredTrees").getValue(getFilteredTreeName(o.getModel())); //$NON-NLS-1$
XFilteredTree tree = getFilteredTree(o.getModel().getRoot());
if(tree != null && !tree.getClass().getName().equals(classname)) {
o.getModel().removeModelTreeListener(syncListener);
projects.remove(p.getLocation().toString());
viewer.remove(o);
viewer.refresh();
}
}
}
}
class C implements Comparator<Object> {
public int compare(Object o1, Object o2) {
return (o1 == null ? "" : o1.toString()).compareToIgnoreCase(o2 == null ? "" : o2.toString()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}