Re: [aspectwerkz-dev] AnnotationC task and anonymous inner class

the_mindstorm <[email protected]> Tue, 12 Oct 2004 13:06:04 +0300
Newsgroups gmane.comp.java.aspectwerkz.devel
Message-ID <[email protected]>
Attached is the new version of AnnotationCTask.

1. I have fixed a little problem in source fileset manipulation
2. I have added support for copying the annonymous inner classes if destdir is used. (currently I 
have used a matching pattern that will copy only 99 annonymous inner classes per source - I think it 
is enough).
3. I have added support for copying the resources from the clsdir-s to destdir in case this is used.
(copyResources attribute for the task).

I can see for the moment one drawback for this. Consider the following project structure
- src1: sources dir (compiled to cls)
- src2: sources dir (compiled to cls)
- cls: classes dir
- dest: destination for AnnotationCTask

If i will use the AnnotationCTask only on sources from src1, i will have in the dest dir the 
annonymous inner classes resulted from the compilation of src2 sources too.
I have tried to solve this problem, following to copy only the annonymous inner classes for the 
sources provided to AnnotationCTask. But as far a compilation unit (a source file) may contain more 
than 1 class there is no way to track this. So I concluded that it is better to have more then less 
necessary classes :-(.

I am waiting for your opinions.

./the_mindstorm


Quote [Alexandre Vasseur]:

> fine
> go for it, just copy the .class files
> 
> plz start a new thread for the javadoc stuff. I had time to do some
> brainstorming with my self :-)
> Alex
> 
> 
> On Sat, 09 Oct 2004 23:50:31 +0300, the_mindstorm
> <[email protected]> wrote:
>> Quote [Alexandre Vasseur]:
>> > i d rather have the equivalent of the Ant snip posted here
>> > (f.e. if you have .dtd / .xml / .properties stuff in your src dir then
>> > everything is preserved.)
>> > Alex
>> 
>> While I see the need for copying anonymous inner classes into the target directory directly from the
>> Ant task, I cannot see why such a task would manage resources. Usually this is done by another task
>> allowing filtering (or more complex tasks: replacing ,etc).
>> Afaik the only tool doing this automatically is the Eclipse builder (but I do not always agree this
>> being a good solution. For example I have some properties file that go inside the final jar and they
>> contain tokens to be replaced).
>> 
>> Surely the task is an easy one to be done, but I would like to clarify the needs before doing it. A
>> solution that will be more configurable would be to present in the task another property
>> (copyResources) to trigger this additional step. (I think I remember this option in AJ).
>> 
>> ./the_mindstorm
>> 
>> PS: unfortunately I wasn't able to finish today the AspectWerkzCTask, but I hope tomorrow will be a
>> more quiet day that will allow me to finish it.
>> 
>> PPS: while thinking of this I have remember at some point we have started a discussion about
>> generating AOP-ish javadoc. I would like to re-start this in order to allow me to start working on
>> it. What do you think?
>> 
>> 
>> 
>> 
>> >
>> >
>> > On Sat, 09 Oct 2004 17:20:08 +0300, the_mindstorm
>> > <[email protected]> wrote:
>> >> Quote [Alexandre Vasseur]:
>> >>
>> >> > exact !
>> >> > Thanks for remembering that Allan
>> >> > The AnnotationCTask is indeed the right place for it.
>> >> > Alex
>> >> >
>> >>
>> >> yep i will fix this asap. But i guess i could copy only the <name>$<name>.class, ain't it?
>> >>
>> >>
>> >> ./the_mindstorm
>> >>
>> >>
>> >>
>> >> >
>> >> > On Sat, 9 Oct 2004 09:12:17 -0300, Allan Jones <[email protected]> wrote:
>> >> >> one day, I wrote:
>> >> >>
>> >> >> "One minor bug.
>> >> >> When we use "-dest" in AnnotationC, Anonymous Inner Classes are not
>> >> >> copied to dest directory. When we run our classes,
>> >> >> java.lang.NoClassDefFoundError occurs"
>> >> >>
>> >> >> and Alexandre Vasseur said:
>> >> >>
>> >> >> "Ok the AnnotationC issue cannot be solved in the framework.
>> >> >>
>> >> >> Anonymous inner classes are not parsed by Qdox that is used to parse
>> >> >> the source code (note: cannot handle annotation on anonymous inner
>> >> >> class neither by the way). Thus the anonymous inner class is not
>> >> >> analyzed by AnnotationC.
>> >> >>
>> >> >> So when using -dest flag, add the following just after the call to
>> >> >> AnnotationC in your Ant script:
>> >> >>       <copy todir="classes/annotated" overwrite="false">
>> >> >>           <fileset dir="classes/regular"/>
>> >> >>       </copy>"
>> >> >>
>> >> >> Now that you´re doing an ant task, the task could resolve this problem
>> >> >> by copying the missing classes to the dest dir.
>> >> >>
>> >> >> --
>> >> >> Allan Jones
>> >> >> Summa Technologies do Brasil ltda.
>> >> >>
>> >> >
>> >>
>> >>
>> >
>> 
>>
>
AnnotationCTask.java (text/plain, 8.2 KB)
/************1**************************************************************************
 * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
 * http://aspectwerkz.codehaus.org                                                    *
 * ---------------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the LGPL license      *
 * a copy of which has been included with this distribution in the license.txt file.  *
 **************************************************************************************/
package org.codehaus.aspectwerkz.annotation;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.PatternSet;

/**
 * AnnotationC Ant task.
 *
 * @author <a href='mailto:[email protected]'>the_mindstorm(at)evolva(dot)ro</a>
 */
public class AnnotationCTask extends Task {
	private static final int SRC_NOTSET	 = 0;
	private static final int SRC_FILESET = 1;
	private static final int SRC_DIR		 = 2;
	private static final int SRC_DIRPATH = 3;
	private static final int SRC_FILE	 = 4;
	
	private static final String[]	MSG = {"", "srcfileset", "srcdir", "inner src", "srcincludes"};

	private static final String[] COPY_ANONYMOUS_INNER_PATTERN	= {"**/*$?.class","**/*$??.class"};
	private static final String COPY_ALL_PATTERN	= "**/*";
	private static final String EXCLUDE_CLASS_PATTERN = "**/*.class";

	protected Path		classpath;
	protected boolean	verbose;
	protected Path		srcdir;
	protected List		filesets	= new ArrayList();
	protected File		srcincludes;
	protected Path		clsdir;
	protected File		properties;
	protected File		destdir;
	protected boolean	copyResources;
	// deal with duplicated parameters
	protected int		srcParamType = SRC_NOTSET;

	public void setCopyResources(boolean copy) {
		this.copyResources = copy;
	}

	public void setVerbose(boolean isVerbose) {
		this.verbose = true;
	}

	public void setDestdir(File destdir) {
		this.destdir = destdir;
	}

	public Path createSrc() {
		if(isSet(SRC_DIRPATH)) {
			throw tooManyParameters(SRC_DIRPATH);
		}
		if(srcdir == null) {
			srcdir = new Path(getProject());
		}
		return srcdir.createPath();
	}

	public void setSrcdir(Path srcdir) {
		if(isSet(SRC_DIR)) {
			throw tooManyParameters(SRC_DIR);
		}

		if(this.srcdir == null) {
			this.srcdir = srcdir;
		} else {
			this.srcdir.append(srcdir);
		}
	}

	public void setSrcincludes(File srcinclude) {
		if(isSet(SRC_FILE)) {
			throw tooManyParameters(SRC_FILE);
		}
		this.srcincludes = srcinclude;
	}

	public void addSrcfileset(FileSet set) {
		if(isSet(SRC_FILESET)) {
			throw tooManyParameters(SRC_FILESET);
		}
		this.filesets.add(set);
	}

	public Path createCls() {
		if(this.clsdir == null) {
			clsdir = new Path(getProject());
		}
		return clsdir.createPath();
	}

	public void setClsdir(Path clsdir) {
		if(this.clsdir == null) {
			this.clsdir = clsdir;
		} else {
			this.clsdir.append(clsdir);
		}
	}

	public void setProperties(File annotationFile) {
		this.properties = annotationFile;
	}

	public void execute() throws BuildException {
		try {
			if((this.srcdir == null || this.srcdir.size() == 0) && this.filesets.size() == 0
					&& (this.srcincludes == null || !this.srcincludes.isFile())) {
				throw new BuildException("srcdir, srcfileset and srcincludes cannot be all empty");
			}
			if(this.clsdir == null || this.clsdir.size() == 0) {
				throw new BuildException("clsdir cannot be null");
			}
			if(this.properties == null || !this.properties.isFile()) {
				throw new BuildException("properties is not a valid file [" + this.properties + "]");
			}

			boolean useDirs = (srcdir != null && srcdir.size() != 0);

			String[] srcList = null;
			switch(this.srcParamType) {
				case SRC_DIR:
				case SRC_DIRPATH:
					srcList = getDirList(this.srcdir, "srcdir", true);
					break;
				case SRC_FILESET:
					srcList = getSourceFiles(this.filesets);
					break;
				case SRC_FILE:
					srcList = getSourceFiles(this.srcincludes);
					break; 
			}

			AnnotationC.compile(this.verbose, 
					srcList, 
					useDirs, 
					getDirList(this.clsdir, "cls", true),
					this.destdir == null ? null : this.destdir.getAbsolutePath(), 
					this.properties.getAbsolutePath()
			);

			if(this.destdir != null) {
				runCopy();
			}
		} catch(Exception e) {
			e.printStackTrace();
			throw new BuildException(e);
		}
	}

	private void runCopy() throws BuildException {
		Copy copy = new Copy();

		copy.setProject(getProject());
		copy.setTodir(this.destdir);
		copy.setOverwrite(false);
		copy.setTaskName("copy");
		copy.setVerbose(this.verbose);

		String[] clsDirs = getDirList(this.clsdir, "cls", false);
		
		for(int i = 0; i < clsDirs.length; i++) {
			FileSet anonFs  = new FileSet();
			File clsFileDir = new File(clsDirs[i]);
			
			anonFs.setDir(clsFileDir);
			
			for(int j=0; j<COPY_ANONYMOUS_INNER_PATTERN.length; j++) {
				PatternSet psAnonymous = anonFs.createPatternSet();
				psAnonymous.setIncludes(COPY_ANONYMOUS_INNER_PATTERN[j]);
			}
			
			copy.addFileset(anonFs);
			
			if(this.copyResources) {
				FileSet resourceFs = new FileSet();
				
				resourceFs.setDir(clsFileDir);
				PatternSet psIncludes = resourceFs.createPatternSet();
				psIncludes.setIncludes(COPY_ALL_PATTERN);
				PatternSet psExcludes = resourceFs.createPatternSet();
				psExcludes.setExcludes(EXCLUDE_CLASS_PATTERN);
				
				copy.addFileset(resourceFs);
			}
		}

		
		copy.execute();
	}

	private String[] getSourceFiles(List fsList) throws BuildException {
		List allfiles = new ArrayList();

		// deal with the filesets
		for(int i = 0; i < filesets.size(); i++) {
			FileSet fs = (FileSet) filesets.get(i);
			DirectoryScanner ds = fs.getDirectoryScanner(getProject());

			File fromDir = fs.getDir(getProject());

			String[] srcFiles = ds.getIncludedFiles();
			for(int j = 0; j < srcFiles.length; j++) {
				allfiles.add(fromDir + File.separator + srcFiles[j]);
			}
		}

		return (String[]) allfiles.toArray(new String[allfiles.size()]);
	}

	private String[] getSourceFiles(File file) throws BuildException {
		List files = new ArrayList();
		BufferedReader reader = null;

		try {
			reader = new BufferedReader(new FileReader(file));

			String line = reader.readLine();
			File tmpFile = null;
			while(line != null) {
				if(line.length() > 0) {
					tmpFile = getProject().resolveFile(line);
					if(!tmpFile.isFile()) {
						log("file not found: [" + tmpFile + "]", Project.MSG_WARN);
					} else {
						files.add(tmpFile.getAbsolutePath());
					}
				}
				line = reader.readLine();
			}
		} catch(IOException ioe) {
			throw new BuildException("an error occured while reading from pattern file: " + file,
					ioe);
		} finally {
			if(null != reader) {
				try {
					reader.close();
				} catch(IOException ioe) {
					;//Ignore exception
				}
			}
		}

		return (String[]) files.toArray(new String[files.size()]);
	}

	private String[] getDirList(Path path, String type, boolean check) throws BuildException {
		String[] list = path.list();
		List dirs = new ArrayList();

		for(int i = 0; i < list.length; i++) {
			File dir = getProject().resolveFile(list[i]);
			if(check) {
				if(!dir.exists()) {
					throw new BuildException(type + " \"" + dir.getPath() + "\" does not exist!",
							getLocation());
				}
			}

			dirs.add(dir.getAbsolutePath());
		}

		return (String[]) dirs.toArray(new String[dirs.size()]);
	}

	/**
	 * Checks the src related parameters.
	 */
	private boolean isSet(int srcParam) {
		if(SRC_NOTSET == this.srcParamType) { // nothing is set
			this.srcParamType = srcParam;
			return false;
		}
		if(SRC_DIRPATH == srcParam && SRC_DIRPATH == this.srcParamType) { // inner is allowed multiple times
			return false;
		}
		return true;
	}

	private BuildException tooManyParameters(int srcType) {
		return new BuildException("cannot set both " + MSG[this.srcParamType] + " and "
				+ MSG[srcType]);
	}
}