Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Side by Side Diff: lib/src/resource.dart

Issue 1387163002: Add Resource package. (Closed) Base URL: https://github.com/dart-lang/resource.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698