I created a new annotation in JUnit5, that creates a stream of replication of the same test, and runs them or disables them according to some conditions.
But, if at least one of the iterations fails it automatically fails the whole test suite, and I want to be able to control the parent test execution result.
For example, I want to set that if a certain number of replicas have passed then the whole suite should pass.
Is there any way to do this?
Here is my code:
public class Test {
private static int i = 0;
#FlakyTest(maxIterations = 10, maxFailuresRate = 0.4)
public void test() {
if(i++ == 0){
assert false;
} else {
assert true;
}
}
}
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#TestTemplate
#ExtendWith(FlakyTestRunner.class)
public #interface FlakyTest {
int maxIterations() default 6;
double maxFailuresRate() default 0.2;
}
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static flaky.FlakyTestRunner.didPassedFailureRate;
import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;
public class FlakyTestRunner implements TestTemplateInvocationContextProvider, AfterTestExecutionCallback {
public static int iteration = 0;
public static int maxIterations;
public static double maxFailuresRate;
private static Map<Integer, Boolean> iterationsResultsMap = new HashMap<>();
#Override
public boolean supportsTestTemplate(ExtensionContext extensionContext) {
return isAnnotated(extensionContext.getTestMethod(), FlakyTest.class);
}
#Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext extensionContext) {
maxIterations = extensionContext.getElement().get().getAnnotation(FlakyTest.class).maxIterations();
maxFailuresRate = extensionContext.getElement().get().getAnnotation(FlakyTest.class).maxFailuresRate();
List invocationContexts = new ArrayList<TestTemplateInvocationContext>();
for (int i = 0; i < maxIterations; i++) {
invocationContexts.add(new FlakyIterationRunnerTemplateInvocationContext());
}
return invocationContexts.stream();
}
#Override
public void afterTestExecution(ExtensionContext extensionContext) {
iterationsResultsMap.put(iteration, !extensionContext.getExecutionException().isPresent());
}
public static boolean didPassedFailureRate() {
if (iteration > 2) {
return getFailedTestsRate() >= maxFailuresRate;
}
return false;
}
private static double getFailedTestsRate() {
int sum = iterationsResultsMap.values()
.stream()
.mapToInt(successFlag -> successFlag ? 0 : 1)
.sum();
return ((double) sum) / maxIterations;
}
}
class FlakyIterationRunnerTemplateInvocationContext implements TestTemplateInvocationContext {
#Override
public List<Extension> getAdditionalExtensions() {
List<Extension> extensions = new ArrayList<>();
extensions.add(new FlakyIterationRunnerExecutionCondition());
return extensions;
}
}
class FlakyIterationRunnerExecutionCondition implements ExecutionCondition {
#Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext) {
FlakyTestRunner.iteration++;
if (FlakyTestRunner.iteration <= FlakyTestRunner.maxIterations && !didPassedFailureRate()) {
return ConditionEvaluationResult.enabled("Passed");
}
return ConditionEvaluationResult.disabled("Iteration number: " + FlakyTestRunner.iteration + ", did passed failure rate? " + didPassedFailureRate()
+ ". Max failures rate allowed - " + FlakyTestRunner.maxFailuresRate);
}
}
I generally think "flakey tests" are a bad idea and some level of refactoring can nearly always be done to remove the need for things like this. However if this is the approach you want to take I think you can implement it by having your extension implement TestExecutionExceptionHandler so that you can decide when the AssertionError should cause test failure. I don't know if this is a particularly good implementation but I guess this would do what you want it to:
public class FlakyTestRunner implements TestTemplateInvocationContextProvider, TestExecutionExceptionHandler {
private Map<Method,MultiIterationResult> results = new HashMap<>();
#Override
public boolean supportsTestTemplate(ExtensionContext extensionContext) {
return isAnnotated(extensionContext.getTestMethod(), FlakyTest.class);
}
#Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ExtensionContext extensionContext) {
int maxIterations = extensionContext.getElement().get().getAnnotation(FlakyTest.class).maxIterations();
double maxFailuresRate = extensionContext.getElement().get().getAnnotation(FlakyTest.class).maxFailuresRate();
results.put(extensionContext.getTestMethod().get(), new MultiIterationResult(maxIterations, maxFailuresRate));
List invocationContexts = new ArrayList<TestTemplateInvocationContext>();
for (int i = 0; i < maxIterations; i++) {
invocationContexts.add(new FlakyIterationRunnerTemplateInvocationContext());
}
return invocationContexts.stream();
}
#Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
results.get(context.getTestMethod().orElseThrow(() -> throwable)).handleFailure(throwable);
}
private class MultiIterationResult {
private final int iterations;
private final double failureThreshold;
private int failCount = 0;
public MultiIterationResult(int iterations, double failureThreshold) {
this.iterations = iterations;
this.failureThreshold = failureThreshold;
}
public void handleFailure(Throwable throwable) throws Throwable {
failCount++;
if((double)failCount/iterations > failureThreshold) {
throw throwable;
}
}
}
private class FlakyIterationRunnerTemplateInvocationContext implements TestTemplateInvocationContext {
}
}
Related
I have started experimenting with the Jenetics library, however I am having some issues with trying to make a very easy "custom" set of gene/chromosomes.
What I tried to do was to create a custom chromosome with a different (random) number of custom genes inside. The genes simply contain an integer value, just for the sake of simplicity. For the same simplicity, the contents of a gene can only be numbers ranging from 0 to 9 and a Gene is considered valid only if it does NOT contain the number 9 (again, retardedly simple, but I just wanted to make them custom)
Here is my code:
CustomGene:
public class CustomGene implements Gene<Integer, CustomGene> {
private Integer value;
private CustomGene(Integer value) {
this.value = value;
}
public static CustomGene of(Integer value) {
return new CustomGene(value);
}
public static ISeq<CustomGene> seq(Integer min, Integer max, int length) {
Random r = RandomRegistry.getRandom();
return MSeq.<CustomGene>ofLength(length).fill(() ->
new CustomGene(random.nextInt(r, min, max))
).toISeq();
}
#Override
public Integer getAllele() {
return value;
}
#Override
public CustomGene newInstance() {
final Random random = RandomRegistry.getRandom();
return new CustomGene(Math.abs(random.nextInt(9)));
}
#Override
public CustomGene newInstance(Integer integer) {
return new CustomGene(integer);
}
#Override
public boolean isValid() {
return value != 9;
}
}
CustomChromosome:
import org.jenetics.Chromosome;
import org.jenetics.util.ISeq;
import org.jenetics.util.RandomRegistry;
import java.util.Iterator;
import java.util.Random;
public class CustomChromosome implements Chromosome<CustomGene> {
private ISeq<CustomGene> iSeq;
private final int length;
public CustomChromosome(ISeq<CustomGene> genes) {
this.iSeq = genes;
this.length = iSeq.length();
}
public static CustomChromosome of(ISeq<CustomGene> genes) {
return new CustomChromosome(genes);
}
#Override
public Chromosome<CustomGene> newInstance(ISeq<CustomGene> iSeq) {
this.iSeq = iSeq;
return this;
}
#Override
public CustomGene getGene(int i) {
return iSeq.get(i);
}
#Override
public int length() {
return iSeq.length();
}
#Override
public ISeq<CustomGene> toSeq() {
return iSeq;
}
#Override
public Chromosome<CustomGene> newInstance() {
final Random random = RandomRegistry.getRandom();
ISeq<CustomGene> genes = ISeq.empty();
for (int i = 0; i < length; i++) {
genes = genes.append(CustomGene.of(Math.abs(random.nextInt(9))));
}
return new CustomChromosome(genes);
}
#Override
public Iterator<CustomGene> iterator() {
return iSeq.iterator();
}
#Override
public boolean isValid() {
return iSeq.stream().allMatch(CustomGene::isValid);
}
}
Main:
import org.jenetics.Genotype;
import org.jenetics.Optimize;
import org.jenetics.engine.Engine;
import org.jenetics.engine.EvolutionResult;
import org.jenetics.util.Factory;
import org.jenetics.util.RandomRegistry;
import java.util.Random;
public class Main {
private static int maxSum = - 100;
private static Integer eval(Genotype<CustomGene> gt) {
final int sum = gt.getChromosome().stream().mapToInt(CustomGene::getAllele).sum();
if(sum > maxSum)
maxSum = sum;
return sum;
}
public static void main(String[] args) {
final Random random = RandomRegistry.getRandom();
Factory<Genotype<CustomGene>> g =
Genotype.of(CustomChromosome.of(CustomGene.seq(0, 9, Math.abs(random.nextInt(9)) + 1)));
Engine<CustomGene, Integer> engine = Engine
.builder(Main::eval, g)
.optimize(Optimize.MAXIMUM)
.populationSize(100)
.build();
Genotype<CustomGene> result = engine.stream().limit(10000)
.collect(EvolutionResult.toBestGenotype());
System.out.println(eval(result));
result.getChromosome().stream().forEach(i -> {
System.out.print(i.getAllele() + " ");
});
System.out.println();
System.out.println(maxSum);
}
}
I do not understand why I get this output:
13 (evaluated result)
1 8 0 4 (all the alleles form the genes of the chosen chromosome)
32 (the actual maximum fitness found)
We can clearly see a difference between the genotype which had the biggest fitness function and the chosen genotype. Why?
I know I'm doing something wrong and it's probably a silly mistake, but I really can't seem to understand what I am doing wrong. Could you please help me out?
Lots of thanks!
As posted by the creator of the library here, the answer was:
you violated the contract of the Chromosome.newInstance(ISeq) method. This method must return a new chromosome instance. After fixing this
#Override
public Chromosome<CustomGene> newInstance(ISeq<CustomGene> iSeq) {
return new CustomChromosome(iSeq);
}
I try to make a thread safe class which allows to follow the scan of something. My class is:
import java.util.concurrent.atomic.AtomicInteger;
public class ScanInProgress {
private final Integer scanId;
private final int nbScans;
private AtomicInteger nbResponses = new AtomicInteger(0);
private AtomicInteger nbErrors = new AtomicInteger(0);
public ScanInProgress(Integer scanId, int nbSites) {
this.scanId = scanId;
this.nbScans = nbSites;
}
public Integer getScanId() {
return scanId;
}
public boolean addSuccess() {
addResponse();
return isDone();
}
public boolean addError() {
addResponse();
nbErrors.incrementAndGet();
return isDone();
}
private void addResponse() {
nbResponses.incrementAndGet();
}
private boolean isDone() {
return nbResponses.get() == nbScans;
}
public int getNbSuccesses() {
return nbResponses.get() - nbErrors.get();
}
public int getNbResponses() {
return nbResponses.get();
}
}
I have the following unit tests class:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class ScanInProgressTest {
#Test
public void testConcurrency() throws Exception {
// given
Integer scanId = 1;
int nbScans = 500_000;
ScanInProgress scanInProgress = new ScanInProgress(scanId, nbScans);
// when
for (int i = 1; i <= nbScans / 2; i++) {
new AddError(scanInProgress).start();
new AddSuccess(scanInProgress).start();
}
Thread.sleep(1000);
// then
assertEquals(nbScans, scanInProgress.getNbResponses());
assertEquals(nbScans / 2, scanInProgress.getNbSuccesses());
}
private class AddError extends Thread {
private ScanInProgress scanInProgress;
public AddError(ScanInProgress scanInProgress) {
this.scanInProgress = scanInProgress;
}
#Override
public void run() {
int before = scanInProgress.getNbResponses();
scanInProgress.addError();
int after = scanInProgress.getNbResponses();
assertTrue("Add error: before=" + before + ", after=" + after, before < after);
}
}
private class AddSuccess extends Thread {
private ScanInProgress scanInProgress;
public AddSuccess(ScanInProgress scanInProgress) {
this.scanInProgress = scanInProgress;
}
#Override
public void run() {
int beforeResponses = scanInProgress.getNbResponses();
int beforeSuccesses = scanInProgress.getNbSuccesses();
scanInProgress.addSuccess();
int afterResponses = scanInProgress.getNbResponses();
int afterSuccesses = scanInProgress.getNbSuccesses();
assertTrue("Add success responses: before=" + beforeResponses + ", after=" + afterResponses, beforeResponses < afterResponses);
assertTrue("Add success successes: before=" + beforeSuccesses + ", after=" + afterSuccesses, beforeSuccesses < afterSuccesses);
}
}
}
When I run my test I can see regularly this error in logs:
Exception in thread "Thread-14723" java.lang.AssertionError: Add success successes: before=7362, after=7362
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.assertTrue(Assert.java:41)
The assertion lets me think that when I call the method scanInProgress.addSuccess() and then scanInProgress.getNbSuccesses(), the instruction in first method nbResponses.incrementAndGet() is not yet acknowledged whereas the instruction in second method nbResponses.get() returns something.
What can I do to correct this ?
As far as I unsterstand the problem I think you need to use locks. Before you get or set an variable you need to aquire a lock. This way a variable can't be set and read at the same time. You get something like the code below.
public final static Object LOCK = new Object();
private int yourvariable;
public void setVar(int var){
synchronized(LOCK){
yourvariable = var;
}
}
public int getVar(){
int toReturn;
synchronized(LOCK){
toReturn = yourvariable;
}
return toReturn;
}
note: If your ScanInProgessClass is the only the ScanInProgressClass class, you can use this instead of a LOCK object.
I have created a custom filter factory for solr 4.2. It is working good. But when I am trying to upgarde solr-4.2 to 4.7 version, it is reporting errors:
Caused by: org.apache.solr.common.SolrException: Plugin init failure for [schema.xml] analyzer/filter: Error instantiating class: 'org.apache.lucene.analysis.ExtendedNameFilterFactory'
Here's the java code for the factory:
package org.apache.lucene.analysis;
import java.util.Map;
import org.apache.lucene.analysis.util.AbstractAnalysisFactory;
import org.apache.lucene.analysis.util.MultiTermAwareComponent;
import org.apache.lucene.analysis.util.TokenFilterFactory;
public class ExtendedNameFilterFactory extends TokenFilterFactory
implements MultiTermAwareComponent
{
int extendedWordCount;
public void init(Map<String, String> args)
{
super.init(args);
assureMatchVersion();
this.extendedWordCount = getInt("extendedWordCount", -1);
}
public ExtendedNameFilter create(TokenStream input) {
return new ExtendedNameFilter(this.luceneMatchVersion, input, this.extendedWordCount);
}
public AbstractAnalysisFactory getMultiTermComponent()
{
return this;
}
}
And for the Filter:
package org.apache.lucene.analysis;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.analysis.util.CharacterUtils;
import org.apache.lucene.util.Version;
public final class ExtendedNameFilter extends TokenFilter
{
private final CharTermAttribute termAtt = (CharTermAttribute)addAttribute(CharTermAttribute.class);
private PositionIncrementAttribute posIncAttr;
private OffsetAttribute setOffsetAttr;
private final int extendedWordCount;
LinkedList<String> list = new LinkedList();
ArrayList<Integer> startOffsetList = new ArrayList();
int endOffset = 0;
int count = 0;
public ExtendedNameFilter(Version matchVersion, TokenStream in, int extendedWordCount)
{
super(in);
CharacterUtils.getInstance(matchVersion);
this.extendedWordCount = extendedWordCount;
this.posIncAttr = ((PositionIncrementAttribute)addAttribute(PositionIncrementAttribute.class));
this.setOffsetAttr = ((OffsetAttribute)addAttribute(OffsetAttribute.class));
}
public final boolean incrementToken()
throws IOException
{
int len = 0;
while (this.input.incrementToken()) {
this.list.add(this.termAtt.toString());
this.startOffsetList.add(Integer.valueOf(this.setOffsetAttr.startOffset()));
this.endOffset = this.setOffsetAttr.endOffset();
}
Iterator iterator = this.list.iterator();
len = this.list.size();
if ((len > 0) && (this.extendedWordCount < 0)) {
this.termAtt.setEmpty();
while (iterator.hasNext()) {
this.termAtt.append((CharSequence)iterator.next());
}
this.list.removeFirst();
this.posIncAttr.setPositionIncrement(10);
this.setOffsetAttr.setOffset(((Integer)this.startOffsetList.get(this.count)).intValue(), this.endOffset);
this.count += 1;
return true;
}
if ((len > 0) && (this.count < this.extendedWordCount)) {
this.termAtt.setEmpty();
while (iterator.hasNext()) {
this.termAtt.append((CharSequence)iterator.next());
}
this.list.removeFirst();
this.posIncAttr.setPositionIncrement(10);
this.setOffsetAttr.setOffset(((Integer)this.startOffsetList.get(this.count)).intValue(), this.endOffset);
this.count += 1;
return true;
}
return false;
}
}
It was working good with solr 4.2. Can anybody tell me the changes that I need to make for running it in solr 4.7.1?
AbstractAnalysisFactory no longer uses an init method for passing in args. Override the constructor instead.
I'm using Eclipse and I'm using Java. My objective it's to sort a vector, with the bogoSort method
in one vector( vectorExample ) adapted to my type of vector and use the Java sort on other vector (javaVector) and compare them.
I did the tests but it did't work, so I don't know what is failing.
*Note: there are few words in spanish: ordenado = sorted, Ejemplo = Example, maximo = maximun, contenido = content.
EjemploVector class
package vector;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.Iterator;
public class EjemploVector <T> {
protected T[] contenido;
private int numeroElementos;
#SuppressWarnings("unchecked")
public EjemploVector () {
contenido = (T[]) new Object[100];
numeroElementos = 0;
}
#SuppressWarnings("unchecked")
public EjemploVector (int maximo) {
contenido = (T[]) new Object[maximo];
numeroElementos = 0;
}
public String toString(){
String toString="[";
for (int k=0; k<numeroElementos;k++){
if (k==numeroElementos-1){
toString = toString + contenido[k].toString();
} else {
toString = toString + contenido[k].toString()+", ";
}
}
toString = toString + "]";
return toString;
}
public boolean equals (Object derecho){
if (!(derecho instanceof Vector<?>)) {
return false;
} else if (numeroElementos != ((Vector<?>)derecho).size()) {
return false;
} else {
Iterator<?> elemento = ((Vector<?>)derecho).iterator();
for (int k=0; k<numeroElementos;k++){
if (!((contenido[k]).equals (elemento.next()))) {
return false;
}
}
return true;
}
}
public void addElement (T elemento){
contenido[numeroElementos++]= elemento;
}
protected T[] getContenido(){
return this.contenido;
}
protected T getContenido (int k){
return this.contenido[k];
}
#SuppressWarnings("unchecked")
protected void setContenido (int k, Object elemento){
this.contenido[k]= (T)elemento;
}
EjemploVectorOrdenadoClass
package vector.ordenado;
import java.util.Arrays;
import java.util.Random;
import vector.EjemploVector;
public class EjemploVectorOrdenado<T extends Comparable<T>> extends EjemploVector<T> {
private boolean organized;
public EjemploVectorOrdenado() {
super();
organized = true;
}
public EjemploVectorOrdenado(int maximo) {
super(maximo);
organized = true; //
}
public boolean getOrdenado() {
return this.organized;
}
// Method bogoSort
public void bogoSort() {
if (!this.organized) {
if (this.size() > 0) {
Random generator;
T tempVariable;
int randomPosition;
do {
generator = new Random();
for (int i = 0; i < this.size(); i++) {
randomPosition = generator.nextInt(this.size());
tempVariable = contenido[i];
contenido[i] = contenido[randomPosition];
contenido[randomPosition] = tempVariable;
}
} while (!organized);
}
}
this.organized = true;
}
public void addElement(T elemento) {
super.addElement(elemento);
if (organized && this.size() > 1) {
T penultimo = this.getContenido(this.size() - 2);
T ultimo = this.getContenido(this.size() - 1);
organized = penultimo.compareTo(ultimo) <= 0;
}
}
}
ElementoTest class
package elementos;
import java.io.Serializable;
public class ElementoTest implements Comparable<ElementoTest>, Serializable {
private static final long serialVersionUID = -7683744298261205956L;
private static int numeroElementosTest = 0;
private int clave;
private int valor;
public ElementoTest(int i){
this.clave = i;
this.valor = numeroElementosTest;
numeroElementosTest++;
}
public String toString(){
return ("(" + this.clave + "," + this.valor + ")");
}
public boolean equals (Object derecho){
if (!(derecho instanceof ElementoTest)) {
return false;
} else {
return clave == ((ElementoTest)derecho).clave;
}
}
public char getClave(){
return this.clave;
}
public int getValor(){
return this.valor;
}
#Override
public int compareTo(ElementoTest elemento) {
if (elemento == null){
return -1;
} else if (this.equals(elemento)){
return 0;
} else if (clave < elemento.clave){
return -1;
} else {
return 1;
}
}
}
TESTS
The first it's a stupid test, because it puts elements in order so... really the methods arenĀ“t doing anything, java just compare and it gives correct
I tried to make an unsorted vector adding elements but there appears the java.lang.ClassCastException: [Ljava.... etc.
package vector.ordenado;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.Vector;
import org.junit.Before;
import org.junit.Test;
import elementos.ElementoTest;
public class EjemploVectorOrdenadoTest {
private Vector<ElementoTest> vectorJava;
private EjemploVectorOrdenado<ElementoTest> vectorExample;
#Before
public void setUp() throws Exception {
vectorJava = new Vector<ElementoTest>(100);
vectorExample = new EjemploVectorOrdenado<ElementoTest>(100);
}
#Test
public void testSortFailTest() {
for (char c = 'a'; c < 'g'; c++) {
vectorJava.addElement(new ElementoTest(c));
vectorExample.addElement(new ElementoTest(c));
}
Collections.sort(vectorJava);
vectorExample.bogoSort();
assertTrue(vectorExample.equals(vectorJava));
assertTrue(vectorExample.getOrdenado());
}
#Test
public void testSort() {
{
vectorJava.addElement(new ElementoTest(1));
vectorJava.addElement(new ElementoTest(3));
vectorJava.addElement(new ElementoTest(2));
vectorExample.addElement(new ElementoTest(3));
vectorExample.addElement(new ElementoTest(2));
vectorExample.addElement(new ElementoTest(1));
}
Collections.sort(vectorJava);
vectorExample.bogoSort();
assertTrue(vectorExample.equals(vectorJava));
assertTrue(vectorExample.getOrdenado());
}
}
Sorry, for the problems and thanks.
The problem is that your test class ElementoTest should implement the Comparable interface. Or you need to provide a Comparator during your comparison.
Does your class ElementtoTest implement Comparable?
If not, it needs to.
I'm suspecting it doesn't, because that's exactly what would cause this error. you'll need to add implements Comparable and then override the int compareTo(Elementtotest e) method, where you specify what criteria you'd like to order the objects based on.
I'm parsing Java Source Files to collect various informations about my classes. Therefore I'm using the JavaParser, since I could not find a good alternative (good suggestions have the chance to become "answers") to parse Source Files.
I already managed to get Annotations of all methods from my class. The code looks like this:
package de.mackaz;
import japa.parser.JavaParser;
import japa.parser.ParseException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.expr.AnnotationExpr;
import japa.parser.ast.expr.MarkerAnnotationExpr;
import japa.parser.ast.expr.MemberValuePair;
import japa.parser.ast.expr.NormalAnnotationExpr;
import japa.parser.ast.visitor.VoidVisitorAdapter;
import java.io.FileInputStream;
public class JavaSourceUtils {
public static void main(String[] args) throws Exception {
File f = new File("/home/mackaz/SourceFile.java");
inspectJavaFile(f);
}
public static void inspectJavaFile(File pFile)
throws FileNotFoundException, ParseException, IOException {
CompilationUnit cu;
FileInputStream in = new FileInputStream(pFile);
try {
cu = JavaParser.parse(in);
} finally {
in.close();
}
new MethodVisitor().visit(cu, null);
}
/**
* Simple visitor implementation for visiting MethodDeclaration nodes.
*/
private static class MethodVisitor extends VoidVisitorAdapter {
#Override
public void visit(MethodDeclaration n, Object arg) {
System.out.println(n.getName());
if (n.getAnnotations() != null) {
for (AnnotationExpr annotation : n.getAnnotations()) {
System.out.println(annotation.getClass());
// MarkerAnnotations, for example #Test
if (annotation.getClass().equals(MarkerAnnotationExpr.class)) {
System.out.println("MarkerAnnotation:" + ((MarkerAnnotationExpr)annotation).getName());
}
if (annotation.getClass().equals(NormalAnnotationExpr.class)) {
for (MemberValuePair pair : ((NormalAnnotationExpr)annotation).getPairs()) {
if (pair.getName().equals("groups"))
System.out.println("Group:\"" + pair.getValue() + "\"");
}
}
}
}
}
}
}
Now how can I get the Annotations of the class itself?
You are overriding public void visit(MethodDeclaration n, Object arg), which visits methods. You can also override public void visit(ClassOrInterfaceDeclaration n, A arg) or public void visit(ClassOrInterfaceType n, A arg), which should give you access to the information you are looking for.
This is how I solved it in the end - I added another Visitor "ClassVisitor":
private static class ClassVisitor extends VoidVisitorAdapter {
#Override
public void visit(ClassOrInterfaceDeclaration n, Object arg) {
for (AnnotationExpr ann: n.getAnnotations()) {
System.out.println(ann.toString());
}
}
}
Looks like you need extend ModifierVisitorAdapter and implement
public Node visit(ClassOrInterfaceDeclaration n, A arg) {
Look at the implementation here for an idea of what you might want to do:
public Node visit(ClassOrInterfaceDeclaration n, A arg) {
if (n.getJavaDoc() != null) {
n.setJavaDoc((JavadocComment) n.getJavaDoc().accept(this, arg));
}
List<AnnotationExpr> annotations = n.getAnnotations();
if (annotations != null) {
for (int i = 0; i < annotations.size(); i++) {
annotations.set(i, (AnnotationExpr) annotations.get(i).accept(this, arg));
}
removeNulls(annotations);
}
List<TypeParameter> typeParameters = n.getTypeParameters();
if (typeParameters != null) {
for (int i = 0; i < typeParameters.size(); i++) {
typeParameters.set(i, (TypeParameter) typeParameters.get(i).accept(this, arg));
}
removeNulls(typeParameters);
}
List<ClassOrInterfaceType> extendz = n.getExtends();
if (extendz != null) {
for (int i = 0; i < extendz.size(); i++) {
extendz.set(i, (ClassOrInterfaceType) extendz.get(i).accept(this, arg));
}
removeNulls(extendz);
}
List<ClassOrInterfaceType> implementz = n.getImplements();
if (implementz != null) {
for (int i = 0; i < implementz.size(); i++) {
implementz.set(i, (ClassOrInterfaceType) implementz.get(i).accept(this, arg));
}
removeNulls(implementz);
}
List<BodyDeclaration> members = n.getMembers();
if (members != null) {
for (int i = 0; i < members.size(); i++) {
members.set(i, (BodyDeclaration) members.get(i).accept(this, arg));
}
removeNulls(members);
}
return n;
}