@@ -44,13 +44,48 @@ class _MemoryFile extends _MemoryFileSystemEntity implements File {
4444 Future <File > copy (String newPath) async => copySync (newPath);
4545
4646 @override
47- File copySync (String newPath) => throw new UnimplementedError ('TODO' );
47+ File copySync (String newPath) {
48+ _FileNode sourceNode = backing;
49+ fileSystem._findNode (
50+ newPath,
51+ segmentVisitor: (
52+ _DirectoryNode parent,
53+ String childName,
54+ _Node child,
55+ int currentSegment,
56+ int finalSegment,
57+ ) {
58+ if (currentSegment == finalSegment) {
59+ if (child != null ) {
60+ if (_isLink (child)) {
61+ StringBuffer ledger = new StringBuffer ();
62+ child = _resolveLinks (child, () => newPath, ledger: ledger);
63+ _checkExists (child, () => newPath);
64+ parent = child.parent;
65+ childName = fileSystem._context.basename (ledger.toString ());
66+ assert (parent.children.containsKey (childName));
67+ }
68+ if (child.type != expectedType) {
69+ throw new io.FileSystemException (
70+ 'Is a ${child .type .toString ().toLowerCase ()}' , newPath);
71+ }
72+ parent.children.remove (childName);
73+ }
74+ _FileNode newNode = new _FileNode (parent);
75+ newNode.copyFrom (sourceNode);
76+ parent.children[childName] = newNode;
77+ }
78+ return child;
79+ },
80+ );
81+ return _clone (newPath);
82+ }
4883
4984 @override
5085 Future <int > length () async => lengthSync ();
5186
5287 @override
53- int lengthSync () => throw new UnimplementedError ( 'TODO' ) ;
88+ int lengthSync () => (backing as _FileNode ).size ;
5489
5590 @override
5691 File get absolute => super .absolute;
@@ -59,7 +94,7 @@ class _MemoryFile extends _MemoryFileSystemEntity implements File {
5994 Future <DateTime > lastModified () async => lastModifiedSync ();
6095
6196 @override
62- DateTime lastModifiedSync () => throw new UnimplementedError ( 'TODO' ) ;
97+ DateTime lastModifiedSync () => (backing as _FileNode ).stat.modified ;
6398
6499 @override
65100 Future <io.RandomAccessFile > open (
@@ -71,53 +106,53 @@ class _MemoryFile extends _MemoryFileSystemEntity implements File {
71106 throw new UnimplementedError ('TODO' );
72107
73108 @override
74- Stream <List <int >> openRead ([int start, int end]) =>
75- throw new UnimplementedError ('TODO' );
109+ Stream <List <int >> openRead ([int start, int end]) {
110+ _FileNode node = backing;
111+ return new Stream .fromIterable (< List <int >> [node.content]);
112+ }
76113
77114 @override
78115 io.IOSink openWrite ({
79116 io.FileMode mode: io.FileMode .WRITE ,
80117 Encoding encoding: UTF8 ,
81- }) =>
82- throw new UnimplementedError ('TODO' );
118+ }) {
119+ _checkWriteMode (mode);
120+ createSync ();
121+ _FileNode node = backing;
122+ _truncateFileIfNecessary (node, mode);
123+ return new _FileSink (node, encoding);
124+ }
83125
84126 @override
85- Future <List <int >> readAsBytes () {
86- throw new UnimplementedError ('TODO' );
87- }
127+ Future <List <int >> readAsBytes () async => readAsBytesSync ();
88128
89129 @override
90- List <int > readAsBytesSync () {
91- throw new UnimplementedError ('TODO' );
92- }
130+ List <int > readAsBytesSync () => (backing as _FileNode ).content;
93131
94132 @override
95- Future <String > readAsString ({Encoding encoding: UTF8 }) {
96- throw new UnimplementedError ('TODO' );
97- }
133+ Future <String > readAsString ({Encoding encoding: UTF8 }) async =>
134+ readAsStringSync (encoding: encoding);
98135
99136 @override
100- String readAsStringSync ({Encoding encoding: UTF8 }) {
101- throw new UnimplementedError ('TODO' );
102- }
137+ String readAsStringSync ({Encoding encoding: UTF8 }) =>
138+ encoding.decode (readAsBytesSync ());
103139
104140 @override
105- Future <List <String >> readAsLines ({Encoding encoding: UTF8 }) {
106- throw new UnimplementedError ('TODO' );
107- }
141+ Future <List <String >> readAsLines ({Encoding encoding: UTF8 }) async =>
142+ readAsLinesSync (encoding: encoding);
108143
109144 @override
110- List <String > readAsLinesSync ({Encoding encoding: UTF8 }) {
111- throw new UnimplementedError ('TODO' );
112- }
145+ List <String > readAsLinesSync ({Encoding encoding: UTF8 }) =>
146+ readAsStringSync (encoding: encoding).split ('\n ' );
113147
114148 @override
115149 Future <File > writeAsBytes (
116150 List <int > bytes, {
117151 io.FileMode mode: io.FileMode .WRITE ,
118152 bool flush: false ,
119- }) {
120- throw new UnimplementedError ('TODO' );
153+ }) async {
154+ writeAsBytesSync (bytes, mode: mode, flush: flush);
155+ return this ;
121156 }
122157
123158 @override
@@ -126,7 +161,11 @@ class _MemoryFile extends _MemoryFileSystemEntity implements File {
126161 io.FileMode mode: io.FileMode .WRITE ,
127162 bool flush: false ,
128163 }) {
129- throw new UnimplementedError ('TODO' );
164+ _checkWriteMode (mode);
165+ createSync ();
166+ _FileNode node = backing;
167+ _truncateFileIfNecessary (node, mode);
168+ node.content.addAll (bytes);
130169 }
131170
132171 @override
@@ -135,8 +174,9 @@ class _MemoryFile extends _MemoryFileSystemEntity implements File {
135174 io.FileMode mode: io.FileMode .WRITE ,
136175 Encoding encoding: UTF8 ,
137176 bool flush: false ,
138- }) {
139- throw new UnimplementedError ('TODO' );
177+ }) async {
178+ writeAsStringSync (contents, mode: mode, encoding: encoding, flush: flush);
179+ return this ;
140180 }
141181
142182 @override
@@ -145,10 +185,102 @@ class _MemoryFile extends _MemoryFileSystemEntity implements File {
145185 io.FileMode mode: io.FileMode .WRITE ,
146186 Encoding encoding: UTF8 ,
147187 bool flush: false ,
148- }) {
149- throw new UnimplementedError ('TODO' );
150- }
188+ }) =>
189+ writeAsBytesSync (encoding.encode (contents), mode: mode, flush: flush);
151190
152191 @override
153192 File _clone (String path) => new _MemoryFile (fileSystem, path);
193+
194+ void _checkWriteMode (io.FileMode mode) {
195+ if (mode != io.FileMode .WRITE &&
196+ mode != io.FileMode .APPEND &&
197+ mode != io.FileMode .WRITE_ONLY &&
198+ mode != io.FileMode .WRITE_ONLY_APPEND ) {
199+ throw new ArgumentError .value (mode, 'mode' ,
200+ 'Must be either WRITE, APPEND, WRITE_ONLY, or WRITE_ONLY_APPEND' );
201+ }
202+ }
203+
204+ void _truncateFileIfNecessary (_FileNode node, io.FileMode mode) {
205+ if (mode == io.FileMode .WRITE || mode == io.FileMode .WRITE_ONLY ) {
206+ node.content.clear ();
207+ }
208+ }
209+ }
210+
211+ /// Implementation of an [io.IOSink] that's backed by a [_FileNode] .
212+ class _FileSink implements io.IOSink {
213+ final _FileNode _node;
214+ final Completer <Null > _doneCompleter = new Completer <Null >();
215+ Encoding _encoding;
216+ bool _isClosed = false ;
217+
218+ _FileSink (this ._node, this ._encoding) {
219+ _checkNotNull (_encoding);
220+ }
221+
222+ dynamic _checkNotNull (dynamic value) {
223+ if (value == null ) {
224+ throw new ArgumentError .notNull ();
225+ }
226+ return value;
227+ }
228+
229+ @override
230+ Encoding get encoding => _encoding;
231+
232+ @override
233+ set encoding (Encoding encoding) => _encoding = _checkNotNull (encoding);
234+
235+ @override
236+ void add (List <int > data) {
237+ if (! _isClosed) {
238+ _node.content.addAll (data);
239+ }
240+ }
241+
242+ @override
243+ void write (Object obj) => add (encoding.encode (obj? .toString () ?? 'null' ));
244+
245+ @override
246+ void writeAll (Iterable objects, [String separator = "" ]) {
247+ bool firstIter = true ;
248+ for (dynamic obj in objects) {
249+ if (! firstIter && separator != null ) {
250+ write (separator);
251+ }
252+ firstIter = false ;
253+ write (obj);
254+ }
255+ }
256+
257+ @override
258+ void writeln ([Object obj = "" ]) {
259+ write (obj);
260+ write ('\n ' );
261+ }
262+
263+ @override
264+ void writeCharCode (int charCode) => write (new String .fromCharCode (charCode));
265+
266+ @override
267+ void addError (error, [StackTrace stackTrace]) =>
268+ throw new UnsupportedError ('addError' );
269+
270+ @override
271+ Future addStream (Stream <List <int >> stream) =>
272+ stream.forEach ((List <int > data) => add (data));
273+
274+ @override
275+ Future flush () => new Future .value ();
276+
277+ @override
278+ Future close () {
279+ _isClosed = true ;
280+ _doneCompleter.complete ();
281+ return _doneCompleter.future;
282+ }
283+
284+ @override
285+ Future get done => _doneCompleter.future;
154286}
0 commit comments