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.jet.lang.resolve.kotlin.header;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.resolve.java.AbiVersionUtil;
022    import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames;
023    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
024    import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass;
025    import org.jetbrains.jet.lang.resolve.name.FqName;
026    import org.jetbrains.jet.lang.resolve.name.Name;
027    
028    import java.util.*;
029    
030    import static org.jetbrains.jet.lang.resolve.java.AbiVersionUtil.isAbiVersionCompatible;
031    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.*;
032    import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.AnnotationArgumentVisitor;
033    import static org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass.AnnotationVisitor;
034    import static org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader.Kind.*;
035    
036    public class ReadKotlinClassHeaderAnnotationVisitor implements AnnotationVisitor {
037    
038        private static final Map<JvmClassName, KotlinClassHeader.Kind> HEADER_KINDS = new HashMap<JvmClassName, KotlinClassHeader.Kind>();
039        static {
040            HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_CLASS), CLASS);
041            HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_PACKAGE), PACKAGE_FACADE);
042            HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(KOTLIN_SYNTHETIC_CLASS), SYNTHETIC_CLASS);
043    
044            @SuppressWarnings("deprecation")
045            List<FqName> incompatible = Arrays.asList(OLD_JET_CLASS_ANNOTATION, OLD_JET_PACKAGE_CLASS_ANNOTATION, OLD_KOTLIN_CLASS,
046                                                      OLD_KOTLIN_PACKAGE, OLD_KOTLIN_PACKAGE_FRAGMENT, OLD_KOTLIN_TRAIT_IMPL);
047            for (FqName fqName : incompatible) {
048                HEADER_KINDS.put(JvmClassName.byFqNameWithoutInnerClasses(fqName), INCOMPATIBLE_ABI_VERSION);
049            }
050        }
051    
052        private int version = AbiVersionUtil.INVALID_VERSION;
053        @Nullable
054        private String[] annotationData = null;
055        @Nullable
056        private KotlinClassHeader.Kind headerKind = null;
057    
058        private ReadKotlinClassHeaderAnnotationVisitor() {
059        }
060    
061        @Nullable
062        public static KotlinClassHeader read(@NotNull KotlinJvmBinaryClass kotlinClass) {
063            ReadKotlinClassHeaderAnnotationVisitor visitor = new ReadKotlinClassHeaderAnnotationVisitor();
064            kotlinClass.loadClassAnnotations(visitor);
065            return visitor.createHeader();
066        }
067    
068        @Nullable
069        public KotlinClassHeader createHeader() {
070            if (headerKind == null) {
071                return null;
072            }
073    
074            if (!AbiVersionUtil.isAbiVersionCompatible(version)) {
075                return new KotlinClassHeader(INCOMPATIBLE_ABI_VERSION, version, null);
076            }
077    
078            if ((headerKind == CLASS || headerKind == PACKAGE_FACADE) && annotationData == null) {
079                // This means that the annotation is found and its ABI version is compatible, but there's no "data" string array in it.
080                // We tell the outside world that there's really no annotation at all
081                return null;
082            }
083            return new KotlinClassHeader(headerKind, version, annotationData);
084        }
085    
086        @Nullable
087        @Override
088        public AnnotationArgumentVisitor visitAnnotation(@NotNull JvmClassName annotationClassName) {
089            KotlinClassHeader.Kind newKind = HEADER_KINDS.get(annotationClassName);
090            if (newKind == null) return null;
091    
092            if (this.headerKind != null) {
093                // Ignore all Kotlin annotations except the first found
094                return null;
095            }
096    
097            headerKind = newKind;
098    
099            if (newKind == CLASS || newKind == PACKAGE_FACADE) {
100                return kotlinClassOrPackageVisitor(annotationClassName);
101            }
102            else if (newKind == SYNTHETIC_CLASS) {
103                return syntheticClassAnnotationVisitor(annotationClassName);
104            }
105    
106            return null;
107        }
108    
109        @Override
110        public void visitEnd() {
111        }
112    
113        @NotNull
114        private AnnotationArgumentVisitor kotlinClassOrPackageVisitor(@NotNull final JvmClassName annotationClassName) {
115            return new AnnotationArgumentVisitor() {
116                @Override
117                public void visit(@Nullable Name name, @Nullable Object value) {
118                    visitIntValueForSupportedAnnotation(name, value, annotationClassName);
119                }
120    
121                @Override
122                public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
123                    unexpectedArgument(name, annotationClassName);
124                }
125    
126                @Override
127                @Nullable
128                public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
129                    if (name.asString().equals(JvmAnnotationNames.DATA_FIELD_NAME)) {
130                        return stringArrayVisitor();
131                    }
132                    else if (isAbiVersionCompatible(version)) {
133                        throw new IllegalStateException("Unexpected array argument " + name + " for annotation " + annotationClassName);
134                    }
135    
136                    return null;
137                }
138    
139                @NotNull
140                private AnnotationArgumentVisitor stringArrayVisitor() {
141                    final List<String> strings = new ArrayList<String>(1);
142                    return new AnnotationArgumentVisitor() {
143                        @Override
144                        public void visit(@Nullable Name name, @Nullable Object value) {
145                            if (!(value instanceof String)) {
146                                throw new IllegalStateException("Unexpected argument value: " + value);
147                            }
148    
149                            strings.add((String) value);
150                        }
151    
152                        @Override
153                        public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
154                            unexpectedArgument(name, annotationClassName);
155                        }
156    
157                        @Nullable
158                        @Override
159                        public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
160                            return unexpectedArgument(name, annotationClassName);
161                        }
162    
163                        @Override
164                        public void visitEnd() {
165                            annotationData = strings.toArray(new String[strings.size()]);
166                        }
167                    };
168                }
169    
170                @Override
171                public void visitEnd() {
172                }
173            };
174        }
175    
176        @NotNull
177        private AnnotationArgumentVisitor syntheticClassAnnotationVisitor(@NotNull final JvmClassName annotationClassName) {
178            return new AnnotationArgumentVisitor() {
179                @Override
180                public void visit(@Nullable Name name, @Nullable Object value) {
181                    visitIntValueForSupportedAnnotation(name, value, annotationClassName);
182                }
183    
184                @Override
185                public void visitEnum(@NotNull Name name, @NotNull JvmClassName enumClassName, @NotNull Name enumEntryName) {
186                    // TODO: save kind to somewhere
187                }
188    
189                @Nullable
190                @Override
191                public AnnotationArgumentVisitor visitArray(@NotNull Name name) {
192                    return unexpectedArgument(name, annotationClassName);
193                }
194    
195                @Override
196                public void visitEnd() {
197                }
198            };
199        }
200    
201        private void visitIntValueForSupportedAnnotation(@Nullable Name name, @Nullable Object value, @NotNull JvmClassName className) {
202            if (name != null && name.asString().equals(JvmAnnotationNames.ABI_VERSION_FIELD_NAME)) {
203                version = value == null ? AbiVersionUtil.INVALID_VERSION : (Integer) value;
204            }
205            else {
206                unexpectedArgument(name, className);
207            }
208        }
209    
210        @Nullable
211        private AnnotationArgumentVisitor unexpectedArgument(@Nullable Name name, @NotNull JvmClassName annotationClassName) {
212            if (isAbiVersionCompatible(version)) {
213                throw new IllegalStateException("Unexpected argument " + name + " for annotation " + annotationClassName);
214            }
215            return null;
216        }
217    }