/*
* Copyright 2013 Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.core.internal.analysis.model;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.index.Index;
import com.google.dart.engine.sdk.DartSdk;
import com.google.dart.engine.search.SearchEngine;
import com.google.dart.engine.search.SearchEngineFactory;
import com.google.dart.engine.source.Source;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.analysis.model.AnalysisEvent;
import com.google.dart.tools.core.analysis.model.AnalysisListener;
import com.google.dart.tools.core.analysis.model.IFileInfo;
import com.google.dart.tools.core.analysis.model.Project;
import com.google.dart.tools.core.analysis.model.ProjectEvent;
import com.google.dart.tools.core.analysis.model.ProjectListener;
import com.google.dart.tools.core.analysis.model.ProjectManager;
import com.google.dart.tools.core.analysis.model.PubFolder;
import com.google.dart.tools.core.analysis.model.ResolvedEvent;
import com.google.dart.tools.core.analysis.model.ResolvedHtmlEvent;
import com.google.dart.tools.core.analysis.model.ResourceMap;
import com.google.dart.tools.core.builder.BuildEvent;
import com.google.dart.tools.core.internal.builder.AnalysisEngineParticipant;
import com.google.dart.tools.core.internal.builder.AnalysisManager;
import com.google.dart.tools.core.internal.builder.AnalysisMarkerManager;
import com.google.dart.tools.core.internal.builder.AnalysisWorker;
import com.google.dart.tools.core.internal.model.DartIgnoreManager;
import com.google.dart.tools.core.model.DartIgnoreListener;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Concrete implementation of {@link ProjectManager}.
*
* @coverage dart.tools.core.model
*/
public class ProjectManagerImpl extends ContextManagerImpl implements ProjectManager {
private static final PubFolder[] NO_PUB_FOLDERS = new PubFolder[] {};
private final IWorkspaceRoot resource;
private final HashMap<IProject, Project> projects = new HashMap<IProject, Project>();
private final Index index;
private boolean indexEnabled = true;
private final DartIgnoreManager ignoreManager;
private final ArrayList<ProjectListener> listeners = new ArrayList<ProjectListener>();
private final AnalysisListener indexNotifier = new AnalysisListener() {
@Override
public void complete(AnalysisEvent event) {
}
@Override
public void resolved(ResolvedEvent event) {
if (!indexEnabled) {
return;
}
index.indexUnit(event.getContext(), event.getUnit());
}
@Override
public void resolvedHtml(ResolvedHtmlEvent event) {
if (!indexEnabled) {
return;
}
index.indexHtmlUnit(event.getContext(), event.getUnit());
}
};
/**
* A listener that updates the manager when a project is closed. In addition, this listener
* processes changes in packages directory because Eclipse builders do not receive deltas for
* changes in symlinked folders.
*/
private IResourceChangeListener resourceChangeListener = new WorkspaceDeltaProcessor(this);
private final DartIgnoreListener ignoreListener;
public ProjectManagerImpl(IWorkspaceRoot resource, DartSdk sdk, String sdkContextId, Index index,
DartIgnoreManager ignoreManager) {
super(sdk, sdkContextId);
this.resource = resource;
this.index = index;
this.ignoreManager = ignoreManager;
this.ignoreListener = new ProjectManagerIgnoreListener(
this,
ResourcesPlugin.getWorkspace().getRoot(),
AnalysisManager.getInstance(),
AnalysisMarkerManager.getInstance(),
index);
}
@Override
public void addProjectListener(ProjectListener listener) {
synchronized (listeners) {
if (listener != null && !listeners.contains(listener)) {
listeners.add(listener);
}
}
}
@Override
public void disableIndex() {
indexEnabled = false;
index.clear();
}
@Override
public PubFolder[] getContainedPubFolders(IContainer container) {
Project project = getProject(container.getProject());
if (project != null) {
return project.getContainedPubFolders(container);
}
return NO_PUB_FOLDERS;
}
@Override
public AnalysisContext getContext(IResource resource) {
if (resource == null) {
return null;
}
Project project = getProject(resource.getProject());
if (project == null) {
return null;
}
return project.getContext(resource);
}
@Override
public String getContextId(IResource resource) {
if (resource == null) {
return null;
}
Project project = getProject(resource.getProject());
if (project == null) {
return null;
}
return project.getContextId(resource);
}
@Override
public IResource getHtmlFileForLibrary(Source source) {
AnalysisContext context = getContext(getResource(source));
if (context != null) {
Source[] htmlSource = context.getHtmlFilesReferencing(source);
if (htmlSource.length > 0) {
return getResource(htmlSource[0]);
}
}
return null;
}
@Override
public DartIgnoreManager getIgnoreManager() {
return ignoreManager;
}
@Override
public Index getIndex() {
return index;
}
@Override
public Source[] getLibrarySources(IFile file) {
AnalysisContext context = getContext(file);
Source source = getSource(file);
return context.getLibrariesContaining(source);
}
@Override
public Source[] getLibrarySources(IProject projectResource) {
Project project = getProject(projectResource);
if (project == null) {
return Source.EMPTY_ARRAY;
}
return project.getLibrarySources();
}
@Override
public Project getProject(IProject resource) {
if (!resource.exists()) {
return null;
}
synchronized (projects) {
Project result = projects.get(resource);
if (result == null) {
result = new ProjectImpl(resource, getSdk(), getSdkContextId());
projects.put(resource, result);
}
return result;
}
}
@Override
public IProject getProjectForContext(AnalysisContext context) {
for (Project project : getProjects()) {
if (project.isContextInProject(context)) {
return project.getResource();
}
}
return null;
}
@Override
public Project[] getProjects() {
IProject[] childResources = resource.getProjects();
List<Project> result = new ArrayList<Project>();
for (int index = 0; index < childResources.length; index++) {
IProject prj = childResources[index];
try {
if (prj.hasNature(DartCore.DART_PROJECT_NATURE)) {
Project project = getProject(childResources[index]);
if (project != null) {
result.add(project);
}
}
} catch (CoreException e) {
// do nothing, just continue
}
}
return result.toArray(new Project[result.size()]);
}
@Override
public PubFolder getPubFolder(IResource resource) {
Project project = getProject(resource.getProject());
if (project == null) {
return null;
}
return project.getPubFolder(resource);
}
@Override
public IWorkspaceRoot getResource() {
return resource;
}
@Override
public IResource getResource(Source source) {
// TODO (danrubel): revisit and optimize performance
if (source == null) {
return null;
}
for (Project project : getProjects()) {
IResource res = project.getResource(source);
if (res != null) {
return res;
}
}
return null;
}
@Override
public ResourceMap getResourceMap(AnalysisContext context) {
for (Project project : getProjects()) {
ResourceMap map = project.getResourceMap(context);
if (map != null) {
return map;
}
}
return null;
}
@Override
public ResourceMap getResourceMap(IResource resource) {
Project project = getProject(resource.getProject());
if (project == null) {
return null;
}
return project.getResourceMap(resource);
}
@Override
public ResourceMap getResourceMap(String contextId) {
for (Project project : getProjects()) {
ResourceMap map = project.getResourceMap(contextId);
if (map != null) {
return map;
}
}
return null;
}
@Override
public void hookListeners() {
resource.getWorkspace().addResourceChangeListener(resourceChangeListener);
ignoreManager.addListener(ignoreListener);
AnalysisWorker.addListener(indexNotifier);
}
@Override
public boolean isClientLibrary(Source librarySource) {
IResource resource = getResource(librarySource);
if (resource != null) {
AnalysisContext context = getContext(resource);
return context.isClientLibrary(librarySource);
}
return false;
}
@Override
public boolean isServerLibrary(Source librarySource) {
IResource resource = getResource(librarySource);
if (resource != null) {
AnalysisContext context = getContext(resource);
return context.isServerLibrary(librarySource);
}
return false;
}
@Override
public SearchEngine newSearchEngine() {
return SearchEngineFactory.createSearchEngine(getIndex());
}
@Override
public void projectAnalyzed(Project project) {
final ProjectEvent event = new ProjectEvent(project);
ProjectListener[] currentListeners = listeners.toArray(new ProjectListener[listeners.size()]);
for (ProjectListener listener : currentListeners) {
try {
listener.projectAnalyzed(event);
} catch (Exception e) {
DartCore.logError("Exception while notifying listeners project was analyzed", e);
}
}
}
@Override
public void projectRemoved(IProject projectResource) {
Project result;
synchronized (projects) {
result = projects.remove(projectResource);
}
if (result != null) {
result.discardContextsIn(projectResource);
}
}
@Override
public void removeProjectListener(ProjectListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
@Override
public IFile resolvePackageUri(IResource relativeTo, String uri) {
ResourceMap map = getResourceMap(relativeTo);
if (map == null) {
return null;
}
Source source = map.getContext().getSourceFactory().forUri(uri);
if (source == null) {
return null;
} else {
return map.getResource(source);
}
}
@Override
public String resolvePathToPackage(IResource resource, String path) {
Project project = getProject(resource.getProject());
if (project != null) {
return project.resolvePathToPackage(path);
}
return null;
}
@Override
public IFileInfo resolveUriToFileInfo(IResource relativeTo, String uri) {
Project project = getProject(relativeTo.getProject());
if (project != null) {
return project.resolveUriToFileInfo(relativeTo, uri);
}
return null;
}
@Override
public void setAngularAnalysisOption(boolean enable) {
for (Project project : getProjects()) {
project.setAngularAnalysisOption(enable);
}
}
@Override
public void setDart2JSHintOption(boolean enableDart2JSHints) {
for (Project project : getProjects()) {
project.setDart2JSHintOption(enableDart2JSHints);
}
}
@Override
public void setHintOption(boolean enableHint) {
for (Project project : getProjects()) {
project.setHintOption(enableHint);
}
}
@Override
public void setLintOption(boolean enableLint) {
for (Project project : getProjects()) {
project.setLintOption(enableLint);
}
}
@Override
public void start() {
new AnalysisWorker(this, getSdkContext()).performAnalysisInBackground();
analyzeAllProjects();
}
@Override
public void stop() {
resource.getWorkspace().removeResourceChangeListener(resourceChangeListener);
ignoreManager.removeListener(ignoreListener);
AnalysisWorker.removeListener(indexNotifier);
AnalysisManager.getInstance().stopBackgroundAnalysis();
AnalysisMarkerManager.getInstance().stop();
}
private void analyzeAllProjects() {
for (Project project : getProjects()) {
BuildEvent event = new BuildEvent(project.getResource(), null, new NullProgressMonitor());
AnalysisEngineParticipant participant = new AnalysisEngineParticipant(
this,
AnalysisMarkerManager.getInstance());
try {
participant.build(event, new NullProgressMonitor());
} catch (CoreException e) {
DartCore.logError(e);
}
}
}
}