How to prepend a license statement to numerous Java source code files

I am working on a project, and had not really decided on which Open Source license to use. In retrospect, I decided on the Apache 2.0 license. However, I had already coded up a lot of Java files. My IDE of choice was to use Eclipse. In Eclipse 3.4, you can set the IDE to insert a license at the top of the Java source code file (under Windows -> Preferences -> Java -> Code Style -> Code Templates). However, this procedure only affects Java source code files created after the settings have been made.

So, what about those Java source code files created in the past? One might be ambitious enough to manually open up each file and insert the license statement at the top (the top of a Java source code file is where most license statements are placed). But, it is known that computer programmers are “lazy” (we are not lazy, but seek out efficient, effective, and automated ways of solving problems), and doing something this laborious would go against our very genetic code (pun intended). One would think that it would be easy to just use the Java I/O API to open a file and prepend the license statement. However, the java.io.FileWriter class only has an append method (add content to the end of the file). In fact, opening a file for prepending is not supported at all using Java. It’s not so much that Java does not support this operation, but the file systems of operating systems do not support such operation.

As an additional side note, to my knowledge, I do not know of any guideline from any Open Source license specifying where precisely to place the license statement in the source code file, and if placing the license statement in a place other than specified, will render the work unprotected by the license. From observing many Open Source source code files, the convention is to place the license statement at the top of the file, right before the package declaration (it’s the very first content displayed). We could just as well append the license statement to the end of the source code file, and in my opinion, this placement will equally protect the work under the license as placing it at the top. But, if we are to append the license statement instead of prepending it, then life would be much too easy and this article would be irrelevant.

So, what do we do to prepend a license statement to a big set of Java source files? One approach, and the one I have taken, involves creating a temporary file. The idea is simple:

  1. create a temporary file,
  2. add the license statement to the temporary file,
  3. add the source code from the original Java file to the temporary file, and
  4. copy the contents of the temporary Java file back into the original Java source code file.

Here is my code to prepend a license statement to a directory of Java source code files.

First, I define an interface called, LicenseWriter. It has one method called, write. The method, write, takes in as input a File object (the file to which we will prepend a license statement).

/**
 * Copyright 2009 Jee Vang 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0 
 *  
 *  Unless required by applicable law or agreed to in writing, software 
 *  distributed under the License is distributed on an "AS IS" BASIS, 
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 *  See the License for the specific language governing permissions and 
 *  limitations under the License. 
 */
package com.vang.jee.util.intf;

import java.io.File;

/**
 * License writer.
 * 
 * @author Jee Vang
 *
 */
public interface LicenseWriter {

	/**
	 * Write license to the output stream.
	 * 
	 * @param file File to write license.
	 * @throws Exception
	 */
	public void write(File file) throws Exception;
}

Here is the implementation, LicenseWriterImpl, of the interface above. In the LicenseWriterImpl method, write, we create a temporary file to which we will write first the license statement and then the source code from the original Java file. When we are done writing to the temporary file, then we read the contents of the temporary file and write it back to the original Java source code file. To run this program, two parameters are required:

  1. the directory where the Java source code files are located, and
  2. the file holding the license statement.

The code will recurse into the directory holding the Java source code files and for every such file, prepend the license statement to the source code.

/**
* Copyright 2009 Jee Vang
*
* Licensed under the Apache License, Version 2.0 (the “License”);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an “AS IS” BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vang.jee.util.impl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import com.vang.jee.util.intf.LicenseWriter;

/**
* License writer implementation. Prepends a license
* statement to a file.
*
* @author Jee Vang
*
*/
public class LicenseWriterImpl implements LicenseWriter {

/**
* File name prefix for temporary file.
*/
public static String TEMP_FILE_PREFIX = “TEMP_”;

/**
* File with license statement.
*/
private File licenseFile;

/**
* String to add after every line. i.e. \r\n or just \n.
*/
private String newLine = “\r\n”;

/**
* License statement in license file.
*/
private String _licenseStatement;

/**
* Constructor.
*/
public LicenseWriterImpl() {

}

/**
* Constructor.
* @param licenseFile File with license statement.
*/
public LicenseWriterImpl(File licenseFile) {
this.licenseFile = licenseFile;
}

public void write(File file) throws Exception {
BufferedWriter writer = null;
BufferedReader reader = null;

try {
//get the new line string
String newLine = getNewLine();

//get the temporary file that we will write to
File tempFile = getTemporaryFile(file);
//create the writer that will write to the temporary file
writer = new BufferedWriter(new FileWriter(tempFile));

//create the reader that will read from the file
reader = new BufferedReader(new FileReader(file));

//write the license statement to the temporary file
String licenseStatement = getLicenseStatement();
writer.write(licenseStatement);
writer.write(newLine);

//now read in the contents of the file, and write it to the temp file
String line = null;
while(null != (line = reader.readLine())) {
writer.write(line);
writer.write(newLine);
}

//close the writer and reader
writer.close();
reader.close();

//now read from the temp file and copy the contents to the original file
writer = new BufferedWriter(new FileWriter(file));
reader = new BufferedReader(new FileReader(tempFile));

while(null != (line = reader.readLine())) {
writer.write(line);
writer.write(newLine);
}

//close the writer and reader
writer.close();
reader.close();

//delete the temporary file
tempFile.delete();
} catch(Exception ex) {
throw ex;
} finally {
if(null != writer) {
try {
writer.close();
writer = null;
} catch(Exception ex) { }
}

if(null != reader) {
try {
reader.close();
reader = null;
} catch(Exception ex) { }
}
}
}

/**
* Get a temporary file from a file.
* @param file File.
* @return Temporary file.
* @throws IOException
*/
public File getTemporaryFile(File file) throws IOException {
String temporaryFileName = getTemporaryFileName(file);
File tempFile = new File(file.getParent(), temporaryFileName);
return tempFile;
}

/**
* Get file name for temporary file.
* @param file File.
* @return File name for temporary file.
*/
public String getTemporaryFileName(File file) {
StringBuffer sb = new StringBuffer();
sb.append(TEMP_FILE_PREFIX);
sb.append(file.getName());
return sb.toString();
}

/**
* Get license statement from license.
* @return License statement.
* @throws Exception
*/
public String getLicenseStatement() throws Exception {
StringBuffer sb = new StringBuffer();
if(null == _licenseStatement) {
BufferedReader reader = null;
try {
File licenseFile = getLicenseFile();
reader = new BufferedReader(new FileReader(licenseFile));
String line = null;
while(null != (line = reader.readLine())) {
sb.append(line);
sb.append(getNewLine());
}
} catch(Exception ex) {
throw ex;
} finally {
if(null != reader) {
try {
reader.close();
reader = null;
} catch(Exception ex) { }
}
}
}
return sb.toString();
}

/**
* Get file with license statement.
* @return the licenseFile File.
*/
public File getLicenseFile() {
return licenseFile;
}

/**
* Set file with license statement.
* @param licenseFile File.
*/
public void setLicenseFile(File licenseFile) {
this.licenseFile = licenseFile;
}

/**
* Get the new line string.
* @return New line string.
*/
public String getNewLine() {
return newLine;
}

/**
* Set the new line string.
* @param newLine New line string.
*/
public void setNewLine(String newLine) {
this.newLine = newLine;
}

/**
* Main method.
* @param args Expects 2 arguments. 1) java source directory
* and 2) file with license statement.
*/
public static void main(String[] args) {
//expects two arguments
if(args.length != 2) {
throw new IllegalArgumentException(“Need to supply 2 arguments: ” +
“1) java source directory and ” +
“2) file with license statement.”);
}

//java source directory is first parameter
String directory = args[0];
File dir = new File(directory);

//name of file with license statement is second parameter
String licenseFileName = args[1];
File licenseFile = new File(licenseFileName);

//construct the license writer
LicenseWriter licenseWriter = new LicenseWriterImpl(licenseFile);

try {
//prepend license statement to files in directory
prependLicenseStatement(dir, licenseWriter);
} catch(Exception ex) {
ex.printStackTrace();
}
}

/**
* Prepend license statement to file. If the file is a directory,
* then recurses into the directory.
* @param file File or directory which to prepend license statement.
* @param licenseWriter License writer.
* @throws Exception
*/
public static void prependLicenseStatement(File file, LicenseWriter licenseWriter) throws Exception {
//if file is a directory
if(file.isDirectory()) {
//get the children of this directory
String[] fileNames = file.list();

for(int i=0; i < fileNames.length; i++) { String fileName = fileNames[i]; File f = new File(file, fileName); //attempt to prepend license statement to child prependLicenseStatement(f, licenseWriter); } } else { //base case, if file represents a file //if file does not have java extension then return if(!file.getName().endsWith("java")) { return; } //else write license to file licenseWriter.write(file); } } } [/source] There are many ways to improve this code. One way would be to make the implementation class unit-testable. Another would be to allow hooks for formatting of the original source code file into the temporary source code file. For example, in all my Java source code files, before the package declaration at the very top, empty comments have been added. If we simply prepended the license statement, then these empty comments would still be present. One could simply write another utility class to remove these empty comments. In conclusion, prepending content (in this case, a license statement) to a large number of Java source code files (after they have already been coded) is not trivial and supported out-of-the-box using Java or an IDE (such as Eclipse, in this case). The approach taken in this article uses a temporary file to write to and store the content in the appropriate order (license statement first, followed by code). A little thought and some coding is required to prepend a license statement to a large set of Java source code files, but, the effort is well worth it since this approach avoids manually opening each Java source code file and prepending as needed. Happing programming! Zoo siab! Cheers!

Advertisements

4 thoughts on “How to prepend a license statement to numerous Java source code files

  1. Of course this is a very solution.
    Why didnt you think of using some options like Perl5Util, using which we can do the same thing?

  2. Of course this is a very good solution.
    Why didn’t you use some options like Perl5Util, using which we can do the same thing with ease?

  3. That assumes I know Perl. I know of Perl, but I don’t have any real experience using Perl. Please let me know how this objective can be accomplished using Perl. Maybe we can co-blog about it. Thanks for reading. Cheers!

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s