/* * 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; } }