Datei file supplier.dart
Version vom 6. Januar 2021, 01:19 Uhr von Hamatoma (Diskussion | Beiträge) (Die Seite wurde neu angelegt: „= Klasse ExitException = Diese Klasse wird als Ausnahme benutzt, um aus einer verschachtelten Situation unkompliziert ans Ende des Programms zu gelangen, wenn…“)
Klasse ExitException[Bearbeiten]
Diese Klasse wird als Ausnahme benutzt, um aus einer verschachtelten Situation unkompliziert ans Ende des Programms zu gelangen, wenn bestimmte Bedingungen erfüllt sind.
/// Used for jump out of nested calls. class ExitException { final String reason; ExitException(this.reason); }
Die Klasse FileOptions[Bearbeiten]
Diese Klasse speichert die Optionen, die sich auf die Dateiauswahl beziehen.
/// A data class for options to control a [FileSupplier] object. class FileOptions { bool recursive; RegExp excluded; RegExp excludedDirs; bool processBinaries; bool yieldFile = true; bool yieldDirectory = true; bool yieldLinkToFile = true; bool yieldLinkToDirectory = true; FileOptions({ this.recursive = false, this.excluded, this.excludedDirs, this.processBinaries = false, this.yieldFile = true, this.yieldDirectory = true, this.yieldLinkToFile = true, this.yieldLinkToDirectory = true, }); FileOptions.fromArgument(ArgResults results) { recursive = results['recursive']; excluded = results['excluded'] == null ? null : RegExp(results['excluded']); excludedDirs = results['excluded-dirs'] == null ? null : RegExp(results['excluded-dirs']); processBinaries = results['process-binaries']; if (results.options.contains('file-type') && results['file-type'] != null) { yieldFile = yieldDirectory = yieldLinkToFile = yieldLinkToDirectory = false; for (var item in results['file-type'].split(',')) { if (item.startsWith('f')) { yieldFile = true; } else if (item.startWith('d')) { yieldDirectory = true; } else if (item == 'l' || item == 'link') { yieldLinkToFile = yieldLinkToDirectory = true; } else if (item == 'ld' || item.startsWith('link)') && item.contains('dir')) { yieldLinkToDirectory = true; } else if (item == 'lf' || item.contains('link') && item.contains('file')) { yieldLinkToFile = true; } } } } }
- Es existieren zwei Konstruktoren:
FileOptions()
wird nur in Tests benutzt.FileOptions.fromArgument()
holt die Optionen aus einem Objekt der KlasseArgResult
aus dem externen Paketargs
.
recursive = results['recursive'];
Das boolsche Attributrecursive
wird direkt aus dem Objekt der KlasseArgResult
übernommen. Wir lernen: Zugriff auf die Optionen erfolgt mittels Namenrecursive
mit dem eckige-Klammer-Operator wie bei Maps.
Die Klasse FileSupplier[Bearbeiten]
Die Attribute[Bearbeiten]
/// Implements a generator delivering filenames from one or more file trees. class FileSupplier { final FileOptions fileOptions; final int verboseLevel; int processedFiles = 0; int processedDirs = 0; int processedLinksDirectory = 0; int processedLinksFile = 0; int ignoredFiles = 0; int ignoredDirs = 0; int countBinaries = 0; final List<String> filePatterns; final summary = <String>[]; ... }
Der Konstruktor[Bearbeiten]
FileSupplier({this.filePatterns, this.fileOptions, this.verboseLevel = 1});
Die Methode next()[Bearbeiten]
Die Methode implementiert den Generator mit Hilfe einer zweiten Generatorfunktion searchFilePattern()
.
Iterable<String> next() sync* { String exitMessage; final paths = <String>[]; final regExpList = <RegExp>[]; for (var item in filePatterns) { if (FileSystemEntity.isDirectorySync(item)) { paths.add(item); regExprList.add(null); } else { var ix = item.lastIndexOf(path.separator); paths.add(ix < 0 ? null : item.substring(0, ix)); final filePattern = shellPatternToRegExp(ix < 0 ? item : item.substring(ix + 1)); regExpList.add(filePattern.isEmpty ? null : RegExp(filePattern)); } } try { for (var ix = 0; ix < paths.length; ix++) { yield* searchFilePattern(regExpList[ix], paths[ix] ?? '.', 0); } } on ExitException catch (exc) { exitMessage = '= search stopped: ' + exc.reason; } if (verboseLevel >= 1) { summary.add( '= processed directories: $processedDirs processed files: $processedFiles'); summary.add( '= ignored directories: $ignoredDirs ignored files: $ignoredFiles binary files: $countBinaries'); summary.add( '= directory links (ignored): $processedLinksDirectory file links: $processedLinksFile'); if (exitMessage != null) { summary.add(exitMessage); } } }
Iterable<String> next() sync* {
- Durch das
sync*
weiß der Compiler, dass eine Generatormethode vorliegt, und zwar eine, die eine andere Generatormethode aufruft (wegen des Sterns). - Der Ergebnistyp ist
Iterable<String>
, eine Verallgemeinerung einer Liste: - Eine Liste hat ihre Elemente gespeichert, ein Iterator ist eine Menge, auf die sequentiell ("der Reihe nach") zugegriffen werden kann. Beispielsweise nutzt die for-Schleife diesen Datentyp:
for (var name in iterator)
.
- Durch das
- Wie in der "alten" Methode search werden zwei Listen aufbereitet, die Liste
paths
mit den Verzeichnisnamen, die ListeregExpList
mit Dateisuchmustern. for (var ix = 0; ix < paths.length; ix++)
Mit dieser Schleife werden alle Dateimuster abgearbeitet.yield* searchFilePattern(regExpList[ix], paths[ix] ?? '.', 0);
yield*
liefert den Wert aus, den die folgende GeneratormethodesearchFilePattern()
abliefert.- nach der Schleife wird die Statistik noch in der Stringliste
summary
aufbereitet.
Die Methode searchFilePattern()[Bearbeiten]
Die Methode ist eine Generatormethode, die rekursiv den Dateibaum durchwandert und Dateinamen zurückliefert.
/// Searches the files matching the [filePattern] in a [directory]. /// This method is recursive on subdirectories. Iterable<String> searchFilePattern( RegExp filePattern, String directory, int depth) sync* { if (verboseLevel >= 2 && depth <= 1 || verboseLevel >= 3) { print('= processing $directory ...'); } final subDirectories = <String>[]; try { processedDirs++; for (var file in Directory(directory).listSync()) { final name = file.path; final node = basename(name); if (FileSystemEntity.isDirectorySync(name)) { if (FileSystemEntity.isLinkSync(name)) { processedLinksDirectory++; if (fileOptions.yieldLinkToDirectory) { yield name; } continue; } if (fileOptions.excludedDirs != null && fileOptions.excludedDirs.firstMatch(node) != null) { if (verboseLevel >= 4) { print('= ignoring not matching directory $name'); } ignoredDirs++; } else { if (fileOptions.yieldDirectory) { yield name; } subDirectories.add(name); } continue; } if (filePattern != null && filePattern.firstMatch(node) == null) { ignoredFiles++; if (verboseLevel >= 4) { print('= ignoring not matching $name'); } continue; } if (fileOptions.excluded != null && fileOptions.excluded.firstMatch(node) != null) { ignoredFiles++; if (verboseLevel >= 4) { print('= ignoring excluded $name'); } continue; } if (!fileOptions.processBinaries && isBinary(name)) { if (verboseLevel >= 4) { print('= ignoring binary $name'); } countBinaries++; continue; } processedFiles++; if (FileSystemEntity.isLinkSync(name)) { processedLinksFile++; if (fileOptions.yieldLinkToFile) { yield name; } continue; } if (fileOptions.yieldFile) { yield name; } } } on FileSystemException catch (exc) { processedDirs--; ignoredDirs++; if (verboseLevel >= 4) { print('= ignored: $exc'); } } if (fileOptions.recursive) { for (var subDir in subDirectories) { yield* searchFilePattern(filePattern, subDir, depth + 1); } } }
Iterable<String> searchFilePattern(...) sync*
sync*
markiert den Generator, Rückgabetyp ist wiederIterable
.for (var file in Directory(directory).listSync())
Die Schleife iteriert über alle Verzeichniseinträge vondirectory
.if (FileSystemEntity.isDirectorySync(name))
Test, ob die aktuelle Datei ein Verzeichnis ist.if (FileSystemEntity.isLinkSync(name))
Test, ob die aktuelle Datei ein symbolischer Link ist. In diesem Fall liegt ein "Link auf ein Verzeichnis" vor, und der Name darf nicht in der Unterverzeichnisliste landen, da sonst Zyklen möglich sind, die zu Endlosschleifen führen.if (fileOptions.yieldLinkToDirectory)
Test, ob "Links auf Verzeichnisse" als Ergebnis gewünscht sind.yield name;
Wenn ja, wird der volle Name (mit Pfad) zurückgegeben.- In ähnlicher Weise wird auch ein "echtes" Verzeichnis, ein "Link auf eine Datei" und eine "sonstige" Datei zurückgeliefert.
if (fileOptions.yieldLinkToDirectory)
Wenn die Option--recursive
gesetzt ist...for (var subDir in subDirectories)
... wird in dieser Schleife die Funktion rekursiv aufgerufen:yield* searchFilePattern(...)
.