001package ball.annotation.processing;
002/*-
003 * ##########################################################################
004 * Utilities
005 * %%
006 * Copyright (C) 2008 - 2022 Allen D. Ball
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * ##########################################################################
020 */
021import ball.annotation.ConstantValueMustConvertTo;
022import ball.annotation.ServiceProviderFor;
023import java.lang.reflect.InvocationTargetException;
024import javax.annotation.processing.Processor;
025import javax.annotation.processing.RoundEnvironment;
026import javax.lang.model.element.AnnotationMirror;
027import javax.lang.model.element.AnnotationValue;
028import javax.lang.model.element.Element;
029import javax.lang.model.element.TypeElement;
030import javax.lang.model.element.VariableElement;
031import javax.lang.model.type.TypeMirror;
032import lombok.NoArgsConstructor;
033import lombok.ToString;
034
035import static javax.tools.Diagnostic.Kind.ERROR;
036
037/**
038 * {@link Processor} implementation to verify constant initializers
039 * marked by the {@link ConstantValueMustConvertTo} can be converted to the
040 * specified type.
041 *
042 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball}
043 */
044@ServiceProviderFor({ Processor.class })
045@For({ ConstantValueMustConvertTo.class })
046@NoArgsConstructor @ToString
047public class ConstantValueMustConvertToProcessor extends AnnotatedProcessor {
048    @Override
049    public void process(RoundEnvironment roundEnv, TypeElement annotation, Element element) {
050        super.process(roundEnv, annotation, element);
051
052        AnnotationMirror mirror = getAnnotationMirror(element, annotation);
053        AnnotationValue value = getAnnotationValue(mirror, "value");
054        TypeElement to = (TypeElement) types.asElement((TypeMirror) value.getValue());
055        String method = (String) getAnnotationValue(mirror, "method").getValue();
056        Object from = null;
057
058        try {
059            from = ((VariableElement) element).getConstantValue();
060
061            Class<?> type = Class.forName(to.getQualifiedName().toString());
062
063            if (! method.isEmpty()) {
064                type.getMethod(method, from.getClass())
065                    .invoke(null, from);
066            } else {
067                type.getConstructor(from.getClass())
068                    .newInstance(from);
069            }
070        } catch (Exception exception) {
071            Throwable throwable = exception;
072
073            while (throwable instanceof InvocationTargetException) {
074                throwable = throwable.getCause();
075            }
076
077            print(ERROR, element,
078                  "Cannot convert %s to %s\n%s: %s",
079                  elements.getConstantExpression(from), to.getQualifiedName(),
080                  throwable.getClass().getName(), throwable.getMessage());
081        }
082    }
083}