blob: 34964be3b603afa0ef1e6fe42c33e65b6550b368 [file] [log] [blame]
/*
* Copyright (c) 2013, the 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.AnalysisEngine;
import com.google.dart.engine.context.AnalysisContext;
import com.google.dart.engine.index.Index;
import com.google.dart.engine.internal.context.AnalysisOptionsImpl;
import com.google.dart.engine.internal.context.InstrumentedAnalysisContextImpl;
import com.google.dart.engine.sdk.DartSdk;
import com.google.dart.engine.sdk.DirectoryBasedDartSdk;
import com.google.dart.engine.source.DartUriResolver;
import com.google.dart.engine.source.DirectoryBasedSourceContainer;
import com.google.dart.engine.source.ExplicitPackageUriResolver;
import com.google.dart.engine.source.FileUriResolver;
import com.google.dart.engine.source.PackageUriResolver;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.source.SourceContainer;
import com.google.dart.engine.source.SourceFactory;
import com.google.dart.engine.source.UriResolver;
import com.google.dart.tools.core.CmdLineOptions;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.core.analysis.model.FileInfo;
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.PubFolder;
import com.google.dart.tools.core.analysis.model.ResourceMap;
import com.google.dart.tools.core.internal.builder.DeltaAdapter;
import com.google.dart.tools.core.internal.builder.DeltaProcessor;
import com.google.dart.tools.core.internal.builder.ResourceDeltaEvent;
import com.google.dart.tools.core.utilities.io.FileUtilities;
import static com.google.dart.tools.core.DartCore.PACKAGES_DIRECTORY_NAME;
import static com.google.dart.tools.core.DartCore.PUBSPEC_FILE_NAME;
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.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import static org.eclipse.core.resources.IResource.PROJECT;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
/**
* Represents an Eclipse project that has a Dart nature.
*
* @coverage dart.tools.core.model
*/
public class ProjectImpl extends ContextManagerImpl implements Project {
public static class AnalysisContextFactory {
public AnalysisContext createContext() {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
throw new IllegalStateException("This method cannot be used with Analysis Server enabled.");
}
return AnalysisEngine.getInstance().createAnalysisContext();
}
public File[] getPackageRoots(IContainer container) {
return ProjectImpl.getPackageRoots(
DartCore.getPlugin(),
CmdLineOptions.getOptions(),
container);
}
}
/**
* Answer the package roots for the specified project with the given options. May return an empty
* array if no package roots are specified either at the project level or in the command line
* options.
*
* @param core the instance of DartCore used to access project preferences
* @param options the command line options (not {@code null})
* @param container the container for which package roots are to be returned
* @return the package roots (not {@code null}, contains no {@code null}s)
*/
public static File[] getPackageRoots(DartCore core, CmdLineOptions options, IContainer container) {
if (container instanceof IProject) {
IEclipsePreferences prefs = core.getProjectPreferences((IProject) container);
String setting = prefs.get(DartCore.PROJECT_PREF_PACKAGE_ROOT, "");
if (setting != null && setting.length() > 0) {
ArrayList<File> files = new ArrayList<File>();
String[] paths = setting.split(File.pathSeparator);
for (String path : paths) {
files.add(new File(path));
}
return files.toArray(new File[files.size()]);
}
File packOverrideDir = options.getPackageOverrideDirectory();
if (packOverrideDir != null) {
File root = new File(packOverrideDir, container.getLocation().toOSString());
return new File[] {new File(root, DartCore.PACKAGES_DIRECTORY_NAME)};
}
}
return options.getPackageRoots();
}
/**
* The Eclipse project associated with this Dart project (not {@code null})
*/
private final IProject projectResource;
/**
* The project resource location on disk or {@code null} if it has not yet been initialized.
*
* @see #getResourceLocation()
*/
private IPath projectResourceLocation;
/**
* The factory used to create new {@link AnalysisContext} (not {@code null})
*/
private final AnalysisContextFactory factory;
/**
* A sparse mapping of {@link IResource#getFullPath()} to {@link PubFolder}. Synchronize against
* this field and call {@link #initialize()} before accessing this field.
*/
private final HashMap<IPath, PubFolder> pubFolders = new HashMap<IPath, PubFolder>();
/**
* The default analysis context for this project or {@code null} if not yet initialized.
* Synchronize against {@link #pubFolders} and call {@link #initialize()} before accessing this
* field.
*/
private AnalysisContext defaultContext;
/**
* The ID of default analysis context for this project or {@code null} if not yet initialized.
* Synchronize against {@link #pubFolders} and call {@link #initialize()} before accessing this
* field.
*/
private String defaultContextId;
/**
* The default resource map for this project (not {@code null}). This resource map is only used if
* no pubspec or package root is defined.
*/
private ResourceMap defaultResourceMap;
/**
* The index which is updated when contexts are discarded (not {@code null}).
*/
private final Index index;
/**
* The default package resolver used for the project
*/
private UriResolver defaultPackageResolver;
/**
* Construct a new instance representing a Dart project
*
* @param resource the Eclipse project associated with this Dart project (not {@code null})
* @param sdk the Dart SDK to use when initializing contexts (not {@code null})
* @param sdkContextId the identifier of the Dart SDK context (not {@code null})
*/
public ProjectImpl(IProject resource, DartSdk sdk, String sdkContextId) {
this(
resource,
sdk,
sdkContextId,
DartCore.getProjectManager().getIndex(),
new AnalysisContextFactory());
}
/**
* Construct a new instance representing a Dart project
*
* @param resource the Eclipse project associated with this Dart project (not {@code null})
* @param sdk the Dart SDK to use when initializing contexts (not {@code null})
* @param sdkContextId the identifier of the Dart SDK context (not {@code null})
* @param index the index to be updated when contexts are discarded (not {@code null})
* @param factory the factory used to construct new analysis contexts (not {@code null})
*/
public ProjectImpl(IProject resource, DartSdk sdk, String sdkContextId, Index index,
AnalysisContextFactory factory) {
super(sdk, sdkContextId);
if (resource == null | factory == null | sdk == null) {
throw new IllegalArgumentException();
}
this.projectResource = resource;
this.index = index;
this.factory = factory;
}
@Override
public void discardContextsIn(IContainer container) {
synchronized (pubFolders) {
if (!isInitialized()) {
return;
}
// Remove contained pub folders
IPath path = container.getFullPath();
Iterator<Entry<IPath, PubFolder>> iter = pubFolders.entrySet().iterator();
while (iter.hasNext()) {
Entry<IPath, PubFolder> entry = iter.next();
IPath key = entry.getKey();
if (path.equals(key) || path.isPrefixOf(key)) {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
// TODO(scheglov) restore or remove for the new API
// String contextId = entry.getValue().getContextId();
// DartCore.getAnalysisServer().deleteContext(contextId);
} else {
AnalysisContext context = entry.getValue().getContext();
stopWorkers(context);
context.dispose();
index.removeContext(context);
}
iter.remove();
}
}
// Reset the state if discarding the entire project
if (projectResource.equals(container)) {
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
// TODO(scheglov) restore or remove for the new API
// DartCore.getAnalysisServer().deleteContext(defaultContextId);
// defaultContextId = null;
} else {
stopWorkers(defaultContext);
defaultContext.dispose();
index.removeContext(defaultContext);
defaultContext = null;
}
defaultResourceMap = null;
}
}
}
@Override
public PubFolder[] getContainedPubFolders(IContainer container) {
if (!getResource().equals(container.getProject())) {
throw new IllegalArgumentException();
}
ArrayList<PubFolder> result = new ArrayList<PubFolder>();
IPath prefix = container.getFullPath();
synchronized (pubFolders) {
initialize();
for (Entry<IPath, PubFolder> entry : pubFolders.entrySet()) {
IPath pubPath = entry.getKey();
if (prefix.segmentCount() < pubPath.segmentCount() && prefix.isPrefixOf(pubPath)) {
result.add(entry.getValue());
}
}
}
return result.toArray(new PubFolder[result.size()]);
}
@Override
public AnalysisContext getContext(IResource resource) {
synchronized (pubFolders) {
PubFolder pubFolder = getPubFolder(resource);
return pubFolder != null ? pubFolder.getContext() : defaultContext;
}
}
@Override
public String getContextId(IResource resource) {
synchronized (pubFolders) {
PubFolder pubFolder = getPubFolder(resource);
return pubFolder != null ? pubFolder.getContextId() : defaultContextId;
}
}
@Override
public AnalysisContext getDefaultContext() {
synchronized (pubFolders) {
initialize();
return defaultContext;
}
}
@Override
public String getDefaultContextId() {
synchronized (pubFolders) {
initialize();
return defaultContextId;
}
}
@Override
public Source[] getLibrarySources() {
Set<Source> sources = new HashSet<Source>();
synchronized (pubFolders) {
for (PubFolder pubfolder : getPubFolders()) {
sources.addAll(Arrays.asList(pubfolder.getContext().getLibrarySources()));
}
sources.addAll(Arrays.asList(getDefaultContext().getLibrarySources()));
}
return sources.toArray(new Source[sources.size()]);
}
@Override
public PubFolder getPubFolder(IResource resource) {
if (!resource.getProject().equals(projectResource)) {
throw new IllegalArgumentException();
}
synchronized (pubFolders) {
initialize();
return getPubFolder(resource.getFullPath());
}
}
@Override
public PubFolder[] getPubFolders() {
Collection<PubFolder> allFolders;
synchronized (pubFolders) {
initialize();
allFolders = pubFolders.values();
}
return allFolders.toArray(new PubFolder[allFolders.size()]);
}
@Override
public IProject getResource() {
return projectResource;
}
@Override
public IResource getResource(Source source) {
// TODO (danrubel): revisit and optimize performance
if (source == null) {
return null;
}
String pathName = source.getFullName();
String pathNameFS = FileUtilities.getFileSystemCase(pathName);
IPath path = new Path(pathNameFS);
IPath projPath = getResourceLocation();
if (projPath != null && projPath.isPrefixOf(path)) {
IPath relPath = path.removeFirstSegments(projPath.segmentCount());
return projectResource.getFile(relPath);
}
// TODO (danrubel): Handle mapped subfolders
return null;
}
@Override
public ResourceMap getResourceMap(AnalysisContext context) {
synchronized (pubFolders) {
for (PubFolder pubFolder : getPubFolders()) {
AnalysisContext folderContext = pubFolder.getContext();
if (folderContext == context) {
return pubFolder;
}
if (folderContext instanceof InstrumentedAnalysisContextImpl
&& ((InstrumentedAnalysisContextImpl) folderContext).getBasis() == context) {
return pubFolder;
}
}
}
if (defaultContext == context) {
return defaultResourceMap;
}
if (defaultContext instanceof InstrumentedAnalysisContextImpl
&& ((InstrumentedAnalysisContextImpl) defaultContext).getBasis() == context) {
return defaultResourceMap;
}
return null;
}
@Override
public ResourceMap getResourceMap(IResource resource) {
synchronized (pubFolders) {
PubFolder pubFolder = getPubFolder(resource);
return pubFolder != null ? pubFolder : defaultResourceMap;
}
}
@Override
public ResourceMap getResourceMap(String contextId) {
synchronized (pubFolders) {
for (PubFolder pubFolder : getPubFolders()) {
String folderContextId = pubFolder.getContextId();
if (folderContextId.equals(contextId)) {
return pubFolder;
}
}
}
if (defaultContextId.equals(contextId)) {
return defaultResourceMap;
}
return null;
}
@Override
public boolean isContextInProject(AnalysisContext context) {
return getResourceMap(context) != null;
}
@Override
public void pubspecAdded(IContainer container) {
synchronized (pubFolders) {
if (!isInitialized()) {
return;
}
// If there is already a pubFolder, then ignore the addition
if (getPubFolder(container.getFullPath()) != null) {
return;
}
// Create and cache a new pub folder
DartSdk sdk = getSdk();
UriResolver pkgResolver = getPackageUriResolver(container, sdk, true);
PubFolderImpl pubFolder;
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
pubFolder = new PubFolderImpl(
container,
null,
createContextId(container, sdk),
sdk,
pkgResolver);
} else {
pubFolder = new PubFolderImpl(
container,
createContext(container, sdk),
null,
sdk,
pkgResolver);
}
pubFolders.put(container.getFullPath(), pubFolder);
// If this is the project, then adjust the context source factory
if (container.getType() == PROJECT) {
initContext(
defaultContext,
projectResource,
sdk,
getPackageUriResolver(projectResource, sdk, true));
}
// Merge any overlapping pub folders
for (Iterator<Entry<IPath, PubFolder>> iter = pubFolders.entrySet().iterator(); iter.hasNext();) {
Entry<IPath, PubFolder> entry = iter.next();
PubFolder parent = getParentPubFolder(entry.getKey());
if (parent != null) {
parent.getContext().mergeContext(entry.getValue().getContext());
iter.remove();
}
}
}
}
@Override
public void pubspecRemoved(IContainer container) {
synchronized (pubFolders) {
if (!isInitialized()) {
return;
}
// If no pubFolder defined for this container, then ignore
PubFolder pubFolder = pubFolders.remove(container.getFullPath());
if (pubFolder == null) {
return;
}
// Merge the context into the default context or re-initialize the default context
AnalysisContext context = pubFolder.getContext();
if (defaultContext != context) {
defaultContext.mergeContext(context);
index.removeContext(context);
} else {
initContext(
defaultContext,
projectResource,
getSdk(),
getPackageUriResolver(container, getSdk(), false));
}
// Traverse container to find pubspec files that were overshadowed by the one just removed
createPubFolders(container);
}
}
@Override
public String resolvePathToPackage(String path) {
PubFolder folder = getPubFolder(new Path(path));
if (folder != null) {
return folder.resolvePathToPackage(path);
} else {
if (defaultPackageResolver instanceof ExplicitPackageUriResolver) {
return ((ExplicitPackageUriResolver) defaultPackageResolver).resolvePathToPackage(path);
}
}
return null;
}
@Override
public IFileInfo resolveUriToFileInfo(IResource relativeTo, String uri) {
AnalysisContext context = getContext(relativeTo);
Source source = context.getSourceFactory().forUri(uri);
ResourceMap map = getResourceMap(relativeTo);
if (source != null && map != null) {
IFile resource = map.getResource(source);
if (resource != null) {
// linked files do not resolve to canonical paths. So find if it is
// a self referenced package and get the resource in lib instead.
if (DartCore.isWindows()) {
int index = 0;
IPath path = resource.getFullPath();
while (index < path.segmentCount()
&& !path.segment(index).equals(PACKAGES_DIRECTORY_NAME)) {
index++;
}
if (index != path.segmentCount()) {
String packageName = path.segment(index + 1);
if (packageName.equals(DartCore.getSelfLinkedPackageName(resource))) {
// src/app/packages/app/mylib.dart => /src/app/lib/mylib.dart
IPath newPath = path.uptoSegment(index).append("lib").append(
path.removeFirstSegments(index + 2));
IResource libResource = ResourcesPlugin.getWorkspace().getRoot().findMember(newPath);
if (libResource != null) {
return new FileInfo(libResource.getLocation().toFile(), (IFile) libResource);
}
} else {
// check if the package is open in the workspace, if so return resource from that
// project instead of read only packages resource
// src/app/packages/mypackage/mylib.dart => /src/mypackage/lib/mylib.dart
String projectPath = getPubFolderPath(packageName);
if (projectPath != null) {
IPath newPath = new Path(projectPath).append("lib").append(
path.removeFirstSegments(index + 2));
IResource libResource = ResourcesPlugin.getWorkspace().getRoot().findMember(newPath);
if (libResource != null) {
return new FileInfo(libResource.getLocation().toFile(), (IFile) libResource);
}
}
}
}
}
return new FileInfo(new File(source.getFullName()), resource);
}
}
if (source != null) {
return new FileInfo(new File(source.getFullName()));
}
return null;
}
@Override
public void setAngularAnalysisOption(boolean enable) {
for (AnalysisContext context : getAnalysisContexts()) {
AnalysisOptionsImpl options = new AnalysisOptionsImpl(context.getAnalysisOptions());
options.setAnalyzeAngular(enable);
context.setAnalysisOptions(options);
}
}
@Override
public void setDart2JSHintOption(boolean enableDart2JSHints) {
for (AnalysisContext context : getAnalysisContexts()) {
AnalysisOptionsImpl options = new AnalysisOptionsImpl(context.getAnalysisOptions());
options.setDart2jsHint(enableDart2JSHints);
context.setAnalysisOptions(options);
}
}
@Override
public void setHintOption(boolean enableHint) {
for (AnalysisContext context : getAnalysisContexts()) {
AnalysisOptionsImpl options = new AnalysisOptionsImpl(context.getAnalysisOptions());
options.setHint(enableHint);
context.setAnalysisOptions(options);
}
}
@Override
public void setLintOption(boolean enableLint) {
//TODO(pquitslund): add lint to analysis options
// for (AnalysisContext context : getAnalysisContexts()) {
// AnalysisOptionsImpl options = new AnalysisOptionsImpl(context.getAnalysisOptions());
// options.setLint(enableLint);
// context.setAnalysisOptions(options);
// }
}
@Override
public String toString() {
String name;
try {
name = getResource().getName();
} catch (Exception e) {
name = "unknown: " + e.getMessage();
}
return getClass().getSimpleName() + "[" + name + "]@" + Integer.toHexString(hashCode());
}
/**
* Answer the {@link AnalysisContext} for the specified container, creating one if necessary. Must
* synchronize against {@link #pubFolders} before calling this method and either call
* {@link #initialize()} or check that {@link #isInitialized()} returns {@code true}.
*
* @param container the container with sources to be analyzed (not {@code null})
* @param sdk the Dart SDK to use when initializing the context (not {@code null})
* @return the context (not {@code null})
*/
private AnalysisContext createContext(IContainer container, DartSdk sdk) {
if (container.equals(projectResource)) {
return defaultContext;
}
AnalysisContext context;
IPath location = container.getLocation();
if (location != null) {
SourceContainer sourceContainer = new DirectoryBasedSourceContainer(location.toFile());
context = defaultContext.extractContext(sourceContainer);
} else {
logNoLocation(container);
context = factory.createContext();
}
return initContext(context, container, sdk, getPackageUriResolver(container, sdk, true));
}
/**
* Answer the ID of a context for the specified container, creating one if necessary. Must
* synchronize against {@link #pubFolders} before calling this method and either call
* {@link #initialize()} or check that {@link #isInitialized()} returns {@code true}.
*
* @param container the container with sources to be analyzed (not {@code null})
* @param sdk the Dart SDK to use when initializing the context (not {@code null})
* @return the ID of context (not {@code null})
*/
private String createContextId(IContainer container, DartSdk sdk) {
if (container.equals(projectResource)) {
return defaultContextId;
}
// TODO(scheglov) Analysis Server
throw new UnsupportedOperationException();
// AnalysisContext context;
// IPath location = container.getLocation();
// if (location != null) {
// // TODO(scheglov)
// SourceContainer sourceContainer = new DirectoryBasedSourceContainer(location.toFile());
// context = defaultContext.extractContext(sourceContainer);
// } else {
// logNoLocation(container);
// context = factory.createContext();
// }
// return initContext(context, container, sdk, getPackageUriResolver(container, sdk, true));
}
/**
* Create pub folders for any pubspec files found within the specified container. Must synchronize
* against {@link #pubFolders} before calling this method and either call {@link #initialize()} or
* check that {@link #isInitialized()} returns {@code true}.
*
* @param container the container (not {@code null})
*/
private void createPubFolders(IContainer container) {
DeltaProcessor processor = new DeltaProcessor(this);
processor.addDeltaListener(new DeltaAdapter() {
@Override
public void pubspecAdded(ResourceDeltaEvent event) {
IContainer container = event.getResource().getParent();
IPath path = container.getFullPath();
// Pub folders do not nest, so don't create a folder if a parent folder already exists
if (getParentPubFolder(path) == null) {
DartSdk sdk = getSdk();
UriResolver pkgResolver = getPackageUriResolver(container, sdk, true);
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
pubFolders.put(path, new PubFolderImpl(
container,
null,
createContextId(container, sdk),
sdk,
pkgResolver));
} else {
pubFolders.put(path, new PubFolderImpl(
container,
createContext(container, sdk),
null,
sdk,
pkgResolver));
}
}
}
});
try {
processor.traverse(container);
} catch (CoreException e) {
DartCore.logError("Failed to build pub folder mapping", e);
}
// Remove nested pub folders
Iterator<Entry<IPath, PubFolder>> iter = pubFolders.entrySet().iterator();
while (iter.hasNext()) {
Entry<IPath, PubFolder> entry = iter.next();
PubFolder parent = getParentPubFolder(entry.getKey());
if (parent != null) {
parent.getContext().mergeContext(entry.getValue().getContext());
iter.remove();
}
}
}
/**
* Answer all the {@link AnalysisContext} for this project. Must synchronize against
* {@link #pubFolders} before calling this method and either call {@link #initialize()} or check
* that {@link #isInitialized()} returns {@code true}.
*
* @return all the analysis contexts in the project
*/
private AnalysisContext[] getAnalysisContexts() {
Set<AnalysisContext> contexts = new HashSet<AnalysisContext>();
for (PubFolder folder : getPubFolders()) {
contexts.add(folder.getContext());
}
contexts.add(getDefaultContext());
return contexts.toArray(new AnalysisContext[contexts.size()]);
}
/**
* Find the most appropriate package resolver for the given container, based on presence of
* pubspec, package roots etc.
*
* @return UriResolver used to resolve package: uris.
*/
private UriResolver getPackageUriResolver(IContainer container, DartSdk sdk, boolean hasPubspec) {
File[] packageRoots = factory.getPackageRoots(container);
if (packageRoots.length > 0) {
return new PackageUriResolver(packageRoots);
}
if (hasPubspec && !DartCoreDebug.NO_PUB_PACKAGES) {
IPath location = container.getLocation();
if (location != null) {
File[] packagesDirs = new File[] {new File(location.toFile(), PACKAGES_DIRECTORY_NAME)};
return new PackageUriResolver(packagesDirs);
}
}
if (sdk instanceof DirectoryBasedDartSdk) {
IPath location = container.getLocation();
if (location != null) {
return new InstrumentedExplicitPackageUriResolver(
(DirectoryBasedDartSdk) sdk,
location.toFile());
}
}
return null;
}
/**
* Find the {@link PubFolder} defined for an ancestor. Must synchronize against
* {@link #pubFolders} before calling this method and either call {@link #initialize()} or check
* that {@link #isInitialized()} returns {@code true}.
*
* @param path the resource's full path
* @return the containing pub folder or {@code null} if none
*/
private PubFolder getParentPubFolder(IPath path) {
return path.segmentCount() > 1 ? getPubFolder(path.removeLastSegments(1)) : null;
}
/**
* Answer the {@link PubFolder} for the specified resource path. Must synchronize against
* {@link #pubFolders} before calling this method and either call {@link #initialize()} or check
* that {@link #isInitialized()} returns {@code true}.
*
* @param path the resource full path (not {@code null})
* @return the folder or {@code null} if none is found
*/
private PubFolder getPubFolder(IPath path) {
if (pubFolders.size() == 0) {
return null;
}
for (int count = path.segmentCount() - 1; count >= 0; count--) {
PubFolder pubFolder = pubFolders.get(path.removeLastSegments(count));
if (pubFolder != null) {
return pubFolder;
}
}
return null;
}
/**
* Check if there is a project in the workspace for the given package name, if present return the
* project path.
*
* @param packageName, the package to look for
* @return the path to project or {@code null}
*/
private String getPubFolderPath(String packageName) {
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
PubFolder[] folders = DartCore.getProjectManager().getProject(project).getPubFolders();
for (PubFolder pubFolder : folders) {
try {
if (pubFolder.getPubspec().getName().equals(packageName)) {
return pubFolder.getResource().getFullPath().toString();
}
} catch (CoreException e) {
DartCore.logError(e);
} catch (IOException e) {
DartCore.logError(e);
}
}
}
return null;
}
/**
* Answer the Eclipse project's resource location or {@code null} if it could not be determined.
*/
private IPath getResourceLocation() {
if (projectResourceLocation == null) {
projectResourceLocation = projectResource.getLocation();
}
return projectResourceLocation;
}
/**
* Initialize the context for analyzing sources in the specified directory. Must synchronize
* against {@link #pubFolders} before calling this method and either call {@link #initialize()} or
* check that {@link #isInitialized()} returns {@code true}.
*
* @param context the context to be initialized (not {@code null})
* @param container the container with sources to be analyzed
* @param sdk the Dart SDK to use when initializing the context (not {@code null})
* @param hasPubspec {@code true} if the container has a pubspec file
* @return the context (not {@code null})
*/
private AnalysisContext initContext(AnalysisContext context, IContainer container, DartSdk sdk,
UriResolver pkgResolver) {
DartUriResolver dartResolver = new DartUriResolver(sdk);
FileUriResolver fileResolver = new FileUriResolver();
SourceFactory sourceFactory;
if (pkgResolver != null) {
sourceFactory = new SourceFactory(dartResolver, pkgResolver, fileResolver);
} else {
logNoLocation(container);
sourceFactory = new SourceFactory(dartResolver, fileResolver);
}
sourceFactory.setLocalSourcePredicate(new WorkspaceLocalSourcePredicate(container));
AnalysisOptionsImpl options = new AnalysisOptionsImpl();
options.setHint(DartCore.getPlugin().isHintsEnabled());
options.setDart2jsHint(DartCore.getPlugin().isHintsDart2JSEnabled());
options.setIncremental(DartCoreDebug.EXPERIMENTAL);
AnalysisEngine.getInstance().setEnableUnionTypes(DartCoreDebug.ENABLE_UNION_TYPES);
AnalysisEngine.getInstance().setStrictUnionTypes(DartCoreDebug.STRICT_UNION_TYPES);
context.setSourceFactory(sourceFactory);
context.setAnalysisOptions(options);
if (DartCoreDebug.NO_PUB_PACKAGES && pkgResolver instanceof ExplicitPackageUriResolver) {
//TODO (danrubel): Add package sources to context so that it is properly indexed
// ChangeSet changeSet = new ChangeSet();
// for (File dir : ((ExplicitPackageUriResolver) pkgResolver).getPackageDirectories()) {
// // traverse package directory tree
// changeSet.addedSource(new FileBasedSource(file));
// }
// context.applyChanges(changeSet);
}
return context;
}
/**
* Initialize {@link #pubFolders} and {@link #defaultContext} if not already been initialized.
* Must synchronize against {@link #pubFolders} before calling this method.
*/
private void initialize() {
if (isInitialized()) {
return;
}
boolean hasPubspec = projectResource.getFile(PUBSPEC_FILE_NAME).exists();
defaultPackageResolver = getPackageUriResolver(projectResource, getSdk(), hasPubspec);
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
// TODO(scheglov) restore or remove for the new API
defaultContextId = "not-a-real-context";
// String sdkPath = ((DirectoryBasedDartSdk) getSdk()).getDirectory().getAbsolutePath();
// // TODO(scheglov) Analysis Server: get packages map from Pub
// Map<String, String> packageMap;
// {
// packageMap = Maps.newHashMap();
// File projectDir = projectResource.getLocation().toFile();
// File packagesDir = new File(projectDir, "packages");
// File[] listFiles = packagesDir.listFiles();
// if (listFiles != null) {
// for (File packageFile : listFiles) {
// try {
// packageMap.put(packageFile.getName(), packageFile.getCanonicalPath());
// } catch (IOException e) {
// }
// }
// }
// }
// AnalysisServer analysisServer = DartCore.getAnalysisServer();
// defaultContextId = analysisServer.createContext(
// projectResource.getName(),
// sdkPath,
// packageMap);
// analysisServer.getFixableErrorCodes(defaultContextId, new FixableErrorCodesConsumer() {
// @Override
// public void computed(ErrorCode[] errorCodes) {
// ((AnalysisServerDataImpl) DartCore.getAnalysisServerData()).internalSetFixableErrorCodes(
// defaultContextId,
// errorCodes);
// }
// });
// analysisServer.subscribe(
// defaultContextId,
// ImmutableMap.of(NotificationKind.ERRORS, SourceSet.EXPLICITLY_ADDED));
// defaultResourceMap = new SimpleResourceMapImpl(
// projectResource,
// defaultContext,
// defaultContextId);
} else {
defaultContext = initContext(
factory.createContext(),
projectResource,
getSdk(),
defaultPackageResolver);
defaultResourceMap = new SimpleResourceMapImpl(
projectResource,
defaultContext,
defaultContextId);
}
createPubFolders(projectResource);
}
/**
* Answer true if {@link #defaultContext} and {@link #pubFolders} have been initialized. Must
* synchronize against {@link #pubFolders} before calling this method.
*
* @return {@code true} if initialized, else {@code false}
*/
private boolean isInitialized() {
return defaultContext != null || defaultContextId != null;
}
private void logNoLocation(IContainer container) {
DartCore.logInformation("No location for " + container);
}
}