Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EnumFactory.getEnum(Class<?>) returns null #563

Closed
simonedavico opened this issue Mar 24, 2016 · 9 comments
Closed

EnumFactory.getEnum(Class<?>) returns null #563

simonedavico opened this issue Mar 24, 2016 · 9 comments

Comments

@simonedavico
Copy link

Simple test case I was trying while playing around with EnumFactory:

import java.time.DayOfWeek;

import spoon.reflect.declaration.*;
import spoon.processing.AbstractProcessor;

public class ClassProcessor extends AbstractProcessor<CtClass> {

    public void process(CtClass element) {
          CtEnum<DayOfWeek> dayOfWeekEnum = getFactory().Enum().getEnum(DayOfWeek.class);
          dayOfWeekEnum.getEnumValue("MONDAY"); 
    }

}

This processor will throw a NullPointerException, since for some reason dayOfWeekEnum will be null.
Am I doing something wrong, or is this a bug in spoon?

I am using 5.1.0 from Maven.

@monperrus
Copy link
Collaborator

It's probably because DayOfWeek.java is not in the spoon source folder.

The feature of reflecting the structural part of runtime classes as Spoon objects is very interesting IMHO

@simonedavico
Copy link
Author

Thank you for the answer @monperrus. From what I understand, it means that I can't get an enum value for an arbitrary enum using spoon? Or maybe there is an alternative way?

@GerardPaligot
Copy link
Contributor

Hi @simonedavico,

  • What is the version 2.18.4.Final of Spoon? I don't know this version.
  • DayOfWeek is a top level enum?
  • DayOfWeek is specified in the input source of the Spoon launcher?

@simonedavico
Copy link
Author

Hi @GerardPaligot,

  • Sorry about the version number, I copied the wrong one from my POM; I am using the latest version, 5.1.0
  • DayOfWeek is an enum in java.time;
  • I didn't specify it as input source to the Spoon launcher. This is how I am launching Spoon:
public class SpoonTest {

    public static void main(String[] args) {

        Path outputDirectory = Paths.get("./src/main/resources");
        Launcher spoonLauncher = new spoon.Launcher();
        spoonLauncher.setSourceOutputDirectory(outputDirectory.toFile());
        spoonLauncher.addProcessor(new ClassProcessor());
        spoonLauncher.addInputResource("./testSpoon/Test.java");
        System.out.println("Before run");
        spoonLauncher.run();

    }
}

As you can see, I am only adding as source the class I want to modify (Test.java). My final aim is to add an annotation to the class which as an enum value as annotation value, such as

@DefaultDay(day = DayOfWeek.MONDAY)
public class Test {
    //...
}

To give more context, I am having trouble setting the value through

addValue("day", DayOfWeek.MONDAY) (throws spoon.SpoonException: Please, submit a valid value.), so I was looking into a way to retrieve the value itself through Spoon to check if it would fix the issue.

@GerardPaligot
Copy link
Contributor

Ok, I understand your issue.

Spoon doesn't build java classes in models. If you want to react with java classes, you must create CtTypeReference for your needs.

For example, if you want all classes annotated with an annotation named DefaultDay and with a field value, you can write something like this:

public class ClassProcessor extends AbstractProcessor<CtClass> {
    @Override
    public boolean isToBeProcessed(CtClass candidate) {
        final CtAnnotation<DefaultDay> annotation = candidate.getAnnotation(getFactory().Type().createReference(DefaultDay.class));
        final CtExpression day = annotation.getValue("day");
        return day instanceof CtFieldRead;
    }

    public void process(CtClass element) {
        // Make what do you want here.
    }
}

@simonedavico
Copy link
Author

@GerardPaligot thank you for your response (that I completely missed, sorry about the late reply).

What I am looking for is not really checking whether my class has an annotation @DefaultDay; I would like to ADD the annotation to the class!

So, for example, I would like to go from

public class Test {
    //body of the class
}

to

@DefaultDay(day = DayOfWeek.MONDAY)
public class Test {
    //body of the class
}

Is there a way I can achieve this with Spoon?

The closes I could achieve, through this code

int dayOfWeek = DayOfWeek.MONDAY.getValue();
CtCodeSnippetExpression<Object> castDaySnippet = getFactory().Code().createCodeSnippetExpression("DayOfWeek.of(" + dayOfWeek + ")");
getFactory().Annotation().annotate(element, DefaultDay.class);
element.getAnnotation(getFactory().Type().createReference(DefaultDay.class)).addValue("day", castDaySnippet);

generates an annotation like this:

@complete.path.to.DefaultDay(day = java.time.DayOfWeek.of(1))

Is there a way to add the value without having to generate the expression snippet?

@tdurieux
Copy link
Collaborator

tdurieux commented Apr 4, 2016

Hi,

In order to create a what you need, you have to do something like this:

CtFieldAccess<?> fieldRead = factory.Core().createFieldRead();
CtTypeReference<DayOfWeek> enumReference = factory.Type().createReference(DayOfWeek.class);
CtFieldReference fieldReference = factory.Field().createReference(enumReference, enumReference, DayOfWeek.MONDAY.name());
fieldReference.setStatic(true);
fieldRead.setVariable(fieldReference);
CtAnnotation<?> ctAnnotation = element.getAnnotation(getFactory().Type().createReference(DefaultDay.class));
ctAnnotation.addValue("day", fieldRead);

@simonedavico
Copy link
Author

@tdurieux thank you, it works as expected!

@GerardPaligot
Copy link
Contributor

Fixed by #569

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants