001    /*
002     * Copyright 2010-2013 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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
022    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
023    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
025    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
026    
027    import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
028    
029    public final class AnnotationsUtils {
030    
031        private AnnotationsUtils() {
032        }
033    
034        private static boolean hasAnnotation(@NotNull DeclarationDescriptor descriptor,
035                                             @NotNull PredefinedAnnotation annotation) {
036            return getAnnotationByName(descriptor, annotation) != null;
037        }
038    
039        @Nullable
040        private static String getAnnotationStringParameter(@NotNull DeclarationDescriptor declarationDescriptor,
041                                                           @NotNull PredefinedAnnotation annotation) {
042            AnnotationDescriptor annotationDescriptor = getAnnotationByName(declarationDescriptor, annotation);
043            assert annotationDescriptor != null;
044            //TODO: this is a quick fix for unsupported default args problem
045            if (annotationDescriptor.getAllValueArguments().isEmpty()) {
046                return null;
047            }
048            CompileTimeConstant<?> constant = annotationDescriptor.getAllValueArguments().values().iterator().next();
049            //TODO: this is a quick fix for unsupported default args problem
050            if (constant == null) {
051                return null;
052            }
053            Object value = constant.getValue();
054            assert value instanceof String : "Native function annotation should have one String parameter";
055            return (String) value;
056        }
057    
058        @Nullable
059        public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor declarationDescriptor,
060                                                       @NotNull PredefinedAnnotation annotation) {
061            if (!hasAnnotation(declarationDescriptor, annotation)) {
062                return null;
063            }
064            return getAnnotationStringParameter(declarationDescriptor, annotation);
065        }
066    
067        @Nullable
068        public static String getNameForAnnotatedObject(@NotNull DeclarationDescriptor descriptor) {
069            for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
070                if (!hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
071                    continue;
072                }
073                String name = getNameForAnnotatedObject(descriptor, annotation);
074                return name != null ? name : descriptor.getName().asString();
075            }
076    
077            return null;
078        }
079    
080        @Nullable
081        private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor,
082                @NotNull PredefinedAnnotation annotation) {
083            return getAnnotationByName(descriptor, annotation.getFQName());
084        }
085    
086        @Nullable
087        private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
088            for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
089                if (getAnnotationClassFQName(annotationDescriptor).equals(fqn)) {
090                    return annotationDescriptor;
091                }
092            }
093            return null;
094        }
095    
096        @NotNull
097        private static String getAnnotationClassFQName(@NotNull AnnotationDescriptor annotationDescriptor) {
098            DeclarationDescriptor annotationDeclaration =
099                    annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
100            assert annotationDeclaration != null : "Annotation supposed to have a declaration";
101            return DescriptorUtils.getFQName(annotationDeclaration).asString();
102        }
103    
104        public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
105            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE);
106        }
107    
108        public static boolean isLibraryObject(@NotNull DeclarationDescriptor descriptor) {
109            return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.LIBRARY);
110        }
111    
112        public static boolean isPredefinedObject(@NotNull DeclarationDescriptor descriptor) {
113            for (PredefinedAnnotation annotation : PredefinedAnnotation.values()) {
114                if (hasAnnotationOrInsideAnnotatedClass(descriptor, annotation)) {
115                    return true;
116                }
117            }
118            return false;
119        }
120    
121        public static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor,
122                @NotNull PredefinedAnnotation annotation) {
123            return hasAnnotationOrInsideAnnotatedClass(descriptor, annotation.getFQName());
124        }
125    
126        private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
127            if (getAnnotationByName(descriptor, fqn) != null) {
128                return true;
129            }
130            ClassDescriptor containingClass = getContainingClass(descriptor);
131            return containingClass != null && getAnnotationByName(containingClass, fqn) != null;
132        }
133    }