/**************************************************************************************
* 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 QPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.attribdef.definition;
import java.util.ArrayList;
import java.util.List;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaField;
import org.codehaus.aspectwerkz.metadata.QDoxParser;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.AttributeEnhancer;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.AspectAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.AroundAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.BeforeAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.AfterAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.IntroduceAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.ImplementsAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.ExecutionAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.CallAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.ClassAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.SetAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.GetAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.ThrowsAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.CFlowAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.CustomAttribute;
import org.codehaus.aspectwerkz.attribdef.definition.attribute.bcel.BcelAttributeEnhancer;
/**
* Compiles attributes for the aspects. Can be called from the command line.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r</a>
* @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur</a>
*/
public class AspectC {
public static final String ATTR_GENERIC_PREFIX = "Attribute.";
public static final String ATTR_ASPECT = "Aspect";
public static final String ATTR_EXECUTION = "Execution";
public static final String ATTR_CALL = "Call";
public static final String ATTR_CLASS = "Class";
public static final String ATTR_SET = "Set";
public static final String ATTR_GET = "Get";
public static final String ATTR_THROWS = "Throws";
public static final String ATTR_CFLOW = "CFlow";
public static final String ATTR_AROUND = "Around";
public static final String ATTR_BEFORE = "Before";
public static final String ATTR_AFTER = "After";
public static final String ATTR_INTRODUCE = "Introduce";
public static final String ATTR_IMPLEMENTS = "Implements";
/**
* Verbose logging.
*/
private boolean m_verbose = false;
/**
* Set verbose mode.
*
* @param verbose
*/
public void setVerbose(final boolean verbose) {
m_verbose = verbose;
}
/**
* Compiles attributes for the aspects.
*
* @param sourcePath the path to the sources to compile attributes for
* @param classPath the path to the compiled classes matching the source files
*/
public void compile(final String sourcePath, final String classPath) {
compile(sourcePath, classPath, classPath);
}
/**
* Compiles attributes for the aspects.
*
* @param sourcePath the path to the sources to compile attributes for
* @param classPath the path to the compiled classes matching the source files
* @param destDir the path where to write the compiled aspects
*/
public void compile(final String sourcePath,
final String classPath,
final String destDir) {
if (sourcePath == null) throw new IllegalArgumentException("source path can not be null");
if (classPath == null) throw new IllegalArgumentException("class path can not be null");
final QDoxParser qdoxParser = new QDoxParser(sourcePath);
String[] classNames = qdoxParser.getAllClassNames();
for (int i = 0; i < classNames.length; i++) {
String className = classNames[i];
AttributeEnhancer enhancer = new BcelAttributeEnhancer(); // TODO: use factory
if (enhancer.initialize(className, classPath)) {
if (qdoxParser.parse(className)) {
JavaClass javaClass = parseClassAttributes(qdoxParser, enhancer);
parseFieldAttributes(javaClass, enhancer);
parseMethodAttributes(javaClass, enhancer);
parseInnerClassAttributes(javaClass, enhancer);
enhancer.write(destDir);
}
}
}
}
/**
* Parses the class attributes.
*
* @param qdoxParser the QDox parser
* @param enhancer the enhancer
* @return the java class
*/
private JavaClass parseClassAttributes(final QDoxParser qdoxParser,
final AttributeEnhancer enhancer) {
JavaClass javaClass = qdoxParser.getJavaClass();
parseCustomAttributes(javaClass, enhancer);
parseAspect(javaClass, enhancer);
return javaClass;
}
/**
* Parses the field attributes.
*
* @param javaClass the java class
* @param enhancer the enhancer
*/
private void parseFieldAttributes(final JavaClass javaClass,
final AttributeEnhancer enhancer) {
JavaField[] javaFields = javaClass.getFields();
for (int j = 0; j < javaFields.length; j++) {
JavaField javaField = javaFields[j];
parseCustomAttributes(javaField, enhancer);
parseExecutionPointcut(javaField, enhancer);
parseCallPointcut(javaField, enhancer);
parseClassPointcut(javaField, enhancer);
parseSetPointcut(javaField, enhancer);
parseGetPointcut(javaField, enhancer);
parseThrowsPointcut(javaField, enhancer);
parseCFlowPointcut(javaField, enhancer);
parseImplementsPointcut(javaField, enhancer);
}
}
/**
* Parses the method attributes.
*
* @param javaClass the java class
* @param enhancer the enhancer
*/
private void parseMethodAttributes(final JavaClass javaClass,
final AttributeEnhancer enhancer) {
JavaMethod[] javaMethods = javaClass.getMethods();
for (int j = 0; j < javaMethods.length; j++) {
JavaMethod javaMethod = javaMethods[j];
parseCustomAttributes(javaMethod, enhancer);
parseAroundAdvice(javaMethod, enhancer);
parseBeforeAdvice(javaMethod, enhancer);
parseAfterAdvice(javaMethod, enhancer);
}
}
/**
* Parses the inner class attributes.
*
* @param javaClass the java class
* @param enhancer the enhancer
*/
private void parseInnerClassAttributes(final JavaClass javaClass,
final AttributeEnhancer enhancer) {
JavaClass[] innerClasses = javaClass.getInnerClasses();
for (int k = 0; k < innerClasses.length; k++) {
parseIntroduction(innerClasses[k], enhancer);
}
}
/**
* Parses the custom class attributes.
*
* @param javaClass the java class
* @param enhancer the attribute enhancer
*/
private void parseCustomAttributes(final JavaClass javaClass,
final AttributeEnhancer enhancer) {
DocletTag[] tags = javaClass.getTags();
for (int i = 0; i < tags.length; i++) {
DocletTag tag = tags[i];
if ( ! isCustomTag(tag) ) {
continue;
}
String name = tag.getName();
String value = tag.getValue();
// String[] parameters = tag.getParameters();
enhancer.insertClassAttribute(new CustomAttribute(name, value, null));
log("class [" + javaClass.getFullyQualifiedName() + "]");
log("\tattribute [" + name + " " + value + "]");
}
}
/**
* Parses the custom field attributes.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseCustomAttributes(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag[] tags = javaField.getTags();
for (int i = 0; i < tags.length; i++) {
DocletTag tag = tags[i];
if ( ! isCustomTag(tag) ) {
continue;
}
String name = tag.getName();
String value = tag.getValue();
// String[] parameters = tag.getParameters();
enhancer.insertFieldAttribute(javaField, new CustomAttribute(name, value, null));
log("field [" + javaField.getParentClass().getFullyQualifiedName() + "." + javaField.getName() + "]");
log("\tattribute [" + name + " " + value + "]");
}
}
/**
* Parses the custom method attributes.
*
* @param javaMethod the java method
* @param enhancer the attribute enhancer
*/
private void parseCustomAttributes(final JavaMethod javaMethod,
final AttributeEnhancer enhancer) {
DocletTag[] tags = javaMethod.getTags();
for (int i = 0; i < tags.length; i++) {
DocletTag tag = tags[i];
if ( ! isCustomTag(tag) ) {
continue;
}
String name = tag.getName();
String value = tag.getValue();
// String[] parameters = tag.getParameters();
enhancer.insertMethodAttribute(javaMethod, new CustomAttribute(name, value, null));
log("method [" + javaMethod.getParentClass().getFullyQualifiedName() + "." + javaMethod.getName() + "]");
log("\tattribute [" + name + " " + value + "]");
}
}
/**
* Parses the aspect attribute.
*
* @param javaClass the java class
* @param enhancer the attribute enhancer
*/
private void parseAspect(final JavaClass javaClass,
final AttributeEnhancer enhancer) {
DocletTag aspectTag = javaClass.getTagByName(ATTR_ASPECT);
if (aspectTag != null) {
String name = aspectTag.getNamedParameter("name");
String deploymentModel = null;
String[] parameters = aspectTag.getParameters();
for (int j = 0; j < parameters.length; j++) {
if (parameters[j].startsWith("name=")) {
continue;
}
else {
deploymentModel = parameters[j];
}
}
enhancer.insertClassAttribute(new AspectAttribute(name, deploymentModel));
log("aspect [" + javaClass.getFullyQualifiedName() + "]");
log("\tdeployment model [" + deploymentModel + "]");
}
}
/**
* Parses the execution pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseExecutionPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_EXECUTION);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new ExecutionAttribute(expression)
);
log("\texecution pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the call pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseCallPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_CALL);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new CallAttribute(expression)
);
log("\tcall pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the class pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseClassPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_CLASS);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new ClassAttribute(expression)
);
log("\tclass pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the set pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseSetPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_SET);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new SetAttribute(expression)
);
log("\tset pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the get pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseGetPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_GET);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new GetAttribute(expression)
);
log("\tget pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the throws pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseThrowsPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_THROWS);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new ThrowsAttribute(expression)
);
log("\tthrows pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the cflow pointcut attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseCFlowPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_CFLOW);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new CFlowAttribute(expression)
);
log("\tcflow pointcut [" + javaField.getName() + "::" + expression + "]");
}
/**
* Parses the implements attribute.
*
* @param javaField the java field
* @param enhancer the attribute enhancer
*/
private void parseImplementsPointcut(final JavaField javaField,
final AttributeEnhancer enhancer) {
DocletTag pointcutTag = javaField.getTagByName(ATTR_IMPLEMENTS);
if (pointcutTag == null) return;
String expression = pointcutTag.getValue();
enhancer.insertFieldAttribute(
javaField,
new ImplementsAttribute(expression)
);
log("\tinterface introduction [" + javaField.getType().getValue() + "::" + expression + "]");
}
/**
* Parses the around advice attribute.
*
* @param javaMethod the java method
* @param enhancer the attribute enhancer
*/
private void parseAroundAdvice(final JavaMethod javaMethod,
final AttributeEnhancer enhancer) {
DocletTag[] aroundAdviceTags = javaMethod.getTagsByName(ATTR_AROUND);
for (int i = 0; i < aroundAdviceTags.length; i++) {
DocletTag aroundAdviceTag = aroundAdviceTags[i];
String name = aroundAdviceTag.getNamedParameter("name");
StringBuffer buf = new StringBuffer();
String[] parameters = aroundAdviceTag.getParameters();
for (int j = 0; j < parameters.length; j++) {
String parameter = parameters[j];
if (parameter.startsWith("name=")) continue;
buf.append(parameter);
buf.append(' ');
}
String expression = buf.toString().trim();
enhancer.insertMethodAttribute(
javaMethod,
new AroundAttribute(name, expression)
);
log("\taround advice [" + javaMethod.getName() + "::" + expression + "]");
}
}
/**
* Parses the pre advice attribute.
*
* @param javaMethod the java method
* @param enhancer the attribute enhancer
*/
private void parseBeforeAdvice(final JavaMethod javaMethod,
final AttributeEnhancer enhancer) {
DocletTag[] beforeAdviceTags = javaMethod.getTagsByName(ATTR_BEFORE);
for (int i = 0; i < beforeAdviceTags.length; i++) {
DocletTag beforeAdviceTag = beforeAdviceTags[i];
String name = beforeAdviceTag.getNamedParameter("name");
StringBuffer buf = new StringBuffer();
String[] parameters = beforeAdviceTag.getParameters();
for (int j = 0; j < parameters.length; j++) {
String parameter = parameters[j];
if (parameter.startsWith("name=")) continue;
buf.append(parameter);
buf.append(' ');
}
String expression = buf.toString().trim();
enhancer.insertMethodAttribute(
javaMethod,
new BeforeAttribute(name, expression)
);
log("\tbefore advice [" + javaMethod.getName() + "::" + expression + "]");
}
}
/**
* Parses the post advice attribute.
*
* @param javaMethod the java method
* @param enhancer the attribute enhancer
*/
private void parseAfterAdvice(final JavaMethod javaMethod,
final AttributeEnhancer enhancer) {
DocletTag[] afterAdviceTags = javaMethod.getTagsByName(ATTR_AFTER);
for (int i = 0; i < afterAdviceTags.length; i++) {
DocletTag afterAdviceTag = afterAdviceTags[i];
String name = afterAdviceTag.getNamedParameter("name");
StringBuffer buf = new StringBuffer();
String[] parameters = afterAdviceTag.getParameters();
for (int j = 0; j < parameters.length; j++) {
String parameter = parameters[j];
if (parameter.startsWith("name=")) continue;
buf.append(parameter);
buf.append(' ');
}
String expression = buf.toString().trim();
enhancer.insertMethodAttribute(
javaMethod,
new AfterAttribute(name, expression)
);
log("\tafter advice [" + javaMethod.getName() + "::" + expression + "]");
}
}
/**
* Parse the @Introduce inner class introductions
*
* @param innerClass
* @param enhancer
*/
private void parseIntroduction(final JavaClass innerClass, final AttributeEnhancer enhancer) {
DocletTag[] introductionTags = innerClass.getTagsByName(ATTR_INTRODUCE);
// support multiples @Introduce tags
for (int i = 0; i < introductionTags.length; i++) {
DocletTag introductionTag = introductionTags[i];
// deploymentModel= parameter
String deploymentModel = introductionTag.getNamedParameter("deploymentModel");
StringBuffer buf = new StringBuffer();
String[] parameters = introductionTag.getParameters();
for (int j = 0; j < parameters.length; j++) {
String parameter = parameters[j];
if (parameter.startsWith("deploymentModel=")) continue;
buf.append(parameter);
buf.append(' ');
}
String expression = buf.toString().trim();
//directly implemented interfaces
JavaClass[] introducedInterfaceClasses = innerClass.getImplementedInterfaces();
String[] introducedInterfaceNames = new String[introducedInterfaceClasses.length];
for (int j = 0; j < introducedInterfaceClasses.length; j++) {
introducedInterfaceNames[j] = introducedInterfaceClasses[j].getFullyQualifiedName();
log("\tintroduction introduce [" + introducedInterfaceNames[j] + "]");
}
// // This snip shows that QDox builds up class hierarchy correctly
// // [ extends junit.framework.TestCase in a mixin and remove junit.jar from path ]
// // ONLY IF super classes are in system classpath
// // ELSE ignores silently.
// // This is not what we want for implicit interfaces
// JavaClass base = innerClass;
// while (base != null) {
// System.out.println(base.getFullyQualifiedName());
// base = base.getSuperJavaClass();
// }
// no explicit implements directive
if (introducedInterfaceNames.length == 0) {
introducedInterfaceNames = enhancer.getNearestInterfacesInHierarchy(innerClass.getFullyQualifiedName());
if (introducedInterfaceNames.length == 0) {
throw new RuntimeException("no implicit interfaces found for " + innerClass.getFullyQualifiedName());
}
for (int j = 0; j < introducedInterfaceNames.length; j++) {
log("\tintroduction introduce implicit [" + introducedInterfaceNames[j] +"]");
}
}
enhancer.insertClassAttribute(
new IntroduceAttribute(
expression,
innerClass.getFullyQualifiedName(),
introducedInterfaceNames,
deploymentModel)
);
log("\tintroduction impl [" + innerClass.getName() + "::" + expression + "] " + deploymentModel);
}
}
/**
* Checks if the attribute is an aspectwerkz specific custom attribute.
* Ie the atribute starts with @Attribute.
*
* @param tag the tag
* @return boolean
*/
private boolean isCustomTag(final DocletTag tag) {
return tag.getName().startsWith(ATTR_GENERIC_PREFIX);
}
/**
* Logs a message.
*
* @TODO: do not log using System.out.println
*
* @param message the message to log
*/
private void log(final String message) {
if (m_verbose) {
System.out.println("AspectC::INFO - " + message);
}
}
/**
* Runs the compiler from the command line.
*
* @param args
*/
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("AspectWerkz (c) 2002-2003 Jonas Bon�r, Alexandre Vasseur");
System.out.println("usage: java [options...] org.codehaus.aspectwerkz.attribdef.definition.AspectC [-verbose] <path to src dir> <path to classes dir> [<path to destination dir>]");
System.out.println(" <path to destination dir> is optional, if omitted the compiled classes will be written to the initial directory");
System.out.println(" use -verbose to activate verbose logging");
System.exit(0);
}
// analyse arguments and options
List arguments = new ArrayList();
List options = new ArrayList();
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("-")) {
options.add(args[i]);
}
else {
arguments.add(args[i]);
}
}
AspectC compiler = new AspectC();
compiler.setVerbose(options.contains("-verbose"));
compiler.log("compiling attributes...");
if (arguments.size() == 2) {
compiler.compile((String)arguments.get(0), (String)arguments.get(1));
compiler.log("compiled classes written to " + (String)arguments.get(1));
}
else {
compiler.compile((String)arguments.get(0), (String)arguments.get(1), (String)arguments.get(2));
compiler.log("compiled classes written to " + (String)arguments.get(2));
}
compiler.log("compilation successful");
}
}