Proposed replacement for SourceFactory

Change-Id: Iadcb71a4f9682d4cb9636aea6bb0b91f3bf040f9
Reviewed-on: https://dart-review.googlesource.com/58160
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
index c1be612..b2997da 100644
--- a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
+++ b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
@@ -6,6 +6,7 @@
 
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/uri_converter.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/ast_factory.dart';
 import 'package:analyzer/dart/ast/token.dart';
@@ -53,7 +54,7 @@
     List<ImportedElements> filteredImportedElements =
         _filterImportedElements(importedElementsList);
     LibraryElement libraryElement = libraryResult.libraryElement;
-    SourceFactory sourceFactory = libraryResult.session.sourceFactory;
+    UriConverter uriConverter = libraryResult.session.uriConverter;
     List<ImportDirective> existingImports = <ImportDirective>[];
     for (var directive in libraryResult.unit.directives) {
       if (directive is ImportDirective) {
@@ -73,7 +74,7 @@
           // so we need to add an import.
           //
           File importedFile = resourceProvider.getFile(importedElements.path);
-          Uri uri = sourceFactory.restoreUri(importedFile.createSource());
+          Uri uri = uriConverter.pathToUri(importedFile.path);
           Source importedSource = importedFile.createSource(uri);
           String importUri =
               _getLibrarySourceUri(libraryElement, importedSource);
diff --git a/pkg/analyzer/lib/dart/analysis/session.dart b/pkg/analyzer/lib/dart/analysis/session.dart
index dd8c595..3c4d9cf 100644
--- a/pkg/analyzer/lib/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/dart/analysis/session.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/analysis/uri_converter.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/exception/exception.dart';
 import 'package:analyzer/file_system/file_system.dart';
@@ -29,7 +30,10 @@
 
   /**
    * Return the source factory used to resolve URIs.
+   *
+   * Deprecated: Use the methods on [uriConverter] instead.
    */
+  @deprecated
   SourceFactory get sourceFactory;
 
   /**
@@ -44,6 +48,11 @@
   Future<TypeSystem> get typeSystem;
 
   /**
+   * Return the URI converter used to convert between URI's and file paths.
+   */
+  UriConverter get uriConverter;
+
+  /**
    * Return a future that will complete with information about the errors
    * contained in the file with the given absolute, normalized [path].
    *
diff --git a/pkg/analyzer/lib/dart/analysis/uri_converter.dart b/pkg/analyzer/lib/dart/analysis/uri_converter.dart
new file mode 100644
index 0000000..ec1ed8d
--- /dev/null
+++ b/pkg/analyzer/lib/dart/analysis/uri_converter.dart
@@ -0,0 +1,30 @@
+// Copyright (c) 2018, 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.
+
+/**
+ * A utility class used to convert between URIs and absolute file paths.
+ */
+abstract class UriConverter {
+  /**
+   * Return the URI that should be used to reference the file at the absolute
+   * [path], or `null` if there is no valid way to reference the file in this
+   * converter’s context. The file at that path is not required to exist.
+   *
+   * If a [containingPath] is provided and both the [path] and [containingPath]
+   * are within the root of this converter’s context, then the returned URI will
+   * be a relative path. Otherwise, the returned URI will be an absolute URI.
+   *
+   * Throws an `ArgumentError` if the [path] is `null` or is not a valid
+   * absolute file path.
+   */
+  Uri pathToUri(String path, {String containingPath});
+
+  /**
+   * Return the absolute path of the file to which the absolute [uri] resolves,
+   * or `null` if the [uri] cannot be resolved in this converter’s context.
+   *
+   * Throws an `ArgumentError` if the [uri] is `null` or is not an absolute URI.
+   */
+  String uriToPath(Uri uri);
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/session.dart b/pkg/analyzer/lib/src/dart/analysis/session.dart
index f627fa8..1a420e1 100644
--- a/pkg/analyzer/lib/src/dart/analysis/session.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/session.dart
@@ -6,10 +6,12 @@
 
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
+import 'package:analyzer/dart/analysis/uri_converter.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as driver;
 import 'package:analyzer/src/dart/analysis/top_level_declaration.dart';
+import 'package:analyzer/src/dart/analysis/uri_converter.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
 
@@ -33,6 +35,11 @@
   TypeSystem _typeSystem;
 
   /**
+   * The URI converter used to convert between URI's and file paths.
+   */
+  UriConverter _uriConverter;
+
+  /**
    * The cache of libraries for URIs.
    */
   final Map<String, LibraryElement> _uriToLibraryCache = {};
@@ -73,6 +80,11 @@
   }
 
   @override
+  UriConverter get uriConverter {
+    return _uriConverter ??= new DriverBasedUriConverter(_driver);
+  }
+
+  @override
   Future<ErrorsResult> getErrors(String path) {
     _checkConsistency();
     return _driver.getErrors(path);
diff --git a/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart b/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart
new file mode 100644
index 0000000..df11c02
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/analysis/uri_converter.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2018, 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.
+
+import 'package:analyzer/dart/analysis/uri_converter.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:path/src/context.dart';
+
+/**
+ * An implementation of a URI converter based on an analysis driver.
+ */
+class DriverBasedUriConverter implements UriConverter {
+  /**
+   * The driver associated with the context in which the conversion will occur.
+   */
+  final AnalysisDriver driver;
+
+  /**
+   * Initialize a newly created URI converter to use the given [context] and
+   * [driver] to perform the conversions.
+   */
+  DriverBasedUriConverter(this.driver);
+
+  @override
+  Uri pathToUri(String path, {String containingPath}) {
+    ResourceProvider provider = driver.resourceProvider;
+    if (containingPath != null) {
+      Context context = provider.pathContext;
+      String root = driver.contextRoot.root;
+      if (context.isWithin(root, path) &&
+          context.isWithin(root, containingPath)) {
+        String relativePath =
+            context.relative(path, from: context.dirname(containingPath));
+        if (context.isRelative(relativePath)) {
+          return new Uri.file(relativePath);
+        }
+      }
+    }
+    Source source = provider.getFile(path).createSource();
+    return driver.sourceFactory.restoreUri(source);
+  }
+
+  @override
+  String uriToPath(Uri uri) => driver.sourceFactory.forUri2(uri).fullName;
+}
diff --git a/pkg/analyzer/test/src/dart/analysis/test_all.dart b/pkg/analyzer/test/src/dart/analysis/test_all.dart
index f452ede..cd75df1 100644
--- a/pkg/analyzer/test/src/dart/analysis/test_all.dart
+++ b/pkg/analyzer/test/src/dart/analysis/test_all.dart
@@ -4,10 +4,10 @@
 
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
-import 'context_builder_test.dart' as context_builder_test;
-import 'context_locator_test.dart' as context_locator_test;
-import 'context_root_test.dart' as context_root_test;
-import 'defined_names_test.dart' as defined_names_test;
+import 'context_builder_test.dart' as context_builder;
+import 'context_locator_test.dart' as context_locator;
+import 'context_root_test.dart' as context_root;
+import 'defined_names_test.dart' as defined_names;
 import 'driver_kernel_test.dart' as driver_kernel;
 import 'driver_resolution_kernel_test.dart' as driver_resolution_kernel;
 import 'driver_resolution_test.dart' as driver_resolution;
@@ -16,16 +16,17 @@
 import 'index_test.dart' as index;
 import 'mutex_test.dart' as mutex;
 import 'referenced_names_test.dart' as referenced_names;
-import 'search_test.dart' as search_test;
-import 'session_helper_test.dart' as session_helper_test;
-import 'session_test.dart' as session_test;
+import 'search_test.dart' as search;
+import 'session_helper_test.dart' as session_helper;
+import 'session_test.dart' as session;
+import 'uri_converter_test.dart' as uri_converter;
 
 main() {
   defineReflectiveSuite(() {
-    context_builder_test.main();
-    context_locator_test.main();
-    context_root_test.main();
-    defined_names_test.main();
+    context_builder.main();
+    context_locator.main();
+    context_root.main();
+    defined_names.main();
     driver.main();
     driver_kernel.main();
     driver_resolution.main();
@@ -34,8 +35,9 @@
     index.main();
     mutex.main();
     referenced_names.main();
-    search_test.main();
-    session_helper_test.main();
-    session_test.main();
+    search.main();
+    session_helper.main();
+    session.main();
+    uri_converter.main();
   }, name: 'analysis');
 }
diff --git a/pkg/analyzer/test/src/dart/analysis/uri_converter_test.dart b/pkg/analyzer/test/src/dart/analysis/uri_converter_test.dart
new file mode 100644
index 0000000..45fad90
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/analysis/uri_converter_test.dart
@@ -0,0 +1,104 @@
+// Copyright (c) 2018, 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.
+
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/memory_file_system.dart';
+import 'package:analyzer/src/context/context_root.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/dart/analysis/uri_converter.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/source/package_map_resolver.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../context/mock_sdk.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(DriverBasedUriConverterTest);
+  });
+}
+
+@reflectiveTest
+class DriverBasedUriConverterTest {
+  MemoryResourceProvider resourceProvider;
+  DriverBasedUriConverter uriConverter;
+
+  void setUp() {
+    resourceProvider = new MemoryResourceProvider();
+    Folder packageFolder = resourceProvider.newFolder('/packages/bar/lib');
+
+    SourceFactory sourceFactory = new SourceFactory([
+      new DartUriResolver(new MockSdk(resourceProvider: resourceProvider)),
+      new PackageMapUriResolver(resourceProvider, {
+        'foo': [resourceProvider.newFolder('/packages/foo/lib')],
+        'bar': [packageFolder],
+      }),
+      new ResourceUriResolver(resourceProvider),
+    ], null, resourceProvider);
+
+    ContextRoot contextRoot = new ContextRoot(packageFolder.path, [],
+        pathContext: resourceProvider.pathContext);
+
+    MockAnalysisDriver driver = new MockAnalysisDriver();
+    driver.resourceProvider = resourceProvider;
+    driver.sourceFactory = sourceFactory;
+    driver.contextRoot = contextRoot;
+
+    uriConverter = new DriverBasedUriConverter(driver);
+  }
+
+  test_pathToUri_dart() {
+    expect(
+        uriConverter
+            .pathToUri(resourceProvider.convertPath('/sdk/lib/core/core.dart')),
+        Uri.parse('dart:core'));
+  }
+
+  test_pathToUri_notRelative() {
+    expect(
+        uriConverter.pathToUri(
+            resourceProvider.convertPath('/packages/foo/lib/foo.dart'),
+            containingPath:
+                resourceProvider.convertPath('/packages/bar/lib/bar.dart')),
+        Uri.parse('package:foo/foo.dart'));
+  }
+
+  test_pathToUri_package() {
+    expect(
+        uriConverter.pathToUri(
+            resourceProvider.convertPath('/packages/foo/lib/foo.dart')),
+        Uri.parse('package:foo/foo.dart'));
+  }
+
+  test_pathToUri_relative() {
+    expect(
+        uriConverter.pathToUri(
+            resourceProvider.convertPath('/packages/bar/lib/src/baz.dart'),
+            containingPath:
+                resourceProvider.convertPath('/packages/bar/lib/bar.dart')),
+        Uri.parse('src/baz.dart'));
+  }
+
+  test_uriToPath_dart() {
+    expect(uriConverter.uriToPath(Uri.parse('dart:core')),
+        resourceProvider.convertPath('/sdk/lib/core/core.dart'));
+  }
+
+  test_uriToPath_package() {
+    expect(uriConverter.uriToPath(Uri.parse('package:foo/foo.dart')),
+        resourceProvider.convertPath('/packages/foo/lib/foo.dart'));
+  }
+}
+
+class MockAnalysisDriver implements AnalysisDriver {
+  ResourceProvider resourceProvider;
+  SourceFactory sourceFactory;
+  ContextRoot contextRoot;
+
+  @override
+  dynamic noSuchMethod(Invocation invocation) {
+    fail('Unexpected invocation of ${invocation.memberName}');
+  }
+}