| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library analyzer.src.context.source; |
| |
| import 'dart:collection'; |
| import 'dart:math' show min; |
| |
| import 'package:analyzer/exception/exception.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/file_system/physical_file_system.dart'; |
| import 'package:analyzer/source/package_map_resolver.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/sdk.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart' as utils; |
| import 'package:package_config/packages.dart'; |
| |
| /** |
| * Instances of the class `SourceFactory` resolve possibly relative URI's |
| * against an existing [Source]. |
| */ |
| class SourceFactoryImpl implements SourceFactory { |
| @override |
| AnalysisContext context; |
| |
| /** |
| * URI processor used to find mappings for `package:` URIs found in a |
| * `.packages` config file. |
| */ |
| final Packages _packages; |
| |
| /** |
| * Resource provider used in working with package maps. |
| */ |
| final ResourceProvider _resourceProvider; |
| |
| /** |
| * The resolvers used to resolve absolute URI's. |
| */ |
| final List<UriResolver> resolvers; |
| |
| /** |
| * The predicate to determine is [Source] is local. |
| */ |
| LocalSourcePredicate _localSourcePredicate = LocalSourcePredicate.NOT_SDK; |
| |
| /** |
| * Cache of mapping of absolute [Uri]s to [Source]s. |
| */ |
| final HashMap<Uri, Source> _absoluteUriToSourceCache = |
| new HashMap<Uri, Source>(); |
| |
| /** |
| * Initialize a newly created source factory with the given absolute URI |
| * [resolvers] and optional [_packages] resolution helper. |
| */ |
| SourceFactoryImpl(this.resolvers, |
| [this._packages, ResourceProvider resourceProvider]) |
| : _resourceProvider = |
| resourceProvider ?? PhysicalResourceProvider.INSTANCE; |
| |
| @override |
| DartSdk get dartSdk { |
| List<UriResolver> resolvers = this.resolvers; |
| int length = resolvers.length; |
| for (int i = 0; i < length; i++) { |
| UriResolver resolver = resolvers[i]; |
| if (resolver is DartUriResolver) { |
| DartUriResolver dartUriResolver = resolver; |
| return dartUriResolver.dartSdk; |
| } |
| } |
| return null; |
| } |
| |
| @override |
| void set localSourcePredicate(LocalSourcePredicate localSourcePredicate) { |
| this._localSourcePredicate = localSourcePredicate; |
| } |
| |
| @override |
| Map<String, List<Folder>> get packageMap { |
| // Start by looking in .packages. |
| if (_packages != null) { |
| Map<String, List<Folder>> packageMap = <String, List<Folder>>{}; |
| _packages.asMap().forEach((String name, Uri uri) { |
| if (uri.scheme == 'file' || uri.scheme == '' /* unspecified */) { |
| packageMap[name] = <Folder>[ |
| _resourceProvider.getFolder(uri.toFilePath()) |
| ]; |
| } |
| }); |
| return packageMap; |
| } |
| |
| // Default to the PackageMapUriResolver. |
| PackageMapUriResolver resolver = resolvers |
| .firstWhere((r) => r is PackageMapUriResolver, orElse: () => null); |
| return resolver?.packageMap; |
| } |
| |
| @override |
| SourceFactory clone() { |
| SourceFactory factory = |
| new SourceFactory(resolvers, _packages, _resourceProvider); |
| factory.localSourcePredicate = _localSourcePredicate; |
| return factory; |
| } |
| |
| @override |
| Source forUri(String absoluteUri) { |
| try { |
| Uri uri = Uri.parse(absoluteUri); |
| if (uri.isAbsolute) { |
| return _internalResolveUri(null, uri); |
| } |
| } catch (exception, stackTrace) { |
| AnalysisEngine.instance.logger.logError( |
| "Could not resolve URI: $absoluteUri", |
| new CaughtException(exception, stackTrace)); |
| } |
| return null; |
| } |
| |
| @override |
| Source forUri2(Uri absoluteUri) { |
| if (absoluteUri.isAbsolute) { |
| try { |
| return _internalResolveUri(null, absoluteUri); |
| } on AnalysisException catch (exception, stackTrace) { |
| AnalysisEngine.instance.logger.logError( |
| "Could not resolve URI: $absoluteUri", |
| new CaughtException(exception, stackTrace)); |
| } |
| } |
| return null; |
| } |
| |
| @override |
| Source fromEncoding(String encoding) { |
| Source source = forUri(encoding); |
| if (source == null) { |
| throw new ArgumentError("Invalid source encoding: '$encoding'"); |
| } |
| return source; |
| } |
| |
| @override |
| bool isLocalSource(Source source) => _localSourcePredicate.isLocal(source); |
| |
| @override |
| Source resolveUri(Source containingSource, String containedUri) { |
| if (containedUri == null || containedUri.isEmpty) { |
| return null; |
| } |
| try { |
| // Force the creation of an escaped URI to deal with spaces, etc. |
| return _internalResolveUri(containingSource, Uri.parse(containedUri)); |
| } on FormatException { |
| return null; |
| } catch (exception, stackTrace) { |
| String containingFullName = |
| containingSource != null ? containingSource.fullName : '<null>'; |
| AnalysisEngine.instance.logger.logInformation( |
| "Could not resolve URI ($containedUri) " |
| "relative to source ($containingFullName)", |
| new CaughtException(exception, stackTrace)); |
| return null; |
| } |
| } |
| |
| @override |
| Uri restoreUri(Source source) { |
| // First see if a resolver can restore the URI. |
| for (UriResolver resolver in resolvers) { |
| Uri uri = resolver.restoreAbsolute(source); |
| if (uri != null) { |
| // Now see if there's a package mapping. |
| Uri packageMappedUri = _getPackageMapping(uri); |
| if (packageMappedUri != null) { |
| return packageMappedUri; |
| } |
| // Fall back to the resolver's computed URI. |
| return uri; |
| } |
| } |
| |
| return null; |
| } |
| |
| Uri _getPackageMapping(Uri sourceUri) { |
| if (_packages == null) { |
| return null; |
| } |
| if (sourceUri.scheme != 'file') { |
| //TODO(pquitslund): verify this works for non-file URIs. |
| return null; |
| } |
| |
| Uri packageUri; |
| _packages.asMap().forEach((String name, Uri uri) { |
| if (packageUri == null) { |
| if (utils.startsWith(sourceUri, uri)) { |
| String relativePath = sourceUri.path |
| .substring(min(uri.path.length, sourceUri.path.length)); |
| packageUri = Uri.parse('package:$name/$relativePath'); |
| } |
| } |
| }); |
| return packageUri; |
| } |
| |
| /** |
| * Return a source object representing the URI that results from resolving |
| * the given (possibly relative) contained URI against the URI associated |
| * with an existing source object, or `null` if the URI could not be resolved. |
| * |
| * @param containingSource the source containing the given URI |
| * @param containedUri the (possibly relative) URI to be resolved against the |
| * containing source |
| * @return the source representing the contained URI |
| * @throws AnalysisException if either the contained URI is invalid or if it |
| * cannot be resolved against the source object's URI |
| */ |
| Source _internalResolveUri(Source containingSource, Uri containedUri) { |
| if (!containedUri.isAbsolute) { |
| if (containingSource == null) { |
| throw new AnalysisException( |
| "Cannot resolve a relative URI without a containing source: " |
| "$containedUri"); |
| } |
| containedUri = |
| utils.resolveRelativeUri(containingSource.uri, containedUri); |
| } |
| |
| Uri actualUri = containedUri; |
| |
| // Check .packages and update target and actual URIs as appropriate. |
| if (_packages != null && containedUri.scheme == 'package') { |
| Uri packageUri = null; |
| try { |
| packageUri = |
| _packages.resolve(containedUri, notFound: (Uri packageUri) => null); |
| } on ArgumentError { |
| // Fall through to try resolvers. |
| } |
| |
| if (packageUri != null) { |
| // Ensure scheme is set. |
| if (packageUri.scheme == '') { |
| packageUri = packageUri.replace(scheme: 'file'); |
| } |
| containedUri = packageUri; |
| } |
| } |
| |
| return _absoluteUriToSourceCache.putIfAbsent(actualUri, () { |
| for (UriResolver resolver in resolvers) { |
| Source result = resolver.resolveAbsolute(containedUri, actualUri); |
| if (result != null) { |
| return result; |
| } |
| } |
| return null; |
| }); |
| } |
| } |