/* * 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.google.devtools.j2objc.gen; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.MultimapBuilder; import com.google.devtools.j2objc.ast.AbstractTypeDeclaration; import com.google.devtools.j2objc.ast.Annotation; import com.google.devtools.j2objc.ast.BodyDeclaration; import com.google.devtools.j2objc.ast.EnumConstantDeclaration; import com.google.devtools.j2objc.ast.EnumDeclaration; import com.google.devtools.j2objc.ast.FieldDeclaration; import com.google.devtools.j2objc.ast.FunctionDeclaration; import com.google.devtools.j2objc.ast.MethodDeclaration; import com.google.devtools.j2objc.ast.Name; import com.google.devtools.j2objc.ast.NativeDeclaration; import com.google.devtools.j2objc.ast.PropertyAnnotation; import com.google.devtools.j2objc.ast.TreeNode; import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.util.ElementUtil; import com.google.devtools.j2objc.util.ErrorUtil; import com.google.devtools.j2objc.util.NameTable; import com.google.devtools.j2objc.util.TranslationUtil; import com.google.devtools.j2objc.util.TypeUtil; import com.google.devtools.j2objc.util.UnicodeUtils; import com.google.j2objc.annotations.Property; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; /** * Base class for generating type declarations, either public or private. * * @author Tom Ball, Keith Stanger */ public class TypeDeclarationGenerator extends TypeGenerator { private static final String DEPRECATED_ATTRIBUTE = "__attribute__((deprecated))"; protected TypeDeclarationGenerator(SourceBuilder builder, AbstractTypeDeclaration node) { super(builder, node); } public static void generate(SourceBuilder builder, AbstractTypeDeclaration node) { new TypeDeclarationGenerator(builder, node).generate(); } protected boolean printPrivateDeclarations() { return false; } @Override protected boolean shouldPrintDeclaration(BodyDeclaration decl) { if (decl instanceof MethodDeclaration && !((MethodDeclaration) decl).hasDeclaration()) { return false; } return decl.hasPrivateDeclaration() == printPrivateDeclarations(); } private void generate() { // If the type is private, then generate nothing in the header. The initial // declaration will go in the implementation file instead. if (!typeNode.hasPrivateDeclaration()) { generateInitialDeclaration(); } } protected void generateInitialDeclaration() { printNativeEnum(); printTypeDocumentation(); if (typeElement.getKind().isInterface()) { printf("@protocol %s", typeName); } else { printf("@interface %s : %s", typeName, getSuperTypeName()); } printImplementedProtocols(); if (!typeElement.getKind().isInterface()) { printInstanceVariables(); } else { newline(); } printProperties(); if (!typeElement.getKind().isInterface()) { printStaticAccessors(); } printInnerDeclarations(); println("\n@end"); if (ElementUtil.isPackageInfo(typeElement)) { printOuterDeclarations(); return; } printCompanionClassDeclaration(); printStaticInitFunction(); printEnumConstants(); printFieldSetters(); printStaticFieldDeclarations(); printOuterDeclarations(); printTypeLiteralDeclaration(); printBoxedOperators(); printUnprefixedAlias(); } private void printNativeEnum() { if (!(typeNode instanceof EnumDeclaration)) { return; } List<EnumConstantDeclaration> constants = ((EnumDeclaration) typeNode).getEnumConstants(); String nativeName = NameTable.getNativeEnumName(typeName); // C doesn't allow empty enum declarations. Java does, so we skip the // C enum declaration and generate the type declaration. if (!constants.isEmpty()) { newline(); printf("typedef NS_ENUM(NSUInteger, %s) {\n", nativeName); // Print C enum typedef. indent(); int ordinal = 0; for (EnumConstantDeclaration constant : constants) { printIndent(); printf("%s_%s = %d,\n", nativeName, nameTable.getVariableBaseName(constant.getVariableElement()), ordinal++); } unindent(); print("};\n"); } } private void printTypeDocumentation() { newline(); JavadocGenerator.printDocComment(getBuilder(), typeNode.getJavadoc()); if (needsDeprecatedAttribute(typeNode.getAnnotations())) { println(DEPRECATED_ATTRIBUTE); } } private String getSuperTypeName() { TypeElement supertype = TranslationUtil.getSuperType(typeNode); if (supertype != null) { return nameTable.getFullName(supertype); } return "NSObject"; } private List<String> getInterfaceNames() { if (ElementUtil.isAnnotationType(typeElement)) { return Lists.newArrayList("JavaLangAnnotationAnnotation"); } List<String> names = Lists.newArrayList(); for (TypeElement intrface : TranslationUtil.getInterfaceTypes(typeNode)) { names.add(nameTable.getFullName(intrface)); } if (ElementUtil.isEnum(typeElement)) { names.remove("NSCopying"); names.add(0, "NSCopying"); } else if (isInterfaceType()) { names.add("JavaObject"); } return names; } private void printImplementedProtocols() { List<String> interfaces = getInterfaceNames(); if (!interfaces.isEmpty()) { print(" < "); boolean isFirst = true; for (String name : interfaces) { if (!isFirst) { print(", "); } isFirst = false; print(name); } print(" >"); } } protected void printStaticInterfaceMethods() { for (BodyDeclaration declaration : getInnerDeclarations()) { if (declaration.getKind().equals(TreeNode.Kind.METHOD_DECLARATION)) { printMethodDeclaration((MethodDeclaration) declaration, true); } } } /** * Prints the list of static variable and/or enum constant accessor methods. */ protected void printStaticAccessors() { if (options.staticAccessorMethods()) { for (VariableDeclarationFragment fragment : getStaticFields()) { VariableElement var = fragment.getVariableElement(); String accessorName = nameTable.getStaticAccessorName(var); String objcType = nameTable.getObjCType(var.asType()); printf("\n+ (%s)%s;\n", objcType, accessorName); if (!ElementUtil.isFinal(var)) { printf("\n+ (void)set%s:(%s)value;\n", NameTable.capitalize(accessorName), objcType); } } if (typeNode instanceof EnumDeclaration) { for (EnumConstantDeclaration constant : ((EnumDeclaration) typeNode).getEnumConstants()) { String accessorName = nameTable.getStaticAccessorName(constant.getVariableElement()); if (options.nullability()) { printf("\n+ (%s * __nonnull)%s;\n", typeName, accessorName); } else { printf("\n+ (%s *)%s;\n", typeName, accessorName); } } } } } /** * Prints the list of instance variables in a type. */ protected void printInstanceVariables() { Iterable<VariableDeclarationFragment> fields = getInstanceFields(); if (Iterables.isEmpty(fields)) { newline(); return; } // Need direct access to fields possibly from inner classes that are // promoted to top level classes, so must make all visible fields public. println(" {"); println(" @public"); indent(); FieldDeclaration lastDeclaration = null; boolean needsAsterisk = false; for (VariableDeclarationFragment fragment : fields) { VariableElement varElement = fragment.getVariableElement(); FieldDeclaration declaration = (FieldDeclaration) fragment.getParent(); if (declaration != lastDeclaration) { if (lastDeclaration != null) { println(";"); } lastDeclaration = declaration; JavadocGenerator.printDocComment(getBuilder(), declaration.getJavadoc()); printIndent(); if (ElementUtil.isWeakReference(varElement) && !ElementUtil.isVolatile(varElement)) { // We must add this even without -use-arc because the header may be // included by a file compiled with ARC. print("__unsafe_unretained "); } String objcType = getDeclarationType(varElement); needsAsterisk = objcType.endsWith("*"); if (needsAsterisk) { // Strip pointer from type, as it will be added when appending fragment. // This is necessary to create "Foo *one, *two;" declarations. objcType = objcType.substring(0, objcType.length() - 2); } print(objcType); print(' '); } else { print(", "); } if (needsAsterisk) { print('*'); } print(nameTable.getVariableShortName(varElement)); } println(";"); unindent(); println("}"); } /** * Locate method which matches either Java or Objective C getter name patterns. */ public static ExecutableElement findGetterMethod( String propertyName, TypeMirror propertyType, TypeElement declaringClass) { // Try Objective-C getter naming convention. ExecutableElement getter = ElementUtil.findMethod(declaringClass, propertyName); if (getter == null) { // Try Java getter naming conventions. String prefix = TypeUtil.isBoolean(propertyType) ? "is" : "get"; getter = ElementUtil.findMethod(declaringClass, prefix + NameTable.capitalize(propertyName)); } return getter; } /** * Locate method which matches the Java/Objective C setter name pattern. */ public static ExecutableElement findSetterMethod( String propertyName, TypeElement declaringClass) { return ElementUtil.findMethod(declaringClass, "set" + NameTable.capitalize(propertyName)); } protected void printProperties() { Iterable<VariableDeclarationFragment> fields = getAllFields(); for (VariableDeclarationFragment fragment : fields) { FieldDeclaration fieldDecl = (FieldDeclaration) fragment.getParent(); VariableElement varElement = fragment.getVariableElement(); PropertyAnnotation property = (PropertyAnnotation) TreeUtil.getAnnotation(Property.class, fieldDecl.getAnnotations()); if (property != null) { print("@property "); TypeMirror varType = varElement.asType(); String propertyName = nameTable.getVariableBaseName(varElement); // Add default getter/setter here, as each fragment needs its own attributes // to support its unique accessors. Set<String> attributes = property.getPropertyAttributes(); TypeElement declaringClass = ElementUtil.getDeclaringClass(varElement); if (property.getGetter() == null) { ExecutableElement getter = findGetterMethod(propertyName, varType, declaringClass); if (getter != null) { attributes.add("getter=" + NameTable.getMethodName(getter)); if (!ElementUtil.isSynchronized(getter)) { attributes.add("nonatomic"); } } } if (property.getSetter() == null) { ExecutableElement setter = findSetterMethod(propertyName, declaringClass); if (setter != null) { attributes.add("setter=" + NameTable.getMethodName(setter)); if (!ElementUtil.isSynchronized(setter)) { attributes.add("nonatomic"); } } } if (ElementUtil.isStatic(varElement)) { attributes.add("class"); } else if (attributes.contains("class")) { ErrorUtil.error(fragment, "Only static fields can be translated to class properties"); } if (attributes.contains("class") && !options.staticAccessorMethods()) { // Class property accessors must be present, as they are not synthesized by runtime. ErrorUtil.error(fragment, "Class properties require either a --swift-friendly or" + " --static-accessor-methods flag"); } if (options.nullability() && !varElement.asType().getKind().isPrimitive()) { if (ElementUtil.hasNullableAnnotation(varElement)) { attributes.add("nullable"); } else if (ElementUtil.isNonnull(varElement, parametersNonnullByDefault)) { attributes.add("nonnull"); } else if (!attributes.contains("null_unspecified")) { attributes.add("null_resettable"); } } if (!attributes.isEmpty()) { print('('); print(PropertyAnnotation.toAttributeString(attributes)); print(") "); } String objcType = nameTable.getObjCType(varType); print(objcType); if (!objcType.endsWith("*")) { print(' '); } println(propertyName + ";"); } } } protected void printCompanionClassDeclaration() { if (!typeElement.getKind().isInterface() || !needsCompanionClass() || printPrivateDeclarations() == needsPublicCompanionClass()) { return; } printf("\n@interface %s : NSObject", typeName); if (ElementUtil.isRuntimeAnnotation(typeElement)) { // Print annotation implementation interface. printf(" < %s >", typeName); } printInstanceVariables(); printStaticInterfaceMethods(); printStaticAccessors(); println("\n@end"); } private void printStaticInitFunction() { if (hasInitializeMethod()) { printf("\nJ2OBJC_STATIC_INIT(%s)\n", typeName); } else { printf("\nJ2OBJC_EMPTY_STATIC_INIT(%s)\n", typeName); } } private void printEnumConstants() { if (typeNode instanceof EnumDeclaration) { newline(); println("/*! INTERNAL ONLY - Use enum accessors declared below. */"); printf("FOUNDATION_EXPORT %s *%s_values_[];\n", typeName, typeName); for (EnumConstantDeclaration constant : ((EnumDeclaration) typeNode).getEnumConstants()) { String varName = nameTable.getVariableBaseName(constant.getVariableElement()); newline(); JavadocGenerator.printDocComment(getBuilder(), constant.getJavadoc()); printf("inline %s *%s_get_%s();\n", typeName, typeName, varName); printf("J2OBJC_ENUM_CONSTANT(%s, %s)\n", typeName, varName); } } } private static final Predicate<VariableDeclarationFragment> NEEDS_SETTER = new Predicate<VariableDeclarationFragment>() { @Override public boolean apply(VariableDeclarationFragment fragment) { VariableElement var = fragment.getVariableElement(); if (ElementUtil.isRetainedWithField(var)) { assert !ElementUtil.isPublic(var) : "@RetainedWith fields cannot be public."; return false; } return !var.asType().getKind().isPrimitive() && !ElementUtil.isSynthetic(var) && !ElementUtil.isWeakReference(var); } }; protected void printFieldSetters() { Iterable<VariableDeclarationFragment> fields = Iterables.filter(getInstanceFields(), NEEDS_SETTER); if (Iterables.isEmpty(fields)) { return; } newline(); for (VariableDeclarationFragment fragment : fields) { VariableElement var = fragment.getVariableElement(); String typeStr = nameTable.getObjCType(var.asType()); if (typeStr.contains(",")) { typeStr = "J2OBJC_ARG(" + typeStr + ')'; } String fieldName = nameTable.getVariableShortName(var); String isVolatile = ElementUtil.isVolatile(var) ? "_VOLATILE" : ""; println(UnicodeUtils.format("J2OBJC%s_FIELD_SETTER(%s, %s, %s)", isVolatile, typeName, fieldName, typeStr)); } } protected void printStaticFieldDeclarations() { for (VariableDeclarationFragment fragment : getStaticFields()) { printStaticFieldFullDeclaration(fragment); } } // Overridden in TypePrivateDeclarationGenerator protected void printStaticFieldDeclaration( VariableDeclarationFragment fragment, String baseDeclaration) { println("/*! INTERNAL ONLY - Use accessor function from above. */"); println("FOUNDATION_EXPORT " + baseDeclaration + ";"); } private void printStaticFieldFullDeclaration(VariableDeclarationFragment fragment) { VariableElement var = fragment.getVariableElement(); boolean isVolatile = ElementUtil.isVolatile(var); String objcType = nameTable.getObjCType(var.asType()); String objcTypePadded = objcType + (objcType.endsWith("*") ? "" : " "); String declType = getDeclarationType(var); declType += (declType.endsWith("*") ? "" : " "); String name = nameTable.getVariableShortName(var); boolean isFinal = ElementUtil.isFinal(var); boolean isPrimitive = var.asType().getKind().isPrimitive(); boolean isConstant = ElementUtil.isPrimitiveConstant(var); String qualifiers = isConstant ? "_CONSTANT" : (isPrimitive ? "_PRIMITIVE" : "_OBJ") + (isVolatile ? "_VOLATILE" : "") + (isFinal ? "_FINAL" : ""); newline(); FieldDeclaration decl = (FieldDeclaration) fragment.getParent(); JavadocGenerator.printDocComment(getBuilder(), decl.getJavadoc()); printf("inline %s%s_get_%s();\n", objcTypePadded, typeName, name); if (!isFinal) { printf("inline %s%s_set_%s(%svalue);\n", objcTypePadded, typeName, name, objcTypePadded); if (isPrimitive && !isVolatile) { printf("inline %s *%s_getRef_%s();\n", objcType, typeName, name); } } if (isConstant) { Object value = var.getConstantValue(); assert value != null; printf("#define %s_%s %s\n", typeName, name, LiteralGenerator.generate(value)); } else { printStaticFieldDeclaration( fragment, UnicodeUtils.format("%s%s_%s", declType, typeName, name)); } printf("J2OBJC_STATIC_FIELD%s(%s, %s, %s)\n", qualifiers, typeName, name, objcType); } private void printTypeLiteralDeclaration() { if (needsTypeLiteral()) { newline(); printf("J2OBJC_TYPE_LITERAL_HEADER(%s)\n", typeName); } } private void printBoxedOperators() { PrimitiveType primitiveType = env.typeUtil().unboxedType(typeElement.asType()); if (primitiveType == null) { return; } char binaryName = env.typeUtil().getSignatureName(primitiveType).charAt(0); String primitiveName = TypeUtil.getName(primitiveType); String capName = NameTable.capitalize(primitiveName); String primitiveTypeName = NameTable.getPrimitiveObjCType(primitiveType); String valueMethod = primitiveName + "Value"; if (primitiveName.equals("long")) { valueMethod = "longLongValue"; } else if (primitiveName.equals("byte")) { valueMethod = "charValue"; } newline(); printf("BOXED_INC_AND_DEC(%s, %s, %s)\n", capName, valueMethod, typeName); if ("DFIJ".indexOf(binaryName) >= 0) { printf("BOXED_COMPOUND_ASSIGN_ARITHMETIC(%s, %s, %s, %s)\n", capName, valueMethod, primitiveTypeName, typeName); } if ("IJ".indexOf(binaryName) >= 0) { printf("BOXED_COMPOUND_ASSIGN_MOD(%s, %s, %s, %s)\n", capName, valueMethod, primitiveTypeName, typeName); } if ("DF".indexOf(binaryName) >= 0) { printf("BOXED_COMPOUND_ASSIGN_FPMOD(%s, %s, %s, %s)\n", capName, valueMethod, primitiveTypeName, typeName); } if ("IJ".indexOf(binaryName) >= 0) { printf("BOXED_COMPOUND_ASSIGN_BITWISE(%s, %s, %s, %s)\n", capName, valueMethod, primitiveTypeName, typeName); } if ("I".indexOf(binaryName) >= 0) { printf("BOXED_SHIFT_ASSIGN_32(%s, %s, %s, %s)\n", capName, valueMethod, primitiveTypeName, typeName); } if ("J".indexOf(binaryName) >= 0) { printf("BOXED_SHIFT_ASSIGN_64(%s, %s, %s, %s)\n", capName, valueMethod, primitiveTypeName, typeName); } } private void printUnprefixedAlias() { if (ElementUtil.isTopLevel(typeElement)) { String unprefixedName = NameTable.camelCaseQualifiedName(ElementUtil.getQualifiedName(typeElement)); if (!unprefixedName.equals(typeName)) { if (typeElement.getKind().isInterface()) { // Protocols can't be used in typedefs. printf("\n#define %s %s\n", unprefixedName, typeName); } else { printf("\n@compatibility_alias %s %s;\n", unprefixedName, typeName); } } } } /** * Emit method declaration. * * @param m The method. * @param isCompanionClass If true, emit only if m is a static interface method. */ private void printMethodDeclaration(MethodDeclaration m, boolean isCompanionClass) { ExecutableElement methodElement = m.getExecutableElement(); TypeElement typeElement = ElementUtil.getDeclaringClass(methodElement); if (typeElement.getKind().isInterface()) { // isCompanion and isStatic must be both false (i.e. this prints a non-static method decl // in @protocol) or must both be true (i.e. this prints a static method decl in the // companion class' @interface). if (isCompanionClass != ElementUtil.isStatic(methodElement)) { return; } } newline(); JavadocGenerator.printDocComment(getBuilder(), m.getJavadoc()); print(getMethodSignature(m)); String methodName = nameTable.getMethodSelector(methodElement); if (!m.isConstructor() && NameTable.needsObjcMethodFamilyNoneAttribute(methodName)) { // Getting around a clang warning. // clang assumes that methods with names starting with new, alloc or copy // return objects of the same type as the receiving class, regardless of // the actual declared return type. This attribute tells clang to not do // that, please. // See http://clang.llvm.org/docs/AutomaticReferenceCounting.html // Sections 5.1 (Explicit method family control) // and 5.2.2 (Related result types) print(" OBJC_METHOD_FAMILY_NONE"); } if (needsDeprecatedAttribute(m.getAnnotations())) { print(" " + DEPRECATED_ATTRIBUTE); } if (m.isUnavailable()) { print(" NS_UNAVAILABLE"); } println(";"); } @Override protected void printMethodDeclaration(MethodDeclaration m) { printMethodDeclaration(m, false); } private boolean needsDeprecatedAttribute(List<Annotation> annotations) { return options.generateDeprecatedDeclarations() && hasDeprecated(annotations); } private boolean hasDeprecated(List<Annotation> annotations) { for (Annotation annotation : annotations) { Name annotationTypeName = annotation.getTypeName(); String expectedTypeName = annotationTypeName.isQualifiedName() ? "java.lang.Deprecated" : "Deprecated"; if (expectedTypeName.equals(annotationTypeName.getFullyQualifiedName())) { return true; } } return false; } @Override protected void printNativeDeclaration(NativeDeclaration declaration) { String code = declaration.getHeaderCode(); if (code != null) { newline(); print(declaration.getHeaderCode()); } } @Override protected void printFunctionDeclaration(FunctionDeclaration function) { print("\nFOUNDATION_EXPORT " + getFunctionSignature(function)); if (function.returnsRetained()) { print(" NS_RETURNS_RETAINED"); } println(";"); } /** * Defines the categories for grouping declarations in the header. The categories will be emitted * in the header in the same order that they are declared here. */ private enum DeclarationCategory { PUBLIC("#pragma mark Public"), PROTECTED("#pragma mark Protected"), PACKAGE_PRIVATE("#pragma mark Package-Private"), PRIVATE("#pragma mark Private"), UNAVAILABLE("// Disallowed inherited constructors, do not use."); private final String header; DeclarationCategory(String header) { this.header = header; } private static DeclarationCategory categorize(BodyDeclaration decl) { if (decl instanceof MethodDeclaration && ((MethodDeclaration) decl).isUnavailable()) { return UNAVAILABLE; } int mods = decl.getModifiers(); if ((mods & Modifier.PUBLIC) > 0) { return PUBLIC; } else if ((mods & Modifier.PROTECTED) > 0) { return PROTECTED; } else if ((mods & Modifier.PRIVATE) > 0) { return PRIVATE; } return PACKAGE_PRIVATE; } } /** * Print method declarations with #pragma mark lines documenting their scope. */ @Override protected void printInnerDeclarations() { // Everything is public in interfaces. if (isInterfaceType() || typeNode.hasPrivateDeclaration()) { super.printInnerDeclarations(); return; } ListMultimap<DeclarationCategory, BodyDeclaration> categorizedDecls = MultimapBuilder.hashKeys().arrayListValues().build(); for (BodyDeclaration innerDecl : getInnerDeclarations()) { categorizedDecls.put(DeclarationCategory.categorize(innerDecl), innerDecl); } // Emit the categorized declarations using the declaration order of the category values. for (DeclarationCategory category : DeclarationCategory.values()) { List<BodyDeclaration> declarations = categorizedDecls.get(category); if (declarations.isEmpty()) { continue; } // Extract MethodDeclaration nodes so that they can be sorted. List<MethodDeclaration> methods = Lists.newArrayList(); for (Iterator<BodyDeclaration> iter = declarations.iterator(); iter.hasNext(); ) { BodyDeclaration decl = iter.next(); if (decl instanceof MethodDeclaration) { methods.add((MethodDeclaration) decl); iter.remove(); } } Collections.sort(methods, METHOD_DECL_ORDER); newline(); println(category.header); printDeclarations(methods); printDeclarations(declarations); } } /** * Method comparator, suitable for documentation and code-completion lists. * * Sort ordering: constructors first, then alphabetical by name. If they have the * same name, then compare the first parameter's simple type name, then the second, etc. */ @VisibleForTesting static final Comparator<MethodDeclaration> METHOD_DECL_ORDER = new Comparator<MethodDeclaration>() { @Override public int compare(MethodDeclaration m1, MethodDeclaration m2) { if (m1.isConstructor() && !m2.isConstructor()) { return -1; } if (!m1.isConstructor() && m2.isConstructor()) { return 1; } String m1Name = ElementUtil.getName(m1.getExecutableElement()); String m2Name = ElementUtil.getName(m2.getExecutableElement()); if (!m1Name.equals(m2Name)) { return m1Name.compareToIgnoreCase(m2Name); } int nParams = m1.getParameters().size(); int nOtherParams = m2.getParameters().size(); int max = Math.min(nParams, nOtherParams); for (int i = 0; i < max; i++) { String paramType = TypeUtil.getName(m1.getParameter(i).getType().getTypeMirror()); String otherParamType = TypeUtil.getName(m2.getParameter(i).getType().getTypeMirror()); if (!paramType.equals(otherParamType)) { return paramType.compareToIgnoreCase(otherParamType); } } return nParams - nOtherParams; } }; }