Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library resoure.resource; | 5 library resoure.resource; |
| 6 | 6 |
| 7 import "dart:async" show Future, Stream; | 7 import "dart:async" show Future, Stream; |
| 8 import "dart:convert" show Encoding; | 8 import "dart:convert" show Encoding; |
| 9 import "dart:isolate" show Isolate; | 9 import "dart:isolate" show Isolate; |
| 10 import "package_resolver.dart"; | 10 import "loader.dart"; |
| 11 import "io.dart" as io; // Loading strategy. TODO: Be configuration dependent. | |
| 12 | 11 |
| 13 /// A strategy for resolving package URIs | 12 /// A strategy for resolving package URIs |
| 14 abstract class PackageResolver { | 13 abstract class PackageResolver { |
| 15 /// Cache of the current resolver, accessible directly after it has first | 14 /// Cache of the current isolate resolver, accessible directly after it has |
| 16 /// been found asynchronously. | 15 /// first been found asynchronously by reading [current]. |
| 17 static PackageResolver _current; | 16 static PackageResolver _current; |
| 18 | 17 |
| 19 /// The package resolution strategy used by the current isolate. | 18 /// The package resolution strategy used by the current isolate. |
| 20 static final Future<PackageResolver> current = _findIsolateResolution(); | 19 static final Future<PackageResolver> current = _findIsolateResolution(); |
| 21 | 20 |
| 22 PackageResolver(); | 21 PackageResolver(); |
| 23 | 22 |
| 24 /// Creates a resolver using a map from package name to package location. | 23 /// Creates a resolver using a map from package name to package location. |
| 25 factory PackageResolver.fromMap(Map<String, Uri> packages) = MapResolver; | 24 factory PackageResolver.fromMap(Map<String, Uri> packages) = MapResolver; |
| 26 | 25 |
| 27 /// Creates a resolver using a package root. | 26 /// Creates a resolver using a package root. |
| 28 factory PackageResolver.fromRoot(Uri packageRoot) = RootResolver; | 27 factory PackageResolver.fromRoot(Uri packageRoot) = RootResolver; |
| 29 | 28 |
| 30 /// Resolves a package URI to its location. | 29 /// Resolves a package URI to its location. |
| 31 /// | 30 /// |
| 32 /// If [uri] does not have `package` as scheme, it is returned again. | 31 /// If [uri] does not have `package` as scheme, it is returned as-is. |
| 33 /// Otherwise the package name is looked up, and if found, a location | 32 /// Otherwise the package name of the package URI is looked up, |
| 34 /// for the package file is returned. | 33 /// and if found, a location for the package file is returned. |
| 35 Future<Uri> resolve(Uri uri); | 34 /// |
| 35 /// Returns `null` if the package name is not recognized. | |
| 36 /// | |
| 37 /// Fails if the [uri] is a `package:` URI but has an invalid format. | |
| 38 /// Package URIs must have the format: | |
| 39 /// | |
| 40 /// package:packageName/someValidPath | |
| 41 /// | |
| 42 /// with no authority, no query, no fragment, and a non-empty package name. | |
| 43 Uri resolve(Uri uri); | |
| 36 | 44 |
| 37 /// Returns a [Resource] for the [uri] as resolved by this resolver. | 45 /// Returns a [Resource] for the [uri] as resolved by this resolver. |
| 38 Resource resource(Uri uri) { | 46 /// |
| 39 return new _UriResource(this, uri); | 47 /// If [loader] is provided, it will be used to load the resource. |
| 48 /// If omitted, it defaults to a default system loader. | |
| 49 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".
| |
| 50 return new UriResource(this, loader ?? const DefaultLoader(), uri); | |
| 40 } | 51 } |
| 41 | 52 |
| 42 /// Finds the way the current isolate resolves package URIs. | 53 /// Finds the way the current isolate resolves package URIs. |
| 43 /// | 54 /// |
| 44 /// Is only called once, and when it has been called, the [_current] | 55 /// Is only called once, and when it has been called, the [_current] |
| 45 /// resolver is initialized, so [UriResource] will be initialized | 56 /// resolver is initialized, so [UriResource] will be initialized |
| 46 /// with the resolver directly. | 57 /// with the resolver directly. |
| 47 static Future<PackageResolver> _findIsolateResolution() async { | 58 static Future<PackageResolver> _findIsolateResolution() async { |
| 48 var pair = await Future.wait([Isolate.packageRoot, Isolate.packageMap]); | 59 var pair = await Future.wait([Isolate.packageRoot, Isolate.packageMap]); |
| 49 Uri root = pair[0]; | 60 Uri root = pair[0]; |
| 50 if (root != null) { | 61 if (root != null) { |
| 51 _current = new RootResolver(root); | 62 _current = new RootResolver(root); |
| 52 } else { | 63 } else { |
| 53 Map<String, Uri> map = pair[1]; | 64 Map<String, Uri> map = pair[1]; |
| 54 _current = new MapResolver(map); | 65 _current = new MapResolver(map); |
| 55 } | 66 } |
| 56 return _current; | 67 return _current; |
| 57 } | 68 } |
| 58 } | 69 } |
| 59 | 70 |
| 71 | |
| 60 /// A resource that can be read into the program. | 72 /// A resource that can be read into the program. |
| 61 /// | 73 /// |
| 62 /// A resource is data that can be located using a URI and read into | 74 /// A resource is data that can be located using a URI and read into |
| 63 /// the program at runtime. | 75 /// the program at runtime. |
| 64 /// The URI may use the `package` scheme to read resources provided | 76 /// The URI may use the `package` scheme to read resources provided |
| 65 /// along with package sources. | 77 /// along with package sources. |
| 66 abstract class Resource { | 78 abstract class Resource { |
| 67 /// Creates a resource object with the given [uri] as location. | 79 /// Creates a resource object with the given [uri] as location. |
| 68 /// | 80 /// |
| 69 /// The `uri` is a string containing a valid URI. | 81 /// The `uri` is a string containing a valid URI. |
| 70 /// If the string is not a valid URI, using any of the functions on | 82 /// If the string is not a valid URI, using any of the functions on |
| 71 /// the resource object will fail. | 83 /// the resource object will fail. |
| 72 /// | 84 /// |
| 73 /// The URI may be relative, in which case it will be resolved | 85 /// The URI may be relative, in which case it will be resolved |
| 74 /// against [Uri.base] before being used. | 86 /// against [Uri.base] before being used. |
| 75 /// | 87 /// |
| 76 /// The URI may use the `package` scheme, which is always supported. | 88 /// The URI may use the `package` scheme, which is always supported. |
| 77 /// Other schemes may also be supported where possible. | 89 /// Other schemes may also be supported where possible. |
| 78 const factory Resource(String uri) = _StringResource; | 90 const factory Resource(String uri) = StringResource; |
| 79 | 91 |
| 80 /// Creates a resource object with the given [uri] as location. | 92 /// Creates a resource object with the given [uri] as location. |
| 81 /// | 93 /// |
| 82 /// The URI may be relative, in which case it will be resolved | 94 /// The URI may be relative, in which case it will be resolved |
| 83 /// against [Uri.base] before being used. | 95 /// against [Uri.base] before being used. |
| 84 /// | 96 /// |
| 85 /// The URI may use the `package` scheme, which is always supported. | 97 /// The URI may use the `package` scheme, which is always supported. |
| 86 /// Other schemes may also be supported where possible. | 98 /// Other schemes may also be supported where possible. |
| 87 factory Resource.forUri(Uri uri) => | 99 factory Resource.forUri(Uri uri, {ResourceLoader loader}) => |
|
floitsch
2015/10/13 12:47:50
I think we often used "fromX" for this use-case.
"
| |
| 88 new _UriResource(PackageResolver._current, uri); | 100 new UriResource(PackageResolver._current, loader, uri); |
| 89 | 101 |
| 90 /// The location `uri` of this resource. | 102 /// The location `uri` of this resource. |
| 91 /// | 103 /// |
| 92 /// This is a [Uri] of the `uri` parameter given to the constructor. | 104 /// This is a [Uri] of the `uri` parameter given to the constructor. |
| 93 /// If the parameter was not a valid URI, reading `uri` may fail. | 105 /// If the parameter was not a valid URI, reading `uri` may fail. |
| 94 Uri get uri; | 106 Uri get uri; |
| 95 | 107 |
| 108 /// Read the resource content as a stream of bytes. | |
| 96 Stream<List<int>> openRead(); | 109 Stream<List<int>> openRead(); |
| 97 | 110 |
| 111 /// Read the resource content as a single list of bytes. | |
| 98 Future<List<int>> readAsBytes(); | 112 Future<List<int>> readAsBytes(); |
| 99 | 113 |
| 100 /// Read the resource content as a string. | 114 /// Read the resource content as a string. |
| 101 /// | 115 /// |
| 102 /// The content is decoded into a string using an [Encoding]. | 116 /// The content is decoded into a string using an [Encoding]. |
| 103 /// If no other encoding is provided, it defaults to UTF-8. | 117 /// If no other encoding is provided, it defaults to UTF-8. |
| 104 Future<String> readAsString({Encoding encoding}); | 118 Future<String> readAsString({Encoding encoding}); |
| 105 } | 119 } |
| 106 | 120 |
| 107 class _StringResource implements Resource { | 121 /// Implementation of [new Resoure] constructor. |
|
Bob Nystrom
2015/10/08 16:48:33
"Resource".
Lasse Reichstein Nielsen
2015/10/08 17:20:54
Acknowledged.
| |
| 122 /// | |
| 123 /// 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.
| |
| 124 class StringResource implements Resource { | |
| 108 final String _uri; | 125 final String _uri; |
| 109 | 126 |
| 110 const _StringResource(String uri) : _uri = uri; | 127 const StringResource(String uri) : _uri = uri; |
| 111 | 128 |
| 112 Uri get uri => Uri.parse(_uri); | 129 Uri get uri => Uri.parse(_uri); |
| 113 | 130 |
| 131 UriResource get _asUriResource => | |
| 132 new UriResource(PackageResolver._current, const DefaultLoader(), uri); | |
| 133 | |
| 114 Stream<List<int>> openRead() { | 134 Stream<List<int>> openRead() { |
| 115 return new _UriResource(PackageResolver._current, uri).openRead(); | 135 return _asUriResource.openRead(); |
| 116 } | 136 } |
| 117 Future<List<int>> readAsBytes() { | 137 Future<List<int>> readAsBytes() { |
| 118 return new _UriResource(PackageResolver._current, uri).readAsBytes(); | 138 return _asUriResource.readAsBytes(); |
| 119 } | 139 } |
| 120 Future<String> readAsString({Encoding encoding}) { | 140 Future<String> readAsString({Encoding encoding}) { |
| 121 return new _UriResource(PackageResolver._current, uri) | 141 return _asUriResource.readAsString(encoding: encoding); |
| 122 .readAsString(encoding: encoding); | 142 } |
| 123 } | 143 } |
| 124 } | 144 |
| 125 | 145 class UriResource implements Resource { |
| 126 class _UriResource implements Resource { | |
| 127 /// The strategy for resolving package: URIs. | 146 /// The strategy for resolving package: URIs. |
| 128 /// | 147 /// |
| 129 /// May be null intially. If so, the [PackageResolver.current] resolver is | 148 /// May be null intially. If so, the [PackageResolver.current] resolver is |
| 130 /// used (and cached for later use). | 149 /// used (and cached for later use). |
| 131 PackageResolver _resolver; | 150 PackageResolver _resolver; |
| 132 | 151 |
| 152 /// Loading strategy for the resource. | |
| 153 ResourceLoader _loader; | |
| 154 | |
| 133 /// The URI of the resource. | 155 /// The URI of the resource. |
| 134 final Uri uri; | 156 final Uri uri; |
| 135 | 157 |
| 136 _UriResource(this.resolver, Uri uri); | 158 UriResource(this._resolver, this._loader, this.uri); |
| 137 | 159 |
| 138 Stream<List<int>> openRead() async* { | 160 Stream<List<int>> openRead() async* { |
| 139 Uri uri = await _resolve(this.uri); | 161 Uri uri = await _resolve(this.uri); |
| 140 return io.readAsStream(uri); | 162 yield* _loader.openRead(uri); |
| 141 } | 163 } |
| 142 | 164 |
| 143 Future<List<int>> readAsBytes() async { | 165 Future<List<int>> readAsBytes() async { |
| 144 Uri uri = await _resolve(this.uri); | 166 Uri uri = await _resolve(this.uri); |
| 145 return io.readAsBytes(uri); | 167 return _loader.readAsBytes(uri); |
| 146 } | 168 } |
| 147 | 169 |
| 148 Future<String> readAsString({Encoding encoding}) async { | 170 Future<String> readAsString({Encoding encoding}) async { |
| 149 Uri uri = await _resolve(this.uri); | 171 Uri uri = await _resolve(this.uri); |
| 150 return io.readAsString(uri, encoding); | 172 return _loader.readAsString(uri, encoding: encoding); |
| 151 } | |
| 152 | |
| 153 static void _checkPackageUri(Uri uri) { | |
| 154 if (uri.scheme != "package") { | |
| 155 throw new ArgumentError.value(uri, "Not a package: URI"); | |
| 156 } | |
| 157 if (uri.hasAuthority) { | |
| 158 throw new ArgumentError.value(uri, | |
| 159 "A package: URI must not have an authority part"); | |
| 160 } | |
| 161 if (uri.path.isEmpty || uri.path.startsWith('/')) { | |
| 162 throw new ArgumentError.value(uri, | |
| 163 "A package: URI must have the form " | |
| 164 "'package:packageName/packagePath'"); | |
| 165 } | |
| 166 if (uri.hasQuery) { | |
| 167 throw new ArgumentError.value(uri, | |
| 168 "A package: URI must not have a query part"); | |
| 169 } | |
| 170 if (uri.hasFragment) { | |
| 171 throw new ArgumentError.value(uri, | |
| 172 "A package: URI must not have a fragment part"); | |
| 173 } | |
| 174 } | 173 } |
| 175 | 174 |
| 176 Future<Uri> _resolve(Uri uri) async { | 175 Future<Uri> _resolve(Uri uri) async { |
| 177 if (uri.scheme != "package") { | 176 if (uri.scheme != "package") { |
| 178 return Uri.base.resolveUri(uri); | 177 return Uri.base.resolveUri(uri); |
| 179 } | 178 } |
| 180 _checkPackageUri(uri); | 179 if (_resolver == null) { |
| 181 _resolver ??= await PackageResolver._current; | 180 _resolver = await PackageResolver.current; |
| 182 return _resolver.resolve(uri); | 181 } |
| 182 var result = _resolver.resolve(uri); | |
| 183 return result; | |
| 183 } | 184 } |
| 184 } | 185 } |
| 185 | 186 |
| 186 /// A [PackageResolver] based on a packags map. | 187 /// A [PackageResolver] based on a packags map. |
| 187 class MapResolver extends PackageResolver { | 188 class MapResolver extends PackageResolver { |
| 188 Map<String, Uri> _mapping; | 189 Map<String, Uri> _mapping; |
| 189 | 190 |
| 190 MapResolver(this._mapping); | 191 MapResolver(this._mapping); |
| 191 | 192 |
| 192 Uri resolve(Uri uri) { | 193 Uri resolve(Uri uri) { |
| 193 if (uri.scheme != "package") return uri; | 194 if (uri.scheme != "package") return uri; |
| 194 var path = uri.path; | 195 int slashIndex = validatePackageUri(uri); |
| 195 int slashIndex = path.indexOf('/'); | 196 String path = uri.path; |
| 196 if (slashIndex <= 0) { | 197 String packageName = path.substring(0, slashIndex); |
| 197 throw new ArgumentError.value(uri, "Invalid package URI"); | 198 Uri base = _mapping[packageName]; |
| 198 } | |
| 199 int packageName = path.substring(0, slashIndex); | |
| 200 var base = _mapping[packageName]; | |
| 201 if (base != null) { | 199 if (base != null) { |
| 202 int packagePath = path.substring(slashIndex + 1); | 200 String packagePath = path.substring(slashIndex + 1); |
| 203 return base.resolveUri(new Uri(path: packagePath)); | 201 return base.resolveUri(new Uri(path: packagePath)); |
| 204 } | 202 } |
| 205 throw new UnsupportedError("No package named '$packageName' found"); | 203 return null; |
| 206 } | 204 } |
| 207 } | 205 } |
| 208 | 206 |
| 209 /// A [PackageResolver] based on a package root. | 207 /// A [PackageResolver] based on a package root. |
| 210 class RootResolver extends PackageResolver { | 208 class RootResolver extends PackageResolver { |
| 211 Uri _root; | 209 Uri _root; |
| 212 RootResolver(this._root); | 210 |
| 211 RootResolver(this._root) { | |
| 212 if (_root.path.contains("local")) { | |
| 213 try { throw 0; } catch (e, s) { print(s); } | |
| 214 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!
| |
| 215 } | |
| 216 } | |
| 213 | 217 |
| 214 Uri resolve(Uri uri) { | 218 Uri resolve(Uri uri) { |
| 215 if (uri.scheme != "package") return uri; | 219 if (uri.scheme != "package") return uri; |
| 220 validatePackageUri(uri); | |
| 216 return _root.resolve(uri.path); | 221 return _root.resolve(uri.path); |
| 217 } | 222 } |
| 218 } | 223 } |
| 224 | |
| 225 /// Validates that a package URI has a valid format. | |
| 226 /// | |
| 227 /// Returns the length of the packageName. | |
| 228 /// This is the index of the first `/` in `uri.path`. | |
| 229 int validatePackageUri(Uri uri) { | |
| 230 if (uri.scheme != "package") { | |
| 231 throw new ArgumentError.value(uri, "Not a package: URI"); | |
| 232 } | |
| 233 if (uri.hasAuthority) { | |
| 234 throw new ArgumentError.value(uri, | |
| 235 "A package: URI must not have an authority part"); | |
| 236 } | |
| 237 int index = uri.path.indexOf('/'); | |
| 238 if (index < 1) { | |
| 239 throw new ArgumentError.value(uri, | |
| 240 "A package: URI must have the form " | |
| 241 "'package:packageName/packagePath'"); | |
| 242 } | |
| 243 if (uri.hasQuery) { | |
| 244 throw new ArgumentError.value(uri, | |
| 245 "A package: URI must not have a query part"); | |
| 246 } | |
| 247 if (uri.hasFragment) { | |
| 248 throw new ArgumentError.value(uri, | |
| 249 "A package: URI must not have a fragment part"); | |
| 250 } | |
| 251 return index; | |
| 252 } | |
| 253 | |
| OLD | NEW |