Single responsibility principle
Recently i needed to compile our all jasper-report files during application start-up.
I was modifying existing code-base to enable precompiling xml reports to binary .jreport files.
It was simple enough, traverse all *.jrxml files at a certain directory and use net.sf.jasperreports.engine.JasperCompileManager to do the actual compiling.
Here is how i traversed all files under a directory.
private void visitAllFiles(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
visitAllFiles(new File(dir, children[i]));
}
} else {
//doSomething();
}
}
Here is what is wrong with this version.
The infrastructure logic to traverse the files and business logic to compile reports resides in same class.
If i needed traverse files for something else (for example to backup old compiled reports) i would need to duplicate infrastructure logic.
It seems this class is trying to do too many things. I separated this class to a reusable FileCrawler and FileVisitor.
public interface FileVisitor {
void visit(File fileToProcess) ;
}
FileCrawler takes a visitor and calls visit method for each file it encounters.
public FileCrawler(final File root, final FileVisitor visitor) {
this.visitor = visitor;
}
private void visitAllFiles(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (int i = 0; i < children.length; i++) {
visitAllFiles(new File(dir, children[i]));
}
} else {
visitor.visit(dir);
}
}
Usage is as follows:
new FileCrawler(new File(this.cacheFilePath), new ReportCompiler()).start();
ReportCompiler implements FileVisitor and looks like as follows:public void visit(File fileToProcess) {
String name = fileToProcess.getName();
if (name.endsWith(".jrxml")){
compileReport(fileToProcess.getParent(),name);
}
}
Now i can use anonymous inner classes to create simple reusable directory traversing logic.
new FileCrawler (new File (root), new FileVisitor() {
public void visit(File fileToProcess) {
String name = fileToProcess.getName();
if (name.endsWith(".jreport")){
fileToProcess.delete();
}
}
});