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

Custom mappers which are SELMA mappers are not injected into the outer mapper #185

Open
tucu00 opened this issue Jan 8, 2018 · 2 comments

Comments

@tucu00
Copy link

tucu00 commented Jan 8, 2018

If I understood the documentation correctly, the following should work:

@Mapper
public abstract class XMapper {
  public abstract String toString(String s);
}

@Mapper(withCustom = XMapper.class)
public abstract class YMapper {
  public abstract List<String> toString(List<String> list);
}

  public static void main(String... args) {
    YMapper mapper = Selma.builder(YMapper.class).build();
    List<String> list = new ArrayList<>();
    list.add("a");
    list = mapper.toString(list);
    System.out.println(list);
  }

However, I'm getting a NullPointerException because in the generated YMapper the custom mapper XMapper is NULL.

My understanding was that Selma.build() would inject a XMapper instance in the generated YMapper instance.

If using a custom mapper that is a concrete class with a default constructor the same is instantiated in the generated 'YMapper' class constructor.

So far the only solution I've found is to cast the YMapper to the generated class and set the an XMapper instance previously obtained. But this does not seem correct, not to mention that for composite Mappers I have to do it recursively and all using reflection as I cannot cast to a generated class because javac fails to find it.

@slemesle
Copy link

slemesle commented Mar 6, 2018

Hi,

in this case XMapper is an abstract class so Selma does not know how to instantiate it by default.
But you can provide the instance when building the YMapper.

Selma.builder(YMapper.class).withCustom(Selma.mapper(XMapper.class)).build();

When the Custom Mapper is a class with default constructor available, Selma will instantiate it for you.

@tucu00
Copy link
Author

tucu00 commented Feb 25, 2019

Hi, Sorry for the long hiatus following this up. What I was suggesting was Selma to be a bit more proactive about finding the Mappers (with default constructor and/or Selma generated). Currently I'm using the following code to do that and it works out nicely. It would be nice if Selma does something like that:

/**

  • Returns a Selma Mapper that has all its custom mappers, transitively, instantiated and injected if
  • the custom mappers are Selma Mappers and/or have a default constructor.
    */
    public static T build(Class mapperClass) {
    T mapper;
    if (mapperClass.isInterface() || (mapperClass.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT) {
    // if interface or abstract, we assume it is a Selma generated Mapper.
    mapper = Selma.builder(mapperClass).build();
    injectCustomMappers(mapper);
    } else {
    // not abstract, if it has a default constructor we instantiate it
    try {
    Constructor constructor = mapperClass.getConstructor();
    mapper = constructor.newInstance();
    } catch (NoSuchMethodException ex) {
    // we could not instantiate it, we return null.
    mapper = null;
    } catch (Exception ex) {
    throw new RuntimeException(ex);
    }
    }
    return mapper;
    }

static void injectCustomMappers(T mapper) {
for (Class customMapperClass : findCustomMappers(mapper.getClass())) {
Object customMapper = build(customMapperClass);
injectCustomMapper(mapper, customMapperClass, customMapper);
}
}

static List findCustomMappers(Class mapperClass) {
List classes = new ArrayList();
for (Field field : mapperClass.getDeclaredFields()) {
if (field.getName().startsWith("customMapper")) {
classes.add(field.getType());
}
}
return classes;
}

static void injectCustomMapper(Object mapper, Class customMapperClass, Object customMapper) {
try {
Method method = mapper.getClass().getMethod(
"setCustomMapper" + customMapperClass.getSimpleName(),
customMapperClass
);
method.invoke(mapper, customMapper);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}

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

2 participants