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 com.intellij.util.Function;
020    import com.intellij.util.containers.ContainerUtil;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
024    import org.jetbrains.kotlin.descriptors.ClassDescriptor;
025    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026    import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.kotlin.js.PredefinedAnnotation;
028    import org.jetbrains.kotlin.name.FqName;
029    import org.jetbrains.kotlin.resolve.DescriptorUtils;
030    import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
031    
032    import java.util.List;
033    import java.util.Set;
034    
035    public final class AnnotationsUtils {
036    
037        private AnnotationsUtils() {
038        }
039    
040        public static boolean hasAnnotation(
041                @NotNull DeclarationDescriptor descriptor,
042                @NotNull PredefinedAnnotation annotation
043        ) {
044            return getAnnotationByName(descriptor, annotation) != null;
045        }
046    
047        @Nullable
048        private static String getAnnotationStringParameter(@NotNull DeclarationDescriptor declarationDescriptor,
049                @NotNull PredefinedAnnotation annotation) {
050            AnnotationDescriptor annotationDescriptor = getAnnotationByName(declarationDescriptor, annotation);
051            assert annotationDescriptor != null;
052            //TODO: this is a quick fix for unsupported default args problem
053            if (annotationDescriptor.getAllValueArguments().isEmpty()) {
054                return null;
055            }
056            CompileTimeConstant<?> constant = annotationDescriptor.getAllValueArguments().values().iterator().next();
057            //TODO: this is a quick fix for unsupported default args problem
058            if (constant == null) {
059                return null;
060            }
061            Object value = constant.getValue();
062            assert value instanceof String : "Native function annotation should have one String parameter";
063            return (String) value;
064        }
065    
066        @Nullable
067        public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor declarationDescriptor,
068                @NotNull PredefinedAnnotation annotation) {
069            if (!hasAnnotation(declarationDescriptor, annotation)) {
070                return null;
071            }
072            return getAnnotationStringParameter(declarationDescriptor, annotation);
073        }
074    
075        @Nullable
076        public static String getNameForAnnotatedObjectWithOverrides(@NotNull DeclarationDescriptor declarationDescriptor) {
077            List<DeclarationDescriptor> descriptors;
078    
079            if (declarationDescriptor instanceof CallableMemberDescriptor &&
080                DescriptorUtils.isOverride((CallableMemberDescriptor) declarationDescriptor)) {
081    
082                Set<CallableMemberDescriptor> overriddenDeclarations =
083                        DescriptorUtils.getAllOverriddenDeclarations((CallableMemberDescriptor) declarationDescriptor);
084    
085                descriptors = ContainerUtil.mapNotNull(overriddenDeclarations, new Function<CallableMemberDescriptor, DeclarationDescriptor>() {
086                    @Override
087                    public DeclarationDescriptor fun(CallableMemberDescriptor descriptor) {
088                        return DescriptorUtils.isOverride(descriptor) ? null : descriptor;
089                    }
090                });
091            }
092            else {
093                descriptors = ContainerUtil.newArrayList(declarationDescriptor);
094            }
095    
096            for (DeclarationDescriptor descriptor : descriptors) {
097                for (PredefinedAnnotation annotation : PredefinedAnnotation.Default.getWITH_CUSTOM_NAME()) {
098                    if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
099                        continue;
100                    }
101                    String name = getNameForAnnotatedObject(descriptor, annotation);
102                    return name != null ? name : descriptor.getName().asString();
103                }
104            }
105            return null;
106        }
107    
108        @Nullable
109        private static AnnotationDescriptor getAnnotationByName(
110                @NotNull DeclarationDescriptor descriptor,
111                @NotNull PredefinedAnnotation annotation
112        ) {
113            return getAnnotationByName(descriptor, annotation.getFqName());
114        }
115    
116        @Nullable
117        private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull FqName fqName) {
118            return descriptor.getAnnotations().findAnnotation(fqName);
119        }
120    
121        public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
122            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE);
123        }
124    
125        public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) {
126            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.LIBRARY);
127        }
128    
129        public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) {
130            for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
131                if (hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
132                    return true;
133                }
134            }
135            return false;
136        }
137    
138        public static boolean hasAnnotationOrInsideAnnotatedClass(
139                @NotNull DeclarationDescriptor descriptor,
140                @NotNull PredefinedAnnotation annotation
141        ) {
142            return hasAnnotationOrInsideAnnotatedClass(descriptor, annotation.getFqName());
143        }
144    
145        private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull FqName fqName) {
146            if (getAnnotationByName(descriptor, fqName) != null) {
147                return true;
148            }
149            ClassDescriptor containingClass = DescriptorUtils.getContainingClass(descriptor);
150            return containingClass != null && hasAnnotationOrInsideAnnotatedClass(containingClass, fqName);
151        }
152    }