1/*
2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.replacements.verifier;
24
25import java.lang.annotation.Annotation;
26
27import javax.annotation.processing.ProcessingEnvironment;
28import javax.lang.model.element.AnnotationMirror;
29import javax.lang.model.element.AnnotationValue;
30import javax.lang.model.element.Element;
31import javax.lang.model.element.TypeElement;
32import javax.lang.model.type.DeclaredType;
33import javax.lang.model.type.TypeKind;
34import javax.lang.model.type.TypeMirror;
35import javax.tools.Diagnostic.Kind;
36
37import org.graalvm.compiler.api.replacements.ClassSubstitution;
38
39public final class ClassSubstitutionVerifier extends AbstractVerifier {
40
41    private static final String TYPE_VALUE = "value";
42    private static final String STRING_VALUE = "className";
43    private static final String OPTIONAL = "optional";
44
45    public ClassSubstitutionVerifier(ProcessingEnvironment env) {
46        super(env);
47    }
48
49    @Override
50    public Class<? extends Annotation> getAnnotationClass() {
51        return ClassSubstitution.class;
52    }
53
54    @Override
55    public void verify(Element element, AnnotationMirror classSubstitution, PluginGenerator generator) {
56        if (!element.getKind().isClass()) {
57            assert false : "Element is guaranteed to be a class.";
58            return;
59        }
60        TypeElement type = (TypeElement) element;
61
62        TypeElement substitutionType = resolveOriginalType(env, type, classSubstitution);
63        if (substitutionType == null) {
64            return;
65        }
66    }
67
68    static TypeElement resolveOriginalType(ProcessingEnvironment env, Element sourceElement, AnnotationMirror classSubstition) {
69        AnnotationValue typeValue = findAnnotationValue(classSubstition, TYPE_VALUE);
70        AnnotationValue stringValue = findAnnotationValue(classSubstition, STRING_VALUE);
71        AnnotationValue optionalValue = findAnnotationValue(classSubstition, OPTIONAL);
72
73        assert typeValue != null && stringValue != null && optionalValue != null;
74
75        TypeMirror type = resolveAnnotationValue(TypeMirror.class, typeValue);
76        String[] classNames = resolveAnnotationValue(String[].class, stringValue);
77        boolean optional = resolveAnnotationValue(Boolean.class, optionalValue);
78
79        if (type.getKind() != TypeKind.DECLARED) {
80            env.getMessager().printMessage(Kind.ERROR, "The provided class must be a declared type.", sourceElement, classSubstition, typeValue);
81            return null;
82        }
83
84        if (!classSubstition.getAnnotationType().asElement().equals(((DeclaredType) type).asElement())) {
85            if (classNames.length != 0) {
86                String msg = "The usage of value and className is exclusive.";
87                env.getMessager().printMessage(Kind.ERROR, msg, sourceElement, classSubstition, stringValue);
88                env.getMessager().printMessage(Kind.ERROR, msg, sourceElement, classSubstition, typeValue);
89            }
90
91            return (TypeElement) ((DeclaredType) type).asElement();
92        }
93
94        if (classNames.length != 0) {
95            TypeElement typeElement = null;
96            for (String className : classNames) {
97                typeElement = env.getElementUtils().getTypeElement(className);
98                if (typeElement != null) {
99                    break;
100                }
101            }
102            if (typeElement == null && !optional) {
103                env.getMessager().printMessage(Kind.ERROR, String.format("The class '%s' was not found on the classpath.", stringValue), sourceElement, classSubstition, stringValue);
104            }
105
106            return typeElement;
107        }
108
109        if (!optional) {
110            env.getMessager().printMessage(Kind.ERROR, String.format("No value for '%s' or '%s' provided but required.", TYPE_VALUE, STRING_VALUE), sourceElement, classSubstition);
111        }
112
113        return null;
114    }
115
116}
117