/************************************************************************************** * 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.definition; import org.codehaus.aspectwerkz.expression.ExpressionInfo; import org.codehaus.aspectwerkz.expression.ExpressionNamespace; import org.codehaus.aspectwerkz.expression.regexp.Pattern; import org.codehaus.aspectwerkz.util.Strings; import org.codehaus.aspectwerkz.aspect.AdviceType; import org.codehaus.aspectwerkz.DeploymentModel; import org.codehaus.aspectwerkz.reflect.MethodInfo; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.exception.DefinitionException; import java.util.Iterator; import java.util.Collection; /** * Helper class for the attribute and the XML definition parsers. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a> */ public class DefinitionParserHelper { public static final String EXPRESSION_PREFIX = "AW_"; /** * Creates and adds pointcut definition to aspect definition. * * @param name * @param expression * @param aspectDef */ public static void createAndAddPointcutDefToAspectDef(final String name, final String expression, final AspectDefinition aspectDef) { PointcutDefinition pointcutDef = new PointcutDefinition(expression); aspectDef.addPointcutDefinition(pointcutDef); // name can be the "pcName(paramType paramName)" // extract the parameter name to type map // and register the pointcut using its name //TODO: support for same pc name and different signature String pointcutName = name; String pointcutCallSignature = null; if (name.indexOf("(") > 0) { pointcutName = name.substring(0, name.indexOf("(")); pointcutCallSignature = name.substring(name.indexOf("(") + 1, name.lastIndexOf(")")); } // do a lookup first to avoid infinite recursion when: // <pointcut name="pc" ...> [will be registered as pc] // <advice bind-to="pc" ...> [will be registered as pc and should not override previous one !] ExpressionNamespace namespace = ExpressionNamespace.getNamespace(aspectDef.getQualifiedName()); ExpressionInfo info = namespace.getExpressionInfoOrNull(pointcutName); if (info == null) { info = new ExpressionInfo(expression, aspectDef.getQualifiedName()); // extract the pointcut signature map if (pointcutCallSignature != null) { String[] parameters = Strings.splitString(pointcutCallSignature, ","); for (int i = 0; i < parameters.length; i++) { String[] parameterInfo = Strings.splitString( Strings.replaceSubString(parameters[i].trim(), " ", " "), " " ); info.addArgument(parameterInfo[1], parameterInfo[0], aspectDef.getClassInfo().getClassLoader()); } } } ExpressionNamespace.getNamespace(aspectDef.getQualifiedName()).addExpressionInfo(pointcutName, info); } /** * Creates and adds a prepared pointcut definition to virtual aspect definition. * * @param name * @param expression * @param systemDef */ public static void createAndAddDeploymentScopeDef(final String name,//FIXME Depl scpope(prasen)field name not unique - need FQN final String expression, final SystemDefinition systemDef) { AspectDefinition aspectDef = systemDef.getAspectDefinition(Virtual.class.getName()); aspectDef.addPointcutDefinition(new PointcutDefinition(expression)); systemDef.addDeploymentScope(new DeploymentScope(name, expression)); } /** * Creates and adds an advisable definition to virtual aspect definition. * * @param expression * @param systemDef */ public static void createAndAddAdvisableDef(final String expression, final SystemDefinition systemDef) { AspectDefinition virtualAspectDef = systemDef.getAspectDefinition(Virtual.class.getName()); virtualAspectDef.addPointcutDefinition(new PointcutDefinition(expression)); AdviceDefinition virtualAdviceDef = (AdviceDefinition) virtualAspectDef.getBeforeAdviceDefinitions().get(0); ExpressionInfo oldExpressionInfo = virtualAdviceDef.getExpressionInfo(); String newExpression; if (oldExpressionInfo != null) { String oldExpression = oldExpressionInfo.toString(); newExpression = oldExpression + " || " + expression; } else { newExpression = expression; } virtualAdviceDef.setExpressionInfo( new ExpressionInfo( newExpression, virtualAspectDef.getQualifiedName() ) ); } /** * Attaches all deployment scopes in a system to the virtual advice. * * @param systemDef the system definition */ public static void attachDeploymentScopeDefsToVirtualAdvice(final SystemDefinition systemDef) { final AspectDefinition virtualAspectDef = systemDef.getAspectDefinition(Virtual.class.getName()); final AdviceDefinition virtualAdviceDef = (AdviceDefinition) virtualAspectDef.getBeforeAdviceDefinitions().get( 0 ); final StringBuffer newExpression = new StringBuffer(); final ExpressionInfo oldExpressionInfo = virtualAdviceDef.getExpressionInfo(); if (oldExpressionInfo != null) { String oldExpression = oldExpressionInfo.toString(); newExpression.append(oldExpression); } final Collection deploymentScopes = systemDef.getDeploymentScopes(); if (deploymentScopes.size() != 0 && oldExpressionInfo != null) { newExpression.append(" || "); } for (Iterator it = deploymentScopes.iterator(); it.hasNext();) { DeploymentScope deploymentScope = (DeploymentScope) it.next(); newExpression.append(deploymentScope.getExpression()); if (it.hasNext()) { newExpression.append(" || "); } } if (newExpression.length() != 0) { virtualAdviceDef.setExpressionInfo( new ExpressionInfo( newExpression.toString(), virtualAspectDef.getQualifiedName() ) ); } } /** * Creates and add mixin definition to system definition. * * @param mixinClassInfo * @param expression * @param deploymentModel * @param isTransient * @param systemDef * @return the mixin definition */ public static MixinDefinition createAndAddMixinDefToSystemDef(final ClassInfo mixinClassInfo, final String expression, final DeploymentModel deploymentModel, final boolean isTransient, final SystemDefinition systemDef) { final MixinDefinition mixinDef = createMixinDefinition( mixinClassInfo, expression, deploymentModel, isTransient, systemDef ); // check doublons - TODO change ArrayList to HashMap since NAME is a key MixinDefinition doublon = null; for (Iterator intros = systemDef.getMixinDefinitions().iterator(); intros.hasNext();) { MixinDefinition intro = (MixinDefinition) intros.next(); if (intro.getMixinImpl().getName().equals(mixinDef.getMixinImpl().getName())) { doublon = intro; intro.addExpressionInfos(mixinDef.getExpressionInfos()); break; } } if (doublon == null) { systemDef.addMixinDefinition(mixinDef); } return mixinDef; } /** * Creates and add interface introduction definition to aspect definition. * * @param expression * @param introductionName * @param interfaceClassName * @param aspectDef */ public static void createAndAddInterfaceIntroductionDefToAspectDef(final String expression, final String introductionName, final String interfaceClassName, final AspectDefinition aspectDef) { // Introduction name is unique within an aspectDef only InterfaceIntroductionDefinition introDef = createInterfaceIntroductionDefinition( introductionName, expression, interfaceClassName, aspectDef ); aspectDef.addInterfaceIntroductionDefinition(introDef); } /** * Creates a new advice definition. * * @param adviceName the advice name * @param adviceType the advice type * @param expression the advice expression * @param specialArgumentType the arg * @param aspectName the aspect name * @param aspectClassName the aspect class name * @param methodInfo the advice methodInfo * @param aspectDef the aspect definition * @return the new advice definition */ public static AdviceDefinition createAdviceDefinition(final String adviceName, final AdviceType adviceType, final String expression, final String specialArgumentType, final String aspectName, final String aspectClassName, final MethodInfo methodInfo, final AspectDefinition aspectDef) { ExpressionInfo expressionInfo = new ExpressionInfo( expression, aspectDef.getQualifiedName() ); // support for pointcut signature String adviceCallSignature = null; String resolvedSpecialArgumentType = specialArgumentType; if (adviceName.indexOf('(') > 0) { adviceCallSignature = adviceName.substring(adviceName.indexOf('(') + 1, adviceName.lastIndexOf(')')); String[] parameters = Strings.splitString(adviceCallSignature, ","); for (int i = 0; i < parameters.length; i++) { String[] parameterInfo = Strings.splitString( Strings.replaceSubString(parameters[i].trim(), " ", " "), " " ); // Note: for XML defined aspect, we support anonymous parameters like // advice(JoinPoint, Rtti) as well as abbreviations, so we have to assign // them a name here, as well as their real type String paramName, paramType = null; if (parameterInfo.length == 2) { paramName = parameterInfo[1]; paramType = parameterInfo[0]; } else { paramName = "anonymous_" + i; paramType = (String) Pattern.ABBREVIATIONS.get(parameterInfo[0]); } // skip the parameter if this ones is a after returning / throwing binding if (paramName.equals(specialArgumentType)) { resolvedSpecialArgumentType = paramType; expressionInfo.setSpecialArgumentName(paramName); } else { expressionInfo.addArgument(paramName, paramType, aspectDef.getClassInfo().getClassLoader()); } } } // check that around advice return Object else the compiler will fail if (adviceType.equals(AdviceType.AROUND)) { if (!"java.lang.Object".equals(methodInfo.getReturnType().getName())) { throw new DefinitionException( "around advice must return java.lang.Object : " + aspectClassName + "." + methodInfo.getName() ); } } final AdviceDefinition adviceDef = new AdviceDefinition( adviceName, adviceType, resolvedSpecialArgumentType, aspectName, aspectClassName, expressionInfo, methodInfo, aspectDef ); return adviceDef; } /** * Creates an introduction definition. * * @param mixinClassInfo * @param expression * @param deploymentModel * @param isTransient * @param systemDef * @return */ public static MixinDefinition createMixinDefinition(final ClassInfo mixinClassInfo, final String expression, final DeploymentModel deploymentModel, final boolean isTransient, final SystemDefinition systemDef) { final MixinDefinition mixinDef = new MixinDefinition(mixinClassInfo, deploymentModel, isTransient, systemDef); if (expression != null) { ExpressionInfo expressionInfo = new ExpressionInfo(expression, systemDef.getUuid()); // auto-name the pointcut which is anonymous for introduction ExpressionNamespace.getNamespace(systemDef.getUuid()).addExpressionInfo( EXPRESSION_PREFIX + expression.hashCode(), expressionInfo ); mixinDef.addExpressionInfo(expressionInfo); } return mixinDef; } /** * Creates a new interface introduction definition. * * @param introductionName the introduction name * @param expression the pointcut expression * @param interfaceClassName the class name of the interface * @param aspectDef the aspect definition * @return the new introduction definition */ public static InterfaceIntroductionDefinition createInterfaceIntroductionDefinition(final String introductionName, final String expression, final String interfaceClassName, final AspectDefinition aspectDef) { final InterfaceIntroductionDefinition introDef = new InterfaceIntroductionDefinition( introductionName, interfaceClassName ); if (expression != null) { ExpressionInfo expressionInfo = new ExpressionInfo(expression, aspectDef.getQualifiedName()); // auto-name the pointcut which is anonymous for introduction ExpressionNamespace.getNamespace(aspectDef.getQualifiedName()).addExpressionInfo( EXPRESSION_PREFIX + expression.hashCode(), expressionInfo ); introDef.addExpressionInfo(expressionInfo); } return introDef; } }