/*
* Copyright (C) 2009 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 signature.converter.doclet;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Map.Entry;
import signature.converter.Visibility;
import signature.model.IAnnotation;
import signature.model.IAnnotationElement;
import signature.model.IAnnotationField;
import signature.model.IApi;
import signature.model.IClassDefinition;
import signature.model.IClassReference;
import signature.model.IConstructor;
import signature.model.IEnumConstant;
import signature.model.IField;
import signature.model.IGenericDeclaration;
import signature.model.IMethod;
import signature.model.IPackage;
import signature.model.IParameter;
import signature.model.ITypeReference;
import signature.model.ITypeVariableDefinition;
import signature.model.ITypeVariableReference;
import signature.model.Kind;
import signature.model.Modifier;
import signature.model.impl.SigAnnotation;
import signature.model.impl.SigAnnotationElement;
import signature.model.impl.SigAnnotationField;
import signature.model.impl.SigApi;
import signature.model.impl.SigClassDefinition;
import signature.model.impl.SigClassReference;
import signature.model.impl.SigConstructor;
import signature.model.impl.SigEnumConstant;
import signature.model.impl.SigExecutableMember;
import signature.model.impl.SigField;
import signature.model.impl.SigMethod;
import signature.model.impl.SigPackage;
import signature.model.impl.SigParameter;
import signature.model.impl.SigPrimitiveType;
import signature.model.impl.SigTypeVariableDefinition;
import signature.model.impl.SigTypeVariableReference;
import signature.model.util.TypePool;
import com.sun.javadoc.AnnotationDesc;
import com.sun.javadoc.AnnotationTypeDoc;
import com.sun.javadoc.AnnotationTypeElementDoc;
import com.sun.javadoc.AnnotationValue;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ConstructorDoc;
import com.sun.javadoc.ExecutableMemberDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.ParameterizedType;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Type;
import com.sun.javadoc.TypeVariable;
import com.sun.javadoc.WildcardType;
import com.sun.javadoc.AnnotationDesc.ElementValuePair;
public class DocletToSigConverter {
TypePool pool;
Set<String> packageNames;
/**
* Converts the signature information javadoc knows about into a
* signature.model.ISources structure.
*/
public IApi convertDocletRoot(String name, RootDoc root,
Visibility visibility, Set<String> packageNames) {
this.pool = new TypePool();
this.packageNames = packageNames;
Set<IPackage> packages = new HashSet<IPackage>();
for (PackageDoc pack : root.specifiedPackages()) {
assert packageNames.contains(pack.name());
packages.add(convertPackage(pack));
}
SigApi sources = new SigApi(name, visibility);
sources.setPackages(packages);
return sources;
}
private IPackage convertPackage(PackageDoc packageDoc) {
Set<IClassDefinition> classes = new HashSet<IClassDefinition>();
for (ClassDoc clazz : packageDoc.allClasses()) {
// classes.add((IClass)convertType(clazz));
classes.add(convertClass(clazz));
}
SigPackage p = new SigPackage(packageDoc.name());
p.setClasses(classes);
p.setAnnotations(convertAnnotations(packageDoc.annotations()));
return p;
}
private SigClassDefinition convertClass(ClassDoc classDoc) {
SigClassDefinition c = pool.getClass(classDoc.containingPackage()
.name(), classDoc.name());
if (c.getKind() != Kind.UNINITIALIZED) return c;
if (classDoc.isEnum())
c.setKind(Kind.ENUM);
else if (classDoc.isInterface())
c.setKind(Kind.INTERFACE);
else if (classDoc.isClass())
c.setKind(Kind.CLASS);
else if (classDoc.isAnnotationType()) c.setKind(Kind.ANNOTATION);
if (!packageNames.contains(c.getPackageName())) {
// no additional conversion for this class is necessary
initializeClass(c);
return c;
}
c.setModifiers(convertModifiers(classDoc.modifierSpecifier()));
if (Kind.INTERFACE.equals(c.getKind())
|| Kind.ANNOTATION.equals(c.getKind())) {
c.getModifiers().add(Modifier.ABSTRACT);
}
// superclass may be a class or a parameterized type (e.g. extends
// List<String>),
// may also be null if classDoc is an interface
Type superclassType = classDoc.superclassType();
if (superclassType != null) {
c.setSuperClass(convertTypeReference(classDoc.superclassType()));
} else {
c.setSuperClass(null);
}
Set<ITypeReference> interfaces = new HashSet<ITypeReference>();
for (Type interfaceType : classDoc.interfaceTypes()) {
interfaces.add(convertTypeReference(interfaceType));
}
c.setInterfaces(interfaces);
ClassDoc containingClass = classDoc.containingClass();
if (containingClass != null)
c.setDeclaringClass(convertClass(containingClass));
else
c.setDeclaringClass(null);
Set<IClassDefinition> innerClasses = new HashSet<IClassDefinition>();
for (ClassDoc innerClass : classDoc.innerClasses()) {
innerClasses.add(convertClass(innerClass));
}
c.setInnerClasses(innerClasses);
Set<IConstructor> constructors = new HashSet<IConstructor>();
for (ConstructorDoc constructor : classDoc.constructors()) {
constructors.add(convertConstructor(constructor));
}
c.setConstructors(constructors);
Set<IMethod> methods = new HashSet<IMethod>();
for (MethodDoc method : classDoc.methods()) {
methods.add(convertMethod(method));
}
c.setMethods(methods);
Set<IField> fields = new HashSet<IField>();
for (FieldDoc field : classDoc.fields()) {
fields.add(convertField(field));
}
c.setFields(fields);
Set<IEnumConstant> enumConstants = new HashSet<IEnumConstant>();
int ordinal = 0;
for (FieldDoc enumConstant : classDoc.enumConstants()) {
enumConstants.add(convertEnumConstant(enumConstant, ordinal++));
}
c.setEnumConstants(enumConstants);
List<ITypeVariableDefinition> typeParameters =
new LinkedList<ITypeVariableDefinition>();
for (TypeVariable typeVariable : classDoc.typeParameters()) {
typeParameters
.add(((ITypeVariableReference) convertTypeReference(
typeVariable)).getTypeVariableDefinition());
}
c.setTypeParameters(typeParameters);
if (classDoc.isAnnotationType()) {
Map<SigAnnotationField, AnnotationTypeElementDoc> annotationFieldAnnotations =
new HashMap<SigAnnotationField, AnnotationTypeElementDoc>();
// AnnotationTypeDoc annotationType =
// classDoc.asAnnotationTypeDoc(); // bug in Doclet Implementation,
// has been reported to sun
AnnotationTypeDoc annotationType = (AnnotationTypeDoc) classDoc;
Set<IAnnotationField> annotationFields =
new HashSet<IAnnotationField>();
for (AnnotationTypeElementDoc annotationElement : annotationType
.elements()) {
SigAnnotationField annotationField = new SigAnnotationField(
annotationElement.name());
annotationField.setModifiers(convertModifiers(annotationElement
.modifierSpecifier()));
annotationField.setType(convertTypeReference(annotationElement
.returnType()));
annotationField
.setDefaultValue(convertAnnotationValue(
annotationElement.defaultValue()));
// the annotations on fields are set later because these
// annotations may be of
// the same type and may use fields which are not yet defined
annotationFieldAnnotations.put(annotationField,
annotationElement);
annotationFields.add(annotationField);
}
c.setAnnotationFields(annotationFields);
// set annotation field annotations
for (Entry<SigAnnotationField, AnnotationTypeElementDoc> entry :
annotationFieldAnnotations.entrySet()) {
entry.getKey().setAnnotations(
convertAnnotations(entry.getValue().annotations()));
}
} else { // no annotation type
c.setAnnotationFields(null);
}
// set class annotations
c.setAnnotations(convertAnnotations(classDoc.annotations()));
return c;
}
private Object convertAnnotationValue(AnnotationValue annotationValue) {
if (annotationValue == null) {
return null;
}
Object value = annotationValue.value();
if (value instanceof Type) {
// Type contains primitive types as well, e.g. void.class
return convertTypeReference((Type) value);
} else if (value instanceof String) {
return value;
} else if (value instanceof Double || value instanceof Float
|| value instanceof Long || value instanceof Integer
|| value instanceof Short || value instanceof Byte
|| value instanceof Character || value instanceof Boolean) {
return value;
} else if (value instanceof FieldDoc) {
FieldDoc field = (FieldDoc) value;
String name = field.name();
ITypeReference fieldType = convertTypeReference(field.type());
IClassReference fieldClassRef = (IClassReference) fieldType;
IClassDefinition fieldClass = fieldClassRef.getClassDefinition();
assert fieldClass.getKind() == Kind.ENUM;
Set<IEnumConstant> constants = fieldClass.getEnumConstants();
for (IEnumConstant enumConstant : constants) {
if (enumConstant.getName().equals(name)) value = enumConstant;
}
assert value instanceof IEnumConstant;
return value;
} else if (value instanceof AnnotationDesc) {
return convertAnnotation((AnnotationDesc) value);
} else if (value instanceof AnnotationValue) {
return convertAnnotationValue((AnnotationValue) value);
} else if (value instanceof AnnotationValue[]) {
AnnotationValue[] arr = (AnnotationValue[]) value;
int length = arr.length;
Object[] annotationArray = new Object[length];
for (int i = 0; i < length; i++) {
annotationArray[i] = convertAnnotationValue(arr[i]);
}
return annotationArray;
} else {
throw new RuntimeException("not expected case");
}
}
private ITypeReference convertArrayType(Type type) {
assert type.asWildcardType() == null;
assert type.asAnnotationTypeDoc() == null;
ITypeReference baseType = null;
if (type.asTypeVariable() != null) {
baseType = convertTypeReference(type.asTypeVariable());
} else if (type.asParameterizedType() != null) {
baseType = convertTypeReference(type.asParameterizedType());
} else if (type.asClassDoc() != null) {
baseType = new SigClassReference(convertClass(type.asClassDoc()));
} else if (type.isPrimitive()) {
baseType = SigPrimitiveType.valueOfTypeName(type.typeName());
} else {
throw new RuntimeException(type.toString());
}
ITypeReference arrayType = baseType;
int dimension = type.dimension().length() / 2;
while (dimension > 0) {
arrayType = pool.getArrayType(arrayType);
dimension--;
}
return arrayType;
}
private SigTypeVariableDefinition currentTypeVariableDefinition = null;
private ITypeReference convertTypeReference(Type type) {
assert type != null;
if (!"".equals(type.dimension())) {
return convertArrayType(type);
}
ParameterizedType pType = type.asParameterizedType();
if (pType != null) {
ITypeReference ownerType = null;
Type containingType = pType.containingType();
if (containingType != null)
ownerType = convertTypeReference(containingType);
IClassReference rawType = new SigClassReference(convertClass(pType
.asClassDoc()));
List<ITypeReference> typeArguments =
new LinkedList<ITypeReference>();
for (Type typeArgument : pType.typeArguments()) {
typeArguments.add(convertTypeReference(typeArgument));
}
if (typeArguments.size() > 0) {
return pool.getParameterizedType(ownerType, rawType,
typeArguments);
} else {
return rawType;
}
}
TypeVariable tv = type.asTypeVariable();
if (tv != null) {
String name = tv.typeName();
if (currentTypeVariableDefinition != null
&& name.equals(currentTypeVariableDefinition.getName()))
return new SigTypeVariableReference(
currentTypeVariableDefinition);
IGenericDeclaration genericDeclaration = null;
ProgramElementDoc programElement = tv.owner();
if (programElement instanceof ClassDoc) {
genericDeclaration = convertClass((ClassDoc) programElement);
} else if (programElement instanceof MethodDoc
&& currentMethod.size() > 0) {
genericDeclaration = currentMethod.peek();
} else if (programElement instanceof ConstructorDoc
&& currentConstructor.size() > 0) {
genericDeclaration = currentConstructor.peek();
} else {
throw new IllegalStateException("situation not expected");
}
SigTypeVariableDefinition typeVariable = pool.getTypeVariable(name,
genericDeclaration);
List<ITypeReference> upperBounds = new LinkedList<ITypeReference>();
for (Type upperBound : tv.bounds()) {
// we are converting a type variable declaration which is stored
// in the
// field currentTypeVariableDefinition
assert currentTypeVariableDefinition == null;
currentTypeVariableDefinition = typeVariable;
upperBounds.add(convertTypeReference(upperBound));
currentTypeVariableDefinition = null;
}
if (upperBounds.size() == 0) {
// no explicit bounds, use java.lang.Object
upperBounds.add(pool.getClassReference("java.lang", "Object"));
}
typeVariable.setUpperBounds(upperBounds);
return new SigTypeVariableReference(typeVariable);
}
WildcardType wt = type.asWildcardType();
if (wt != null) {
ITypeReference lowerBound = null;
for (Type superBound : wt.superBounds()) {
lowerBound = convertTypeReference(superBound);
}
List<ITypeReference> upperBounds = new LinkedList<ITypeReference>();
for (Type upperBound : wt.extendsBounds()) {
upperBounds.add(convertTypeReference(upperBound));
}
if (upperBounds.size() == 0) {
// no explicit bounds, use java.lang.Object
upperBounds.add(pool.getClassReference("java.lang", "Object"));
}
return pool.getWildcardType(lowerBound, upperBounds);
}
ClassDoc c = type.asClassDoc();
if (c != null) {
return new SigClassReference(convertClass(c));
}
if (type.isPrimitive()) {
return SigPrimitiveType.valueOfTypeName(type.typeName());
}
throw new IllegalStateException(type.toString());
}
private void convertExecutableMember(ExecutableMemberDoc member,
SigExecutableMember m) {
Set<Modifier> modifiers = convertModifiers(member.modifierSpecifier());
// Doclet Bug: final values method is not considered as final
if (member.containingClass().isEnum() && member.name().equals("values")
&& member.parameters().length == 0) {
modifiers.add(Modifier.FINAL);
}
if (member.containingClass().isInterface()) {
modifiers.add(Modifier.ABSTRACT);
}
m.setModifiers(modifiers);
m.setAnnotations(convertAnnotations(member.annotations()));
m.setDeclaringClass(convertClass(member.containingClass()));
List<ITypeVariableDefinition> typeParameters =
new LinkedList<ITypeVariableDefinition>();
for (TypeVariable typeParameter : member.typeParameters()) {
String name = typeParameter.typeName();
IGenericDeclaration genericDeclaration = null;
if (currentMethod.size() > 0)
genericDeclaration = currentMethod.peek();
else if (currentConstructor.size() > 0)
genericDeclaration = currentConstructor.peek();
else
throw new RuntimeException();
SigTypeVariableDefinition p = pool.getTypeVariable(name,
genericDeclaration);
List<ITypeReference> upperBounds = new LinkedList<ITypeReference>();
for (Type u : typeParameter.bounds()) {
upperBounds.add(convertTypeReference(u));
}
p.setUpperBounds(upperBounds);
typeParameters.add(p);
}
m.setTypeParameters(typeParameters);
List<IParameter> parameters = new LinkedList<IParameter>();
for (Parameter parameter : member.parameters()) {
SigParameter p = new SigParameter(convertTypeReference(parameter
.type()));
p.setAnnotations(convertAnnotations(parameter.annotations()));
parameters.add(p);
}
m.setParameters(parameters);
Set<ITypeReference> exceptions = new HashSet<ITypeReference>();
for (Type exceptionType : member.thrownExceptionTypes()) {
exceptions.add(convertTypeReference(exceptionType));
}
m.setExceptions(exceptions);
}
private Stack<SigMethod> currentMethod = new Stack<SigMethod>();
private IMethod convertMethod(MethodDoc method) {
SigMethod m = new SigMethod(method.name());
currentMethod.push(m);
convertExecutableMember(method, m);
m.setReturnType(convertTypeReference(method.returnType()));
currentMethod.pop();
return m;
}
private Stack<SigConstructor> currentConstructor =
new Stack<SigConstructor>();
private IConstructor convertConstructor(ConstructorDoc constructor) {
SigConstructor c = new SigConstructor(constructor.name());
currentConstructor.push(c);
convertExecutableMember(constructor, c);
currentConstructor.pop();
return c;
}
private IField convertField(FieldDoc field) {
SigField f = new SigField(field.name());
f.setAnnotations(convertAnnotations(field.annotations()));
f.setModifiers(convertModifiers(field.modifierSpecifier()));
f.setType(convertTypeReference(field.type()));
return f;
}
private IEnumConstant convertEnumConstant(FieldDoc enumConstant,
int ordinal) {
SigEnumConstant ec = new SigEnumConstant(enumConstant.name());
ec.setOrdinal(ordinal);
ec.setAnnotations(convertAnnotations(enumConstant.annotations()));
ec.setModifiers(convertModifiers(enumConstant.modifierSpecifier()));
ec.setType(convertTypeReference(enumConstant.type()));
return ec;
}
private Set<IAnnotation> convertAnnotations(
AnnotationDesc[] annotationDescs) {
Set<IAnnotation> annotations = new HashSet<IAnnotation>();
for (AnnotationDesc annotationDesc : annotationDescs) {
if (!annotationRetentionIsSource(annotationDesc))
annotations.add(convertAnnotation(annotationDesc));
}
return annotations;
}
private boolean annotationRetentionIsSource(AnnotationDesc annotationDesc) {
AnnotationTypeDoc type = annotationDesc.annotationType();
AnnotationDesc[] annotations = type.annotations();
for (AnnotationDesc d : annotations) {
if ("java.lang.annotation.Retention".equals(d.annotationType()
.qualifiedName())) {
for (ElementValuePair value : d.elementValues()) {
if ("value".equals(value.element().name())) {
return "java.lang.annotation.RetentionPolicy.SOURCE"
.equals(value.value().value().toString());
}
}
}
}
// default retention policy is CLASS
return false;
}
private IAnnotation convertAnnotation(AnnotationDesc annotationDesc) {
SigAnnotation a = new SigAnnotation();
IClassReference annotationType = (IClassReference) convertTypeReference(
annotationDesc.annotationType());
a.setType(annotationType);
Set<IAnnotationElement> elements = new HashSet<IAnnotationElement>();
for (AnnotationDesc.ElementValuePair pair : annotationDesc
.elementValues()) {
SigAnnotationElement element = new SigAnnotationElement();
elements.add(element);
element.setValue(convertAnnotationValue(pair.value()));
String name = pair.element().name();
for (IAnnotationField field : annotationType.getClassDefinition()
.getAnnotationFields()) {
if (field.getName().equals(name)) {
element.setDeclaringField(field);
}
}
}
a.setElements(elements);
return a;
}
private void initializeClass(SigClassDefinition c) {
c.setAnnotationFields(null);
c.setAnnotations(null);
c.setConstructors(null);
c.setDeclaringClass(null);
c.setEnumConstants(null);
c.setFields(null);
c.setInnerClasses(null);
c.setInterfaces(null);
c.setMethods(null);
c.setModifiers(null);
c.setSuperClass(null);
c.setTypeParameters(null);
}
private Set<Modifier> convertModifiers(int mod) {
Set<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
if (java.lang.reflect.Modifier.isAbstract(mod))
modifiers.add(Modifier.ABSTRACT);
if (java.lang.reflect.Modifier.isFinal(mod))
modifiers.add(Modifier.FINAL);
// if (java.lang.reflect.Modifier.isNative(mod))
// modifiers.add(Modifier.NATIVE);
if (java.lang.reflect.Modifier.isPrivate(mod))
modifiers.add(Modifier.PRIVATE);
if (java.lang.reflect.Modifier.isProtected(mod))
modifiers.add(Modifier.PROTECTED);
if (java.lang.reflect.Modifier.isPublic(mod))
modifiers.add(Modifier.PUBLIC);
if (java.lang.reflect.Modifier.isStatic(mod))
modifiers.add(Modifier.STATIC);
// if (java.lang.reflect.Modifier.isStrict(mod))
// modifiers.add(Modifier.STRICT);
// if (java.lang.reflect.Modifier.isSynchronized(mod))
// modifiers.add(Modifier.SYNCHRONIZED);
// if (java.lang.reflect.Modifier.isTransient(mod))
// modifiers.add(Modifier.TRANSIENT);
if (java.lang.reflect.Modifier.isVolatile(mod))
modifiers.add(Modifier.VOLATILE);
return modifiers;
}
}