/*
* Rapid Beans Framework, SDK, Ant Tasks: TaskFileOverview.java
*
* Copyright (C) 2009 Martin Bluemel
*
* Creation Date: 11/03/2010
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
* You should have received a copies of the GNU Lesser General Public License and the
* GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
package org.rapidbeans.ant;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
/**
* Generates an XML file containing a list of folders and files that might be
* used as an input for an XSLT transformation step.
*
* @author Martin Bluemel
*/
public final class TaskFileOverview extends Task {
private static final String LF = PlatformHelper.getLineFeed();
/**
* force flag.
*/
private boolean force = false;
/**
* set the force flag which determines if the generation should be performed
* not regarding modification dates.
*
* @param argForce
* determines if the generation should be performed not regarding
* modification dates
*/
public void setForce(final boolean argForce) {
this.force = argForce;
}
/**
* absolute flag.
*/
private boolean absolute = false;
/**
* set the absolute flag which determines if absolute paths should be
* generated.
*
* @param argForce
* determines if the generation should be performed not regarding
* modification dates
*/
public void setAbsolute(final boolean absolute) {
this.absolute = absolute;
}
/**
* exclude dot directories flag.
*/
private boolean excludedotdirs = true;
/**
* set the exclude dot directories flag which determines if the directories
* starting with '.' (e. g. .svn) character should be excluded.
*
* @param exclude
* determines if the generation should be performed not regarding
* modification dates
*/
public void setExcludedotdirs(final boolean exclude) {
this.excludedotdirs = exclude;
}
/**
* the (root) directory to analyze
*/
private String dir = null;
/**
* set the (root) directory to analyze.
*
* @param the
* (root) directory to analyze
*/
public void setDir(final String dir) {
this.dir = dir;
}
/**
* the (root) directory to analyze
*/
private File out = null;
/**
* set the file to generate
*
* @param the
* file to generate
*/
public void setOut(final File out) {
this.out = out;
}
/**
* The depth to analyze the file system tree.
*/
private int depth = -1;
/**
* set the depth.
*
* @param depth
* the depth to set
*/
public void setDepth(final int depth) {
this.depth = depth;
}
/**
* the execute method.
*/
public void execute() {
final File rootDir = new File(this.dir);
getProject().log(" [fileoverview] dir: " + this.dir, Project.MSG_VERBOSE);
getProject().log(" [fileoverview] path: " + rootDir.getPath(), Project.MSG_VERBOSE);
getProject().log(" [fileoverview] absolute path: " + rootDir.getAbsolutePath(), Project.MSG_VERBOSE);
// checks for attributes
if (this.dir == null) {
throw new BuildException("No directory defined. Please define value for attribute \"dir\".");
}
if (!rootDir.exists()) {
throw new BuildException("Directory \"" + this.dir + "\" not found");
}
if (!rootDir.isDirectory()) {
throw new BuildException("Invalid directory. File \"" + this.dir + " is not a directory");
}
if (this.out == null) {
throw new BuildException("No out file defined. Please define value for attribute \"out\".");
}
if (!this.out.getParentFile().exists()) {
if (!this.out.getParentFile().mkdirs())
throw new BuildException("Problems to create directory \"" + this.out.getParentFile() + "\".");
}
long dirLatestModified = 0;
if (!this.force) {
dirLatestModified = getLatestModificationDirDate(rootDir, this.depth);
}
if (!this.out.exists() || this.force || dirLatestModified > this.out.lastModified()) {
this.getProject().log(
" [fileoverview] Processing directory " + rootDir.getAbsolutePath() + " to "
+ this.out.getAbsolutePath(), Project.MSG_INFO);
this.getProject().log(
" [fileoverview] depth = " + this.depth + "\n" + " [fileoverview] absolute = "
+ this.absolute + "\n" + " [fileoverview] excludedotdirs = " + this.excludedotdirs,
Project.MSG_VERBOSE);
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(new FileOutputStream(this.out), "UTF-8");
generateFolderAndFilesXML(rootDir, this.depth, 0, writer, this.absolute, this.excludedotdirs,
this.getProject());
} catch (IOException e) {
throw new BuildException(e);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
throw new BuildException(e);
}
}
}
}
}
/**
* Generate a folder and files XML list starting from the given directory /
* folder.
*
* @param dir
* the starting point
* @param maxdepth
* the maximal depth. Give -1 if depth is unlimited
* @param depth
* the current depth for recursion. Give in zero here.
* @param writer
* the write to write the list into
* @param absolute
* @param excludedotdirs
*/
protected static void generateFolderAndFilesXML(final File dir, final int maxdepth, final int depth,
final Writer writer, final boolean absolute, final boolean excludedotdirs, final Project project) {
try {
if (depth == 0) {
writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + LF);
}
final StringBuffer indent = new StringBuffer();
for (int i = 0; i < depth; i++) {
indent.append('\t');
}
String foldername = dir.getPath();
if (absolute) {
foldername = dir.getAbsolutePath();
}
writer.write(indent + "<folder path=\"" + foldername.replace(File.separatorChar, '/') + "\">" + LF);
final int depth1 = depth + 1;
final StringBuffer indent1 = new StringBuffer();
for (int i = 0; i < depth1; i++) {
indent1.append('\t');
}
for (final File file : dir.listFiles()) {
if (file.isDirectory()) {
if ((maxdepth > depth || maxdepth < 0) && (!excludedotdirs || !file.getName().startsWith("."))) {
generateFolderAndFilesXML(file, maxdepth, depth1, writer, absolute, excludedotdirs, project);
}
} else {
writer.write(indent1 + "<file name=\"" + file.getName() + "\"/>" + LF);
}
}
writer.write(indent + "</folder>" + LF);
} catch (IOException e) {
throw new BuildException(e);
}
}
/**
* iterates recursively over a complete directory tree and determines the
* newest (latest) modification date of all the directories contained in the
* file system hierarchy.
*
* @param dir
* directory the directory / folder to analyze,
* @param depth
* the depth
*
* @throws BuildException
*/
protected static long getLatestModificationDirDate(final File dir, final int depth) {
long lastModified = dir.lastModified();
if (depth > 0 || depth < 0) {
File[] files = dir.listFiles();
for (final File file : files) {
if (file.isDirectory()) {
final long lastModifiedTest = getLatestModificationDirDate(file, depth - 1);
if (lastModifiedTest > lastModified) {
lastModified = lastModifiedTest;
}
}
}
}
return lastModified;
}
}