| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 class _FileInputStream extends _BaseDataInputStream implements InputStream { | 5 class _FileInputStream extends _BaseDataInputStream implements InputStream { |
| 6 _FileInputStream(String name) { | 6 _FileInputStream(String name) { |
| 7 var file = new File(name); | 7 var file = new File(name); |
| 8 _data = []; | 8 _data = []; |
| 9 _position = 0; | 9 _position = 0; |
| 10 var chained = file.open(FileMode.READ).chain((openedFile) { | 10 var chained = file.open(FileMode.READ).chain((openedFile) { |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 // file was successfully opened. | 206 // file was successfully opened. |
| 207 List<List<int>> _pendingOperations; | 207 List<List<int>> _pendingOperations; |
| 208 | 208 |
| 209 Function _onNoPendingWrites; | 209 Function _onNoPendingWrites; |
| 210 Function _onClosed; | 210 Function _onClosed; |
| 211 } | 211 } |
| 212 | 212 |
| 213 | 213 |
| 214 // Helper class containing static file helper methods. | 214 // Helper class containing static file helper methods. |
| 215 class _FileUtils { | 215 class _FileUtils { |
| 216 static final kExistsRequest = 0; | 216 static final EXISTS_REQUEST = 0; |
| 217 static final kCreateRequest = 1; | 217 static final CREATE_REQUEST = 1; |
| 218 static final kDeleteRequest = 2; | 218 static final DELETE_REQUEST = 2; |
| 219 static final kOpenRequest = 3; | 219 static final OPEN_REQUEST = 3; |
| 220 static final kFullPathRequest = 4; | 220 static final FULL_PATH_REQUEST = 4; |
| 221 static final kDirectoryRequest = 5; | 221 static final DIRECTORY_REQUEST = 5; |
| 222 static final kCloseRequest = 6; | 222 static final CLOSE_REQUEST = 6; |
| 223 static final kPositionRequest = 7; | 223 static final POSITION_REQUEST = 7; |
| 224 static final kSetPositionRequest = 8; | 224 static final SET_POSITION_REQUEST = 8; |
| 225 static final kTruncateRequest = 9; | 225 static final TRUNCATE_REQUEST = 9; |
| 226 static final kLengthRequest = 10; | 226 static final LENGTH_REQUEST = 10; |
| 227 static final kLengthFromNameRequest = 11; | 227 static final LENGTH_FROM_NAME_REQUEST = 11; |
| 228 static final kFlushRequest = 12; | 228 static final LAST_MODIFIED_REQUEST = 12; |
| 229 static final kReadByteRequest = 13; | 229 static final FLUSH_REQUEST = 13; |
| 230 static final kWriteByteRequest = 14; | 230 static final READ_BYTE_REQUEST = 14; |
| 231 static final kReadListRequest = 15; | 231 static final WRITE_BYTE_REQUEST = 15; |
| 232 static final kWriteListRequest = 16; | 232 static final READ_LIST_REQUEST = 16; |
| 233 static final kWriteStringRequest = 17; | 233 static final WRITE_LIST_REQUEST = 17; |
| 234 static final WRITE_STRING_REQUEST = 18; |
| 234 | 235 |
| 235 static final kSuccessResponse = 0; | 236 static final SUCCESS_RESPONSE = 0; |
| 236 static final kIllegalArgumentResponse = 1; | 237 static final ILLEGAL_ARGUMENT_RESPONSE = 1; |
| 237 static final kOSErrorResponse = 2; | 238 static final OSERROR_RESPONSE = 2; |
| 238 static final kFileClosedResponse = 3; | 239 static final FILE_CLOSED_RESPONSE = 3; |
| 239 | 240 |
| 240 static final kErrorResponseErrorType = 0; | 241 static final ERROR_RESPONSE_ERROR_TYPE = 0; |
| 241 static final kOSErrorResponseErrorCode = 1; | 242 static final OSERROR_RESPONSE_ERROR_CODE = 1; |
| 242 static final kOSErrorResponseMessage = 2; | 243 static final OSERROR_RESPONSE_MESSAGE = 2; |
| 243 | 244 |
| 244 static List ensureFastAndSerializableBuffer( | 245 static List ensureFastAndSerializableBuffer( |
| 245 List buffer, int offset, int bytes) { | 246 List buffer, int offset, int bytes) { |
| 246 // When using the Dart C API to access raw data, using a ByteArray is | 247 // When using the Dart C API to access raw data, using a ByteArray is |
| 247 // currently much faster. This function will make a copy of the | 248 // currently much faster. This function will make a copy of the |
| 248 // supplied List to a ByteArray if it isn't already. | 249 // supplied List to a ByteArray if it isn't already. |
| 249 List outBuffer; | 250 List outBuffer; |
| 250 int outOffset = offset; | 251 int outOffset = offset; |
| 251 if (buffer is Uint8List || buffer is ObjectArray) { | 252 if (buffer is Uint8List || buffer is ObjectArray) { |
| 252 outBuffer = buffer; | 253 outBuffer = buffer; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 267 return [outBuffer, outOffset]; | 268 return [outBuffer, outOffset]; |
| 268 } | 269 } |
| 269 | 270 |
| 270 static exists(String name) native "File_Exists"; | 271 static exists(String name) native "File_Exists"; |
| 271 static open(String name, int mode) native "File_Open"; | 272 static open(String name, int mode) native "File_Open"; |
| 272 static create(String name) native "File_Create"; | 273 static create(String name) native "File_Create"; |
| 273 static delete(String name) native "File_Delete"; | 274 static delete(String name) native "File_Delete"; |
| 274 static fullPath(String name) native "File_FullPath"; | 275 static fullPath(String name) native "File_FullPath"; |
| 275 static directory(String name) native "File_Directory"; | 276 static directory(String name) native "File_Directory"; |
| 276 static lengthFromName(String name) native "File_LengthFromName"; | 277 static lengthFromName(String name) native "File_LengthFromName"; |
| 278 static lastModified(String name) native "File_LastModified"; |
| 277 static int close(int id) native "File_Close"; | 279 static int close(int id) native "File_Close"; |
| 278 static readByte(int id) native "File_ReadByte"; | 280 static readByte(int id) native "File_ReadByte"; |
| 279 static readList(int id, List<int> buffer, int offset, int bytes) | 281 static readList(int id, List<int> buffer, int offset, int bytes) |
| 280 native "File_ReadList"; | 282 native "File_ReadList"; |
| 281 static writeByte(int id, int value) native "File_WriteByte"; | 283 static writeByte(int id, int value) native "File_WriteByte"; |
| 282 static writeList(int id, List<int> buffer, int offset, int bytes) { | 284 static writeList(int id, List<int> buffer, int offset, int bytes) { |
| 283 List result = | 285 List result = |
| 284 _FileUtils.ensureFastAndSerializableBuffer(buffer, offset, bytes); | 286 _FileUtils.ensureFastAndSerializableBuffer(buffer, offset, bytes); |
| 285 List outBuffer = result[0]; | 287 List outBuffer = result[0]; |
| 286 int outOffset = result[1]; | 288 int outOffset = result[1]; |
| 287 return writeListNative(id, outBuffer, outOffset, bytes); | 289 return writeListNative(id, outBuffer, outOffset, bytes); |
| 288 } | 290 } |
| 289 static writeListNative(int id, List<int> buffer, int offset, int bytes) | 291 static writeListNative(int id, List<int> buffer, int offset, int bytes) |
| 290 native "File_WriteList"; | 292 native "File_WriteList"; |
| 291 static writeString(int id, String string) native "File_WriteString"; | 293 static writeString(int id, String string) native "File_WriteString"; |
| 292 static position(int id) native "File_Position"; | 294 static position(int id) native "File_Position"; |
| 293 static setPosition(int id, int position) native "File_SetPosition"; | 295 static setPosition(int id, int position) native "File_SetPosition"; |
| 294 static truncate(int id, int length) native "File_Truncate"; | 296 static truncate(int id, int length) native "File_Truncate"; |
| 295 static length(int id) native "File_Length"; | 297 static length(int id) native "File_Length"; |
| 296 static flush(int id) native "File_Flush"; | 298 static flush(int id) native "File_Flush"; |
| 297 static int openStdio(int fd) native "File_OpenStdio"; | 299 static int openStdio(int fd) native "File_OpenStdio"; |
| 298 static SendPort newServicePort() native "File_NewServicePort"; | 300 static SendPort newServicePort() native "File_NewServicePort"; |
| 299 | 301 |
| 300 static bool checkedExists(String name) { | 302 static bool checkedExists(String name) { |
| 301 if (name is !String) { | 303 if (name is !String) throw new IllegalArgumentException(); |
| 302 throw new IllegalArgumentException(); | |
| 303 } | |
| 304 var result = exists(name); | 304 var result = exists(name); |
| 305 if (result is OSError) { | 305 throwIfError(result, "Cannot check existence of file '$name'"); |
| 306 throw new FileIOException("Cannot check existence of file '$name'", | |
| 307 result); | |
| 308 } | |
| 309 return result; | 306 return result; |
| 310 } | 307 } |
| 311 | 308 |
| 312 static int checkedOpen(String name, int mode) { | 309 static int checkedOpen(String name, int mode) { |
| 313 if (name is !String || mode is !int) { | 310 if (name is !String || mode is !int) throw new IllegalArgumentException(); |
| 314 throw new IllegalArgumentException(); | |
| 315 }; | |
| 316 var result = open(name, mode); | 311 var result = open(name, mode); |
| 317 if (result is OSError) { | 312 throwIfError(result, "Cannot open file '$name'"); |
| 318 throw new FileIOException("Cannot open file '$name'", result); | |
| 319 } | |
| 320 return result; | 313 return result; |
| 321 } | 314 } |
| 322 | 315 |
| 323 static bool checkedCreate(String name) { | 316 static bool checkedCreate(String name) { |
| 324 if (name is !String) { | 317 if (name is !String) throw new IllegalArgumentException(); |
| 325 throw new IllegalArgumentException(); | |
| 326 }; | |
| 327 var result = create(name); | 318 var result = create(name); |
| 328 if (result is OSError) { | 319 throwIfError(result, "Cannot create file '$name'"); |
| 329 throw new FileIOException("Cannot create file '$name'", result); | |
| 330 } | |
| 331 return true; | 320 return true; |
| 332 } | 321 } |
| 333 | 322 |
| 334 static bool checkedDelete(String name) { | 323 static bool checkedDelete(String name) { |
| 335 if (name is !String) { | 324 if (name is !String) throw new IllegalArgumentException(); |
| 336 throw new IllegalArgumentException(); | |
| 337 }; | |
| 338 var result = delete(name); | 325 var result = delete(name); |
| 339 if (result is OSError) { | 326 throwIfError(result, "Cannot delete file '$name'"); |
| 340 throw new FileIOException("Cannot delete file '$name'", result); | |
| 341 } | |
| 342 return true; | 327 return true; |
| 343 } | 328 } |
| 344 | 329 |
| 345 static String checkedFullPath(String name) { | 330 static String checkedFullPath(String name) { |
| 346 if (name is !String) { | 331 if (name is !String) throw new IllegalArgumentException(); |
| 347 throw new IllegalArgumentException(); | |
| 348 }; | |
| 349 var result = fullPath(name); | 332 var result = fullPath(name); |
| 350 if (result is OSError) { | 333 throwIfError(result, "Cannot retrieve full path for file '$name'"); |
| 351 throw new FileIOException( | |
| 352 "Cannot retrieve full path for file '$name'", result); | |
| 353 } | |
| 354 return result; | 334 return result; |
| 355 } | 335 } |
| 356 | 336 |
| 357 static String checkedDirectory(String name) { | 337 static String checkedDirectory(String name) { |
| 358 if (name is !String) { | 338 if (name is !String) throw new IllegalArgumentException(); |
| 359 throw new IllegalArgumentException(); | |
| 360 } | |
| 361 var result = directory(name); | 339 var result = directory(name); |
| 362 if (result is OSError) { | 340 throwIfError(result, "Cannot retrieve directory for file '$name'"); |
| 363 throw new FileIOException( | |
| 364 "Cannot retrieve directory for file '$name'", result); | |
| 365 } | |
| 366 return result; | 341 return result; |
| 367 } | 342 } |
| 368 | 343 |
| 369 static int checkedLengthFromName(String name) { | 344 static int checkedLengthFromName(String name) { |
| 370 if (name is !String) { | 345 if (name is !String) throw new IllegalArgumentException(); |
| 371 throw new IllegalArgumentException(); | |
| 372 } | |
| 373 var result = lengthFromName(name); | 346 var result = lengthFromName(name); |
| 374 if (result is OSError) { | 347 throwIfError(result, "Cannot retrieve length of file '$name'"); |
| 375 throw new FileIOException( | |
| 376 "Cannot retrieve length of file '$name'", result); | |
| 377 } | |
| 378 return result; | 348 return result; |
| 379 } | 349 } |
| 380 | 350 |
| 351 static int checkedLastModified(String name) { |
| 352 if (name is !String) throw new IllegalArgumentException(); |
| 353 var result = lastModified(name); |
| 354 throwIfError(result, "Cannot retrieve modification time for file '$name'"); |
| 355 return result; |
| 356 } |
| 357 |
| 381 static int checkReadWriteListArguments(int length, int offset, int bytes) { | 358 static int checkReadWriteListArguments(int length, int offset, int bytes) { |
| 382 if (offset < 0) return offset; | 359 if (offset < 0) return offset; |
| 383 if (bytes < 0) return bytes; | 360 if (bytes < 0) return bytes; |
| 384 if ((offset + bytes) > length) return offset + bytes; | 361 if ((offset + bytes) > length) return offset + bytes; |
| 385 return 0; | 362 return 0; |
| 386 } | 363 } |
| 387 | 364 |
| 388 static int checkedWriteString(int id, String string) { | 365 static int checkedWriteString(int id, String string) { |
| 389 if (string is !String) return -1; | 366 if (string is !String) throw new IllegalArgumentException(); |
| 390 return writeString(id, string); | 367 return writeString(id, string); |
| 391 } | 368 } |
| 392 | 369 |
| 370 static throwIfError(Object result, String msg) { |
| 371 if (result is OSError) { |
| 372 throw new FileIOException(msg, result); |
| 373 } |
| 374 } |
| 393 } | 375 } |
| 394 | 376 |
| 395 // Base class for _File and _RandomAccessFile with shared functions. | 377 // Base class for _File and _RandomAccessFile with shared functions. |
| 396 class _FileBase { | 378 class _FileBase { |
| 397 bool _isErrorResponse(response) { | 379 bool _isErrorResponse(response) { |
| 398 return response is List && response[0] != _FileUtils.kSuccessResponse; | 380 return response is List && response[0] != _FileUtils.SUCCESS_RESPONSE; |
| 399 } | 381 } |
| 400 | 382 |
| 401 Exception _exceptionFromResponse(response, String message) { | 383 Exception _exceptionFromResponse(response, String message) { |
| 402 assert(_isErrorResponse(response)); | 384 assert(_isErrorResponse(response)); |
| 403 switch (response[_FileUtils.kErrorResponseErrorType]) { | 385 switch (response[_FileUtils.ERROR_RESPONSE_ERROR_TYPE]) { |
| 404 case _FileUtils.kIllegalArgumentResponse: | 386 case _FileUtils.ILLEGAL_ARGUMENT_RESPONSE: |
| 405 return new IllegalArgumentException(); | 387 return new IllegalArgumentException(); |
| 406 case _FileUtils.kOSErrorResponse: | 388 case _FileUtils.OSERROR_RESPONSE: |
| 407 var err = new OSError(response[_FileUtils.kOSErrorResponseMessage], | 389 var err = new OSError(response[_FileUtils.OSERROR_RESPONSE_MESSAGE], |
| 408 response[_FileUtils.kOSErrorResponseErrorCode]); | 390 response[_FileUtils.OSERROR_RESPONSE_ERROR_CODE]); |
| 409 return new FileIOException(message, err); | 391 return new FileIOException(message, err); |
| 410 case _FileUtils.kFileClosedResponse: | 392 case _FileUtils.FILE_CLOSED_RESPONSE: |
| 411 return new FileIOException("File closed"); | 393 return new FileIOException("File closed"); |
| 412 default: | 394 default: |
| 413 return new Exception("Unknown error"); | 395 return new Exception("Unknown error"); |
| 414 } | 396 } |
| 415 } | 397 } |
| 416 } | 398 } |
| 417 | 399 |
| 418 // Class for encapsulating the native implementation of files. | 400 // Class for encapsulating the native implementation of files. |
| 419 class _File extends _FileBase implements File { | 401 class _File extends _FileBase implements File { |
| 420 // Constructor for file. | 402 // Constructor for file. |
| 421 _File(String this._name); | 403 _File(String this._name); |
| 422 | 404 |
| 423 Future<bool> exists() { | 405 Future<bool> exists() { |
| 424 _ensureFileService(); | 406 _ensureFileService(); |
| 425 List request = new List(2); | 407 List request = new List(2); |
| 426 request[0] = _FileUtils.kExistsRequest; | 408 request[0] = _FileUtils.EXISTS_REQUEST; |
| 427 request[1] = _name; | 409 request[1] = _name; |
| 428 return _fileService.call(request).transform((response) { | 410 return _fileService.call(request).transform((response) { |
| 429 if (_isErrorResponse(response)) { | 411 if (_isErrorResponse(response)) { |
| 430 throw _exceptionFromResponse(response, "Cannot open file '$_name'"); | 412 throw _exceptionFromResponse(response, "Cannot open file '$_name'"); |
| 431 } | 413 } |
| 432 return response; | 414 return response; |
| 433 }); | 415 }); |
| 434 } | 416 } |
| 435 | 417 |
| 436 bool existsSync() { | 418 bool existsSync() { |
| 437 return _FileUtils.checkedExists(_name); | 419 return _FileUtils.checkedExists(_name); |
| 438 } | 420 } |
| 439 | 421 |
| 440 Future<File> create() { | 422 Future<File> create() { |
| 441 _ensureFileService(); | 423 _ensureFileService(); |
| 442 List request = new List(2); | 424 List request = new List(2); |
| 443 request[0] = _FileUtils.kCreateRequest; | 425 request[0] = _FileUtils.CREATE_REQUEST; |
| 444 request[1] = _name; | 426 request[1] = _name; |
| 445 return _fileService.call(request).transform((response) { | 427 return _fileService.call(request).transform((response) { |
| 446 if (_isErrorResponse(response)) { | 428 if (_isErrorResponse(response)) { |
| 447 throw _exceptionFromResponse(response, "Cannot create file '$_name'"); | 429 throw _exceptionFromResponse(response, "Cannot create file '$_name'"); |
| 448 } | 430 } |
| 449 return this; | 431 return this; |
| 450 }); | 432 }); |
| 451 } | 433 } |
| 452 | 434 |
| 453 void createSync() { | 435 void createSync() { |
| 454 bool created = _FileUtils.checkedCreate(_name); | 436 bool created = _FileUtils.checkedCreate(_name); |
| 455 if (!created) { | 437 if (!created) { |
| 456 throw new FileIOException("Cannot create file '$_name'"); | 438 throw new FileIOException("Cannot create file '$_name'"); |
| 457 } | 439 } |
| 458 } | 440 } |
| 459 | 441 |
| 460 Future<File> delete() { | 442 Future<File> delete() { |
| 461 _ensureFileService(); | 443 _ensureFileService(); |
| 462 List request = new List(2); | 444 List request = new List(2); |
| 463 request[0] = _FileUtils.kDeleteRequest; | 445 request[0] = _FileUtils.DELETE_REQUEST; |
| 464 request[1] = _name; | 446 request[1] = _name; |
| 465 return _fileService.call(request).transform((response) { | 447 return _fileService.call(request).transform((response) { |
| 466 if (_isErrorResponse(response)) { | 448 if (_isErrorResponse(response)) { |
| 467 throw _exceptionFromResponse(response, "Cannot delete file '$_name'"); | 449 throw _exceptionFromResponse(response, "Cannot delete file '$_name'"); |
| 468 } | 450 } |
| 469 return this; | 451 return this; |
| 470 }); | 452 }); |
| 471 } | 453 } |
| 472 | 454 |
| 473 void deleteSync() { | 455 void deleteSync() { |
| 474 _FileUtils.checkedDelete(_name); | 456 _FileUtils.checkedDelete(_name); |
| 475 } | 457 } |
| 476 | 458 |
| 477 Future<Directory> directory() { | 459 Future<Directory> directory() { |
| 478 _ensureFileService(); | 460 _ensureFileService(); |
| 479 List request = new List(2); | 461 List request = new List(2); |
| 480 request[0] = _FileUtils.kDirectoryRequest; | 462 request[0] = _FileUtils.DIRECTORY_REQUEST; |
| 481 request[1] = _name; | 463 request[1] = _name; |
| 482 return _fileService.call(request).transform((response) { | 464 return _fileService.call(request).transform((response) { |
| 483 if (_isErrorResponse(response)) { | 465 if (_isErrorResponse(response)) { |
| 484 throw _exceptionFromResponse(response, | 466 throw _exceptionFromResponse(response, |
| 485 "Cannot retrieve directory for " | 467 "Cannot retrieve directory for " |
| 486 "file '$_name'"); | 468 "file '$_name'"); |
| 487 } | 469 } |
| 488 return new Directory(response); | 470 return new Directory(response); |
| 489 }); | 471 }); |
| 490 } | 472 } |
| 491 | 473 |
| 492 Directory directorySync() { | 474 Directory directorySync() { |
| 493 _FileUtils.checkedDirectory(_name); | 475 _FileUtils.checkedDirectory(_name); |
| 494 return new Directory(_FileUtils.directory(_name)); | 476 return new Directory(_FileUtils.directory(_name)); |
| 495 } | 477 } |
| 496 | 478 |
| 497 Future<RandomAccessFile> open([FileMode mode = FileMode.READ]) { | 479 Future<RandomAccessFile> open([FileMode mode = FileMode.READ]) { |
| 498 _ensureFileService(); | 480 _ensureFileService(); |
| 499 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); | 481 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| 500 if (mode != FileMode.READ && | 482 if (mode != FileMode.READ && |
| 501 mode != FileMode.WRITE && | 483 mode != FileMode.WRITE && |
| 502 mode != FileMode.APPEND) { | 484 mode != FileMode.APPEND) { |
| 503 new Timer(0, (t) { | 485 new Timer(0, (t) { |
| 504 completer.completeException(new IllegalArgumentException()); | 486 completer.completeException(new IllegalArgumentException()); |
| 505 }); | 487 }); |
| 506 return completer.future; | 488 return completer.future; |
| 507 } | 489 } |
| 508 List request = new List(3); | 490 List request = new List(3); |
| 509 request[0] = _FileUtils.kOpenRequest; | 491 request[0] = _FileUtils.OPEN_REQUEST; |
| 510 request[1] = _name; | 492 request[1] = _name; |
| 511 request[2] = mode._mode; // Direct int value for serialization. | 493 request[2] = mode._mode; // Direct int value for serialization. |
| 512 return _fileService.call(request).transform((response) { | 494 return _fileService.call(request).transform((response) { |
| 513 if (_isErrorResponse(response)) { | 495 if (_isErrorResponse(response)) { |
| 514 throw _exceptionFromResponse(response, "Cannot open file '$_name'"); | 496 throw _exceptionFromResponse(response, "Cannot open file '$_name'"); |
| 515 } | 497 } |
| 516 return new _RandomAccessFile(response, _name); | 498 return new _RandomAccessFile(response, _name); |
| 517 }); | 499 }); |
| 518 } | 500 } |
| 519 | 501 |
| 520 Future<int> length() { | 502 Future<int> length() { |
| 521 _ensureFileService(); | 503 _ensureFileService(); |
| 522 List request = new List(2); | 504 List request = new List(2); |
| 523 request[0] = _FileUtils.kLengthFromNameRequest; | 505 request[0] = _FileUtils.LENGTH_FROM_NAME_REQUEST; |
| 524 request[1] = _name; | 506 request[1] = _name; |
| 525 return _fileService.call(request).transform((response) { | 507 return _fileService.call(request).transform((response) { |
| 526 if (_isErrorResponse(response)) { | 508 if (_isErrorResponse(response)) { |
| 527 throw _exceptionFromResponse(response, | 509 throw _exceptionFromResponse(response, |
| 528 "Cannot retrieve length of " | 510 "Cannot retrieve length of " |
| 529 "file '$_name'"); | 511 "file '$_name'"); |
| 530 } | 512 } |
| 531 return response; | 513 return response; |
| 532 }); | 514 }); |
| 533 } | 515 } |
| 534 | 516 |
| 535 int lengthSync() { | 517 int lengthSync() { |
| 536 var result = _FileUtils.checkedLengthFromName(_name); | 518 return _FileUtils.checkedLengthFromName(_name); |
| 537 if (result is OSError) { | 519 } |
| 538 throw new FileIOException("Cannot retrieve length of file '$_name'", | 520 |
| 539 result); | 521 Future<Date> lastModified() { |
| 540 } | 522 _ensureFileService(); |
| 541 return result; | 523 List request = new List(2); |
| 524 request[0] = _FileUtils.LAST_MODIFIED_REQUEST; |
| 525 request[1] = _name; |
| 526 return _fileService.call(request).transform((response) { |
| 527 if (_isErrorResponse(response)) { |
| 528 throw _exceptionFromResponse(response, |
| 529 "Cannot retrieve modification time " |
| 530 "for file '$_name'"); |
| 531 } |
| 532 return new Date.fromEpoch(response); |
| 533 }); |
| 534 } |
| 535 |
| 536 Date lastModifiedSync() { |
| 537 return new Date.fromEpoch(_FileUtils.checkedLastModified(_name)); |
| 542 } | 538 } |
| 543 | 539 |
| 544 RandomAccessFile openSync([FileMode mode = FileMode.READ]) { | 540 RandomAccessFile openSync([FileMode mode = FileMode.READ]) { |
| 545 if (mode != FileMode.READ && | 541 if (mode != FileMode.READ && |
| 546 mode != FileMode.WRITE && | 542 mode != FileMode.WRITE && |
| 547 mode != FileMode.APPEND) { | 543 mode != FileMode.APPEND) { |
| 548 throw new FileIOException("Unknown file mode. Use FileMode.READ, " | 544 throw new FileIOException("Unknown file mode. Use FileMode.READ, " |
| 549 "FileMode.WRITE or FileMode.APPEND."); | 545 "FileMode.WRITE or FileMode.APPEND."); |
| 550 } | 546 } |
| 551 var id = _FileUtils.checkedOpen(_name, mode._mode); | 547 var id = _FileUtils.checkedOpen(_name, mode._mode); |
| 552 assert(id != 0); | 548 assert(id != 0); |
| 553 return new _RandomAccessFile(id, _name); | 549 return new _RandomAccessFile(id, _name); |
| 554 } | 550 } |
| 555 | 551 |
| 556 static RandomAccessFile _openStdioSync(int fd) { | 552 static RandomAccessFile _openStdioSync(int fd) { |
| 557 var id = _FileUtils.openStdio(fd); | 553 var id = _FileUtils.openStdio(fd); |
| 558 if (id == 0) { | 554 if (id == 0) { |
| 559 throw new FileIOException("Cannot open stdio file for: $fd"); | 555 throw new FileIOException("Cannot open stdio file for: $fd"); |
| 560 } | 556 } |
| 561 return new _RandomAccessFile(id, ""); | 557 return new _RandomAccessFile(id, ""); |
| 562 } | 558 } |
| 563 | 559 |
| 564 Future<String> fullPath() { | 560 Future<String> fullPath() { |
| 565 _ensureFileService(); | 561 _ensureFileService(); |
| 566 List request = new List(2); | 562 List request = new List(2); |
| 567 request[0] = _FileUtils.kFullPathRequest; | 563 request[0] = _FileUtils.FULL_PATH_REQUEST; |
| 568 request[1] = _name; | 564 request[1] = _name; |
| 569 return _fileService.call(request).transform((response) { | 565 return _fileService.call(request).transform((response) { |
| 570 if (_isErrorResponse(response)) { | 566 if (_isErrorResponse(response)) { |
| 571 throw _exceptionFromResponse(response, | 567 throw _exceptionFromResponse(response, |
| 572 "Cannot retrieve full path" | 568 "Cannot retrieve full path" |
| 573 " for '$_name'"); | 569 " for '$_name'"); |
| 574 } | 570 } |
| 575 return response; | 571 return response; |
| 576 }); | 572 }); |
| 577 } | 573 } |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 685 | 681 |
| 686 | 682 |
| 687 class _RandomAccessFile extends _FileBase implements RandomAccessFile { | 683 class _RandomAccessFile extends _FileBase implements RandomAccessFile { |
| 688 _RandomAccessFile(int this._id, String this._name); | 684 _RandomAccessFile(int this._id, String this._name); |
| 689 | 685 |
| 690 Future<RandomAccessFile> close() { | 686 Future<RandomAccessFile> close() { |
| 691 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); | 687 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| 692 if (_isClosed) return _completeWithClosedException(completer); | 688 if (_isClosed) return _completeWithClosedException(completer); |
| 693 _ensureFileService(); | 689 _ensureFileService(); |
| 694 List request = new List(2); | 690 List request = new List(2); |
| 695 request[0] = _FileUtils.kCloseRequest; | 691 request[0] = _FileUtils.CLOSE_REQUEST; |
| 696 request[1] = _id; | 692 request[1] = _id; |
| 697 // Set the id_ to 0 (NULL) to ensure the no more async requests | 693 // Set the id_ to 0 (NULL) to ensure the no more async requests |
| 698 // can be issues for this file. | 694 // can be issues for this file. |
| 699 _id = 0; | 695 _id = 0; |
| 700 return _fileService.call(request).transform((result) { | 696 return _fileService.call(request).transform((result) { |
| 701 if (result != -1) { | 697 if (result != -1) { |
| 702 _id = result; | 698 _id = result; |
| 703 return this; | 699 return this; |
| 704 } else { | 700 } else { |
| 705 throw new FileIOException("Cannot close file '$_name'"); | 701 throw new FileIOException("Cannot close file '$_name'"); |
| 706 } | 702 } |
| 707 }); | 703 }); |
| 708 } | 704 } |
| 709 | 705 |
| 710 void closeSync() { | 706 void closeSync() { |
| 711 var id = _FileUtils.close(_id); | 707 var id = _FileUtils.close(_id); |
| 712 if (id == -1) { | 708 if (id == -1) { |
| 713 throw new FileIOException("Cannot close file '$_name'"); | 709 throw new FileIOException("Cannot close file '$_name'"); |
| 714 } | 710 } |
| 715 _id = id; | 711 _id = id; |
| 716 } | 712 } |
| 717 | 713 |
| 718 Future<int> readByte() { | 714 Future<int> readByte() { |
| 719 _ensureFileService(); | 715 _ensureFileService(); |
| 720 Completer<int> completer = new Completer<int>(); | 716 Completer<int> completer = new Completer<int>(); |
| 721 if (_isClosed) return _completeWithClosedException(completer); | 717 if (_isClosed) return _completeWithClosedException(completer); |
| 722 List request = new List(2); | 718 List request = new List(2); |
| 723 request[0] = _FileUtils.kReadByteRequest; | 719 request[0] = _FileUtils.READ_BYTE_REQUEST; |
| 724 request[1] = _id; | 720 request[1] = _id; |
| 725 return _fileService.call(request).transform((response) { | 721 return _fileService.call(request).transform((response) { |
| 726 if (_isErrorResponse(response)) { | 722 if (_isErrorResponse(response)) { |
| 727 throw _exceptionFromResponse(response, | 723 throw _exceptionFromResponse(response, |
| 728 "readByte failed for file '$_name'"); | 724 "readByte failed for file '$_name'"); |
| 729 } | 725 } |
| 730 return response; | 726 return response; |
| 731 }); | 727 }); |
| 732 } | 728 } |
| 733 | 729 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 748 // handlers without getting exceptions when registering the | 744 // handlers without getting exceptions when registering the |
| 749 // then handler. | 745 // then handler. |
| 750 new Timer(0, (t) { | 746 new Timer(0, (t) { |
| 751 completer.completeException(new FileIOException( | 747 completer.completeException(new FileIOException( |
| 752 "Invalid arguments to readList for file '$_name'")); | 748 "Invalid arguments to readList for file '$_name'")); |
| 753 }); | 749 }); |
| 754 return completer.future; | 750 return completer.future; |
| 755 }; | 751 }; |
| 756 if (_isClosed) return _completeWithClosedException(completer); | 752 if (_isClosed) return _completeWithClosedException(completer); |
| 757 List request = new List(3); | 753 List request = new List(3); |
| 758 request[0] = _FileUtils.kReadListRequest; | 754 request[0] = _FileUtils.READ_LIST_REQUEST; |
| 759 request[1] = _id; | 755 request[1] = _id; |
| 760 request[2] = bytes; | 756 request[2] = bytes; |
| 761 return _fileService.call(request).transform((response) { | 757 return _fileService.call(request).transform((response) { |
| 762 if (_isErrorResponse(response)) { | 758 if (_isErrorResponse(response)) { |
| 763 throw _exceptionFromResponse(response, | 759 throw _exceptionFromResponse(response, |
| 764 "readList failed for file '$_name'"); | 760 "readList failed for file '$_name'"); |
| 765 } | 761 } |
| 766 var read = response[1]; | 762 var read = response[1]; |
| 767 var data = response[2]; | 763 var data = response[2]; |
| 768 buffer.setRange(offset, read, data); | 764 buffer.setRange(offset, read, data); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 798 // handlers without getting exceptions when registering the | 794 // handlers without getting exceptions when registering the |
| 799 // then handler. | 795 // then handler. |
| 800 new Timer(0, (t) { | 796 new Timer(0, (t) { |
| 801 completer.completeException(new FileIOException( | 797 completer.completeException(new FileIOException( |
| 802 "Invalid argument to writeByte for file '$_name'")); | 798 "Invalid argument to writeByte for file '$_name'")); |
| 803 }); | 799 }); |
| 804 return completer.future; | 800 return completer.future; |
| 805 } | 801 } |
| 806 if (_isClosed) return _completeWithClosedException(completer); | 802 if (_isClosed) return _completeWithClosedException(completer); |
| 807 List request = new List(3); | 803 List request = new List(3); |
| 808 request[0] = _FileUtils.kWriteByteRequest; | 804 request[0] = _FileUtils.WRITE_BYTE_REQUEST; |
| 809 request[1] = _id; | 805 request[1] = _id; |
| 810 request[2] = value; | 806 request[2] = value; |
| 811 return _fileService.call(request).transform((response) { | 807 return _fileService.call(request).transform((response) { |
| 812 if (_isErrorResponse(response)) { | 808 if (_isErrorResponse(response)) { |
| 813 throw _exceptionFromResponse(response, | 809 throw _exceptionFromResponse(response, |
| 814 "writeByte failed for file '$_name'"); | 810 "writeByte failed for file '$_name'"); |
| 815 } | 811 } |
| 816 return this; | 812 return this; |
| 817 }); | 813 }); |
| 818 } | 814 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 854 // Complete asynchronously so the user has a chance to setup | 850 // Complete asynchronously so the user has a chance to setup |
| 855 // handlers without getting exceptions when registering the | 851 // handlers without getting exceptions when registering the |
| 856 // then handler. | 852 // then handler. |
| 857 new Timer(0, (t) => completer.completeException(e)); | 853 new Timer(0, (t) => completer.completeException(e)); |
| 858 return completer.future; | 854 return completer.future; |
| 859 } | 855 } |
| 860 List outBuffer = result[0]; | 856 List outBuffer = result[0]; |
| 861 int outOffset = result[1]; | 857 int outOffset = result[1]; |
| 862 | 858 |
| 863 List request = new List(5); | 859 List request = new List(5); |
| 864 request[0] = _FileUtils.kWriteListRequest; | 860 request[0] = _FileUtils.WRITE_LIST_REQUEST; |
| 865 request[1] = _id; | 861 request[1] = _id; |
| 866 request[2] = outBuffer; | 862 request[2] = outBuffer; |
| 867 request[3] = outOffset; | 863 request[3] = outOffset; |
| 868 request[4] = bytes; | 864 request[4] = bytes; |
| 869 return _fileService.call(request).transform((response) { | 865 return _fileService.call(request).transform((response) { |
| 870 if (_isErrorResponse(response)) { | 866 if (_isErrorResponse(response)) { |
| 871 throw _exceptionFromResponse(response, | 867 throw _exceptionFromResponse(response, |
| 872 "writeList failed for file '$_name'"); | 868 "writeList failed for file '$_name'"); |
| 873 } | 869 } |
| 874 return this; | 870 return this; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 893 } | 889 } |
| 894 return result; | 890 return result; |
| 895 } | 891 } |
| 896 | 892 |
| 897 Future<RandomAccessFile> writeString(String string, | 893 Future<RandomAccessFile> writeString(String string, |
| 898 [Encoding encoding = Encoding.UTF_8]) { | 894 [Encoding encoding = Encoding.UTF_8]) { |
| 899 _ensureFileService(); | 895 _ensureFileService(); |
| 900 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); | 896 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| 901 if (_isClosed) return _completeWithClosedException(completer); | 897 if (_isClosed) return _completeWithClosedException(completer); |
| 902 List request = new List(3); | 898 List request = new List(3); |
| 903 request[0] = _FileUtils.kWriteStringRequest; | 899 request[0] = _FileUtils.WRITE_STRING_REQUEST; |
| 904 request[1] = _id; | 900 request[1] = _id; |
| 905 request[2] = string; | 901 request[2] = string; |
| 906 return _fileService.call(request).transform((response) { | 902 return _fileService.call(request).transform((response) { |
| 907 if (_isErrorResponse(response)) { | 903 if (_isErrorResponse(response)) { |
| 908 throw _exceptionFromResponse(response, | 904 throw _exceptionFromResponse(response, |
| 909 "writeString failed for file '$_name'"); | 905 "writeString failed for file '$_name'"); |
| 910 } | 906 } |
| 911 return this; | 907 return this; |
| 912 }); | 908 }); |
| 913 } | 909 } |
| 914 | 910 |
| 915 int writeStringSync(String string, [Encoding encoding = Encoding.UTF_8]) { | 911 int writeStringSync(String string, [Encoding encoding = Encoding.UTF_8]) { |
| 916 _checkNotClosed(); | 912 _checkNotClosed(); |
| 917 var result = _FileUtils.checkedWriteString(_id, string); | 913 var result = _FileUtils.checkedWriteString(_id, string); |
| 918 if (result is OSError) { | 914 if (result is OSError) { |
| 919 throw new FileIOException("writeString failed for file '$_name'", result); | 915 throw new FileIOException("writeString failed for file '$_name'"); |
| 920 } | 916 } |
| 921 return result; | 917 return result; |
| 922 } | 918 } |
| 923 | 919 |
| 924 Future<int> position() { | 920 Future<int> position() { |
| 925 _ensureFileService(); | 921 _ensureFileService(); |
| 926 Completer<int> completer = new Completer<int>(); | 922 Completer<int> completer = new Completer<int>(); |
| 927 if (_isClosed) return _completeWithClosedException(completer); | 923 if (_isClosed) return _completeWithClosedException(completer); |
| 928 List request = new List(2); | 924 List request = new List(2); |
| 929 request[0] = _FileUtils.kPositionRequest; | 925 request[0] = _FileUtils.POSITION_REQUEST; |
| 930 request[1] = _id; | 926 request[1] = _id; |
| 931 return _fileService.call(request).transform((response) { | 927 return _fileService.call(request).transform((response) { |
| 932 if (_isErrorResponse(response)) { | 928 if (_isErrorResponse(response)) { |
| 933 throw _exceptionFromResponse(response, | 929 throw _exceptionFromResponse(response, |
| 934 "position failed for file '$_name'"); | 930 "position failed for file '$_name'"); |
| 935 } | 931 } |
| 936 return response; | 932 return response; |
| 937 }); | 933 }); |
| 938 } | 934 } |
| 939 | 935 |
| 940 int positionSync() { | 936 int positionSync() { |
| 941 _checkNotClosed(); | 937 _checkNotClosed(); |
| 942 var result = _FileUtils.position(_id); | 938 var result = _FileUtils.position(_id); |
| 943 if (result is OSError) { | 939 if (result is OSError) { |
| 944 throw new FileIOException("position failed for file '$_name'", result); | 940 throw new FileIOException("position failed for file '$_name'", result); |
| 945 } | 941 } |
| 946 return result; | 942 return result; |
| 947 } | 943 } |
| 948 | 944 |
| 949 Future<RandomAccessFile> setPosition(int position) { | 945 Future<RandomAccessFile> setPosition(int position) { |
| 950 _ensureFileService(); | 946 _ensureFileService(); |
| 951 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); | 947 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| 952 if (_isClosed) return _completeWithClosedException(completer); | 948 if (_isClosed) return _completeWithClosedException(completer); |
| 953 List request = new List(3); | 949 List request = new List(3); |
| 954 request[0] = _FileUtils.kSetPositionRequest; | 950 request[0] = _FileUtils.SET_POSITION_REQUEST; |
| 955 request[1] = _id; | 951 request[1] = _id; |
| 956 request[2] = position; | 952 request[2] = position; |
| 957 return _fileService.call(request).transform((response) { | 953 return _fileService.call(request).transform((response) { |
| 958 if (_isErrorResponse(response)) { | 954 if (_isErrorResponse(response)) { |
| 959 throw _exceptionFromResponse(response, | 955 throw _exceptionFromResponse(response, |
| 960 "setPosition failed for file '$_name'"); | 956 "setPosition failed for file '$_name'"); |
| 961 } | 957 } |
| 962 return this; | 958 return this; |
| 963 }); | 959 }); |
| 964 } | 960 } |
| 965 | 961 |
| 966 void setPositionSync(int position) { | 962 void setPositionSync(int position) { |
| 967 _checkNotClosed(); | 963 _checkNotClosed(); |
| 968 var result = _FileUtils.setPosition(_id, position); | 964 var result = _FileUtils.setPosition(_id, position); |
| 969 if (result is OSError) { | 965 if (result is OSError) { |
| 970 throw new FileIOException("setPosition failed for file '$_name'", result); | 966 throw new FileIOException("setPosition failed for file '$_name'", result); |
| 971 } | 967 } |
| 972 } | 968 } |
| 973 | 969 |
| 974 Future<RandomAccessFile> truncate(int length) { | 970 Future<RandomAccessFile> truncate(int length) { |
| 975 _ensureFileService(); | 971 _ensureFileService(); |
| 976 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); | 972 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| 977 if (_isClosed) return _completeWithClosedException(completer); | 973 if (_isClosed) return _completeWithClosedException(completer); |
| 978 List request = new List(3); | 974 List request = new List(3); |
| 979 request[0] = _FileUtils.kTruncateRequest; | 975 request[0] = _FileUtils.TRUNCATE_REQUEST; |
| 980 request[1] = _id; | 976 request[1] = _id; |
| 981 request[2] = length; | 977 request[2] = length; |
| 982 return _fileService.call(request).transform((response) { | 978 return _fileService.call(request).transform((response) { |
| 983 if (_isErrorResponse(response)) { | 979 if (_isErrorResponse(response)) { |
| 984 throw _exceptionFromResponse(response, | 980 throw _exceptionFromResponse(response, |
| 985 "truncate failed for file '$_name'"); | 981 "truncate failed for file '$_name'"); |
| 986 } | 982 } |
| 987 return this; | 983 return this; |
| 988 }); | 984 }); |
| 989 } | 985 } |
| 990 | 986 |
| 991 void truncateSync(int length) { | 987 void truncateSync(int length) { |
| 992 _checkNotClosed(); | 988 _checkNotClosed(); |
| 993 var result = _FileUtils.truncate(_id, length); | 989 var result = _FileUtils.truncate(_id, length); |
| 994 if (result is OSError) { | 990 if (result is OSError) { |
| 995 throw new FileIOException("truncate failed for file '$_name'", result); | 991 throw new FileIOException("truncate failed for file '$_name'", result); |
| 996 } | 992 } |
| 997 } | 993 } |
| 998 | 994 |
| 999 Future<int> length() { | 995 Future<int> length() { |
| 1000 _ensureFileService(); | 996 _ensureFileService(); |
| 1001 Completer<int> completer = new Completer<int>(); | 997 Completer<int> completer = new Completer<int>(); |
| 1002 if (_isClosed) return _completeWithClosedException(completer); | 998 if (_isClosed) return _completeWithClosedException(completer); |
| 1003 List request = new List(2); | 999 List request = new List(2); |
| 1004 request[0] = _FileUtils.kLengthRequest; | 1000 request[0] = _FileUtils.LENGTH_REQUEST; |
| 1005 request[1] = _id; | 1001 request[1] = _id; |
| 1006 return _fileService.call(request).transform((response) { | 1002 return _fileService.call(request).transform((response) { |
| 1007 if (_isErrorResponse(response)) { | 1003 if (_isErrorResponse(response)) { |
| 1008 throw _exceptionFromResponse(response, | 1004 throw _exceptionFromResponse(response, |
| 1009 "length failed for file '$_name'"); | 1005 "length failed for file '$_name'"); |
| 1010 } | 1006 } |
| 1011 return response; | 1007 return response; |
| 1012 }); | 1008 }); |
| 1013 } | 1009 } |
| 1014 | 1010 |
| 1015 int lengthSync() { | 1011 int lengthSync() { |
| 1016 _checkNotClosed(); | 1012 _checkNotClosed(); |
| 1017 var result = _FileUtils.length(_id); | 1013 var result = _FileUtils.length(_id); |
| 1018 if (result is OSError) { | 1014 if (result is OSError) { |
| 1019 throw new FileIOException("length failed for file '$_name'", result); | 1015 throw new FileIOException("length failed for file '$_name'", result); |
| 1020 } | 1016 } |
| 1021 return result; | 1017 return result; |
| 1022 } | 1018 } |
| 1023 | 1019 |
| 1024 Future<RandomAccessFile> flush() { | 1020 Future<RandomAccessFile> flush() { |
| 1025 _ensureFileService(); | 1021 _ensureFileService(); |
| 1026 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); | 1022 Completer<RandomAccessFile> completer = new Completer<RandomAccessFile>(); |
| 1027 if (_isClosed) return _completeWithClosedException(completer); | 1023 if (_isClosed) return _completeWithClosedException(completer); |
| 1028 List request = new List(2); | 1024 List request = new List(2); |
| 1029 request[0] = _FileUtils.kFlushRequest; | 1025 request[0] = _FileUtils.FLUSH_REQUEST; |
| 1030 request[1] = _id; | 1026 request[1] = _id; |
| 1031 return _fileService.call(request).transform((response) { | 1027 return _fileService.call(request).transform((response) { |
| 1032 if (_isErrorResponse(response)) { | 1028 if (_isErrorResponse(response)) { |
| 1033 throw _exceptionFromResponse(response, | 1029 throw _exceptionFromResponse(response, |
| 1034 "flush failed for file '$_name'"); | 1030 "flush failed for file '$_name'"); |
| 1035 } | 1031 } |
| 1036 return this; | 1032 return this; |
| 1037 }); | 1033 }); |
| 1038 } | 1034 } |
| 1039 | 1035 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1067 new FileIOException("File closed '$_name'")); | 1063 new FileIOException("File closed '$_name'")); |
| 1068 }); | 1064 }); |
| 1069 return completer.future; | 1065 return completer.future; |
| 1070 } | 1066 } |
| 1071 | 1067 |
| 1072 final String _name; | 1068 final String _name; |
| 1073 int _id; | 1069 int _id; |
| 1074 | 1070 |
| 1075 SendPort _fileService; | 1071 SendPort _fileService; |
| 1076 } | 1072 } |
| OLD | NEW |