/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others. All rights reserved. This program
* and the accompanying materials are 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: IBM Corporation - initial API and implementation
*******************************************************************************/
package org.erlide.engine.internal.model;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IPathVariableChangeEvent;
import org.eclipse.core.resources.IPathVariableChangeListener;
import org.eclipse.core.resources.IPathVariableManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
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.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SafeRunner;
import org.erlide.engine.ErlangEngine;
import org.erlide.engine.internal.ModelPlugin;
import org.erlide.engine.internal.model.cache.ErlModelCache;
import org.erlide.engine.internal.model.erlang.ErlModule;
import org.erlide.engine.internal.model.root.ErlElement;
import org.erlide.engine.internal.model.root.ErlElementDelta;
import org.erlide.engine.internal.model.root.ErlFolder;
import org.erlide.engine.internal.model.root.ErlProject;
import org.erlide.engine.internal.util.ModelConfig;
import org.erlide.engine.model.ElementChangedEvent;
import org.erlide.engine.model.ErlModelException;
import org.erlide.engine.model.ErlModelStatus;
import org.erlide.engine.model.IElementChangedListener;
import org.erlide.engine.model.IErlModel;
import org.erlide.engine.model.IErlModelChangeListener;
import org.erlide.engine.model.erlang.ErlangFunction;
import org.erlide.engine.model.erlang.FunctionRef;
import org.erlide.engine.model.erlang.IErlFunction;
import org.erlide.engine.model.erlang.IErlModule;
import org.erlide.engine.model.root.ErlElementKind;
import org.erlide.engine.model.root.IErlElement;
import org.erlide.engine.model.root.IErlElementDelta;
import org.erlide.engine.model.root.IErlElementLocator;
import org.erlide.engine.model.root.IErlFolder;
import org.erlide.engine.model.root.IErlProject;
import org.erlide.engine.model.root.ProjectConfigurationChangeListener;
import org.erlide.engine.util.CommonUtils;
import org.erlide.engine.util.NatureUtil;
import org.erlide.engine.util.ResourceUtil;
import org.erlide.util.ErlLogger;
import org.erlide.util.SystemConfiguration;
import org.erlide.util.erlang.OtpErlang;
import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangString;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Implementation of <code>IErlModel<code>. The Erlang Model maintains a cache of
* active <code>IErlProject</code>s in a workspace. A Erlang Model is specific to a
* workspace. To retrieve a workspace's model, use the
* <code>#getErlangModel(IWorkspace)</code> method.
*
* @see IErlModel
*/
public class ErlModel extends ErlElement implements IErlModel {
private final List<IErlModelChangeListener> fListeners;
private final IPathVariableChangeListener fPathVariableChangeListener;
final List<IElementChangedListener> elementChangedListeners;
private final ErlModelDeltaManager deltaManager;
OtpErlangList fCachedPathVars;
/**
* Constructs a new Erlang Model on the given workspace. Note that only one instance
* of ErlModel handle should ever be created.
*/
public ErlModel() {
super(null, ""); //$NON-NLS-1$
fPathVariableChangeListener = new PathVariableChangeListener();
setupWorkspaceListeners();
fListeners = Lists.newArrayList();
elementChangedListeners = Lists.newArrayList();
deltaManager = new ErlModelDeltaManager(this);
}
public final void setupWorkspaceListeners() {
final IWorkspace workspace = ResourcesPlugin.getWorkspace();
final IPathVariableManager pvm = workspace.getPathVariableManager();
pvm.addChangeListener(fPathVariableChangeListener);
final IResourceChangeListener listener = new ResourceChangeListener();
workspace.addResourceChangeListener(listener);
}
@Override
public boolean buildStructure(final IProgressMonitor pm) {
setChildren(null);
// determine my children
final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
.getProjects();
for (final IProject project : projects) {
if (NatureUtil.hasErlangNature(project) && getErlangProject(project) == null) {
addChild(createErlangProject(project));
}
}
return true;
}
/**
* @see IErlElement
*/
@Override
public ErlElementKind getKind() {
return ErlElementKind.MODEL;
}
/**
* @see IErlModel
*/
@Override
public IErlProject getErlangProject(final IProject project) {
if (!project.isAccessible()) {
return null;
}
final IErlElement e = getChildWithResource(project);
if (e instanceof IErlProject) {
return (IErlProject) e;
}
if (NatureUtil.hasErlangNature(project)) {
return createErlangProject(project);
}
return null;
}
private IErlProject createErlangProject(final IProject project) {
final IErlProject ep = new ErlProject(this, project);
addChild(ep);
final ErlModelCache cache = getModelCache();
cache.newProjectCreated();
return ep;
}
/**
* @see IErlModel
*/
@Override
public Collection<IErlProject> getErlangProjects() throws ErlModelException {
final Collection<IErlElement> list = getChildrenOfKind(ErlElementKind.PROJECT);
final Collection<IErlProject> result = Lists.newArrayList();
for (final IErlElement e : list) {
result.add((IErlProject) e);
}
return result;
}
@Override
public IResource getResource() {
return ResourcesPlugin.getWorkspace().getRoot();
}
/**
* @private Debugging purposes
*/
@Override
protected void toStringInfo(final int tab, final StringBuilder buffer,
final Object info) {
buffer.append(tabString(tab));
buffer.append("Erlang Model"); //$NON-NLS-1$
if (info == null) {
buffer.append(" (not open)"); //$NON-NLS-1$
}
}
@Override
public void notifyChange(final IErlElement element) {
if (System.getProperty("erlide.model.notify") != null) {
ErlLogger.debug(" caller = " + getStack());
}
for (final IErlModelChangeListener listener : fListeners) {
listener.elementChanged(element);
}
}
private static synchronized String getStack() {
final StringBuilder result = new StringBuilder();
final StackTraceElement[] st = new Throwable().getStackTrace();
for (final StackTraceElement el : st) {
result.append(" ").append(el.toString()).append("\n");
}
return result.toString();
}
@Override
public void addModelChangeListener(final IErlModelChangeListener listener) {
if (!fListeners.contains(listener)) {
fListeners.add(listener);
}
}
@Override
public void removeModelChangeListener(final IErlModelChangeListener listener) {
fListeners.remove(listener);
}
@Override
protected void closing(final Object info) throws ErlModelException {
final IPathVariableManager pvm = ResourcesPlugin.getWorkspace()
.getPathVariableManager();
pvm.removeChangeListener(fPathVariableChangeListener);
}
@Override
public IErlElement findElement(final IResource rsrc) {
return findElement(rsrc, false);
}
@Override
public IErlElement findElement(final IResource rsrc, final boolean openElements) {
if (rsrc == null) {
return null;
}
final IPath path = rsrc.getFullPath();
IErlElement p = this;
for (final String segment : path.segments()) {
IErlElement c = p.getChildWithResource(rsrc);
if (c != null) {
return c;
}
c = p.getChildNamed(segment);
if (c == null) {
return null;
}
if (openElements) {
final IErlElement o = c;
try {
o.open(null);
} catch (final ErlModelException e) {
return null;
}
}
final IResource resource = c.getResource();
if (resource != null && resource.equals(rsrc)) {
return c;
}
p = c;
}
return null;
}
@Override
public IErlElement innermostThat(final IErlElement el,
final Predicate<IErlElement> firstThat) {
if (el != null) {
final IErlElement p = el;
try {
for (final IErlElement child : p.getChildren()) {
final IErlElement e2 = innermostThat(child, firstThat);
if (e2 != null) {
return e2;
}
}
} catch (final ErlModelException e) {
}
}
if (firstThat.apply(el)) {
return el;
}
return null;
}
@Override
public IErlModule findModule(final IFile file) {
try {
open(null);
} catch (final ErlModelException e) {
}
IErlElement element = findElement(file, false);
if (element == null) {
element = findElement(file, true);
}
if (element == null) {
return (IErlModule) create(file);
}
return (IErlModule) element;
}
@Override
public IErlProject findProject(final IProject project) {
try {
open(null);
} catch (final ErlModelException e) {
}
final IErlElement e = findElement(project);
if (e == null) {
return null;
}
return (IErlProject) e;
}
@Override
public IErlModule findModule(final String name) throws ErlModelException {
return findModuleFromProject(null, name, null, false,
IErlElementLocator.Scope.ALL_PROJECTS);
}
@Override
public final IErlProject newProject(final String name, final String path)
throws ErlModelException {
final IWorkspace ws = ResourcesPlugin.getWorkspace();
final IProject project = ws.getRoot().getProject(name);
try {
if (!project.exists()) {
IProjectDescription description = ws.newProjectDescription(name);
description.setLocation(new Path(path));
project.create(description, null);
project.open(null);
description = project.getDescription();
description.setNatureIds(new String[] { ModelPlugin.NATURE_ID });
description.setName(name);
project.setDescription(description, null);
}
if (!project.isOpen()) {
project.open(null);
}
return createProject(project);
} catch (final CoreException e) {
throw new ErlModelException(e, new ErlModelStatus(e));
}
}
private final class PathVariableChangeListener implements IPathVariableChangeListener {
@Override
public void pathVariableChanged(final IPathVariableChangeEvent event) {
fCachedPathVars = null;
ErlModelCache.getDefault().pathVarsChanged();
try {
// broadcast this change to projects, they need to clear their
// caches
for (final IErlProject project : getErlangProjects()) {
((ErlProject) project).pathVarsChanged();
}
} catch (final ErlModelException e) {
}
}
}
@Override
public OtpErlangList getPathVars(final IResource context) {
// if (fCachedPathVars == null) {
final IPathVariableManager pvm = context.getProject().getPathVariableManager();
final List<OtpErlangObject> objects = Lists.newArrayList();
final String[] names = pvm.getPathVariableNames();
for (final String name : names) {
final String value = URIUtil.toPath(pvm.getURIValue(name)).toPortableString();
objects.add(new OtpErlangTuple(new OtpErlangObject[] {
new OtpErlangString(name), new OtpErlangString(value) }));
}
fCachedPathVars = OtpErlang.mkList(objects);
// }
return fCachedPathVars;
}
@Override
public IErlFunction findFunction(final FunctionRef r) throws ErlModelException {
final IErlModule module = findModule(r.module);
if (module == null) {
return null;
}
module.open(null);
return module.findFunction(new ErlangFunction(r.function, r.arity));
}
@Override
public IErlModule findModule(final String moduleName, final String modulePath)
throws ErlModelException {
return findModuleFromProject(null, moduleName, modulePath, true,
IErlElementLocator.Scope.ALL_PROJECTS);
}
@Override
public IErlModule findInclude(final String includeName, final String includePath)
throws ErlModelException {
return findIncludeFromProject(null, includeName, includePath, false,
IErlElementLocator.Scope.ALL_PROJECTS);
}
/**
* Adds the given listener for changes to Erlang elements. Has no effect if an
* identical listener is already registered. After completion of this method, the
* given listener will be registered for exactly the specified events. If they were
* previously registered for other events, they will be deregistered.
* <p>
* Once registered, a listener starts receiving notification of changes to Erlang
* elements in the model. The listener continues to receive notifications until it is
* replaced or removed.
* </p>
* <p>
* Listeners can listen for several types of event as defined in
* <code>ElementChangeEvent</code>. Clients are free to register for any number of
* event types however if they register for more than one, it is their responsibility
* to ensure they correctly handle the case where the same Erlang element change shows
* up in multiple notifications. Clients are guaranteed to receive only the events for
* which they are registered.
* </p>
*
* @param listener
* the listener
* @param eventMask
* the bit-wise OR of all event types of interest to the listener
* @see IElementChangedListener
* @see ElementChangedEvent
* @see #removeElementChangedListener(IElementChangedListener)
*/
@Override
public void addElementChangedListener(final IElementChangedListener listener,
final int eventMask) {
// getDefault().addElementChangedListener(listener, eventMask);
}
/**
* Removes the given element changed listener. Has no affect if an identical listener
* is not registered.
*
* @param listener
* the listener
*/
@Override
public void removeElementChangedListener(final IElementChangedListener listener) {
// getDefault().removeElementChangedListener(listener);
}
/**
* Adds the given listener for changes to Erlang elements. Has no effect if an
* identical listener is already registered.
*
* This listener will only be notified during the POST_CHANGE resource change
* notification and any reconcile operation (POST_RECONCILE). For finer control of the
* notification, use
* <code>addElementChangedListener(IElementChangedListener,int)</code>, which allows
* to specify a different eventMask.
*
* @param listener
* the listener
* @see ElementChangedEvent
*/
@Override
public void addElementChangedListener(final IElementChangedListener listener) {
addElementChangedListener(listener, ElementChangedEvent.POST_CHANGE);
// | ElementChangedEvent.POST_RECONCILE);
}
private static Map<Object, IErlModule> moduleMap = new HashMap<Object, IErlModule>();
private static Map<IErlModule, Object> mapModule = new HashMap<IErlModule, Object>();
@Override
public IErlModule getModuleFromFile(final IErlElement parent, final String name,
final String path, final String encoding, final String key) {
return getModuleWithoutResource(parent, name, path, encoding, null, key);
}
@Override
public IErlModule getModuleFromText(final IErlElement parent, final String name,
final String initialText, final String key) {
return getModuleWithoutResource(parent, name, null, null, initialText, key);
}
private IErlModule getModuleWithoutResource(final IErlElement parent,
final String name, final String path, final String encoding,
final String initialText, final String key) {
IErlModule m = moduleMap.get(key);
if (m == null) {
final IErlElement parent2 = parent == null ? this : parent;
m = new ErlModule(parent2, name, path, encoding, initialText);
if (key != null) {
moduleMap.put(key, m);
mapModule.put(m, key);
}
}
return m;
}
@Override
public void removeModule(final IErlModule module) {
final Object key = mapModule.get(module);
if (key != null) {
mapModule.remove(module);
moduleMap.remove(key);
}
ErlModelCache.getDefault().removeModule(module);
}
@Override
public void putEdited(final String path, final IErlModule module) {
ErlModelCache.getDefault().putEdited(path, module);
}
/**
* Registers the given delta with this manager. This API is to be used to registered
* deltas that are created explicitly by the Erlang Model. Deltas created as
* translations of <code>IResourceDeltas</code> are to be registered with
* <code>#registerResourceDelta</code>.
*/
@Override
public void registerModelDelta(final IErlElementDelta delta) {
deltaManager.erlModelDeltas.add(delta);
}
public void notifyListeners(final IErlElementDelta deltaToNotify,
final int eventType, final IElementChangedListener[] listeners,
final int[] listenerMask, final int listenerCount) {
final ElementChangedEvent extraEvent = new ElementChangedEvent(deltaToNotify,
eventType);
for (int i = 0; i < listenerCount; i++) {
if (listenerMask == null || (listenerMask[i] & eventType) != 0) {
final IElementChangedListener listener = listeners[i];
long start = -1;
if (ModelConfig.verbose) {
ErlLogger.debug("Listener #" + (i + 1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$
start = System.currentTimeMillis();
}
// wrap callbacks with Safe runnable for subsequent listeners to
// be called
// when some are causing grief
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(final Throwable exception) {
// CCorePlugin.log(exception, "Exception occurred in
// listener of C
// element change notification"); //$NON-NLS-1$
ErlLogger.error(exception);
}
@Override
public void run() throws Exception {
listener.elementChanged(extraEvent);
}
});
if (ModelConfig.verbose) {
ErlLogger.debug(" -> " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
}
public IErlElement create(final IResource resource, final IErlElement parent) {
if (resource == null) {
return null;
}
final IErlElement e = findElement(resource);
if (e != null) {
return e;
}
final int type = resource.getType();
switch (type) {
case IResource.PROJECT:
return createProject((IProject) resource);
case IResource.FILE:
return createFile((IFile) resource, parent);
case IResource.FOLDER:
return createFolder((IFolder) resource, parent);
case IResource.ROOT:
return createRoot((IWorkspaceRoot) resource);
default:
return null;
}
}
void remove(final IResource rsrc) {
final IErlElement element = findElement(rsrc);
if (element != null) {
final IErlElement p = element.getParent();
p.removeChild(element);
final IErlElement ErlElement = element;
try {
ErlElement.close();
} catch (final ErlModelException e) {
ErlLogger.error(e);
}
}
}
void change(final IResource rsrc, final IResourceDelta delta) {
final IErlElement e = findElement(rsrc);
if (e != null) {
e.resourceChanged(delta);
}
}
/**
* Returns the Erlang element corresponding to the given file, its project being the
* given project. Returns <code>null</code> if unable to associate the given file with
* a Erlang element.
*
* <p>
* The file must be one of:
* <ul>
* <li>a <code>.erl</code> file - the element returned is the corresponding
* <code>IErlModule</code></li>
* <li>a <code>.beam</code> file - the element returned is the corresponding
* <code>IBeamFile</code></li>
* </ul>
* <p>
* Creating a Erlang element has the side effect of creating and opening all of the
* element's parents if they are not yet open.
*/
public IErlElement createFile(final IFile file, final IErlElement parent0) {
if (file == null) {
return null;
}
IErlElement parent = parent0;
if (parent == null) {
final IContainer parentResource = file.getParent();
if (parentResource != null) {
final IErlElement element = findElement(parentResource);
if (element != null) {
parent = element;
}
}
}
if (CommonUtils.isErlangFileContentFileName(file.getName())) {
return createModuleFromFile(file, parent);
}
return null;
}
public IErlFolder createFolder(final IFolder folder, final IErlElement parent) {
if (folder == null) {
return null;
}
final IErlFolder f = new ErlFolder(parent, folder);
final IErlElement p = parent;
if (p != null) {
p.addChild(f);
} else {
// ErlLogger.warn("creating folder %s in null parent?!", folder
// .getName());
}
return f;
}
public IErlModule createModuleFromFile(final IFile file, final IErlElement parent) {
if (file == null) {
return null;
}
final String name = file.getName();
if (CommonUtils.isErlangFileContentFileName(name)) {
final IErlModule module = new ErlModule(parent, name, file);
if (parent != null) {
parent.addChild(module);
}
return module;
}
return null;
}
/**
* Returns the Erlang project corresponding to the given project.
* <p>
* Creating a Erlang Project has the side effect of creating and opening all of the
* project's parents if they are not yet open.
* <p>
* Note that no check is done at this time on the existence or the Erlang nature of
* this project.
*
* @param project
* the given project
* @return the Erlang project corresponding to the given project, null if the given
* project is null
*/
public IErlProject createProject(final IProject project) {
if (project == null) {
return null;
}
return createErlangProject(project);
}
/**
* Returns the Erlang element corresponding to the given resource, or
* <code>null</code> if unable to associate the given resource with a Erlang element.
* <p>
* The resource must be one of:
* <ul>
* <li>a project - the element returned is the corresponding <code>IErlProject</code></li>
* <li>a <code>.erl</code> file - the element returned is the corresponding
* <code>IErlModule</code></li>
* <li>a folder - the element returned is the corresponding <code>IErlFolder</code></li>
* <li>the workspace root resource - the element returned is the
* <code>IErlModel</code></li>
* </ul>
* <p>
* Creating a Erlang element has the side effect of creating and opening all of the
* element's parents if they are not yet open.
*
* @param resource
* the given resource
* @return the Erlang element corresponding to the given resource, or
* <code>null</code> if unable to associate the given resource with a Erlang
* element
*/
@Override
public IErlElement create(final IResource resource) {
IErlElement parent = null;
final IContainer resourceParent = resource.getParent();
if (resourceParent != null) {
IErlElement element = findElement(resourceParent);
if (element == null) {
element = create(resourceParent);
}
if (element != null) {
parent = element;
}
}
return create(resource, parent);
}
/**
* Returns the Erlang model.
*
* @param root
* the given root
* @return the Erlang model, or <code>null</code> if the root is null
*/
private IErlModel createRoot(final IWorkspaceRoot root) {
if (root == null) {
return null;
}
return this;
}
class ResourceChangeListener implements IResourceChangeListener {
private final class NoOpVisitor implements IResourceDeltaVisitor {
@Override
public boolean visit(final IResourceDelta delta) throws CoreException {
return false;
}
}
private final class PreCloseVisitor implements IResourceDeltaVisitor {
private final List<IResource> removed;
private PreCloseVisitor(final List<IResource> removed) {
this.removed = removed;
}
@Override
public boolean visit(final IResourceDelta delta) throws CoreException {
final IResource resource = delta.getResource();
final boolean erlangProject = resource.getType() == IResource.PROJECT
&& NatureUtil.hasErlangNature((IProject) resource);
if (erlangProject) {
removed.add(resource);
}
return false;
}
}
private final class PostChangeVisitor implements IResourceDeltaVisitor {
private final List<IResource> removed;
private final List<IResource> added;
private final List<IResource> changed;
private final Map<IResource, IResourceDelta> changedDelta;
private PostChangeVisitor(final List<IResource> removed,
final List<IResource> added, final List<IResource> changed,
final Map<IResource, IResourceDelta> changedDelta) {
this.removed = removed;
this.added = added;
this.changed = changed;
this.changedDelta = changedDelta;
}
@Override
public boolean visit(final IResourceDelta delta) {
final IResource resource = delta.getResource();
if (ModelConfig.verbose) {
ErlLogger.debug("delta " + delta.getKind() + " for "
+ resource.getLocation());
}
final boolean erlangFile = resource.getType() == IResource.FILE
&& CommonUtils.isErlangFileContentFileName(resource.getName());
final boolean erlangProject = resource.getType() == IResource.PROJECT;
final boolean erlangFolder = resource.getType() == IResource.FOLDER;
// &&
// ErlideUtil.isOnSourcePathOrParentToFolderOnSourcePath((
// IFolder)
// resource);
if (erlangFile || erlangProject || erlangFolder) {
if (delta.getKind() == IResourceDelta.ADDED) {
added.add(resource);
}
if (delta.getKind() == IResourceDelta.CHANGED) {
changed.add(resource);
changedDelta.put(resource, delta);
}
if (delta.getKind() == IResourceDelta.REMOVED) {
removed.add(resource);
}
}
return !erlangFile;
}
}
@Override
public void resourceChanged(final IResourceChangeEvent event) {
final IResourceDelta rootDelta = event.getDelta();
final List<IResource> added = Lists.newArrayList();
final List<IResource> changed = Lists.newArrayList();
final List<IResource> removed = Lists.newArrayList();
final Map<IResource, IResourceDelta> changedDelta = Maps.newHashMap();
final IResourceDeltaVisitor visitor;
switch (event.getType()) {
case IResourceChangeEvent.POST_CHANGE:
visitor = new PostChangeVisitor(removed, added, changed, changedDelta);
break;
case IResourceChangeEvent.PRE_CLOSE:
visitor = new PreCloseVisitor(removed);
final IResource resource = event.getResource();
final boolean erlangProject = resource.getType() == IResource.PROJECT
&& NatureUtil.hasErlangNature((IProject) resource);
if (erlangProject) {
removed.add(resource);
}
break;
default:
visitor = new NoOpVisitor();
}
if (rootDelta != null) {
try {
rootDelta.accept(visitor);
} catch (final CoreException e) {
ErlLogger.warn(e);
}
}
final Set<IProject> prjs = Sets.newHashSet();
for (final IResource rsrc : added) {
prjs.add(rsrc.getProject());
create(rsrc);
}
for (final IResource rsrc : changed) {
prjs.add(rsrc.getProject());
change(rsrc, changedDelta.get(rsrc));
}
// make sure we don't dispose trees before leaves...
Collections.sort(removed, new Comparator<IResource>() {
@Override
public int compare(final IResource o1, final IResource o2) {
if (o1.equals(o2)) {
return 0;
} else if (o1.getFullPath().isPrefixOf(o2.getFullPath())) {
return 1;
} else {
return -1;
}
}
});
for (final IResource rsrc : removed) {
remove(rsrc);
}
for (final IProject prj : prjs) {
notifyProject(prj);
}
}
private void notifyProject(final IProject prj0) {
if (!prj0.exists()) {
return;
}
final IErlProject prj = findProject(prj0);
if (prj instanceof ProjectConfigurationChangeListener) {
((ProjectConfigurationChangeListener) prj).configurationChanged();
}
}
}
private static IErlModule getModuleFromCacheByNameOrPath(final ErlProject project,
final String moduleName, final String modulePath,
final IErlElementLocator.Scope scope) {
final ErlModelCache erlModelCache = ErlModelCache.getDefault();
if (modulePath != null) {
final IErlModule module = erlModelCache.getModuleByPath(modulePath);
if (module != null && (project == null || project.moduleInProject(module))) {
return module;
}
}
return null;
}
private Collection<IErlModule> getAllIncludes(final IErlProject project,
final boolean checkExternals, final IErlElementLocator.Scope scope)
throws ErlModelException {
final List<IErlProject> projects = Lists.newArrayList();
final List<IErlModule> result = Lists.newArrayList();
final Set<String> paths = Sets.newHashSet();
if (project != null) {
projects.add(project);
if (scope == IErlElementLocator.Scope.REFERENCED_PROJECTS) {
projects.addAll(project.getReferencedProjects());
}
}
if (scope == IErlElementLocator.Scope.ALL_PROJECTS) {
for (final IErlProject project2 : getErlangProjects()) {
if (!projects.contains(project2)) {
projects.add(project2);
}
}
}
for (final IErlProject project2 : projects) {
getAllModulesAux(project2.getIncludes(), result, paths);
}
if (checkExternals && project != null) {
getAllModulesAux(project.getExternalIncludes(), result, paths);
}
return result;
}
static void getAllModulesAux(final Collection<IErlModule> modules,
final Collection<IErlModule> result, final Set<String> paths) {
for (final IErlModule module : modules) {
final String path = module.getFilePath();
if (path != null) {
if (paths.contains(path)) {
continue;
}
paths.add(path);
}
result.add(module);
}
}
private IErlModule findIncludeFromProject(final IErlProject project,
final String includeName, final String includePath,
final boolean checkExternals, final IErlElementLocator.Scope scope)
throws ErlModelException {
if (project != null) {
final IErlModule module = getModuleFromCacheByNameOrPath(
(ErlProject) project, includeName, includePath, scope);
if (module != null && module.isOnIncludePath()) {
return module;
}
}
final Collection<IErlModule> includes = getAllIncludes(project, checkExternals,
scope);
ErlModelCache.getDefault().putModules(includes);
if (includePath != null) {
for (final IErlModule module2 : includes) {
final String path2 = module2.getFilePath();
if (path2 != null && ResourceUtil.samePath(includePath, path2)) {
return module2;
}
}
}
if (includeName != null) {
final boolean hasExtension = SystemConfiguration.hasExtension(includeName);
for (final IErlModule module2 : includes) {
final String name = hasExtension ? module2.getName() : module2
.getModuleName();
if (ResourceUtil.samePath(includeName, name)) {
return module2;
}
}
}
return null;
}
@Override
public IErlModule findModuleFromProject(final IErlProject project,
final String moduleName, final String modulePath,
final IErlElementLocator.Scope scope) throws ErlModelException {
return findModuleFromProject(project, moduleName, modulePath, true, scope);
}
@Override
public IErlModule findIncludeFromProject(final IErlProject project,
final String moduleName, final String modulePath,
final IErlElementLocator.Scope scope) throws ErlModelException {
return findIncludeFromProject(project, moduleName, modulePath, true, scope);
}
@Override
public IErlModule findModuleFromProject(final IErlProject project,
final String moduleName, final String modulePath,
final boolean checkExternals, final IErlElementLocator.Scope scope)
throws ErlModelException {
if (project != null) {
final IErlModule module = getModuleFromCacheByNameOrPath(
(ErlProject) project, moduleName, modulePath, scope);
if (module != null && module.isOnSourcePath()) {
return module;
}
}
final List<IErlModule> allModules = Lists.newArrayList();
final Set<String> paths = Sets.newHashSet();
try {
for (int i = 0; i < 2; ++i) {
final boolean externalModules = i > 0;
if (externalModules && !checkExternals) {
break;
}
if (project != null) {
final IErlModule module = tryFindModule(Sets.newHashSet(project),
moduleName, modulePath, allModules, paths, externalModules);
if (module != null) {
return module;
}
}
if ((scope == Scope.REFERENCED_PROJECTS || scope == Scope.ALL_PROJECTS)
&& project != null) {
final Collection<IErlProject> projects = project
.getReferencedProjects();
final IErlModule module = tryFindModule(projects, moduleName,
modulePath, allModules, paths, externalModules);
if (module != null) {
return module;
}
}
if (scope == Scope.ALL_PROJECTS) {
final Collection<IErlProject> projects = getErlangProjects();
final IErlModule module = tryFindModule(projects, moduleName,
modulePath, allModules, paths, externalModules);
if (module != null) {
return module;
}
}
}
return null;
} finally {
ErlModelCache.getDefault().putModules(allModules);
}
}
private IErlModule tryFindModule(final Collection<IErlProject> projects,
final String moduleName, final String modulePath,
final List<IErlModule> allModules, final Set<String> paths,
final boolean externalModules) throws ErlModelException {
IErlModule module;
for (final IErlProject project : projects) {
final Collection<IErlModule> modules = Lists.newArrayList();
final Collection<IErlModule> modulesOrExternals = externalModules ? project
.getExternalModules() : project.getModules();
getAllModulesAux(modulesOrExternals, modules, paths);
allModules.addAll(modules);
module = findModule(modules, moduleName, modulePath);
if (module != null) {
return module;
}
}
return null;
}
private IErlModule findModule(final Collection<IErlModule> modules,
final String moduleName, final String modulePath) {
if (modulePath != null) {
for (final IErlModule module : modules) {
final String path = module.getFilePath();
if (path != null && ResourceUtil.samePath(modulePath, path)) {
return module;
}
}
}
if (moduleName != null) {
final boolean hasExtension = SystemConfiguration.hasExtension(moduleName);
for (final IErlModule module : modules) {
final String name = hasExtension ? module.getName() : module
.getModuleName();
if (moduleName.equals(name)) {
return module;
}
}
}
return null;
}
@Override
public IErlModule findIncludeFromModule(final IErlModule module,
final String includeName, final String includePath,
final IErlElementLocator.Scope scope) throws ErlModelException {
final IErlElement parent = module.getParent();
if (parent instanceof IErlFolder) {
final IErlFolder folder = (IErlFolder) parent;
folder.open(null);
final IErlModule include = folder.findInclude(includeName, includePath);
if (include != null) {
return include;
}
}
return findIncludeFromProject(ErlangEngine.getInstance().getModelUtilService()
.getProject(module), includeName, includePath, true, scope);
}
private final Object fModelLock = new Object();
@Override
public Object getModelLock() {
return fModelLock;
}
@Override
public IErlElementDelta createElementDelta(final int kind, final int flags,
final IErlElement element) {
return new ErlElementDelta(kind, flags, element);
}
}