/******************************************************************************* * 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.SafeRunner; import org.eclipse.xtext.xbase.lib.Functions.Function1; import org.eclipse.xtext.xbase.lib.IterableExtensions; 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.root.ErlElementDelta; import org.erlide.engine.internal.model.root.ErlFolder; import org.erlide.engine.internal.model.root.ErlModule; import org.erlide.engine.internal.model.root.ErlOtpLibrary; import org.erlide.engine.internal.model.root.ErlProject; import org.erlide.engine.internal.model.root.Openable; import org.erlide.engine.internal.util.ModelConfig; import org.erlide.engine.model.ErlElementKind; import org.erlide.engine.model.ErlModelException; import org.erlide.engine.model.ErlModelStatus; import org.erlide.engine.model.IErlElement; import org.erlide.engine.model.IParent; 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.root.ElementChangedEvent; import org.erlide.engine.model.root.IElementChangedListener; 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.IErlLibrary; import org.erlide.engine.model.root.IErlModel; import org.erlide.engine.model.root.IErlModelChangeListener; import org.erlide.engine.model.root.IErlModule; import org.erlide.engine.model.root.IErlProject; import org.erlide.engine.model.root.IOpenable; 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.runtime.runtimeinfo.RuntimeVersion; import org.erlide.util.ErlLogger; import org.erlide.util.SystemConfiguration; 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 Openable 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(project, this); 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(); IParent 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 && c instanceof IOpenable) { final IOpenable o = (IOpenable) 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 = (IParent) c; } return null; } @Override public IErlElement innermostThat(final IErlElement el, final Predicate<IErlElement> firstThat) { if (el instanceof IParent) { final IParent p = (IParent) 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()) { project.create(null); project.open(null); final IProjectDescription description = project.getDescription(); description.setNatureIds(new String[] { ModelPlugin.NATURE_ID }); description.setName(name); project.setDescription(description, null); } if (!project.isOpen()) { project.open(null); } return findProject(project); } catch (final CoreException e) { e.printStackTrace(); 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() { // if (fCachedPathVars == null) { final IPathVariableManager pvm = ResourcesPlugin.getWorkspace() .getPathVariableManager(); final String[] names = pvm.getPathVariableNames(); final OtpErlangObject[] objects = new OtpErlangObject[names.length]; for (int i = 0; i < names.length; i++) { final String name = names[i]; final String value = URIUtil.toPath(pvm.getURIValue(name)).toPortableString(); objects[i] = new OtpErlangTuple(new OtpErlangObject[] { new OtpErlangString(name), new OtpErlangString(value) }); } fCachedPathVars = new OtpErlangList(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<>(); private static Map<IErlModule, Object> mapModule = new HashMap<>(); @Override public IErlModule getModuleFromFile(final IParent 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 IParent parent, final String name, final String initialText, final String key) { return getModuleWithoutResource(parent, name, null, null, initialText, key); } private IErlModule getModuleWithoutResource(final IParent 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 IParent 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 IParent 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; } // TODO should we make Erlidemodelevents and fire them? } void remove(final IResource rsrc) { final IErlElement element = findElement(rsrc); if (element != null) { final IParent p = element.getParent(); p.removeChild(element); if (element instanceof IOpenable) { final IOpenable openable = (IOpenable) element; try { openable.close(); } catch (final ErlModelException e) { ErlLogger.error(e); } } } // TODO should we make Erlidemodelevents and fire them? } void change(final IResource rsrc, final IResourceDelta delta) { final IErlElement e = findElement(rsrc); if (e != null) { e.resourceChanged(delta); } // TODO should we make Erlidemodelevents and fire them? } /** * 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 IParent parent0) { if (file == null) { return null; } IParent parent = parent0; if (parent == null) { final IContainer parentResource = file.getParent(); if (parentResource != null) { final IErlElement element = findElement(parentResource); if (element instanceof IParent) { parent = (IParent) element; } } } if (CommonUtils.isErlangFileContentFileName(file.getName())) { return createModuleFromFile(file, parent); } return null; } public IErlFolder createFolder(final IFolder folder, final IParent parent) { if (folder == null) { return null; } final IErlFolder f = new ErlFolder(folder, parent); final IParent 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 IParent 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); } public IErlLibrary createLibrary(final RuntimeVersion version) { if (version == null) { return null; } final IErlLibrary ep = new ErlOtpLibrary(version, this); addChild(ep); return ep; } /** * 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) { IParent parent = null; final IContainer resourceParent = resource.getParent(); if (resourceParent != null) { IErlElement element = findElement(resourceParent); if (element == null) { element = create(resourceParent); } if (element instanceof IParent) { parent = (IParent) 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 && includePath.equals(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 IParent 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); } @Override public Collection<IErlLibrary> getLibraries() throws ErlModelException { final Collection<IErlElement> list = getChildrenOfKind(ErlElementKind.LIBRARY); final Collection<IErlLibrary> result = Lists.newArrayList(); for (final IErlElement e : list) { result.add((IErlLibrary) e); } return result; } @Override public IErlLibrary getLibrary(final String name) throws ErlModelException { return IterableExtensions.findFirst(getLibraries(), new Function1<IErlLibrary, Boolean>() { @Override public Boolean apply(final IErlLibrary input) { return input.getName().equals(name); } }); } }