001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.js.translate.utils;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
022    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
023    import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor;
024    import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
025    import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget;
027    import org.jetbrains.kotlin.descriptors.annotations.Annotations;
028    import org.jetbrains.kotlin.js.PredefinedAnnotation;
029    import org.jetbrains.kotlin.name.FqName;
030    import org.jetbrains.kotlin.resolve.DescriptorUtils;
031    import org.jetbrains.kotlin.resolve.constants.ConstantValue;
032    
033    public final class AnnotationsUtils {
034        private static final String JS_NAME = "kotlin.js.JsName";
035    
036        private AnnotationsUtils() {
037        }
038    
039        public static boolean hasAnnotation(
040                @NotNull DeclarationDescriptor descriptor,
041                @NotNull PredefinedAnnotation annotation
042        ) {
043            return getAnnotationByName(descriptor, annotation) != null;
044        }
045    
046        @Nullable
047        private static String getAnnotationStringParameter(@NotNull DeclarationDescriptor declarationDescriptor,
048                @NotNull PredefinedAnnotation annotation) {
049            AnnotationDescriptor annotationDescriptor = getAnnotationByName(declarationDescriptor, annotation);
050            assert annotationDescriptor != null;
051            //TODO: this is a quick fix for unsupported default args problem
052            if (annotationDescriptor.getAllValueArguments().isEmpty()) {
053                return null;
054            }
055            ConstantValue<?> constant = annotationDescriptor.getAllValueArguments().values().iterator().next();
056            //TODO: this is a quick fix for unsupported default args problem
057            if (constant == null) {
058                return null;
059            }
060            Object value = constant.getValue();
061            assert value instanceof String : "Native function annotation should have one String parameter";
062            return (String) value;
063        }
064    
065        @Nullable
066        public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor declarationDescriptor,
067                @NotNull PredefinedAnnotation annotation) {
068            if (!hasAnnotation(declarationDescriptor, annotation)) {
069                return null;
070            }
071            return getAnnotationStringParameter(declarationDescriptor, annotation);
072        }
073    
074        @Nullable
075        public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor descriptor) {
076            for (PredefinedAnnotation annotation : PredefinedAnnotation.Companion.getWITH_CUSTOM_NAME()) {
077                if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
078                    continue;
079                }
080                String name = getNameForAnnotatedObject(descriptor, annotation);
081                if (name == null) {
082                    name = getJsName(descriptor);
083                }
084                return name != null ? name : descriptor.getName().asString();
085            }
086    
087            return getJsName(descriptor);
088        }
089    
090        @Nullable
091        private static AnnotationDescriptor getAnnotationByName(
092                @NotNull DeclarationDescriptor descriptor,
093                @NotNull PredefinedAnnotation annotation
094        ) {
095            return getAnnotationByName(descriptor, annotation.getFqName());
096        }
097    
098        @Nullable
099        private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull FqName fqName) {
100            AnnotationWithTarget annotationWithTarget = Annotations.Companion.findAnyAnnotation(descriptor.getAnnotations(), (fqName));
101            return annotationWithTarget != null ? annotationWithTarget.getAnnotation() : null;
102        }
103    
104        public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
105            if (hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE)) return true;
106    
107            if (descriptor instanceof PropertyAccessorDescriptor) {
108                PropertyAccessorDescriptor accessor = (PropertyAccessorDescriptor) descriptor;
109                return hasAnnotationOrInsideAnnotatedClass(accessor.getCorrespondingProperty(), PredefinedAnnotation.NATIVE);
110            }
111    
112            return false;
113        }
114    
115        public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) {
116            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.LIBRARY);
117        }
118    
119        @Nullable
120        public static String getJsName(@NotNull DeclarationDescriptor descriptor) {
121            AnnotationDescriptor annotation = getJsNameAnnotation(descriptor);
122            if (annotation == null) return null;
123    
124            ConstantValue<?> value = annotation.getAllValueArguments().values().iterator().next();
125            assert value != null : "JsName annotation should always declare string parameter";
126    
127            Object result = value.getValue();
128            assert result instanceof String : "Parameter of JsName annotation should be string";
129            return (String) result;
130        }
131    
132        @Nullable
133        public static AnnotationDescriptor getJsNameAnnotation(@NotNull DeclarationDescriptor descriptor) {
134            return getAnnotationByName(descriptor, new FqName(JS_NAME));
135        }
136    
137        public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) {
138            for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
139                if (hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
140                    return true;
141                }
142            }
143            return false;
144        }
145    
146        public static boolean hasAnnotationOrInsideAnnotatedClass(
147                @NotNull DeclarationDescriptor descriptor,
148                @NotNull PredefinedAnnotation annotation
149        ) {
150            return hasAnnotationOrInsideAnnotatedClass(descriptor, annotation.getFqName());
151        }
152    
153        private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull FqName fqName) {
154            if (getAnnotationByName(descriptor, fqName) != null) {
155                return true;
156            }
157            ClassDescriptor containingClass = DescriptorUtils.getContainingClass(descriptor);
158            return containingClass != null && hasAnnotationOrInsideAnnotatedClass(containingClass, fqName);
159        }
160    
161        public static boolean hasJsNameInAccessors(@NotNull PropertyDescriptor property) {
162            for (PropertyAccessorDescriptor accessor : property.getAccessors()) {
163                if (getJsName(accessor) != null) return true;
164            }
165            return false;
166        }
167    }