1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.databinding.tool.reflection.annotation;
17
18import android.databinding.Bindable;
19import android.databinding.tool.reflection.ModelClass;
20import android.databinding.tool.reflection.ModelMethod;
21import android.databinding.tool.reflection.SdkUtil;
22import android.databinding.tool.reflection.TypeUtil;
23
24import java.util.List;
25
26import javax.lang.model.element.Element;
27import javax.lang.model.element.ElementKind;
28import javax.lang.model.element.ExecutableElement;
29import javax.lang.model.element.Modifier;
30import javax.lang.model.element.TypeElement;
31import javax.lang.model.type.DeclaredType;
32import javax.lang.model.type.ExecutableType;
33import javax.lang.model.type.TypeKind;
34import javax.lang.model.type.TypeMirror;
35import javax.lang.model.util.Elements;
36import javax.lang.model.util.Types;
37
38class AnnotationMethod extends ModelMethod {
39    final ExecutableType mMethod;
40    final DeclaredType mDeclaringType;
41    final ExecutableElement mExecutableElement;
42    int mApiLevel = -1; // calculated on demand
43    ModelClass mReceiverType;
44
45    public AnnotationMethod(DeclaredType declaringType, ExecutableElement executableElement) {
46        mDeclaringType = declaringType;
47        mExecutableElement = executableElement;
48        Types typeUtils = AnnotationAnalyzer.get().getTypeUtils();
49        mMethod = (ExecutableType) typeUtils.asMemberOf(declaringType, executableElement);
50    }
51
52    @Override
53    public ModelClass getDeclaringClass() {
54        if (mReceiverType == null) {
55            mReceiverType = findReceiverType(mDeclaringType);
56            if (mReceiverType == null) {
57                mReceiverType = new AnnotationClass(mDeclaringType);
58            }
59        }
60        return mReceiverType;
61    }
62
63    // TODO: When going to Java 1.8, use mExecutableElement.getReceiverType()
64    private ModelClass findReceiverType(DeclaredType subType) {
65        List<? extends TypeMirror> supers = getTypeUtils().directSupertypes(subType);
66        for (TypeMirror superType : supers) {
67            if (superType.getKind() == TypeKind.DECLARED) {
68                DeclaredType declaredType = (DeclaredType) superType;
69                ModelClass inSuper = findReceiverType(declaredType);
70                if (inSuper != null) {
71                    return inSuper;
72                } else if (hasExecutableMethod(declaredType)) {
73                    return new AnnotationClass(declaredType);
74                }
75            }
76        }
77        return null;
78    }
79
80    private boolean hasExecutableMethod(DeclaredType declaredType) {
81        Elements elementUtils = getElementUtils();
82        TypeElement enclosing = (TypeElement) mExecutableElement.getEnclosingElement();
83        TypeElement typeElement = (TypeElement) declaredType.asElement();
84        for (Element element : typeElement.getEnclosedElements()) {
85            if (element.getKind() == ElementKind.METHOD) {
86                ExecutableElement executableElement = (ExecutableElement) element;
87                if (executableElement.equals(mExecutableElement) ||
88                        elementUtils.overrides(mExecutableElement, executableElement, enclosing)) {
89                    return true;
90                }
91            }
92        }
93        return false;
94    }
95
96    @Override
97    public ModelClass[] getParameterTypes() {
98        List<? extends TypeMirror> parameters = mMethod.getParameterTypes();
99        ModelClass[] parameterTypes = new ModelClass[parameters.size()];
100        for (int i = 0; i < parameters.size(); i++) {
101            parameterTypes[i] = new AnnotationClass(parameters.get(i));
102        }
103        return parameterTypes;
104    }
105
106    @Override
107    public String getName() {
108        return mExecutableElement.getSimpleName().toString();
109    }
110
111    @Override
112    public ModelClass getReturnType(List<ModelClass> args) {
113        TypeMirror returnType = mMethod.getReturnType();
114        // TODO: support argument-supplied types
115        // for example: public T[] toArray(T[] arr)
116        return new AnnotationClass(returnType);
117    }
118
119    @Override
120    public boolean isVoid() {
121        return mMethod.getReturnType().getKind() == TypeKind.VOID;
122    }
123
124    @Override
125    public boolean isPublic() {
126        return mExecutableElement.getModifiers().contains(Modifier.PUBLIC);
127    }
128
129    @Override
130    public boolean isStatic() {
131        return mExecutableElement.getModifiers().contains(Modifier.STATIC);
132    }
133
134    @Override
135    public boolean isAbstract() {
136        return mExecutableElement.getModifiers().contains(Modifier.ABSTRACT);
137    }
138
139    @Override
140    public boolean isBindable() {
141        return mExecutableElement.getAnnotation(Bindable.class) != null;
142    }
143
144    @Override
145    public int getMinApi() {
146        if (mApiLevel == -1) {
147            mApiLevel = SdkUtil.getMinApi(this);
148        }
149        return mApiLevel;
150    }
151
152    @Override
153    public String getJniDescription() {
154        return TypeUtil.getInstance().getDescription(this);
155    }
156
157    @Override
158    public boolean isVarArgs() {
159        return mExecutableElement.isVarArgs();
160    }
161
162    private static Types getTypeUtils() {
163        return AnnotationAnalyzer.get().mProcessingEnv.getTypeUtils();
164    }
165
166    private static Elements getElementUtils() {
167        return AnnotationAnalyzer.get().mProcessingEnv.getElementUtils();
168    }
169
170    @Override
171    public String toString() {
172        return "AnnotationMethod{" +
173                "mMethod=" + mMethod +
174                ", mDeclaringType=" + mDeclaringType +
175                ", mExecutableElement=" + mExecutableElement +
176                ", mApiLevel=" + mApiLevel +
177                '}';
178    }
179}
180