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

Side by Side Diff: packages/glob/lib/src/list_tree.dart

Issue 3014633002: Roll to pickup pool changes (Closed)
Patch Set: Created 3 years, 2 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 | « packages/glob/lib/src/ast.dart ('k') | packages/glob/lib/src/parser.dart » ('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) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 import 'dart:async';
5 import 'dart:io'; 6 import 'dart:io';
6 import 'dart:async';
7 7
8 import 'package:async/async.dart'; 8 import 'package:async/async.dart';
9 import 'package:path/path.dart' as p; 9 import 'package:path/path.dart' as p;
10 10
11 import 'ast.dart'; 11 import 'ast.dart';
12 import 'utils.dart'; 12 import 'utils.dart';
13 13
14 /// The errno for a file or directory not existing on Mac and Linux. 14 /// The errno for a file or directory not existing on Mac and Linux.
15 const _ENOENT = 2; 15 const _ENOENT = 2;
16 16
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 // On the other hand if there are more components, add [component] 136 // On the other hand if there are more components, add [component]
137 // to [parent]'s children and not its validator. Since we process 137 // to [parent]'s children and not its validator. Since we process
138 // each option's components separately, the same component is never 138 // each option's components separately, the same component is never
139 // both a validator and a child. 139 // both a validator and a child.
140 if (!parent.children.containsKey(component)) { 140 if (!parent.children.containsKey(component)) {
141 parent.children[component] = new _ListTreeNode(); 141 parent.children[component] = new _ListTreeNode();
142 } 142 }
143 parent = parent.children[component]; 143 parent = parent.children[component];
144 } 144 }
145 } else if (recursive) { 145 } else if (recursive) {
146 _trees[root] = new _ListTreeNode.recursive( 146 _trees[root] =
147 _join(components.sublist(i))); 147 new _ListTreeNode.recursive(_join(components.sublist(i)));
148 return; 148 return;
149 } else if (complete) { 149 } else if (complete) {
150 _trees[root] = new _ListTreeNode()..addOption(component); 150 _trees[root] = new _ListTreeNode()..addOption(component);
151 } else { 151 } else {
152 _trees[root] = new _ListTreeNode(); 152 _trees[root] = new _ListTreeNode();
153 _trees[root].children[component] = new _ListTreeNode(); 153 _trees[root].children[component] = new _ListTreeNode();
154 parent = _trees[root].children[component]; 154 parent = _trees[root].children[component];
155 } 155 }
156 } 156 }
157 } 157 }
(...skipping 28 matching lines...) Expand all
186 if (seen.contains(entity.path)) return false; 186 if (seen.contains(entity.path)) return false;
187 seen.add(entity.path); 187 seen.add(entity.path);
188 return true; 188 return true;
189 }); 189 });
190 } 190 }
191 191
192 /// Synchronosuly list all entities that match this glob beneath [root]. 192 /// Synchronosuly list all entities that match this glob beneath [root].
193 List<FileSystemEntity> listSync({String root, bool followLinks: true}) { 193 List<FileSystemEntity> listSync({String root, bool followLinks: true}) {
194 if (root == null) root = '.'; 194 if (root == null) root = '.';
195 195
196 // TODO(nweiz): Remove the explicit annotation when sdk#26139 is fixed. 196 var result = _trees.keys.expand((rootDir) {
197 var result = _trees.keys.expand/*<FileSystemEntity>*/((rootDir) {
198 var dir = rootDir == '.' ? root : rootDir; 197 var dir = rootDir == '.' ? root : rootDir;
199 return _trees[rootDir].listSync(dir, followLinks: followLinks); 198 return _trees[rootDir].listSync(dir, followLinks: followLinks);
200 }); 199 });
201 200
202 if (!_canOverlap) return result.toList(); 201 if (!_canOverlap) return result.toList();
203 202
204 // TODO(nweiz): Rather than filtering here, avoid double-listing directories 203 // TODO(nweiz): Rather than filtering here, avoid double-listing directories
205 // in the first place. 204 // in the first place.
206 var seen = new Set<String>(); 205 var seen = new Set<String>();
207 return result.where((entity) { 206 return result.where((entity) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 return children.keys.first.caseSensitive; 239 return children.keys.first.caseSensitive;
241 } 240 }
242 241
243 /// Whether this node doesn't itself need to be listed. 242 /// Whether this node doesn't itself need to be listed.
244 /// 243 ///
245 /// If a node has no validator and all of its children are literal filenames, 244 /// If a node has no validator and all of its children are literal filenames,
246 /// there's no need to list its contents. We can just directly traverse into 245 /// there's no need to list its contents. We can just directly traverse into
247 /// its children. 246 /// its children.
248 bool get _isIntermediate { 247 bool get _isIntermediate {
249 if (_validator != null) return false; 248 if (_validator != null) return false;
250 if (!_caseSensitive) return false;
251 return children.keys.every((sequence) => 249 return children.keys.every((sequence) =>
252 sequence.nodes.length == 1 && sequence.nodes.first is LiteralNode); 250 sequence.nodes.length == 1 && sequence.nodes.first is LiteralNode);
253 } 251 }
254 252
255 /// Returns whether listing this node might return overlapping results. 253 /// Returns whether listing this node might return overlapping results.
256 bool get canOverlap { 254 bool get canOverlap {
257 // A recusive node can never overlap with itself, because it will only ever 255 // A recusive node can never overlap with itself, because it will only ever
258 // involve a single call to [Directory.list] that's then filtered with 256 // involve a single call to [Directory.list] that's then filtered with
259 // [_validator]. 257 // [_validator].
260 if (isRecursive) return false; 258 if (isRecursive) return false;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 var child = children[sequence]; 292 var child = children[sequence];
295 child.makeRecursive(); 293 child.makeRecursive();
296 return _join([sequence, child._validator]); 294 return _join([sequence, child._validator]);
297 }), caseSensitive: _caseSensitive); 295 }), caseSensitive: _caseSensitive);
298 children = null; 296 children = null;
299 } 297 }
300 298
301 /// Adds [validator] to this node's existing validator. 299 /// Adds [validator] to this node's existing validator.
302 void addOption(SequenceNode validator) { 300 void addOption(SequenceNode validator) {
303 if (_validator == null) { 301 if (_validator == null) {
304 _validator = new OptionsNode([validator], 302 _validator =
305 caseSensitive: validator.caseSensitive); 303 new OptionsNode([validator], caseSensitive: validator.caseSensitive);
306 } else { 304 } else {
307 _validator.options.add(validator); 305 _validator.options.add(validator);
308 } 306 }
309 } 307 }
310 308
311 /// Lists all entities within [dir] matching this node or its children. 309 /// Lists all entities within [dir] matching this node or its children.
312 /// 310 ///
313 /// This may return duplicate entities. These will be filtered out in 311 /// This may return duplicate entities. These will be filtered out in
314 /// [ListTree.list]. 312 /// [ListTree.list].
315 Stream<FileSystemEntity> list(String dir, {bool followLinks: true}) { 313 Stream<FileSystemEntity> list(String dir, {bool followLinks: true}) {
316 if (isRecursive) { 314 if (isRecursive) {
317 return new Directory(dir).list(recursive: true, followLinks: followLinks) 315 return new Directory(dir)
316 .list(recursive: true, followLinks: followLinks)
318 .where((entity) => _matches(p.relative(entity.path, from: dir))); 317 .where((entity) => _matches(p.relative(entity.path, from: dir)));
319 } 318 }
320 319
321 var resultGroup = new StreamGroup<FileSystemEntity>();
322
323 // Don't spawn extra [Directory.list] calls when we already know exactly 320 // Don't spawn extra [Directory.list] calls when we already know exactly
324 // which subdirectories we're interested in. 321 // which subdirectories we're interested in.
325 if (_isIntermediate) { 322 if (_isIntermediate && _caseSensitive) {
323 var resultGroup = new StreamGroup<FileSystemEntity>();
326 children.forEach((sequence, child) { 324 children.forEach((sequence, child) {
327 resultGroup.add(child.list( 325 resultGroup.add(child.list(
328 p.join(dir, (sequence.nodes.single as LiteralNode).text), 326 p.join(dir, (sequence.nodes.single as LiteralNode).text),
329 followLinks: followLinks)); 327 followLinks: followLinks));
330 }); 328 });
331 resultGroup.close(); 329 resultGroup.close();
332 return resultGroup.stream; 330 return resultGroup.stream;
333 } 331 }
334 332
335 var resultController = new StreamController<FileSystemEntity>(sync: true); 333 return StreamCompleter.fromFuture(() async {
336 resultGroup.add(resultController.stream); 334 var entities =
337 new Directory(dir).list(followLinks: followLinks).listen((entity) { 335 await new Directory(dir).list(followLinks: followLinks).toList();
338 var basename = p.relative(entity.path, from: dir); 336 await _validateIntermediateChildrenAsync(dir, entities);
339 if (_matches(basename)) resultController.add(entity);
340 337
341 children.forEach((sequence, child) { 338 var resultGroup = new StreamGroup<FileSystemEntity>();
342 if (entity is! Directory) return; 339 var resultController = new StreamController<FileSystemEntity>(sync: true);
343 if (!sequence.matches(basename)) return; 340 resultGroup.add(resultController.stream);
344 var stream = child.list(p.join(dir, basename), followLinks: followLinks) 341 for (var entity in entities) {
345 .handleError((_) {}, test: (error) { 342 var basename = p.relative(entity.path, from: dir);
346 // Ignore errors from directories not existing. We do this here so 343 if (_matches(basename)) resultController.add(entity);
347 // that we only ignore warnings below wild cards. For example, the 344
348 // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but 345 children.forEach((sequence, child) {
349 // succeed if "foo/bar/qux/baz" doesn't exist. 346 if (entity is! Directory) return;
350 return error is FileSystemException && 347 if (!sequence.matches(basename)) return;
351 (error.osError.errorCode == _ENOENT || 348 var stream = child
352 error.osError.errorCode == _ENOENT_WIN); 349 .list(p.join(dir, basename), followLinks: followLinks)
350 .handleError((_) {}, test: (error) {
351 // Ignore errors from directories not existing. We do this here so
352 // that we only ignore warnings below wild cards. For example, the
353 // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but
354 // succeed if "foo/bar/qux/baz" doesn't exist.
355 return error is FileSystemException &&
356 (error.osError.errorCode == _ENOENT ||
357 error.osError.errorCode == _ENOENT_WIN);
358 });
359 resultGroup.add(stream);
353 }); 360 });
354 resultGroup.add(stream); 361 }
355 }); 362 resultController.close();
356 }, 363 resultGroup.close();
357 onError: resultController.addError, 364 return resultGroup.stream;
358 onDone: () { 365 }());
359 resultController.close(); 366 }
360 resultGroup.close();
361 });
362 367
363 return resultGroup.stream; 368 /// If this is a case-insensitive list, validates that all intermediate
369 /// children (according to [_isIntermediate]) match at least one entity in
370 /// [entities].
371 ///
372 /// This ensures that listing "foo/bar/*" fails on case-sensitive systems if
373 /// "foo/bar" doesn't exist.
374 Future _validateIntermediateChildrenAsync(
375 String dir, List<FileSystemEntity> entities) async {
376 if (_caseSensitive) return;
377
378 for (var sequence in children.keys) {
379 var child = children[sequence];
380 if (!child._isIntermediate) continue;
381 if (entities.any(
382 (entity) => sequence.matches(p.relative(entity.path, from: dir)))) {
383 continue;
384 }
385
386 // We know this will fail, we're just doing it to force dart:io to emit
387 // the exception it would if we were listing case-sensitively.
388 await child
389 .list(p.join(dir, (sequence.nodes.single as LiteralNode).text))
390 .toList();
391 }
364 } 392 }
365 393
366 /// Synchronously lists all entities within [dir] matching this node or its 394 /// Synchronously lists all entities within [dir] matching this node or its
367 /// children. 395 /// children.
368 /// 396 ///
369 /// This may return duplicate entities. These will be filtered out in 397 /// This may return duplicate entities. These will be filtered out in
370 /// [ListTree.listSync]. 398 /// [ListTree.listSync].
371 Iterable<FileSystemEntity> listSync(String dir, {bool followLinks: true}) { 399 Iterable<FileSystemEntity> listSync(String dir, {bool followLinks: true}) {
372 if (isRecursive) { 400 if (isRecursive) {
373 return new Directory(dir) 401 return new Directory(dir)
374 .listSync(recursive: true, followLinks: followLinks) 402 .listSync(recursive: true, followLinks: followLinks)
375 .where((entity) => _matches(p.relative(entity.path, from: dir))); 403 .where((entity) => _matches(p.relative(entity.path, from: dir)));
376 } 404 }
377 405
378 // Don't spawn extra [Directory.listSync] calls when we already know exactly 406 // Don't spawn extra [Directory.listSync] calls when we already know exactly
379 // which subdirectories we're interested in. 407 // which subdirectories we're interested in.
380 if (_isIntermediate) { 408 if (_isIntermediate && _caseSensitive) {
381 return children.keys.expand((sequence) { 409 return children.keys.expand((sequence) {
382 return children[sequence].listSync( 410 return children[sequence].listSync(
383 p.join(dir, (sequence.nodes.single as LiteralNode).text), 411 p.join(dir, (sequence.nodes.single as LiteralNode).text),
384 followLinks: followLinks); 412 followLinks: followLinks);
385 }); 413 });
386 } 414 }
387 415
388 return new Directory(dir).listSync(followLinks: followLinks) 416 var entities = new Directory(dir).listSync(followLinks: followLinks);
389 .expand((entity) { 417 _validateIntermediateChildrenSync(dir, entities);
418
419 return entities.expand((entity) {
390 var entities = <FileSystemEntity>[]; 420 var entities = <FileSystemEntity>[];
391 var basename = p.relative(entity.path, from: dir); 421 var basename = p.relative(entity.path, from: dir);
392 if (_matches(basename)) entities.add(entity); 422 if (_matches(basename)) entities.add(entity);
393 if (entity is! Directory) return entities; 423 if (entity is! Directory) return entities;
394 424
395 entities.addAll(children.keys 425 entities.addAll(children.keys
396 .where((sequence) => sequence.matches(basename)) 426 .where((sequence) => sequence.matches(basename))
397 .expand((sequence) { 427 .expand((sequence) {
398 try { 428 try {
399 return children[sequence].listSync( 429 return children[sequence]
400 p.join(dir, basename), followLinks: followLinks).toList(); 430 .listSync(p.join(dir, basename), followLinks: followLinks)
431 .toList();
401 } on FileSystemException catch (error) { 432 } on FileSystemException catch (error) {
402 // Ignore errors from directories not existing. We do this here so 433 // Ignore errors from directories not existing. We do this here so
403 // that we only ignore warnings below wild cards. For example, the 434 // that we only ignore warnings below wild cards. For example, the
404 // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but 435 // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but
405 // succeed if "foo/bar/qux/baz" doesn't exist. 436 // succeed if "foo/bar/qux/baz" doesn't exist.
406 if (error.osError.errorCode == _ENOENT || 437 if (error.osError.errorCode == _ENOENT ||
407 error.osError.errorCode == _ENOENT_WIN) { 438 error.osError.errorCode == _ENOENT_WIN) {
408 return const []; 439 return const [];
409 } else { 440 } else {
410 rethrow; 441 rethrow;
411 } 442 }
412 } 443 }
413 })); 444 }));
414 445
415 return entities; 446 return entities;
416 }); 447 });
417 } 448 }
418 449
450 /// If this is a case-insensitive list, validates that all intermediate
451 /// children (according to [_isIntermediate]) match at least one entity in
452 /// [entities].
453 ///
454 /// This ensures that listing "foo/bar/*" fails on case-sensitive systems if
455 /// "foo/bar" doesn't exist.
456 void _validateIntermediateChildrenSync(
457 String dir, List<FileSystemEntity> entities) {
458 if (_caseSensitive) return;
459
460 children.forEach((sequence, child) {
461 if (!child._isIntermediate) return;
462 if (entities.any(
463 (entity) => sequence.matches(p.relative(entity.path, from: dir)))) {
464 return;
465 }
466
467 // If there are no [entities] that match [sequence], manually list the
468 // directory to force `dart:io` to throw an error. This allows us to
469 // ensure that listing "foo/bar/*" fails on case-sensitive systems if
470 // "foo/bar" doesn't exist.
471 child.listSync(p.join(dir, (sequence.nodes.single as LiteralNode).text));
472 });
473 }
474
419 /// Returns whether the native [path] matches [_validator]. 475 /// Returns whether the native [path] matches [_validator].
420 bool _matches(String path) { 476 bool _matches(String path) {
421 if (_validator == null) return false; 477 if (_validator == null) return false;
422 return _validator.matches(toPosixPath(p.context, path)); 478 return _validator.matches(toPosixPath(p.context, path));
423 } 479 }
424 480
425 String toString() => "($_validator) $children"; 481 String toString() => "($_validator) $children";
426 } 482 }
427 483
428 /// Joins each [components] into a new glob where each component is separated by 484 /// Joins each [components] into a new glob where each component is separated by
429 /// a path separator. 485 /// a path separator.
430 SequenceNode _join(Iterable<AstNode> components) { 486 SequenceNode _join(Iterable<AstNode> components) {
431 var componentsList = components.toList(); 487 var componentsList = components.toList();
432 var first = componentsList.removeAt(0); 488 var first = componentsList.removeAt(0);
433 var nodes = [first]; 489 var nodes = [first];
434 for (var component in componentsList) { 490 for (var component in componentsList) {
435 nodes.add(new LiteralNode('/', caseSensitive: first.caseSensitive)); 491 nodes.add(new LiteralNode('/', caseSensitive: first.caseSensitive));
436 nodes.add(component); 492 nodes.add(component);
437 } 493 }
438 return new SequenceNode(nodes, caseSensitive: first.caseSensitive); 494 return new SequenceNode(nodes, caseSensitive: first.caseSensitive);
439 } 495 }
OLDNEW
« no previous file with comments | « packages/glob/lib/src/ast.dart ('k') | packages/glob/lib/src/parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698