Chromium Code Reviews| Index: lib/src/resource.dart |
| diff --git a/lib/src/resource.dart b/lib/src/resource.dart |
| index 2fef5f6325910a8cb9e4a06c720cbcabe5bc9000..6075b41a2cfe695245fb4d6b4875f92d3af12283 100644 |
| --- a/lib/src/resource.dart |
| +++ b/lib/src/resource.dart |
| @@ -1,218 +1,115 @@ |
| -// 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 resoure.resource; |
| - |
| -import "dart:async" show Future, Stream; |
| -import "dart:convert" show Encoding; |
| -import "dart:isolate" show Isolate; |
| -import "package_resolver.dart"; |
| -import "io.dart" as io; // Loading strategy. TODO: Be configuration dependent. |
| - |
| -/// A strategy for resolving package URIs |
| -abstract class PackageResolver { |
| - /// Cache of the current resolver, accessible directly after it has first |
| - /// been found asynchronously. |
| - static PackageResolver _current; |
| - |
| - /// The package resolution strategy used by the current isolate. |
| - static final Future<PackageResolver> current = _findIsolateResolution(); |
| - |
| - PackageResolver(); |
| - |
| - /// Creates a resolver using a map from package name to package location. |
| - factory PackageResolver.fromMap(Map<String, Uri> packages) = MapResolver; |
| - |
| - /// Creates a resolver using a package root. |
| - factory PackageResolver.fromRoot(Uri packageRoot) = RootResolver; |
| - |
| - /// Resolves a package URI to its location. |
| - /// |
| - /// If [uri] does not have `package` as scheme, it is returned again. |
| - /// Otherwise the package name is looked up, and if found, a location |
| - /// for the package file is returned. |
| - Future<Uri> resolve(Uri uri); |
| - |
| - /// Returns a [Resource] for the [uri] as resolved by this resolver. |
| - Resource resource(Uri uri) { |
| - return new _UriResource(this, uri); |
| - } |
| - |
| - /// Finds the way the current isolate resolves package URIs. |
| - /// |
| - /// Is only called once, and when it has been called, the [_current] |
| - /// resolver is initialized, so [UriResource] will be initialized |
| - /// with the resolver directly. |
| - static Future<PackageResolver> _findIsolateResolution() async { |
| - var pair = await Future.wait([Isolate.packageRoot, Isolate.packageMap]); |
| - Uri root = pair[0]; |
| - if (root != null) { |
| - _current = new RootResolver(root); |
| - } else { |
| - Map<String, Uri> map = pair[1]; |
| - _current = new MapResolver(map); |
| - } |
| - return _current; |
| - } |
| -} |
| - |
| -/// A resource that can be read into the program. |
| -/// |
| -/// A resource is data that can be located using a URI and read into |
| -/// the program at runtime. |
| -/// The URI may use the `package` scheme to read resources provided |
| -/// along with package sources. |
| -abstract class Resource { |
| - /// Creates a resource object with the given [uri] as location. |
| - /// |
| - /// The `uri` is a string containing a valid URI. |
| - /// If the string is not a valid URI, using any of the functions on |
| - /// the resource object will fail. |
| - /// |
| - /// The URI may be relative, in which case it will be resolved |
| - /// against [Uri.base] before being used. |
| - /// |
| - /// The URI may use the `package` scheme, which is always supported. |
| - /// Other schemes may also be supported where possible. |
| - const factory Resource(String uri) = _StringResource; |
| - |
| - /// Creates a resource object with the given [uri] as location. |
| - /// |
| - /// The URI may be relative, in which case it will be resolved |
| - /// against [Uri.base] before being used. |
| - /// |
| - /// The URI may use the `package` scheme, which is always supported. |
| - /// Other schemes may also be supported where possible. |
| - factory Resource.forUri(Uri uri) => |
| - new _UriResource(PackageResolver._current, uri); |
| - |
| - /// The location `uri` of this resource. |
| - /// |
| - /// This is a [Uri] of the `uri` parameter given to the constructor. |
| - /// If the parameter was not a valid URI, reading `uri` may fail. |
| - Uri get uri; |
| - |
| - Stream<List<int>> openRead(); |
| - |
| - Future<List<int>> readAsBytes(); |
| - |
| - /// Read the resource content as a string. |
| - /// |
| - /// The content is decoded into a string using an [Encoding]. |
| - /// If no other encoding is provided, it defaults to UTF-8. |
| - Future<String> readAsString({Encoding encoding}); |
| -} |
| - |
| -class _StringResource implements Resource { |
| - final String _uri; |
| - |
| - const _StringResource(String uri) : _uri = uri; |
| - |
| - Uri get uri => Uri.parse(_uri); |
| - |
| - Stream<List<int>> openRead() { |
| - return new _UriResource(PackageResolver._current, uri).openRead(); |
| - } |
| - Future<List<int>> readAsBytes() { |
| - return new _UriResource(PackageResolver._current, uri).readAsBytes(); |
| - } |
| - Future<String> readAsString({Encoding encoding}) { |
| - return new _UriResource(PackageResolver._current, uri) |
| - .readAsString(encoding: encoding); |
| - } |
| -} |
| - |
| -class _UriResource implements Resource { |
| - /// The strategy for resolving package: URIs. |
| - /// |
| - /// May be null intially. If so, the [PackageResolver.current] resolver is |
| - /// used (and cached for later use). |
| - PackageResolver _resolver; |
| - |
| - /// The URI of the resource. |
| - final Uri uri; |
| - |
| - _UriResource(this.resolver, Uri uri); |
| - |
| - Stream<List<int>> openRead() async* { |
| - Uri uri = await _resolve(this.uri); |
| - return io.readAsStream(uri); |
| - } |
| - |
| - Future<List<int>> readAsBytes() async { |
| - Uri uri = await _resolve(this.uri); |
| - return io.readAsBytes(uri); |
| - } |
| - |
| - Future<String> readAsString({Encoding encoding}) async { |
| - Uri uri = await _resolve(this.uri); |
| - return io.readAsString(uri, encoding); |
| - } |
| - |
| - static void _checkPackageUri(Uri uri) { |
| - if (uri.scheme != "package") { |
| - throw new ArgumentError.value(uri, "Not a package: URI"); |
| - } |
| - if (uri.hasAuthority) { |
| - throw new ArgumentError.value(uri, |
| - "A package: URI must not have an authority part"); |
| - } |
| - if (uri.path.isEmpty || uri.path.startsWith('/')) { |
| - throw new ArgumentError.value(uri, |
| - "A package: URI must have the form " |
| - "'package:packageName/packagePath'"); |
| - } |
| - if (uri.hasQuery) { |
| - throw new ArgumentError.value(uri, |
| - "A package: URI must not have a query part"); |
| - } |
| - if (uri.hasFragment) { |
| - throw new ArgumentError.value(uri, |
| - "A package: URI must not have a fragment part"); |
| - } |
| - } |
| - |
| - Future<Uri> _resolve(Uri uri) async { |
| - if (uri.scheme != "package") { |
| - return Uri.base.resolveUri(uri); |
| - } |
| - _checkPackageUri(uri); |
| - _resolver ??= await PackageResolver._current; |
| - return _resolver.resolve(uri); |
| - } |
| -} |
| - |
| -/// A [PackageResolver] based on a packags map. |
| -class MapResolver extends PackageResolver { |
| - Map<String, Uri> _mapping; |
| - |
| - MapResolver(this._mapping); |
| - |
| - Uri resolve(Uri uri) { |
| - if (uri.scheme != "package") return uri; |
| - var path = uri.path; |
| - int slashIndex = path.indexOf('/'); |
| - if (slashIndex <= 0) { |
| - throw new ArgumentError.value(uri, "Invalid package URI"); |
| - } |
| - int packageName = path.substring(0, slashIndex); |
| - var base = _mapping[packageName]; |
| - if (base != null) { |
| - int packagePath = path.substring(slashIndex + 1); |
| - return base.resolveUri(new Uri(path: packagePath)); |
| - } |
| - throw new UnsupportedError("No package named '$packageName' found"); |
| - } |
| -} |
| - |
| -/// A [PackageResolver] based on a package root. |
| -class RootResolver extends PackageResolver { |
| - Uri _root; |
| - RootResolver(this._root); |
| - |
| - Uri resolve(Uri uri) { |
| - if (uri.scheme != "package") return uri; |
| - return _root.resolve(uri.path); |
| - } |
| -} |
| +// 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. |
| + |
| +import "dart:async" show Future, Stream; |
| +import "dart:convert" show Encoding; |
| +import "dart:isolate" show Isolate; |
| +import "loader.dart"; |
| + |
| +/// A resource that can be read into the program. |
| +/// |
| +/// A resource is data that can be located using a URI and read into |
| +/// the program at runtime. |
| +/// The URI may use the `package` scheme to read resources provided |
| +/// along with package sources. |
| +abstract class Resource { |
| + /// Creates a resource object with the given [uri] as location. |
| + /// |
| + /// The [uri] must be either a [Uri] or a string containing a valid URI. |
| + /// If the string is not a valid URI, using any of the functions on |
| + /// the resource object will fail. |
| + /// |
| + /// The URI may be relative, in which case it will be resolved |
| + /// against [Uri.base] before being used. |
| + /// |
| + /// The URI may use the `package` scheme, which is always supported. |
| + /// Other schemes may also be supported where possible. |
| + /// |
| + /// If [loader] is provided, it is used to load absolute non-package URIs. |
| + /// Package: URIs are resolved to a non-package URI before being loaded, so |
| + /// the loader doesn't have to support package: URIs, nor does it need to |
| + /// support relative URI references. |
| + /// If [loader] is omitted, a default implementation is used which supports |
| + /// as many of `http`, `https`, `file` and `data` as are available on the |
| + /// current platform. |
| + const factory Resource(uri, {ResourceLoader loader}) = _Resource; |
| + |
| + /// The location `uri` of this resource. |
| + /// |
| + /// This is a [Uri] of the `uri` parameter given to the constructor. |
| + /// If the parameter was not a valid URI, reading `uri` may fail. |
| + Uri get uri; |
| + |
| + /// Read the resource content as a stream of bytes. |
|
floitsch
2016/01/14 15:11:04
Reads
Lasse Reichstein Nielsen
2016/01/15 09:40:02
Done.
|
| + Stream<List<int>> openRead(); |
| + |
| + /// Read the resource content as a single list of bytes. |
|
floitsch
2016/01/14 15:11:04
ditto.
Lasse Reichstein Nielsen
2016/01/15 09:40:02
Done.
|
| + Future<List<int>> readAsBytes(); |
| + |
| + /// Read the resource content as a string. |
| + /// |
| + /// The content is decoded into a string using an [Encoding]. |
| + /// If no other encoding is provided, it defaults to UTF-8. |
| + Future<String> readAsString({Encoding encoding}); |
| +} |
| + |
| +class _Resource implements Resource { |
| + /// Loading strategy for the resource. |
| + final ResourceLoader _loader; |
| + |
| + /// The URI of the resource. |
| + final _uri; |
| + |
| + const _Resource(uri, {ResourceLoader loader}) |
| + : _uri = uri, _loader = (loader != null) ? loader : const DefaultLoader(); |
| + // TODO: Make this `loader ?? const DefaultLoader()` when ?? is const. |
| + |
| + Uri get uri => (_uri is String) ? Uri.parse(_uri) : (_uri as Uri); |
| + |
| + Stream<List<int>> openRead() async* { |
| + Uri uri = await _resolvedUri; |
| + yield* _loader.openRead(uri); |
| + } |
| + |
| + Future<List<int>> readAsBytes() async { |
| + Uri uri = await _resolvedUri; |
| + return _loader.readAsBytes(uri); |
| + } |
| + |
| + Future<String> readAsString({Encoding encoding}) async { |
| + Uri uri = await _resolvedUri; |
| + return _loader.readAsString(uri, encoding: encoding); |
| + } |
| + |
| + Future<Uri> get _resolvedUri => resolveUri(uri); |
| +} |
| + |
| +/// Validates that a package URI has a valid format. |
| +/// |
| +/// Returns the length of the packageName. |
| +/// This is the index of the first `/` in `uri.path`. |
| +int validatePackageUri(Uri uri) { |
| + if (uri.scheme != "package") { |
| + throw new ArgumentError.value(uri, "Not a package: URI"); |
| + } |
| + if (uri.hasAuthority) { |
| + throw new ArgumentError.value(uri, |
| + "A package: URI must not have an authority part"); |
| + } |
| + int index = uri.path.indexOf('/'); |
| + if (index < 1) { |
| + throw new ArgumentError.value(uri, |
| + "A package: URI must have the form " |
| + "'package:packageName/packagePath'"); |
| + } |
| + if (uri.hasQuery) { |
| + throw new ArgumentError.value(uri, |
| + "A package: URI must not have a query part"); |
| + } |
| + if (uri.hasFragment) { |
| + throw new ArgumentError.value(uri, |
| + "A package: URI must not have a fragment part"); |
| + } |
| + return index; |
| +} |