package com.googlecode.netburstjbehaveidea;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiArrayInitializerMemberValue;
import com.intellij.psi.PsiMethod;
import org.apache.commons.collections.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static java.util.Collections.emptyList;
import static org.apache.commons.collections.CollectionUtils.exists;
public class IdeaAnnotation {
private final PsiAnnotation primaryPsiAnnotation;
private final PsiMethod psiMethod;
public IdeaAnnotation(final PsiAnnotation primaryPsiAnnotation, final PsiMethod psiMethod) {
this.primaryPsiAnnotation = primaryPsiAnnotation;
this.psiMethod = psiMethod;
}
public boolean matches(final String stepText) {
return primaryAnnotationStepTextMatcher().matches(stepText) ||
aliasAnnotationStepTextMatcher().matches(stepText) ||
aliasesAnnotationStepTextMatcher().matches(stepText);
}
public PsiMethod getPsiMethod() {
return psiMethod;
}
private StepTextMatcher primaryAnnotationStepTextMatcher() {
return stepTextMatcherFor(primaryPsiAnnotation);
}
private StepTextMatcher aliasAnnotationStepTextMatcher() {
PsiAnnotation aliasPsiAnnotation = psiMethod.getModifierList().findAnnotation("org.jbehave.core.annotations.Alias");
return stepTextMatcherFor(aliasPsiAnnotation);
}
private StepTextMatcher aliasesAnnotationStepTextMatcher() {
PsiAnnotation aliasesPsiAnnotation = psiMethod.getModifierList().findAnnotation("org.jbehave.core.annotations.Aliases");
return stepTextMatcherFor(aliasesPsiAnnotation);
}
private StepTextMatcher stepTextMatcherFor(PsiAnnotation psiAnnotation) {
if (psiAnnotation == null) {
return new UnknownAnnotationValueStepTextMatcher();
}
return (annotationValueFor(psiAnnotation) != null) ?
new SingleAnnotationValueStepTextMatcher(annotationValueFor(psiAnnotation)) :
new MultipleAnnotationValuesStepTextMatcher(annotationValuesFor(psiAnnotation));
}
private String annotationValueFor(PsiAnnotation psiAnnotation) {
PsiAnnotationMemberValue psiAnnotationMemberValue = psiAnnotation.findAttributeValue("value");
if (psiAnnotationMemberValue == null) {
return null;
}
return psiAnnotationMemberValue.getText();
}
private List<String> annotationValuesFor(PsiAnnotation psiAnnotation) {
PsiArrayInitializerMemberValue psiArrayInitializerMemberValue = (PsiArrayInitializerMemberValue) psiAnnotation.findAttributeValue("values");
if (psiArrayInitializerMemberValue == null) {
return emptyList();
}
PsiAnnotationMemberValue[] psiAnnotationMemberValues = psiArrayInitializerMemberValue.getInitializers();
List<String> annotationValues = new ArrayList<String>();
for (PsiAnnotationMemberValue psiAnnotationMemberValue : psiAnnotationMemberValues) {
annotationValues.add(psiAnnotationMemberValue.getText());
}
return annotationValues;
}
private static interface StepTextMatcher {
boolean matches(String stepText);
}
private static class UnknownAnnotationValueStepTextMatcher implements StepTextMatcher {
public boolean matches(String stepText) {
return false;
}
}
private static class SingleAnnotationValueStepTextMatcher implements StepTextMatcher {
private String annotationValue;
public SingleAnnotationValueStepTextMatcher(String annotationValue) {
this.annotationValue = annotationValue;
}
public boolean matches(String stepText) {
String stepTextWithoutKeywordAndExtraSpacesIgnored = stepText.replaceAll("\\s+", " ");
return valueAsPattern().matcher(stepTextWithoutKeywordAndExtraSpacesIgnored).matches();
}
private Pattern valueAsPattern() {
String value = annotationValue();
String basicPlaceholderPattern = "\\$\\w+";
String patternWithLastPlaceholderNeedingSpaceDelimiterIfNotBlank = value.replaceAll("\\s+" + basicPlaceholderPattern + "\\s*$", "(\\\\s+\\\\S+)*\\\\s*\\$");
String patternWithFirstPlaceholderNeedingSpaceDelimiterIfNotBlank = patternWithLastPlaceholderNeedingSpaceDelimiterIfNotBlank.replaceAll("^\\s*" + basicPlaceholderPattern + "\\s+", "^\\\\s*(\\\\S+\\\\s+)*");
String patternWithOtherPlaceholdersAsNormal = patternWithFirstPlaceholderNeedingSpaceDelimiterIfNotBlank.replaceAll(basicPlaceholderPattern, ".*?");
return Pattern.compile(patternWithOtherPlaceholdersAsNormal);
}
private String annotationValue() {
return removeSurroundingQuotes(annotationValue);
}
private String removeSurroundingQuotes(String value) {
return value.replaceAll("\\A\"", "").replaceAll("\"\\Z", "");
}
}
private static class MultipleAnnotationValuesStepTextMatcher implements StepTextMatcher {
private List<String> annotationValues;
public MultipleAnnotationValuesStepTextMatcher(List<String> annotationValues) {
this.annotationValues = annotationValues;
}
@Override
public boolean matches(final String stepText) {
return exists(annotationValues, new Predicate() {
@Override
public boolean evaluate(Object annotationValue) {
return new SingleAnnotationValueStepTextMatcher((String) annotationValue).matches(stepText);
}
});
}
}
}