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
017package org.jetbrains.k2js.translate.utils;
018
019import org.jetbrains.annotations.NotNull;
020import org.jetbrains.annotations.Nullable;
021import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
022import org.jetbrains.jet.lang.descriptors.ClassKind;
023import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
024import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
025import org.jetbrains.jet.lang.resolve.DescriptorUtils;
026import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
027
028import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
029
030public 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    private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor,
072            @NotNull PredefinedAnnotation annotation) {
073        return getAnnotationByName(descriptor, annotation.getFQName());
074    }
075
076    @Nullable
077    private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
078        for (AnnotationDescriptor annotationDescriptor : descriptor.getAnnotations()) {
079            if (getAnnotationClassFQName(annotationDescriptor).equals(fqn)) {
080                return annotationDescriptor;
081            }
082        }
083        return null;
084    }
085
086    @NotNull
087    private static String getAnnotationClassFQName(@NotNull AnnotationDescriptor annotationDescriptor) {
088        DeclarationDescriptor annotationDeclaration =
089                annotationDescriptor.getType().getConstructor().getDeclarationDescriptor();
090        assert annotationDeclaration != null : "Annotation supposed to have a declaration";
091        return DescriptorUtils.getFQName(annotationDeclaration).asString();
092    }
093
094    public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
095        return hasAnnotationOrInsideAnnotatedClass(descriptor, PredefinedAnnotation.NATIVE);
096    }
097
098    public static boolean isEnumerable(@NotNull DeclarationDescriptor descriptor) {
099        if (getAnnotationByName(descriptor, ENUMERABLE) != null) {
100            return true;
101        }
102        ClassDescriptor containingClass = getContainingClass(descriptor);
103        return containingClass != null &&
104               (getAnnotationByName(containingClass, ENUMERABLE) != null ||
105                (containingClass.getKind().equals(ClassKind.OBJECT) && containingClass.getName().isSpecial()));
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}