TaskFileWalker.java
package de.japrost.staproma;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.DirectoryWalker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import de.japrost.staproma.spm.DefaultSpmFactory;
import de.japrost.staproma.spm.SpmFormat;
import de.japrost.staproma.spm.SpmFormatFactory;
import de.japrost.staproma.task.DirectoryTask;
import de.japrost.staproma.task.FolderTask;
import de.japrost.staproma.task.Task;
/**
* The TaksFileWalker scans the given directory for files and directories to convert them to a Task tree.
*
* @author alexxismachine (Ulrich David)
*/
public class TaskFileWalker extends DirectoryWalker<String> {
// TODO replace with logging
private static final PrintStream OUT = System.out;
private final File startDirectory;
private final boolean filterCompleted;
private final SpmFormatFactory spmFormatFactory;
// TODO put this into the fileFilter! and use one pattern
private static final Pattern MINMAL_DIR_PATTERN = Pattern.compile("^([A-Z]*)$");
private static final Pattern NUMBER_DIR_PATTERN = Pattern.compile("^([A-Z]*)-(\\d*)$");
private static final Pattern NAME_DIR_PATTERN = Pattern.compile("^([A-Z]*)_(.*$)");
private static final Pattern FULL_DIR_PATTERN = Pattern.compile("^([A-Z]*)-(\\d*)_(.*$)");
// (X*)
// (X*)_(*)
// (X*)-(N*)
// (X*)-(N*)_(*)
// => (X*)(-(N*))?(_(*))?
// TODO put this into the fileFilter?
// FIXME remove dependency between given IOFileFilter and this pattern
private static final Pattern FILE_STATUS_PATTERN = Pattern.compile("^\\d*_?(.*)\\..*$");
private static final Pattern FILE_NAME_PATTERN = Pattern.compile("^([A-Z]*)-(\\d*)_(.*)\\.spm$");
private int level = 0;
private Task currentTask;
/**
* Instanciate a with the given parameters. This instance will use the {@link DefaultSpmFactory} and filter completed
* tasks.
*
* @param rootTask the task to add the found tasks to.
* @param startDirectory the base directory for the scan.
* @param fileFilter the file filter to use.
*/
public TaskFileWalker(final Task rootTask, final File startDirectory, final IOFileFilter fileFilter) {
// TODO really needed?
this(rootTask, startDirectory, fileFilter, new DefaultSpmFactory(), true);
}
/**
* Instanciate a with the given parameters.
*
* @param rootTask the task to add the found tasks to.
* @param startDirectory the base directory for the scan.
* @param fileFilter the file filter to use.
* @param spmFormatFactory the factory to create {@link SpmFormat}s.
* @param filterCompleted flag if completed tasks should be filtered.
*/
public TaskFileWalker(final Task rootTask, final File startDirectory, final IOFileFilter fileFilter,
final SpmFormatFactory spmFormatFactory, final boolean filterCompleted) {
super(FileFilterUtils.directoryFileFilter(), fileFilter, -1);
this.startDirectory = startDirectory;
this.filterCompleted = filterCompleted;
this.currentTask = rootTask;
this.spmFormatFactory = spmFormatFactory;
}
/**
* Start the walk.
*
* @throws IOException on io errors.
*/
public void crawl() throws IOException {
walk(startDirectory, null);
}
@Override
protected boolean handleDirectory(final File directory, final int depth, final Collection<String> results)
throws IOException {
OUT.println("Handle DIR :" + directory);
if (!directory.equals(startDirectory)) {
if (filterCompleted) {
// FIXME set to status?
if (containsCompleted(directory)) {
OUT.println("-> Completed: " + directory.getAbsolutePath());
return false;
}
}
final String dirName = directory.getName();
String description = dirName;
boolean match = false;
// TODO faster
Matcher matcher = MINMAL_DIR_PATTERN.matcher(dirName);
if (matcher.matches()) {
match = true;
// keep description
}
matcher = NUMBER_DIR_PATTERN.matcher(dirName);
if (matcher.matches()) {
match = true;
// keep description
}
matcher = NAME_DIR_PATTERN.matcher(dirName);
if (matcher.matches()) {
match = true;
//description = matcher.group(1) + " " + matcher.group(2).replace('_', ' ');
description = matcher.group(2).replace('_', ' ');
}
matcher = FULL_DIR_PATTERN.matcher(dirName);
if (matcher.matches()) {
match = true;
description = matcher.group(1) + "-" + matcher.group(2) + " " + matcher.group(3).replace('_', ' ');
}
if (match) {
OUT.println(" DO THE STUFF and ignore results");
final String path = directory.getPath();
final String startDirPath = startDirectory.getPath();
final String relativePath = "." + path.substring(startDirPath.length());
if (depth == level) {
OUT.println("SAME");
final Task addTo = currentTask.getParent();
final DirectoryTask task = new DirectoryTask(addTo, relativePath, description);
//task.setState("FOLDER");
//task.setState(TaskState.CURRENT);
addTo.addChild(task);
currentTask = task;
} else if (depth < level) {
OUT.println("PARENT");
final Task addTo = currentTask.getParent().getParent();
final DirectoryTask task = new DirectoryTask(addTo, relativePath, description);
//task.setState("FOLDER");
//task.setState(TaskState.CURRENT);
addTo.addChild(task);
currentTask = task;
} else { // depth > level
OUT.println("SUB");
final Task addTo = currentTask;
final DirectoryTask task = new DirectoryTask(addTo, relativePath, description);
//task.setState("FOLDER");
//task.setState(TaskState.CURRENT);
addTo.addChild(task);
currentTask = task;
}
level = depth;
return true;
} else {
OUT.println("-> Ignoring: " + directory.getAbsolutePath());
return false;
}
} else {
return true;
}
}
@Override
protected void handleFile(final File file, final int depth, final Collection<String> results) throws IOException {
OUT.println("Handle FILE:" + file);
String status = "CURRENT";
Task fileRoot = currentTask;
final Matcher fileName = FILE_NAME_PATTERN.matcher(file.getName());
if (fileName.matches()) {
status = "GTD";
final String description = fileName.group(1) + "-" + fileName.group(2) + " "
+ fileName.group(3).replace('_', ' ');
FolderTask folderTask = new FolderTask(currentTask, description);
fileRoot.addChild(folderTask);
fileRoot = folderTask;
} else {
final Matcher m = FILE_STATUS_PATTERN.matcher(file.getName());
if (m.matches()) {
status = m.group(1).toUpperCase();
OUT.println("Status from File: '" + status + "'");
} else {
OUT.println("This should not happen");
}
}
final SpmFormat spmFormat = spmFormatFactory.construct(status);
final List<String> lines = readLines(file);
spmFormat.parseLines(fileRoot, lines);
}
// TODO handle with some interface?
List<String> readLines(final File file) throws IOException {
return IOUtils.readLines(new FileInputStream(file));
}
@Override
protected File[] filterDirectoryContents(final File directory, final int depth, final File[] inFiles)
throws IOException {
OUT.println("filterDirectoryContents " + directory);
final List<File> files = new ArrayList<>();
final List<File> dirs = new ArrayList<>();
// FIXME inFiles may be null!
for (final File f : inFiles) {
if (f.isFile()) {
files.add(f);
} else {
dirs.add(f);
}
}
Collections.sort(files);
Collections.sort(dirs);
files.addAll(dirs);
return files.toArray(new File[0]);
}
private boolean containsCompleted(final File directory) {
OUT.println("containsCompleted " + directory);
final IOFileFilter completedFilter = FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
new RegexFileFilter("^\\d*_?completed\\.?.*$", IOCase.SENSITIVE));
if (FileUtils.listFiles(directory, completedFilter, null).size() > 0) {
return true;
}
return false;
}
}