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

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: Address comments. 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
« no previous file with comments | « lib/src/loader.dart ('k') | pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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; 28 ///
29 /// If [loader] is provided, it is used to load absolute non-package URIs.
30 /// Package: URIs are resolved to a non-package URI before being loaded, so
31 /// the loader doesn't have to support package: URIs, nor does it need to
32 /// support relative URI references.
33 /// If [loader] is omitted, a default implementation is used which supports
34 /// as many of `http`, `https`, `file` and `data` as are available on the
35 /// current platform.
36 const factory Resource(uri, {ResourceLoader loader}) = _Resource;
79 37
80 /// Creates a resource object with the given [uri] as location. 38 /// The location URI of this resource.
81 ///
82 /// The URI may be relative, in which case it will be resolved
83 /// against [Uri.base] before being used.
84 ///
85 /// The URI may use the `package` scheme, which is always supported.
86 /// Other schemes may also be supported where possible.
87 factory Resource.forUri(Uri uri) =>
88 new _UriResource(PackageResolver._current, uri);
89
90 /// 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 a string that did not contain a valid URI,
42 /// reading `uri` will fail.
94 Uri get uri; 43 Uri get uri;
95 44
45 /// Reads the resource content as a stream of bytes.
96 Stream<List<int>> openRead(); 46 Stream<List<int>> openRead();
97 47
48 /// Reads the resource content as a single list of bytes.
98 Future<List<int>> readAsBytes(); 49 Future<List<int>> readAsBytes();
99 50
100 /// Read the resource content as a string. 51 /// Reads the resource content as a string.
101 /// 52 ///
102 /// The content is decoded into a string using an [Encoding]. 53 /// The content is decoded into a string using an [Encoding].
103 /// If no other encoding is provided, it defaults to UTF-8. 54 /// If no other encoding is provided, it defaults to UTF-8.
104 Future<String> readAsString({Encoding encoding}); 55 Future<String> readAsString({Encoding encoding});
105 } 56 }
106 57
107 class _StringResource implements Resource { 58 class _Resource implements Resource {
108 final String _uri; 59 /// Loading strategy for the resource.
109 60 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 61
133 /// The URI of the resource. 62 /// The URI of the resource.
134 final Uri uri; 63 final _uri;
135 64
136 _UriResource(this.resolver, Uri uri); 65 const _Resource(uri, {ResourceLoader loader})
66 : _uri = uri, _loader = (loader != null) ? loader : const DefaultLoader();
67 // TODO: Make this `loader ?? const DefaultLoader()` when ?? is const.
68
69 Uri get uri => (_uri is String) ? Uri.parse(_uri) : (_uri as Uri);
137 70
138 Stream<List<int>> openRead() async* { 71 Stream<List<int>> openRead() async* {
139 Uri uri = await _resolve(this.uri); 72 Uri uri = await _resolvedUri;
140 return io.readAsStream(uri); 73 yield* _loader.openRead(uri);
141 } 74 }
142 75
143 Future<List<int>> readAsBytes() async { 76 Future<List<int>> readAsBytes() async {
144 Uri uri = await _resolve(this.uri); 77 Uri uri = await _resolvedUri;
145 return io.readAsBytes(uri); 78 return _loader.readAsBytes(uri);
146 } 79 }
147 80
148 Future<String> readAsString({Encoding encoding}) async { 81 Future<String> readAsString({Encoding encoding}) async {
149 Uri uri = await _resolve(this.uri); 82 Uri uri = await _resolvedUri;
150 return io.readAsString(uri, encoding); 83 return _loader.readAsString(uri, encoding: encoding);
151 } 84 }
152 85
153 static void _checkPackageUri(Uri uri) { 86 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 } 87 }
185
186 /// A [PackageResolver] based on a packags map.
187 class MapResolver extends PackageResolver {
188 Map<String, Uri> _mapping;
189
190 MapResolver(this._mapping);
191
192 Uri resolve(Uri 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 }
207 }
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
« no previous file with comments | « lib/src/loader.dart ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698