Index: lib/src/resource.dart |
diff --git a/lib/src/resource.dart b/lib/src/resource.dart |
index 2fef5f6325910a8cb9e4a06c720cbcabe5bc9000..f1a20f55c8c34cce2fc4afdabdc616fac1640f7f 100644 |
--- a/lib/src/resource.dart |
+++ b/lib/src/resource.dart |
@@ -1,218 +1,253 @@ |
-// 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. |
+ |
+library resoure.resource; |
+ |
+import "dart:async" show Future, Stream; |
+import "dart:convert" show Encoding; |
+import "dart:isolate" show Isolate; |
+import "loader.dart"; |
+ |
+/// A strategy for resolving package URIs |
+abstract class PackageResolver { |
+ /// Cache of the current isolate resolver, accessible directly after it has |
+ /// first been found asynchronously by reading [current]. |
+ 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 as-is. |
+ /// Otherwise the package name of the package URI is looked up, |
+ /// and if found, a location for the package file is returned. |
+ /// |
+ /// Returns `null` if the package name is not recognized. |
+ /// |
+ /// Fails if the [uri] is a `package:` URI but has an invalid format. |
+ /// Package URIs must have the format: |
+ /// |
+ /// package:packageName/someValidPath |
+ /// |
+ /// with no authority, no query, no fragment, and a non-empty package name. |
+ Uri resolve(Uri uri); |
+ |
+ /// Returns a [Resource] for the [uri] as resolved by this resolver. |
+ /// |
+ /// If [loader] is provided, it will be used to load the resource. |
+ /// If omitted, it defaults to a default system loader. |
+ Resource resource(Uri uri, { ResourceLoader loader }) { |
Bob Nystrom
2015/10/08 16:48:33
Can this have a verb phrase name? Maybe "loadResou
Lasse Reichstein Nielsen
2015/10/08 17:20:54
createResource?
Bob Nystrom
2015/10/08 17:32:20
"create" feels like the wrong word to me because t
floitsch
2015/10/13 12:47:50
I would go with "get".
|
+ return new UriResource(this, loader ?? const DefaultLoader(), 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, {ResourceLoader loader}) => |
floitsch
2015/10/13 12:47:50
I think we often used "fromX" for this use-case.
"
|
+ new UriResource(PackageResolver._current, loader, 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; |
+ |
+ /// Read the resource content as a stream of bytes. |
+ Stream<List<int>> openRead(); |
+ |
+ /// Read the resource content as a single list of bytes. |
+ 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}); |
+} |
+ |
+/// Implementation of [new Resoure] constructor. |
Bob Nystrom
2015/10/08 16:48:33
"Resource".
Lasse Reichstein Nielsen
2015/10/08 17:20:54
Acknowledged.
|
+/// |
+/// Stores only a plain string, and |
Bob Nystrom
2015/10/08 16:48:33
...?
Lasse Reichstein Nielsen
2015/10/08 17:20:54
Maybe it's all it does? :)
Will fix.
|
+class StringResource implements Resource { |
+ final String _uri; |
+ |
+ const StringResource(String uri) : _uri = uri; |
+ |
+ Uri get uri => Uri.parse(_uri); |
+ |
+ UriResource get _asUriResource => |
+ new UriResource(PackageResolver._current, const DefaultLoader(), uri); |
+ |
+ Stream<List<int>> openRead() { |
+ return _asUriResource.openRead(); |
+ } |
+ Future<List<int>> readAsBytes() { |
+ return _asUriResource.readAsBytes(); |
+ } |
+ Future<String> readAsString({Encoding encoding}) { |
+ return _asUriResource.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; |
+ |
+ /// Loading strategy for the resource. |
+ ResourceLoader _loader; |
+ |
+ /// The URI of the resource. |
+ final Uri uri; |
+ |
+ UriResource(this._resolver, this._loader, this.uri); |
+ |
+ Stream<List<int>> openRead() async* { |
+ Uri uri = await _resolve(this.uri); |
+ yield* _loader.openRead(uri); |
+ } |
+ |
+ Future<List<int>> readAsBytes() async { |
+ Uri uri = await _resolve(this.uri); |
+ return _loader.readAsBytes(uri); |
+ } |
+ |
+ Future<String> readAsString({Encoding encoding}) async { |
+ Uri uri = await _resolve(this.uri); |
+ return _loader.readAsString(uri, encoding: encoding); |
+ } |
+ |
+ Future<Uri> _resolve(Uri uri) async { |
+ if (uri.scheme != "package") { |
+ return Uri.base.resolveUri(uri); |
+ } |
+ if (_resolver == null) { |
+ _resolver = await PackageResolver.current; |
+ } |
+ var result = _resolver.resolve(uri); |
+ return result; |
+ } |
+} |
+ |
+/// 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; |
+ int slashIndex = validatePackageUri(uri); |
+ String path = uri.path; |
+ String packageName = path.substring(0, slashIndex); |
+ Uri base = _mapping[packageName]; |
+ if (base != null) { |
+ String packagePath = path.substring(slashIndex + 1); |
+ return base.resolveUri(new Uri(path: packagePath)); |
+ } |
+ return null; |
+ } |
+} |
+ |
+/// A [PackageResolver] based on a package root. |
+class RootResolver extends PackageResolver { |
+ Uri _root; |
+ |
+ RootResolver(this._root) { |
+ if (_root.path.contains("local")) { |
+ try { throw 0; } catch (e, s) { print(s); } |
+ throw "WHAT?"; |
Bob Nystrom
2015/10/08 16:48:33
?
Lasse Reichstein Nielsen
2015/10/08 17:20:54
Whoops, debug code.
It did find the bug!
|
+ } |
+ } |
+ |
+ Uri resolve(Uri uri) { |
+ if (uri.scheme != "package") return uri; |
+ validatePackageUri(uri); |
+ return _root.resolve(uri.path); |
+ } |
+} |
+ |
+/// 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; |
+} |
+ |