/*
* Rapid Beans Framework, SDK, Ant Tasks: TaskMergeSections.java
*
* Copyright (C) 2009 Martin Bluemel
*
* Creation Date: 10/29/2005
*
* 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.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
/**
* This Ant task supports incremental code generation by merging the content of
* specially marked sections with manual code from one file (sercfileman) to
* another file (srcfilegen).
*
* Example:
*
* <p>
* 1) srcfileman:<br/>
* <code> ...<br/>
* // BEGIN manual code section </code><-- section begin comment <code><br/>
* // TestClass.doSomething() </code><-- section signature<code><br/>
* this.xxx = YYY;<br/>
* // END manual code section </code><-- section end comment <code><br/>
* ...</code><br/>
* </p>
* <p>
* 2) srcfilegen:<br/>
* <code> ...<br/>
* // BEGIN manual code section<br/>
* // TestClass.doSomething()<br/>
* // END manual code section<br/>
* ...</code>
* </p>
*
* <p>
* merged file<br/>
* <code> ...<br/>
* // BEGIN manual code section<br/>
* // TestClass.doSomething()<br/>
* this.xxx = YYY;<br/>
* // END manual code section<br/>
* ...</code><br/>
* </p>
*
* As you see you need three lines to mark a section that should be merged:
*
* <p>
* <b>- section begin comment:</b> a fix one line comment to mark the begin of a section.
* </p>
* <p>
* <b>- section signature:</b> a one line comment with a string that uniqely identifies the section within one code file. The section signature always directly follows the section begin comment.
* </p>
* <p>
* <b>- section end comment:</b> a fix one line comment to mark the begin of a section.
* </p>
*
* @author Martin.Bluemel
*/
public final class TaskMergeSections extends Task {
/**
* marks a one line comment.
*/
private String oneLineComment = "//";
/**
* set the one line comment characters.
*
* @param s
* the one line comment characters
*/
public void setOneLineComment(final String s) {
this.oneLineComment = s;
}
/**
* begin comment.
*/
private String sectionBegin = "BEGIN manual code section";
/**
* set the string to mark the begin of a code section.
*
* @param s
* the string to mark the begin of a code section
*/
public void setSectionBegin(final String s) {
this.sectionBegin = s;
}
/**
* end comment.
*/
private String sectionEnd = "END manual code section";
/**
* set the string to mark the end of a code section.
*
* @param s
* the string to mark the end of a code section
*/
public void setSectionEnd(final String s) {
this.sectionEnd = s;
}
/**
* unmatched section begin marker.
*/
private String sectionUnmatchedBegin = ">>> BEGIN unmatched code section";
/**
* set the string to mark the begin of an unmatched code section.
*
* @param s
* the string to mark the begin of an unmatched code section. If
* you take some characters that guarantee the compilation to
* fail then you instantly are aware of unmatched sections.
*/
public void setSectionUnmatchedBegin(final String s) {
this.sectionUnmatchedBegin = s;
}
/**
* unmatched section end marker.
*/
private String sectionUnmatchedEnd = ">>> END unmatched code section";
/**
* set the string to mark the end of an unmatched code section.
*
* @param s
* the string to mark the end of an unmatched code section. If
* you take some characters that guarantee the compilation to
* fail then you instantly are aware of unmatched sections.
*/
public void setSectionUnmatchedEnd(final String s) {
this.sectionUnmatchedEnd = s;
}
/**
* source file 1, the generated class frame.
*/
private CodeFileGen srcfilegen = null;
/**
* setter for source file 1, the generated class frame.
*
* @param f
* the new value for this property.
*/
public void setSrcfilegen(final File f) {
this.srcfilegen = new CodeFileGen(f);
}
/**
* source file 2, the class with manually coded method bodies.
*/
private CodeFileMan srcfileman = null;
/**
* setter for source file 2, the class with manually coded method bodies.
*
* @param f
* the new value for this property.
*/
public void setSrcfileman(final File f) {
if (f != null) {
this.srcfileman = new CodeFileMan(f);
}
}
/**
* destination file with the merged methods.
*/
private CodeFileDest destfile = null;
/**
* setter for the destination file with the merged methods.
*
* @param f
* the new value for this property.
*/
public void setDestfile(final File f) {
this.destfile = new CodeFileDest(f, this.antGateway);
}
/**
* the Ant gateway for convenient use of Ant tesks.
*/
private AntGateway antGateway = new AntGateway(new Project(), "");
/**
* The execute method has to be implemented from every Ant task.
*/
public void execute() {
try {
validateProperties();
this.log("srcfilegen = \"" + this.srcfilegen.getFile().getAbsolutePath(), Project.MSG_VERBOSE);
if (this.srcfileman != null) {
this.log("srcfileman = \"" + this.srcfileman.getFile().getAbsolutePath(), Project.MSG_VERBOSE);
}
if (this.destfile == null) {
this.destfile = new CodeFileDest(this.srcfileman.getFile(), this.antGateway);
} else {
this.log("destfile = \"" + this.destfile.getFile().getAbsolutePath(), Project.MSG_VERBOSE);
}
this.srcfilegen.parse(this.oneLineComment, this.sectionBegin, this.sectionEnd);
if (this.srcfileman != null && this.srcfileman.getFile().exists()) {
this.srcfileman.parse(this.oneLineComment, this.sectionBegin, this.sectionEnd);
}
if (this.srcfilegen.getFile().lastModified() > this.destfile.getFile().lastModified()
|| this.srcfileman.getFile().lastModified() > this.destfile.getFile().lastModified()) {
this.log("merging files", Project.MSG_VERBOSE);
if (this.srcfileman != null && this.srcfileman.getFile().exists()) {
this.log("merging new file\n" + this.srcfilegen.getFile().getAbsolutePath()
+ "\nwith bodies of file\n" + this.srcfileman.getFile().getAbsolutePath() + "\n to file\n"
+ this.destfile.getFile().getAbsolutePath() + "...", Project.MSG_VERBOSE);
} else {
this.log("copying new file\n" + this.srcfilegen.getFile().getAbsolutePath() + "\n to file\n"
+ this.destfile.getFile().getAbsolutePath() + "...", Project.MSG_VERBOSE);
}
this.destfile.merge(this.srcfilegen, this.srcfileman, this.sectionUnmatchedBegin,
this.sectionUnmatchedEnd);
this.destfile.write();
} else {
this.log("nothing to merge", Project.MSG_VERBOSE);
}
} catch (IOException e) {
throw new BuildException(e);
}
}
/**
* the propertie's validation method.
*/
protected void validateProperties() {
if (this.srcfilegen == null) {
throw new BuildException("No value defined for mandatory attribute \"srcfilegen\".");
}
if (this.srcfileman == null && this.destfile == null) {
throw new BuildException("No values defined for attributes \"srcfileman\" and "
+ "\"destfile\". Please ether define srcfileman or destfile.");
}
}
}