/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.tasks.annotations;
import com.android.annotations.NonNull;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Javadoc;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/** Gathers information about typedefs (@IntDef and @StringDef */
public class TypedefCollector extends ASTVisitor {
private Map<String,List<Annotation>> mMap = Maps.newHashMap();
private final boolean mRequireHide;
private final boolean mRequireSourceRetention;
private CompilationUnitDeclaration mCurrentUnit;
private List<String> mTypedefClasses = Lists.newArrayList();
public TypedefCollector(
@NonNull Collection<CompilationUnitDeclaration> units,
boolean requireHide,
boolean requireSourceRetention) {
mRequireHide = requireHide;
mRequireSourceRetention = requireSourceRetention;
for (CompilationUnitDeclaration unit : units) {
mCurrentUnit = unit;
unit.traverse(this, unit.scope);
mCurrentUnit = null;
}
}
public List<String> getNonPublicTypedefClasses() {
return mTypedefClasses;
}
public Map<String,List<Annotation>> getTypedefs() {
return mMap;
}
@Override
public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
return recordTypedefs(memberTypeDeclaration);
}
@Override
public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
return recordTypedefs(typeDeclaration);
}
private boolean recordTypedefs(TypeDeclaration declaration) {
SourceTypeBinding binding = declaration.binding;
if (binding == null) {
return false;
}
Annotation[] annotations = declaration.annotations;
if (annotations != null) {
if (declaration.binding.isAnnotationType()) {
for (Annotation annotation : annotations) {
String typeName = Extractor.getFqn(annotation);
if (typeName == null) {
continue;
}
if (Extractor.isNestedAnnotation(typeName)) {
String fqn = new String(binding.readableName());
List<Annotation> list = mMap.get(fqn);
if (list == null) {
list = new ArrayList<Annotation>(2);
mMap.put(fqn, list);
}
list.add(annotation);
if (mRequireHide) {
Javadoc javadoc = declaration.javadoc;
if (javadoc != null) {
StringBuffer stringBuffer = new StringBuffer(200);
javadoc.print(0, stringBuffer);
String documentation = stringBuffer.toString();
if (!documentation.contains("@hide")) {
Extractor.warning(getFileName()
+ ": The typedef annotation " + fqn
+ " should specify @hide in a doc comment");
}
}
}
if (mRequireSourceRetention
&& !Extractor.hasSourceRetention(annotations)) {
Extractor.warning(getFileName()
+ ": The typedef annotation " + fqn
+ " should have @Retention(RetentionPolicy.SOURCE)");
}
if (declaration.binding != null
&& (declaration.modifiers & ClassFileConstants.AccPublic) == 0) {
StringBuilder sb = new StringBuilder(100);
for (char c : declaration.binding.qualifiedPackageName()) {
if (c == '.') {
sb.append('/');
} else {
sb.append(c);
}
}
sb.append(File.separatorChar);
for (char c : declaration.binding.qualifiedSourceName()) {
if (c == '.') {
sb.append('$');
} else {
sb.append(c);
}
}
mTypedefClasses.add(sb.toString());
}
}
}
}
}
return true;
}
private String getFileName() {
return new String(mCurrentUnit.getFileName());
}
}