/*******************************************************************************
* Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package de.gebit.integrity.annotation;
import static com.google.common.collect.Iterables.filter;
import static java.util.Collections.emptyList;
import java.lang.annotation.Annotation;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.eclipse.xtext.common.types.JvmAnnotationReference;
import org.eclipse.xtext.common.types.JvmAnnotationValue;
import org.eclipse.xtext.common.types.JvmBooleanAnnotationValue;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmStringAnnotationValue;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import de.gebit.integrity.dsl.ArbitraryParameterOrResultName;
import de.gebit.integrity.dsl.FixedParameterName;
import de.gebit.integrity.dsl.MethodReference;
import de.gebit.integrity.dsl.ParameterName;
import de.gebit.integrity.utils.IntegrityDSLUtil;
/**
* Evaluates JvmFixtures reflectively without loading them in the classpath.
*
* @author tilois - Initial API and Implementation
*/
public class JvmFixtureEvaluation {
/**
* Processes the given method and returns a list of all annotated parameters. Only supports one annotation per
* parameter.
*
* @param aMethod
* Method to be checked.
* @param someAnnotationTypes
* Types that should be collected.
* @return A list of all parameters linked to the annotation of the given types for this method.
*/
public List<Pair<JvmFormalParameter, JvmAnnotationReference>> getAllAnnotatedParameter(MethodReference aMethod,
Set<Class<? extends Annotation>> someAnnotationTypes) {
if (aMethod == null) {
return emptyList();
}
final JvmOperation tempOperation = aMethod.getMethod();
Preconditions.checkArgument(tempOperation != null, "Methodreference did not resolve to a JVM operation!");
List<Pair<JvmFormalParameter, JvmAnnotationReference>> tempResult = new LinkedList<Pair<JvmFormalParameter, JvmAnnotationReference>>();
for (JvmFormalParameter tempParameter : tempOperation.getParameters()) {
try {
tempResult
.add(Tuples.create(tempParameter, Iterables.getOnlyElement(filter(
tempParameter.getAnnotations(), isOneOf(someAnnotationTypes)))));
} catch (NoSuchElementException exc) {
// Expected if there's no matching annotation
continue;
}
}
return tempResult;
}
/**
* Produces a predicate that matches an annotation if it is one of the specified ones.
*
* @param someAnnotationTypes
* Annotations that should match.
* @return <code>true</code> if the annotation represents one of the specified ones, <code>false</code> otherwise.
*/
protected Predicate<JvmAnnotationReference> isOneOf(final Set<Class<? extends Annotation>> someAnnotationTypes) {
return new Predicate<JvmAnnotationReference>() {
public boolean apply(JvmAnnotationReference anAnnotation) {
if (anAnnotation == null) {
return false;
}
final String tempQualifiedAnnotationName = anAnnotation.getAnnotation().getQualifiedName();
for (Class<? extends Annotation> tempAnnotation : someAnnotationTypes) {
if (tempQualifiedAnnotationName.equals(tempAnnotation.getCanonicalName())) {
return true;
}
}
return false;
}
};
}
/**
* Extracts an annotation value by its name.
*
* @param anAnnotation
* Annotation where to search.
* @param aName
* Name to be searched.
* @return annotation value if it got found, <code>null</code> otherwise.
*/
public JvmAnnotationValue getValueByName(JvmAnnotationReference anAnnotation, String aName) {
for (JvmAnnotationValue tempValue : anAnnotation.getValues()) {
if (aName.equals(tempValue.getValueName())) {
return tempValue;
}
}
return null;
}
/**
* Returns the name of a single given parameter defined by a {@link ParameterName} instance.
*
* @param aParameterName
* the parameter name instance
* @return the parameter name string
*/
public String getParameterNameOf(ParameterName aParameterName) {
if (aParameterName instanceof FixedParameterName) {
JvmAnnotationValue tempName = getValueByName(((FixedParameterName) aParameterName).getAnnotation(), "name");
return evaluateSingle(tempName, String.class);
} else if (aParameterName instanceof ArbitraryParameterOrResultName) {
return IntegrityDSLUtil
.getIdentifierFromArbitraryParameterOrResultName((ArbitraryParameterOrResultName) aParameterName);
} else {
throw new UnsupportedOperationException("This subtype of ParameterName ("
+ aParameterName.getClass().toString() + ") is not supported yet!");
}
}
/**
* Evaluates an Jvm annotation value which is expected to hold the specified type.
*
* @param anAnnotationValue
* Annotation value to evaluate.
* @param anExpectedType
* Expected type of the value.
* @return a list of all values
*/
@SuppressWarnings("unchecked")
public <T> List<T> evaluateValues(JvmAnnotationValue anAnnotationValue, Class<T> anExpectedType) {
if (Boolean.class.isAssignableFrom(anExpectedType)) {
return (List<T>) ((JvmBooleanAnnotationValue) anAnnotationValue).getValues();
}
if (String.class.isAssignableFrom(anExpectedType)) {
return (List<T>) ((JvmStringAnnotationValue) anAnnotationValue).getValues();
}
throw new UnsupportedOperationException("Type " + anExpectedType + " is not supported!");
}
/**
* Evaluates an Jvm annotation value which is expected to hold a single value of the specified type.
*
* @param anAnnotationValue
* Annotation value to evaluate.
* @param anExpectedType
* Expected type of the value.
* @see #evaluateValues(JvmAnnotationValue, Class) does the same for an arbitrary list of values.
* @return the value
*/
public <T> T evaluateSingle(JvmAnnotationValue anAnnotationValue, Class<T> anExpectedType) {
List<T> tempValues = evaluateValues(anAnnotationValue, anExpectedType);
return tempValues.isEmpty() ? null : tempValues.get(0);
}
}