package org.rascalmpl.eclipse.navigator;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.StreamSupport;
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.IResourceDeltaVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.UIJob;
import org.rascalmpl.eclipse.Activator;
import org.rascalmpl.eclipse.IRascalResources;
import org.rascalmpl.eclipse.editor.IDEServicesModelProvider;
import org.rascalmpl.eclipse.nature.ProjectEvaluatorFactory;
import org.rascalmpl.interpreter.load.RascalSearchPath;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.uri.URIStorage;
import org.rascalmpl.uri.URIUtil;
import org.rascalmpl.values.ValueFactoryFactory;
import io.usethesource.vallang.IBool;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IDateTime;
import io.usethesource.vallang.IExternalValue;
import io.usethesource.vallang.IInteger;
import io.usethesource.vallang.IList;
import io.usethesource.vallang.IMap;
import io.usethesource.vallang.INode;
import io.usethesource.vallang.IRational;
import io.usethesource.vallang.IReal;
import io.usethesource.vallang.ISet;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IString;
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.IValueFactory;
import io.usethesource.vallang.visitors.IValueVisitor;
public class NavigatorContentProvider implements ITreeContentProvider, IResourceChangeListener,
IResourceDeltaVisitor {
public TreeViewer _viewer;
public NavigatorContentProvider() {
super();
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
}
@Override
public void dispose() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
_viewer = (TreeViewer) viewer;
}
@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof IWorkspaceRoot) {
IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();
IWorkingSet[] workingSets = manager.getWorkingSets();
if (workingSets.length == 0) {
return ((IWorkspaceRoot) inputElement).getProjects();
}
else {
return workingSets;
}
}
else if (inputElement instanceof IContainer) {
try {
return ((IContainer) inputElement).members();
} catch (CoreException e) {
Activator.log("navigator exception", e);
}
}
return new Object[] { };
}
@Override
public Object[] getChildren(Object parentElement) {
try {
if (parentElement instanceof IWorkspaceRoot) {
IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();
return manager.getWorkingSets();
}
else if (parentElement instanceof IWorkingSet) {
IAdaptable[] elems = ((IWorkingSet) parentElement).getElements();
IResource[] resources = new IResource[elems.length];
for (int i = 0, j = 0; i < elems.length; i++) {
Object ad = elems[i].getAdapter(IResource.class);
if (ad != null && ad instanceof IResource) {
resources[j++] = (IResource) ad;
}
}
return resources;
}
else if (parentElement instanceof IProject) {
IProject project = (IProject) parentElement;
if (project.isOpen() && project.hasNature(IRascalResources.ID_RASCAL_NATURE)) {
IResource[] members = project.members();
Object[] result = new Object[members.length + 2];
System.arraycopy(members, 0, result, 0, members.length);
IConstructor pcfg = IDEServicesModelProvider.getInstance().getPathConfig(project);
result[members.length] = new ValueContent(pcfg, project, project);
result[members.length + 1] = new SearchPath(project);
return result;
}
else if (project.isOpen()) {
return project.members();
}
}
else if (parentElement instanceof IContainer) {
return ((IContainer) parentElement).members();
}
else if (parentElement instanceof SearchPath) {
return ((SearchPath) parentElement).getSearchPath().toArray();
}
else if (parentElement instanceof URIContent) {
URIContent storage = (URIContent) parentElement;
if (storage.isDirectory()) {
return storage.listEntries();
}
}
else if (parentElement instanceof ValueContent) {
ValueContent content = (ValueContent) parentElement;
if (content.isDirectory()) {
return content.listEntries();
}
}
} catch (CoreException e) {
Activator.log(e.getMessage(), e);
}
return new Object[] {};
}
public static class SearchPath {
private final IProject project;
private final RascalSearchPath resolver;
public SearchPath(IProject project) {
this.project = project;
this.resolver = ProjectEvaluatorFactory.getInstance().getProjectSearchPath(project);
}
public List<URIContent> getSearchPath() {
List<URIContent> result = new LinkedList<>();
for (ISourceLocation root : resolver.collect()) {
result.add(new URIContent(root, project, true));
}
return result;
}
public IProject getProject() {
return project;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SearchPath) {
return ((SearchPath) obj).project.getName().equals(project.getName());
}
return false;
}
public int hashCode() {
return project.hashCode();
};
}
public static class ValueContent {
private final IValue val;
private final IProject project;
private Object parent;
public ValueContent(IValue val, IProject project, Object parent) {
this.val = val;
this.project = project;
this.parent = parent;
}
@Override
public int hashCode() {
return val.hashCode() * 13 + parent.hashCode() * 17 + 13333331;
}
public Object getParent() {
return parent;
}
public IValue getValue() {
return val;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ValueContent) {
return parent.equals(((ValueContent) obj).parent) && val.equals(((ValueContent) obj).val);
}
return false;
}
public IProject getProject() {
return project;
}
public boolean isDirectory() {
return val.accept(new IValueVisitor<Boolean, RuntimeException>() {
@Override
public Boolean visitBoolean(IBool arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitConstructor(IConstructor arg0) throws RuntimeException {
return true;
}
@Override
public Boolean visitDateTime(IDateTime arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitExternal(IExternalValue arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitInteger(IInteger arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitList(IList arg0) throws RuntimeException {
return true;
}
@Override
public Boolean visitListRelation(IList arg0) throws RuntimeException {
return true;
}
@Override
public Boolean visitMap(IMap arg0) throws RuntimeException {
return true;
}
@Override
public Boolean visitNode(INode arg0) throws RuntimeException {
return true;
}
@Override
public Boolean visitRational(IRational arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitReal(IReal arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitRelation(ISet arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitSet(ISet arg0) throws RuntimeException {
return true;
}
@Override
public Boolean visitSourceLocation(ISourceLocation arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitString(IString arg0) throws RuntimeException {
return false;
}
@Override
public Boolean visitTuple(ITuple arg0) throws RuntimeException {
return true;
}
}).booleanValue();
}
public Object[] listEntries() {
return val.accept(new IValueVisitor<Object[], RuntimeException>() {
private Object[] empty = new Object[0];
private IValueFactory vf = ValueFactoryFactory.getValueFactory();
@Override
public Object[] visitBoolean(IBool arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitConstructor(IConstructor arg0) throws RuntimeException {
return arg0.asWithKeywordParameters().getParameters().entrySet().stream()
.map(x -> new ValueContent(vf.node(x.getKey(), x.getValue()), project, this))
.toArray(Object[]::new);
}
@Override
public Object[] visitDateTime(IDateTime arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitExternal(IExternalValue arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitInteger(IInteger arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitList(IList arg0) throws RuntimeException {
return StreamSupport.stream(arg0.spliterator(), false)
.map(x -> newChild(x))
.toArray(Object[]::new);
}
private Object newChild(IValue x) {
return x.getType().isSourceLocation() ? new URIContent((ISourceLocation) x, project, true) : new ValueContent(x, project, this);
}
@Override
public Object[] visitListRelation(IList arg0) throws RuntimeException {
return visitList(arg0);
}
@Override
public Object[] visitMap(IMap arg0) throws RuntimeException {
return StreamSupport.stream(arg0.spliterator(), false)
.map(x -> new ValueContent(vf.node(x.toString(), arg0.get(x)), project, this))
.toArray(Object[]::new);
}
@Override
public Object[] visitNode(INode arg0) throws RuntimeException {
if (arg0.arity() == 1 && arg0.get(0) instanceof IList) {
return visitList((IList) arg0.get(0));
}
else {
return StreamSupport.stream(arg0.spliterator(), false)
.map(x -> newChild(x))
.toArray(Object[]::new);
}
}
@Override
public Object[] visitRational(IRational arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitReal(IReal arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitRelation(ISet arg0) throws RuntimeException {
return visitSet(arg0);
}
@Override
public Object[] visitSet(ISet arg0) throws RuntimeException {
return StreamSupport.stream(arg0.spliterator(), false)
.map(x -> newChild(x))
.toArray(Object[]::new);
}
@Override
public Object[] visitSourceLocation(ISourceLocation arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitString(IString arg0) throws RuntimeException {
return empty;
}
@Override
public Object[] visitTuple(ITuple arg0) throws RuntimeException {
return StreamSupport.stream(arg0.spliterator(), false).map(x -> new ValueContent(x, project, this)).toArray(Object[]::new);
}
});
}
public String getName() {
return val.accept(new IValueVisitor<String, RuntimeException>() {
@Override
public String visitBoolean(IBool arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitConstructor(IConstructor arg0) throws RuntimeException {
return arg0.getName();
}
@Override
public String visitDateTime(IDateTime arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitExternal(IExternalValue arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitInteger(IInteger arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitList(IList arg0) throws RuntimeException {
return "[" + arg0.length() + "]";
}
@Override
public String visitListRelation(IList arg0) throws RuntimeException {
return visitList(arg0);
}
@Override
public String visitMap(IMap arg0) throws RuntimeException {
return "(" + arg0.size() + ")";
}
@Override
public String visitNode(INode arg0) throws RuntimeException {
return arg0.getName();
}
@Override
public String visitRational(IRational arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitReal(IReal arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitRelation(ISet arg0) throws RuntimeException {
return visitSet(arg0);
}
@Override
public String visitSet(ISet arg0) throws RuntimeException {
return "{" + arg0.size() + "}";
}
@Override
public String visitSourceLocation(ISourceLocation arg0) throws RuntimeException {
return arg0.toString();
}
@Override
public String visitString(IString arg0) throws RuntimeException {
return arg0.getValue();
}
@Override
public String visitTuple(ITuple arg0) throws RuntimeException {
return "<>";
}
});
}
}
public static class URIContent {
private static final URIResolverRegistry reg = URIResolverRegistry.getInstance();
private final ISourceLocation uri;
private final IProject project;
private final boolean isRoot;
public URIContent(ISourceLocation uri, IProject project, boolean isRoot) {
this.uri = uri;
this.project = project;
this.isRoot = isRoot;
}
public boolean isRoot() {
return isRoot;
}
public String getName() {
return URIUtil.getLocationName(uri);
}
public ISourceLocation getURI() {
return uri;
}
public IProject getProject() {
return project;
}
public URIContent[] listEntries() {
ISourceLocation l = uri;
if (isJarLoc(l)) {
try {
l = URIUtil.changePath(URIUtil.changeScheme(l, "jar+" + l.getScheme()), l.getPath() + "!/");
} catch (URISyntaxException e) {
Activator.log("navigator could not dive into jar", e);
}
}
return doList(l);
}
private boolean isJarLoc(ISourceLocation l) {
return l.getPath() != null && l.getPath().endsWith(".jar");
}
private URIContent[] doList(ISourceLocation l) {
try {
return Arrays.stream(reg.list(l))
.map(loc -> new URIContent(loc, project, false))
.toArray(URIContent[]::new);
} catch (IOException e) {
Activator.log("could not list entries", e);
return new URIContent[0];
}
}
public boolean isDirectory() {
return reg.isDirectory(uri) || isJarLoc(uri);
}
public boolean exists() {
return reg.exists(uri);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof URIContent) {
return ((URIContent) obj).project.getName().equals(project.getName())
&& ((URIContent) obj).uri.equals(uri);
}
return false;
}
@Override
public int hashCode() {
return 7 + 17 * project.hashCode() + 13 * uri.hashCode();
}
}
@Override
public Object getParent(Object element) {
if (element instanceof IWorkingSet) {
ResourcesPlugin.getWorkspace().getRoot();
}
else if (element instanceof IProject) {
IWorkingSetManager manager = PlatformUI.getWorkbench().getWorkingSetManager();
for (IWorkingSet set : manager.getWorkingSets()) {
IAdaptable elems[] = set.getElements();
for (int i = 0; i < elems.length; i++) {
if (element == elems[i]) {
return set;
}
}
}
}
else if (element instanceof IResource) {
return ((IResource) element).getParent();
}
else if (element instanceof SearchPath) {
return ((SearchPath) element).getProject();
}
else if (element instanceof ValueContent) {
return ((ValueContent) element).getParent();
}
else if (element instanceof URIStorage) {
return null;
}
return null;
}
@Override
public boolean hasChildren(Object element) {
return getChildren(element).length > 0;
}
@Override
public void resourceChanged(IResourceChangeEvent event) {
IResourceDelta delta = event.getDelta();
try {
delta.accept(this);
} catch (CoreException e) {
e.printStackTrace();
}
}
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
final IResource source = delta.getResource();
new UIJob("Refresh viewer") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (_viewer != null && !_viewer.getControl().isDisposed())
_viewer.refresh(source);
return Status.OK_STATUS;
}
}.schedule();
return false;
}
}