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

Unified Diff: pkg/watcher/lib/src/path_set.dart

Issue 66163002: Wrap Directory.watch on Mac OS for the watcher package. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 7 years, 1 month 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/watcher/lib/src/directory_watcher/mac_os.dart ('k') | pkg/watcher/lib/src/utils.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/watcher/lib/src/path_set.dart
diff --git a/pkg/watcher/lib/src/path_set.dart b/pkg/watcher/lib/src/path_set.dart
new file mode 100644
index 0000000000000000000000000000000000000000..01d4208f4f79ab86661ec87348afdfcfc41ed9b8
--- /dev/null
+++ b/pkg/watcher/lib/src/path_set.dart
@@ -0,0 +1,168 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library watcher.path_dart;
+
+import 'dart:collection';
+
+import 'package:path/path.dart' as p;
+
+/// A set of paths, organized into a directory hierarchy.
+///
+/// When a path is [add]ed, it creates an implicit directory structure above
+/// that path. Directories can be inspected using [containsDir] and removed
+/// using [remove]. If they're removed, their contents are removed as well.
+///
+/// The paths in the set are normalized so that they all begin with [root].
+class PathSet {
+ /// The root path, which all paths in the set must be under.
+ final String root;
+
+ /// The path set's directory hierarchy.
+ ///
+ /// Each level of this hierarchy has the same structure: a map from strings to
+ /// other maps, which are further levels of the hierarchy. A map with no
+ /// elements indicates a path that was added to the set that has no paths
+ /// beneath it. Such a path should not be treated as a directory by
+ /// [containsDir].
+ final _entries = new Map<String, Map>();
+
+ /// The set of paths that were explicitly added to this set.
+ ///
+ /// This is needed to disambiguate a directory that was explicitly added to
+ /// the set from a directory that was implicitly added by adding a path
+ /// beneath it.
+ final _paths = new Set<String>();
+
+ PathSet(this.root);
+
+ /// Adds [path] to the set.
+ void add(String path) {
+ path = _normalize(path);
+ _paths.add(path);
+
+ var parts = _split(path);
+ var dir = _entries;
+ for (var part in parts) {
+ dir = dir.putIfAbsent(part, () => {});
+ }
+ }
+
+ /// Removes [path] and any paths beneath it from the set and returns the
+ /// removed paths.
+ ///
+ /// Even if [path] itself isn't in the set, if it's a directory containing
+ /// paths that are in the set those paths will be removed and returned.
+ ///
+ /// If neither [path] nor any paths beneath it are in the set, returns an
+ /// empty set.
+ Set<String> remove(String path) {
+ path = _normalize(path);
+ var parts = new Queue.from(_split(path));
+
+ // Remove the children of [dir], as well as [dir] itself if necessary.
+ //
+ // [partialPath] is the path to [dir], and a prefix of [path]; the remaining
+ // components of [path] are in [parts].
+ recurse(dir, partialPath) {
+ if (parts.length > 1) {
+ // If there's more than one component left in [path], recurse down to
+ // the next level.
+ var part = parts.removeFirst();
+ var entry = dir[part];
+ if (entry.isEmpty) return new Set();
+
+ partialPath = p.join(partialPath, part);
+ var paths = recurse(entry, partialPath);
+ // After removing this entry's children, if it has no more children and
+ // it's not in the set in its own right, remove it as well.
+ if (entry.isEmpty && !_paths.contains(partialPath)) dir.remove(part);
+ return paths;
+ }
+
+ // If there's only one component left in [path], we should remove it.
+ var entry = dir.remove(parts.first);
+ if (entry == null) return new Set();
+
+ if (entry.isEmpty) {
+ _paths.remove(path);
+ return new Set.from([path]);
+ }
+
+ var set = _removePathsIn(entry, path);
+ if (_paths.contains(path)) {
+ _paths.remove(path);
+ set.add(path);
+ }
+ return set;
+ }
+
+ return recurse(_entries, root);
+ }
+
+ /// Recursively removes and returns all paths in [dir].
+ ///
+ /// [root] should be the path to [dir].
+ Set<String> _removePathsIn(Map dir, String root) {
+ var removedPaths = new Set();
+ recurse(dir, path) {
+ dir.forEach((name, entry) {
+ var entryPath = p.join(path, name);
+ if (_paths.remove(entryPath)) removedPaths.add(entryPath);
+ recurse(entry, entryPath);
+ });
+ }
+
+ recurse(dir, root);
+ return removedPaths;
+ }
+
+ /// Returns whether [this] contains [path].
+ ///
+ /// This only returns true for paths explicitly added to [this].
+ /// Implicitly-added directories can be inspected using [containsDir].
+ bool contains(String path) => _paths.contains(_normalize(path));
+
+ /// Returns whether [this] contains paths beneath [path].
+ bool containsDir(String path) {
+ path = _normalize(path);
+ var dir = _entries;
+
+ for (var part in _split(path)) {
+ dir = dir[part];
+ if (dir == null) return false;
+ }
+
+ return !dir.isEmpty;
+ }
+
+ /// Returns a [Set] of all paths in [this].
+ Set<String> toSet() => _paths.toSet();
+
+ /// Removes all paths from [this].
+ void clear() {
+ _paths.clear();
+ _entries.clear();
+ }
+
+ String toString() => _paths.toString();
+
+ /// Returns a normalized version of [path].
+ ///
+ /// This removes any extra ".." or "."s and ensure that the returned path
+ /// begins with [root]. It's an error if [path] isn't within [root].
+ String _normalize(String path) {
+ var relative = p.relative(p.normalize(path), from: root);
+ var parts = p.split(relative);
+ // TODO(nweiz): replace this with [p.isWithin] when that exists (issue
+ // 14980).
+ if (!p.isRelative(relative) || parts.first == '..' || parts.first == '.') {
+ throw new ArgumentError('Path "$path" is not inside "$root".');
+ }
+ return p.join(root, relative);
+ }
+
+ /// Returns the segments of [path] beneath [root].
+ List<String> _split(String path) => p.split(p.relative(path, from: root));
+}
« no previous file with comments | « pkg/watcher/lib/src/directory_watcher/mac_os.dart ('k') | pkg/watcher/lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698