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

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: Final tweaks. Created 4 years, 11 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;
6
7 import "dart:async" show Future, Stream; 5 import "dart:async" show Future, Stream;
8 import "dart:convert" show Encoding; 6 import "dart:convert" show Encoding;
9 import "dart:isolate" show Isolate; 7 import "dart:isolate" show Isolate;
10 import "package_resolver.dart"; 8 import "loader.dart";
11 import "io.dart" as io; // Loading strategy. TODO: Be configuration dependent.
12
13 /// A strategy for resolving package URIs
14 abstract class PackageResolver {
15 /// Cache of the current resolver, accessible directly after it has first
16 /// been found asynchronously.
17 static PackageResolver _current;
18
19 /// The package resolution strategy used by the current isolate.
20 static final Future<PackageResolver> current = _findIsolateResolution();
21
22 PackageResolver();
23
24 /// Creates a resolver using a map from package name to package location.
25 factory PackageResolver.fromMap(Map<String, Uri> packages) = MapResolver;
26
27 /// Creates a resolver using a package root.
28 factory PackageResolver.fromRoot(Uri packageRoot) = RootResolver;
29
30 /// Resolves a package URI to its location.
31 ///
32 /// If [uri] does not have `package` as scheme, it is returned again.
33 /// Otherwise the package name is looked up, and if found, a location
34 /// for the package file is returned.
35 Future<Uri> resolve(Uri uri);
36
37 /// Returns a [Resource] for the [uri] as resolved by this resolver.
38 Resource resource(Uri uri) {
39 return new _UriResource(this, uri);
40 }
41
42 /// Finds the way the current isolate resolves package URIs.
43 ///
44 /// Is only called once, and when it has been called, the [_current]
45 /// resolver is initialized, so [UriResource] will be initialized
46 /// with the resolver directly.
47 static Future<PackageResolver> _findIsolateResolution() async {
48 var pair = await Future.wait([Isolate.packageRoot, Isolate.packageMap]);
49 Uri root = pair[0];
50 if (root != null) {
51 _current = new RootResolver(root);
52 } else {
53 Map<String, Uri> map = pair[1];
54 _current = new MapResolver(map);
55 }
56 return _current;
57 }
58 }
59 9
60 /// A resource that can be read into the program. 10 /// A resource that can be read into the program.
61 /// 11 ///
62 /// A resource is data that can be located using a URI and read into 12 /// A resource is data that can be located using a URI and read into
63 /// the program at runtime. 13 /// the program at runtime.
64 /// The URI may use the `package` scheme to read resources provided 14 /// The URI may use the `package` scheme to read resources provided
65 /// along with package sources. 15 /// along with package sources.
66 abstract class Resource { 16 abstract class Resource {
67 /// Creates a resource object with the given [uri] as location. 17 /// Creates a resource object with the given [uri] as location.
68 /// 18 ///
69 /// The `uri` is a string containing a valid URI. 19 /// The [uri] must be either a [Uri] or a string containing a valid URI.
70 /// If the string is not a valid URI, using any of the functions on 20 /// If the string is not a valid URI, using any of the functions on
71 /// the resource object will fail. 21 /// the resource object will fail.
72 /// 22 ///
73 /// The URI may be relative, in which case it will be resolved 23 /// The URI may be relative, in which case it will be resolved
74 /// against [Uri.base] before being used. 24 /// against [Uri.base] before being used.
75 /// 25 ///
76 /// The URI may use the `package` scheme, which is always supported. 26 /// The URI may use the `package` scheme, which is always supported.
77 /// Other schemes may also be supported where possible. 27 /// Other schemes may also be supported where possible.
78 const factory Resource(String uri) = _StringResource;
79
80 /// Creates a resource object with the given [uri] as location.
81 /// 28 ///
82 /// The URI may be relative, in which case it will be resolved 29 /// If [loader] is provided, it is used to load absolute non-package URIs.
83 /// against [Uri.base] before being used. 30 /// Package: URIs are resolved to a non-package URI before being loaded, so
84 /// 31 /// the loader doesn't have to support package: URIs, nor does it need to
85 /// The URI may use the `package` scheme, which is always supported. 32 /// support relative URI references.
86 /// Other schemes may also be supported where possible. 33 /// If [loader] is omitted, a default implementation is used which supports
87 factory Resource.forUri(Uri uri) => 34 /// as many of `http`, `https`, `file` and `data` as are available on the
88 new _UriResource(PackageResolver._current, uri); 35 /// current platform.
36 const factory Resource(uri, {ResourceLoader loader}) = _Resource;
89 37
90 /// The location `uri` of this resource. 38 /// The location `uri` of this resource.
91 /// 39 ///
92 /// This is a [Uri] of the `uri` parameter given to the constructor. 40 /// 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. 41 /// If the parameter was not a valid URI, reading `uri` may fail.
94 Uri get uri; 42 Uri get uri;
95 43
44 /// 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.
96 Stream<List<int>> openRead(); 45 Stream<List<int>> openRead();
97 46
47 /// 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.
98 Future<List<int>> readAsBytes(); 48 Future<List<int>> readAsBytes();
99 49
100 /// Read the resource content as a string. 50 /// Read the resource content as a string.
101 /// 51 ///
102 /// The content is decoded into a string using an [Encoding]. 52 /// The content is decoded into a string using an [Encoding].
103 /// If no other encoding is provided, it defaults to UTF-8. 53 /// If no other encoding is provided, it defaults to UTF-8.
104 Future<String> readAsString({Encoding encoding}); 54 Future<String> readAsString({Encoding encoding});
105 } 55 }
106 56
107 class _StringResource implements Resource { 57 class _Resource implements Resource {
108 final String _uri; 58 /// Loading strategy for the resource.
109 59 final ResourceLoader _loader;
110 const _StringResource(String uri) : _uri = uri;
111
112 Uri get uri => Uri.parse(_uri);
113
114 Stream<List<int>> openRead() {
115 return new _UriResource(PackageResolver._current, uri).openRead();
116 }
117 Future<List<int>> readAsBytes() {
118 return new _UriResource(PackageResolver._current, uri).readAsBytes();
119 }
120 Future<String> readAsString({Encoding encoding}) {
121 return new _UriResource(PackageResolver._current, uri)
122 .readAsString(encoding: encoding);
123 }
124 }
125
126 class _UriResource implements Resource {
127 /// The strategy for resolving package: URIs.
128 ///
129 /// May be null intially. If so, the [PackageResolver.current] resolver is
130 /// used (and cached for later use).
131 PackageResolver _resolver;
132 60
133 /// The URI of the resource. 61 /// The URI of the resource.
134 final Uri uri; 62 final _uri;
135 63
136 _UriResource(this.resolver, Uri uri); 64 const _Resource(uri, {ResourceLoader loader})
65 : _uri = uri, _loader = (loader != null) ? loader : const DefaultLoader();
66 // TODO: Make this `loader ?? const DefaultLoader()` when ?? is const.
67
68 Uri get uri => (_uri is String) ? Uri.parse(_uri) : (_uri as Uri);
137 69
138 Stream<List<int>> openRead() async* { 70 Stream<List<int>> openRead() async* {
139 Uri uri = await _resolve(this.uri); 71 Uri uri = await _resolvedUri;
140 return io.readAsStream(uri); 72 yield* _loader.openRead(uri);
141 } 73 }
142 74
143 Future<List<int>> readAsBytes() async { 75 Future<List<int>> readAsBytes() async {
144 Uri uri = await _resolve(this.uri); 76 Uri uri = await _resolvedUri;
145 return io.readAsBytes(uri); 77 return _loader.readAsBytes(uri);
146 } 78 }
147 79
148 Future<String> readAsString({Encoding encoding}) async { 80 Future<String> readAsString({Encoding encoding}) async {
149 Uri uri = await _resolve(this.uri); 81 Uri uri = await _resolvedUri;
150 return io.readAsString(uri, encoding); 82 return _loader.readAsString(uri, encoding: encoding);
151 } 83 }
152 84
153 static void _checkPackageUri(Uri uri) { 85 Future<Uri> get _resolvedUri => resolveUri(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 }
175
176 Future<Uri> _resolve(Uri uri) async {
177 if (uri.scheme != "package") {
178 return Uri.base.resolveUri(uri);
179 }
180 _checkPackageUri(uri);
181 _resolver ??= await PackageResolver._current;
182 return _resolver.resolve(uri);
183 }
184 } 86 }
185 87
186 /// A [PackageResolver] based on a packags map. 88 /// Validates that a package URI has a valid format.
187 class MapResolver extends PackageResolver { 89 ///
188 Map<String, Uri> _mapping; 90 /// Returns the length of the packageName.
189 91 /// This is the index of the first `/` in `uri.path`.
190 MapResolver(this._mapping); 92 int validatePackageUri(Uri uri) {
191 93 if (uri.scheme != "package") {
192 Uri resolve(Uri uri) { 94 throw new ArgumentError.value(uri, "Not a package: URI");
193 if (uri.scheme != "package") return uri;
194 var path = uri.path;
195 int slashIndex = path.indexOf('/');
196 if (slashIndex <= 0) {
197 throw new ArgumentError.value(uri, "Invalid package URI");
198 }
199 int packageName = path.substring(0, slashIndex);
200 var base = _mapping[packageName];
201 if (base != null) {
202 int packagePath = path.substring(slashIndex + 1);
203 return base.resolveUri(new Uri(path: packagePath));
204 }
205 throw new UnsupportedError("No package named '$packageName' found");
206 } 95 }
96 if (uri.hasAuthority) {
97 throw new ArgumentError.value(uri,
98 "A package: URI must not have an authority part");
99 }
100 int index = uri.path.indexOf('/');
101 if (index < 1) {
102 throw new ArgumentError.value(uri,
103 "A package: URI must have the form "
104 "'package:packageName/packagePath'");
105 }
106 if (uri.hasQuery) {
107 throw new ArgumentError.value(uri,
108 "A package: URI must not have a query part");
109 }
110 if (uri.hasFragment) {
111 throw new ArgumentError.value(uri,
112 "A package: URI must not have a fragment part");
113 }
114 return index;
207 } 115 }
208
209 /// A [PackageResolver] based on a package root.
210 class RootResolver extends PackageResolver {
211 Uri _root;
212 RootResolver(this._root);
213
214 Uri resolve(Uri uri) {
215 if (uri.scheme != "package") return uri;
216 return _root.resolve(uri.path);
217 }
218 }
OLDNEW
« lib/src/loader.dart ('K') | « lib/src/loader.dart ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698