📄 aspectjadviceparameternamediscoverer.java
字号:
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Binding of returning parameter '" +
this.returningName + "' is ambiguous, there are " + this.numberOfRemainingUnboundArguments
+ " candidates.");
}
// we're all set... find the unbound parameter, and bind it.
for (int i = 0; i < this.parameterNameBindings.length; i++) {
if (this.parameterNameBindings[i] == null) {
bindParameterName(i,this.returningName);
break;
}
}
}
}
/**
* Parse the string pointcut expression looking for:
* @this, @target, @args, @within, @withincode, @annotation. If
* we find one of these pointcut expressions, try and extract a candidate variable name
* (or variable names, in the case of args).
*
* Some more support from AspectJ in doing this exercise would be nice...:)
*/
private void maybeBindAnnotationsFromPointcutExpression() {
List varNames = new ArrayList();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression," ");
for (int i = 0; i < tokens.length; i++) {
String toMatch = tokens[i];
int firstParenIndex = toMatch.indexOf("(");
if (firstParenIndex != -1) {
toMatch = toMatch.substring(0,firstParenIndex);
}
if (singleValuedAnnotationPcds.contains(toMatch)) {
PointcutBody body = getPointcutBody(tokens,i);
i += body.numTokensConsumed;
String varName = maybeExtractVariableName(body.text);
if (varName != null) {
varNames.add(varName);
}
} else if (tokens[i].startsWith("@args(") || tokens[i].equals("@args")) {
PointcutBody body = getPointcutBody(tokens,i);
i += body.numTokensConsumed;
maybeExtractVariableNamesFromArgs(body.text,varNames);
}
}
bindAnnotationsFromVarNames(varNames);
}
/**
* Match the given list of extracted variable names to argument slots
* @param varNames
*/
private void bindAnnotationsFromVarNames(List varNames) {
if (!varNames.isEmpty()) {
// we have work to do...
int numAnnotationSlots = countNumberOfUnboundAnnotationArguments();
if (numAnnotationSlots > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" potential annotation variable(s), and " +
numAnnotationSlots + " potential argument slots");
} else if (numAnnotationSlots == 1) {
if (varNames.size() == 1) {
// it's a match
findAndBind(annotationClass,(String) varNames.get(0));
} else {
// multiple candidate vars, but only one slot
throw new IllegalArgumentException("Found " + varNames.size() +
" candidate annotation binding variables" +
" but only one potential argument binding slot" );
}
} else {
// no slots so presume those candidate vars were actually type names
}
}
}
/**
* If the token starts meets Java identifier conventions, it's in.
* @param candidateToken
* @return
*/
private String maybeExtractVariableName(String candidateToken) {
if (candidateToken == null) { return null; }
if (Character.isJavaIdentifierStart(candidateToken.charAt(0)) &&
Character.isLowerCase(candidateToken.charAt(0))) {
char[] tokenChars = candidateToken.toCharArray();
for (int i = 0; i < tokenChars.length; i++) {
if (!Character.isJavaIdentifierPart(tokenChars[i])) {
return null;
}
}
return candidateToken;
} else {
return null;
}
}
/**
* Given an args pointcut body (could be args or at_args), add any
* candidate variable names to the given list.
*/
private void maybeExtractVariableNamesFromArgs(String argsSpec,List varNames) {
if (argsSpec == null) { return; }
String[] tokens = StringUtils.tokenizeToStringArray(argsSpec,",");
for (int i = 0; i < tokens.length; i++) {
tokens[i] = StringUtils.trimWhitespace(tokens[i]);
String varName = maybeExtractVariableName(tokens[i]);
if (varName != null) {
varNames.add(varName);
}
}
}
/**
* Parse the string pointcut expression looking for this(), target() and args() expressions.
* If we find one, try and extract a candidate variable name and bind it.
*/
private void maybeBindThisOrTargetOrArgsFromPointcutExpression() {
if (this.numberOfRemainingUnboundArguments > 1) {
throw new AmbiguousBindingException("Still " + this.numberOfRemainingUnboundArguments
+ " unbound args at this(),target(),args() binding stage, with no way to determine between them");
}
List varNames = new ArrayList();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression," ");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("this") ||
tokens[i].startsWith("this(") ||
tokens[i].equals("target") ||
tokens[i].startsWith("target(")) {
PointcutBody body = getPointcutBody(tokens,i);
i += body.numTokensConsumed;
String varName = maybeExtractVariableName(body.text);
if (varName != null) {
varNames.add(varName);
}
} else if (tokens[i].equals("args") || tokens[i].startsWith("args(") ) {
PointcutBody body = getPointcutBody(tokens,i);
i += body.numTokensConsumed;
List candidateVarNames = new ArrayList();
maybeExtractVariableNamesFromArgs(body.text,candidateVarNames);
// we may have found some var names that were bound in previous primitive args binding step,
// filter them out...
for (Iterator iter = candidateVarNames.iterator(); iter.hasNext();) {
String varName = (String) iter.next();
if (!alreadyBound(varName)) {
varNames.add(varName);
}
}
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate this(), target() or args() variables but only one unbound argument slot");
} else if (varNames.size() == 1) {
for (int j = 0; j < this.parameterNameBindings.length; j++) {
if (isUnbound(j)) {
bindParameterName(j,(String)varNames.get(0));
break;
}
}
}
// else varNames.size must be 0 and we have nothing to bind.
}
/**
* We've found the start of a binding pointcut at the given index into
* the token array. Now we need to extract the pointcut body and return
* it.
* @param tokens
* @param startIndex
* @return
*/
private PointcutBody getPointcutBody(String[] tokens, int startIndex) {
int numTokensConsumed = 0;
String currentToken = tokens[startIndex];
int bodyStart = currentToken.indexOf('(');
if (currentToken.charAt(currentToken.length() - 1) == ')') {
// it's an all in one... get the text between the first ( and the last )
return new PointcutBody(0,currentToken.substring(bodyStart + 1,currentToken.length() - 1));
} else {
StringBuffer sb = new StringBuffer();
if (bodyStart >= 0 && bodyStart != (currentToken.length() - 1)) {
sb.append(currentToken.substring(bodyStart + 1));
sb.append(" ");
}
numTokensConsumed++;
int currentIndex = startIndex + numTokensConsumed;
while( currentIndex < tokens.length) {
if (tokens[currentIndex].equals("(")) {
continue;
}
if (tokens[currentIndex].endsWith(")")) {
sb.append(tokens[currentIndex].substring(0,tokens[currentIndex].length() - 1));
return new PointcutBody(numTokensConsumed,sb.toString().trim());
}
String toAppend = tokens[currentIndex];
if (toAppend.startsWith("(")) {
toAppend = toAppend.substring(1);
}
sb.append(toAppend);
sb.append(" ");
currentIndex++;
numTokensConsumed++;
}
}
// we looked and failed...
return new PointcutBody(numTokensConsumed,null);
}
/**
* match up args against unbound arguments of primitive types
*/
private void maybeBindPrimitiveArgsFromPointcutExpression() {
int numUnboundPrimitives = countNumberOfUnboundPrimitiveArguments();
if (numUnboundPrimitives > 1) {
throw new AmbiguousBindingException("Found '" + numUnboundPrimitives +
"' unbound primitive arguments with no way to distinguish between them.");
}
if (numUnboundPrimitives == 1) {
// look for arg variable and bind it if we find exactly one
List varNames = new ArrayList();
String[] tokens = StringUtils.tokenizeToStringArray(this.pointcutExpression," ");
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("args") || tokens[i].startsWith("args(") ) {
PointcutBody body = getPointcutBody(tokens,i);
i += body.numTokensConsumed;
maybeExtractVariableNamesFromArgs(body.text,varNames);
}
}
if (varNames.size() > 1) {
throw new AmbiguousBindingException("Found " + varNames.size() +
" candidate variable names but only one candidate binding slot when matching primitive args");
} else if (varNames.size() == 0) {
throw new IllegalArgumentException("Unable to find args match for unbound primitive argument");
} else {
// 1 primitive arg, and one candidate...
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
bindParameterName(i,(String) varNames.get(0));
break;
}
}
}
}
}
/**
* true if the parameter name binding for the given parameter index has not yet been assigned
* @param i
* @return
*/
private boolean isUnbound(int i) {
return this.parameterNameBindings[i] == null;
}
private boolean alreadyBound(String varName) {
for (int i = 0; i < this.parameterNameBindings.length; i++) {
if (!isUnbound(i) && varName.equals(this.parameterNameBindings[i])) {
return true;
}
}
return false;
}
/**
* true if the given argument type is a subclass of the given supertype
* @param supertype
* @param argumentNumber
* @return
*/
private boolean isSubtypeOf(Class supertype, int argumentNumber) {
return supertype.isAssignableFrom(this.argumentTypes[argumentNumber]);
}
private int countNumberOfUnboundAnnotationArguments() {
if (annotationClass == null) {
// we're running on a JDK < 1.5
return 0;
}
int count = 0;
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && isSubtypeOf(annotationClass,i)) {
count++;
}
}
return count;
}
private int countNumberOfUnboundPrimitiveArguments() {
int count = 0;
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && this.argumentTypes[i].isPrimitive()) {
count++;
}
}
return count;
}
/**
* find the argument index with the given type, and bind the
* given varName in that position
* @param argumentType
* @param varName
*/
private void findAndBind(Class argumentType, String varName) {
for (int i = 0; i < this.argumentTypes.length; i++) {
if (isUnbound(i) && isSubtypeOf(argumentType,i)) {
bindParameterName(i,varName);
return;
}
}
throw new IllegalStateException("Expected to find an unbound argument of type '" +
argumentType.getName() + "'");
}
/**
* Simple struct to hold the extracted text from a pointcut body, together
* with the number of tokens consumed in extracting it.
*/
private static class PointcutBody {
int numTokensConsumed = 0;
String text = null;
public PointcutBody(int tokens, String text) {
this.numTokensConsumed = tokens;
this.text = text;
}
}
public static class AmbiguousBindingException extends RuntimeException {
public AmbiguousBindingException(String explanation) {
super(explanation);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -