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 }