001    /*
002     * Copyright 2010-2014 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.k2js.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.jet.lang.descriptors.CallableMemberDescriptor;
024    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.jet.lang.resolve.OverrideResolver;
028    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
029    import org.jetbrains.jet.lang.resolve.name.FqName;
030    
031    import java.util.List;
032    import java.util.Set;
033    
034    import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
035    import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.isOverride;
036    
037    public final class AnnotationsUtils {
038    
039        private AnnotationsUtils() {
040        }
041    
042        private static boolean hasAnnotation(@NotNull DeclarationDescriptor descriptor,
043                                             @NotNull PredefinedAnnotation annotation) {
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                isOverride((CallableMemberDescriptor) declarationDescriptor)) {
081    
082                Set<CallableMemberDescriptor> overriddenDeclarations =
083                        OverrideResolver.getAllOverriddenDeclarations((CallableMemberDescriptor) declarationDescriptor);
084    
085                descriptors = ContainerUtil.mapNotNull(overriddenDeclarations, new Function<CallableMemberDescriptor, DeclarationDescriptor>() {
086                    @Override
087                    public DeclarationDescriptor fun(CallableMemberDescriptor descriptor) {
088                        return 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.values()) {
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(@NotNull DeclarationDescriptor descriptor,
110                @NotNull PredefinedAnnotation annotation) {
111            return getAnnotationByName(descriptor, annotation.getFQName());
112        }
113    
114        @Nullable
115        private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
116            return descriptor.getAnnotations().findAnnotation(new FqName(fqn));
117        }
118    
119        public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
120            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE);
121        }
122    
123        public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) {
124            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.LIBRARY);
125        }
126    
127        public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) {
128            for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
129                if (hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
130                    return true;
131                }
132            }
133            return false;
134        }
135    
136        public static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor,
137                @NotNull PredefinedAnnotation annotation) {
138            return hasAnnotationOrInsideAnnotatedClass(descriptor, annotation.getFQName());
139        }
140    
141        private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
142            if (getAnnotationByName(descriptor, fqn) != null) {
143                return true;
144            }
145            ClassDescriptor containingClass = getContainingClass(descriptor);
146            return containingClass != null && hasAnnotationOrInsideAnnotatedClass(containingClass, fqn);
147        }
148    }