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