Na minha empresa temos muitos Enums que implementam uma interface como esta:
public interface DomainEnum {
String getDomainId();
}
@Getter
@AllArgsConstructor
public enum ExampleEnum implements DomainEnum {
A("1"), B("2"), C("3");
private final String domainId;
}
Gostaria de usar o MapStruct para mapear Strings para qualquer Enum implementado DomainEnum
usando o domainId. Nos documentos, vi que você pode implementar uma estratégia de nomeação de enum personalizada que descreve exatamente o que eu quero. Meu problema atual é que não tenho certeza de como obter o domainId
do enum. Este é meu código até agora:
public class CustomEnumMappingStrategy extends DefaultEnumMappingStrategy {
// other methods like in example from docs
protected String getCustomEnumConstant(TypeElement enumTypeElement, String enumConstant) {
var enumValue = getEnumValue(enumTypeElement, enumConstant);
// How can I now get the domainId from the ElementKind?
}
protected ElementKind getEnumValue(TypeElement enumTypeElement, String enumConstant) {
return enumTypeElement.getEnclosedElements().stream()
.filter(it -> it.getKind().equals(ElementKind.ENUM_CONSTANT))
.filter(it -> it.toString().equals(enumConstant))
.findFirst()
.orElse(null);
}
}
Então, como eu poderia começar domainId
do ElementKind
agora para finalizar minha estratégia de mapeamento?
O Processamento de Anotações é limitado somente a assinaturas. Para definições de constantes enum, ele termina com o nome.
("1")
peçaA("1"),
em um processador de anotações.ExampleEnum.values()
para então chamar.getDomainId()
cada valor por vez de dentro do seu processador de anotações: Você executa durante a compilação , oExampleEnum
é apenas um arquivo de origem neste ponto, ele ainda não existe como um arquivo de classe. Mesmo que existisse, carregar classes arbitrárias durante a compilação é um problema de ovo e galinha e, portanto, uma ideia bastante ruim que tornará seu código muito frágil. Por exemplo, se um de seus enums usar um tipo não Java-core em qualquer lugar, por exemplo, um métodopublic SomeTypeYouDefined getFoo()
? A compilação explode; seu código não pode mais ser compilado.Assim chegamos à resposta à sua pergunta: Infelizmente, o que você quer é impossível .
Outro problema é que sua "definição" do que você quer é fundamentalmente impraticável.
Por exemplo, aqui está uma implementação correta do seu sistema:
Você não terá um erro de compilador ou um erro de tempo de execução, isso 'simplesmente funcionará', mas obviamente qualquer tentativa em tempo de compilação de criar um mapeamento de todos os IDs de domínio falhará completamente. Esse é um lugar ruim para se estar.
Eu sugiro fortemente que você pare com esse plano. Ele simplesmente não "se encaixa". Fazer coisas em tempo de compilação onde uma das entradas depende de implementações de um método decretado por uma interface nunca é uma boa ideia por esse exato motivo.
É isso que você realmente quer.
Anote as próprias constantes enum!
Este trecho é independente, basta inseri-lo
Test.java
e seguir em frente.O snippet mostra como você pode acessar os IDs de domínio em tempo de execução. Acessá-los em tempo de anotação seria um exemplo autônomo significativamente mais complexo, mas é lógica básica de processamento de anotação. Acione na anotação e inspecione o 'elemento' que a anotação está anotando. Os valores Enum são tratados como campos para esse propósito, e você pode obter o nome deles em seu processador de anotação muito bem.
NB: O exemplo acima precisa de algum desenvolvimento; detectar que a
DomainId
anotação está faltando e produzir uma exceção apropriada, por exemplo. Seu processador de anotação deve detectar quaisquer enums que implementam,DomainEnum
mas não têm uma@DomainId
anotação em cada constante que definem e emitir um erro. Isso é coisa básica de AP e não requer truques especiais: você pode 'obter' todos os arquivos de origem sendo compilados como AP facilmente (inscreva-se*
como uma anotação para obtê-los), você pode perguntar por todos os tipos de nível superior neles, então filtrá-los para enums, então filtrar todos os enums que não implementamDomainEnum
e partir daí.Como o 'ID de domínio' é transmitido por meio de um parâmetro de anotação, o próprio compilador forçará os autores de quaisquer enumerações de domínio a tornar esses valores constantes, eliminando assim completamente o exemplo complicado.
NB: O Lombok hackeia diretamente todo o AST, mas o lombok não é um processador de anotações e tem vários caminhos de código para as várias implementações AST comumente usadas por aí. Você provavelmente não quer seguir esse caminho, mas se realmente quiser, pode bifurcar o lombok e adicionar um processador lombok (você não precisa de uma anotação; os plugins do lombok veem todo o código e, portanto, podem decidir apenas escanear os tipos de enumeração que implementam
DomainEnum
e procurar por eles). Mas você ainda teria que ditar, conforme a documentação, que todas as enumerações de domínio DEVEM ter seu ID de domínio como primeiro parâmetro de cada valor constante de enumeração. Eu seguiria o plano acima, muito mais simples e melhor.