OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 barback.asset; | 5 library barback.asset; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | |
9 import 'dart:io'; | |
10 | 8 |
11 import 'asset_id.dart'; | 9 import 'asset_id.dart'; |
12 import 'file_pool.dart'; | 10 import 'internal_asset.dart'; |
13 import 'stream_replayer.dart'; | |
14 import 'utils.dart'; | |
15 | 11 |
16 /// A blob of content. | 12 /// A blob of content. |
17 /// | 13 /// |
18 /// Assets may come from the file system, or as the output of a [Transformer]. | 14 /// Assets may come from the file system, or as the output of a [Transformer]. |
19 /// They are identified by [AssetId]. | 15 /// They are identified by [AssetId]. |
| 16 /// |
| 17 /// Custom implementations of [Asset] are not currently supported. |
20 abstract class Asset { | 18 abstract class Asset { |
21 /// The ID for this asset. | 19 /// The ID for this asset. |
22 final AssetId id; | 20 final AssetId id; |
23 | 21 |
24 Asset(this.id); | |
25 | |
26 factory Asset.fromBytes(AssetId id, List<int> bytes) => | 22 factory Asset.fromBytes(AssetId id, List<int> bytes) => |
27 new _BinaryAsset(id, bytes); | 23 new BinaryAsset(id, bytes); |
28 | 24 |
29 factory Asset.fromFile(AssetId id, File file) => | 25 factory Asset.fromFile(AssetId id, File file) => |
30 new _FileAsset(id, file); | 26 new FileAsset(id, file.path); |
31 | 27 |
32 factory Asset.fromString(AssetId id, String content) => | 28 factory Asset.fromString(AssetId id, String content) => |
33 new _StringAsset(id, content); | 29 new StringAsset(id, content); |
34 | 30 |
35 factory Asset.fromPath(AssetId id, String path) => | 31 factory Asset.fromPath(AssetId id, String path) => |
36 new _FileAsset(id, new File(path)); | 32 new FileAsset(id, path); |
37 | 33 |
38 /// Creates an asset from a stream. | |
39 /// | |
40 /// This immediately starts draining [stream]. | |
41 factory Asset.fromStream(AssetId id, Stream<List<int>> stream) => | 34 factory Asset.fromStream(AssetId id, Stream<List<int>> stream) => |
42 new _StreamAsset(id, stream); | 35 new StreamAsset(id, stream); |
43 | 36 |
44 /// Returns the contents of the asset as a string. | 37 /// Returns the contents of the asset as a string. |
45 /// | 38 /// |
46 /// If the asset was created from a [String] the original string is always | 39 /// If the asset was created from a [String] the original string is always |
47 /// returned and [encoding] is ignored. Otherwise, the binary data of the | 40 /// returned and [encoding] is ignored. Otherwise, the binary data of the |
48 /// asset is decoded using [encoding], which defaults to [UTF8]. | 41 /// asset is decoded using [encoding], which defaults to [UTF8]. |
49 Future<String> readAsString({Encoding encoding}); | 42 Future<String> readAsString({Encoding encoding}); |
50 | 43 |
51 /// Streams the binary contents of the asset. | 44 /// Streams the binary contents of the asset. |
52 /// | 45 /// |
53 /// If the asset was created from a [String], this returns its UTF-8 encoding. | 46 /// If the asset was created from a [String], this returns its UTF-8 encoding. |
54 Stream<List<int>> read(); | 47 Stream<List<int>> read(); |
55 } | 48 } |
56 | |
57 /// An asset whose data is stored in a list of bytes. | |
58 class _BinaryAsset extends Asset { | |
59 final List<int> _contents; | |
60 | |
61 _BinaryAsset(AssetId id, this._contents) | |
62 : super(id); | |
63 | |
64 Future<String> readAsString({Encoding encoding}) { | |
65 if (encoding == null) encoding = UTF8; | |
66 | |
67 return new Future.value(encoding.decode(_contents)); | |
68 } | |
69 | |
70 Stream<List<int>> read() => new Future<List<int>>.value(_contents).asStream(); | |
71 | |
72 String toString() { | |
73 var buffer = new StringBuffer(); | |
74 buffer.write("Bytes ["); | |
75 | |
76 // Don't show the whole list if it's long. | |
77 if (_contents.length > 11) { | |
78 for (var i = 0; i < 5; i++) { | |
79 buffer.write(byteToHex(_contents[i])); | |
80 buffer.write(" "); | |
81 } | |
82 | |
83 buffer.write("..."); | |
84 | |
85 for (var i = _contents.length - 5; i < _contents.length; i++) { | |
86 buffer.write(" "); | |
87 buffer.write(byteToHex(_contents[i])); | |
88 } | |
89 } else { | |
90 for (var i = 0; i < _contents.length; i++) { | |
91 if (i > 0) buffer.write(" "); | |
92 buffer.write(byteToHex(_contents[i])); | |
93 } | |
94 } | |
95 | |
96 buffer.write("]"); | |
97 return buffer.toString(); | |
98 } | |
99 } | |
100 | |
101 /// An asset backed by a file on the local file system. | |
102 class _FileAsset extends Asset { | |
103 /// Use a [FilePool] to handle reads so we can try to cope with running out | |
104 /// of file descriptors more gracefully. | |
105 static final _pool = new FilePool(); | |
106 | |
107 final File _file; | |
108 _FileAsset(AssetId id, this._file) | |
109 : super(id); | |
110 | |
111 Future<String> readAsString({Encoding encoding}) { | |
112 if (encoding == null) encoding = UTF8; | |
113 return _pool.readAsString(_file, encoding); | |
114 } | |
115 | |
116 Stream<List<int>> read() => _pool.openRead(_file); | |
117 | |
118 String toString() => 'File "${_file.path}"'; | |
119 } | |
120 | |
121 /// An asset whose data is stored in a string. | |
122 class _StringAsset extends Asset { | |
123 final String _contents; | |
124 | |
125 _StringAsset(AssetId id, this._contents) | |
126 : super(id); | |
127 | |
128 Future<String> readAsString({Encoding encoding}) => | |
129 new Future.value(_contents); | |
130 | |
131 Stream<List<int>> read() => | |
132 new Future<List<int>>.value(UTF8.encode(_contents)).asStream(); | |
133 | |
134 String toString() { | |
135 // Don't show the whole string if it's long. | |
136 var contents = _contents; | |
137 if (contents.length > 40) { | |
138 contents = contents.substring(0, 20) + " ... " + | |
139 contents.substring(contents.length - 20); | |
140 } | |
141 | |
142 contents = _escape(contents); | |
143 return 'String "$contents"'; | |
144 } | |
145 | |
146 String _escape(String string) { | |
147 return string | |
148 .replaceAll("\"", r'\"') | |
149 .replaceAll("\n", r"\n") | |
150 .replaceAll("\r", r"\r") | |
151 .replaceAll("\t", r"\t"); | |
152 } | |
153 } | |
154 | |
155 /// An asset whose data is available from a stream. | |
156 class _StreamAsset extends Asset { | |
157 /// A stream replayer that records and replays the contents of the input | |
158 /// stream. | |
159 final StreamReplayer<List<int>> _replayer; | |
160 | |
161 _StreamAsset(AssetId id, Stream<List<int>> stream) | |
162 : _replayer = new StreamReplayer(stream), | |
163 super(id); | |
164 | |
165 Future<String> readAsString({Encoding encoding}) { | |
166 if (encoding == null) encoding = UTF8; | |
167 return _replayer.getReplay().toList() | |
168 .then((chunks) => encoding.decode(flatten(chunks))); | |
169 } | |
170 | |
171 Stream<List<int>> read() => _replayer.getReplay(); | |
172 | |
173 String toString() => "Stream"; | |
174 } | |
OLD | NEW |