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