/* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.gwt4nb.hints; import java.util.List; import java.util.ArrayList; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.DeclaredType; import javax.lang.model.util.ElementFilter; import org.netbeans.api.java.source.ElementHandle; /** * * @author Tomasz.Slota@Sun.COM * @author see https://github.com/gwt4nb/gwt4nb/ */ public class JavaModelUtils { /** * <p> * Returns the name of the non-primitive wrapper for the given * {@code TypeMirror}. This method will either return the fully-qualified * name of the given type, or the name of it's wrapper (if it's * a {@link TypeKind#isPrimitive() primitive type}). * </p><p> * For example, passing in a {@code TypeMirror} of kind * {@link TypeKind#BOOLEAN} will return {@literal "Boolean"} for * an un-qualified-name, or {@literal "java.lang.Boolean"} in the case * of a qualified-name. * </p> * * @param normalType * @param qualifiedName * @return */ public static String getNonPrimitiveTypeName( final TypeMirror normalType, final boolean qualifiedName) { final TypeKind kind = normalType.getKind(); if(kind.isPrimitive()) { String name = "java.lang."; // NOI18N switch(normalType.getKind()) { case BOOLEAN: name += "Boolean"; // NOI18N break; case CHAR: name += "Character"; // NOI18N break; case BYTE: name += "Byte"; // NOI18N break; case SHORT: name += "Short"; // NOI18N break; case INT: name += "Integer"; // NOI18N break; case LONG: name += "Long"; // NOI18N break; case FLOAT: name += "Float"; // NOI18N break; case DOUBLE: name += "Double"; // NOI18N break; default: throw new IllegalArgumentException( "Unknown primitive type: " + // NOI18N normalType.toString()); } return name; } return normalType.toString(); } public static ExecutableElement[] getMethod( final TypeElement clazz, final String methodName) { final List<ExecutableElement> methods = new ArrayList<ExecutableElement>(); for (final ExecutableElement method : ElementFilter.methodsIn(clazz.getEnclosedElements())){ if (method.getSimpleName().contentEquals(methodName)){ methods.add(method); } } return methods.toArray(new ExecutableElement[methods.size()]); } /** * Tests to see if the given {@code syncMethod} matches the signature of {@code asyncMethod}. * This method tests that all the parameters are correct, and that the {@code asyncMethod} * has an additional {@code AsyncCallback} parameter on the end (which may include a * generic). * * @param syncMethod the synchronous version of the method to test * @param asyncMethod the async version of the method to test * @return {@literal true} if the {@code asyncMethod} is the async duplicate of the * specified {@code syncMethod} */ public static boolean isMethodMatched( final ExecutableElement syncMethod, final ExecutableElement asyncMethod) { final List<? extends VariableElement> syncMethodParamsList = syncMethod.getParameters(); final List<? extends VariableElement> asyncMethodParamsList = asyncMethod.getParameters(); final int syncParameterCount = syncMethodParamsList.size(); final int asyncCallbackIndex = asyncMethodParamsList.size() - 1; //if number of parameters don't match or AysnCallback parameter is missing, skip the method if(syncParameterCount != asyncCallbackIndex) { return false; } final VariableElement lastParamElement = asyncMethodParamsList.get(asyncCallbackIndex); final TypeMirror lastParamTypeMirror = lastParamElement.asType(); final CharSequence lastParamType = lastParamTypeMirror.getKind() == TypeKind.DECLARED ? ((DeclaredType)lastParamTypeMirror).asElement().toString() : lastParamTypeMirror.toString(); if (!"com.google.gwt.user.client.rpc.AsyncCallback". // NOI18N contentEquals(lastParamType)) { return false; } else if(syncMethodParamsList.size() == 0) { return true; } // test to see if the generic type of the AsyncCallback is the same // as the return type of the sync method (if the AsyncCallback has a // declared // generic type for the onSuccess method). if (lastParamTypeMirror.getKind() == TypeKind.DECLARED) { final DeclaredType declaredType = (DeclaredType) lastParamTypeMirror; final List<? extends TypeMirror> types = declaredType.getTypeArguments(); if (syncMethod.getReturnType().getKind() == TypeKind.VOID) { if (types.size() == 1 && !types.get(0).toString().equals( "java.lang.Void")) // NOI18N return false; } else { final String expectedGenericType = getNonPrimitiveTypeName( syncMethod.getReturnType(), true); if (types.size() == 1 && !expectedGenericType.equals( types.get(0).toString())) { return false; } } } for (int i = 0; i < syncParameterCount; i++) { final TypeMirror syncParameter = syncMethodParamsList.get(i).asType(); final TypeMirror asyncParameter = asyncMethodParamsList.get(i).asType(); //if the class of the corresponding parameters don't match, we skip the method if(!syncParameter.toString().equals(asyncParameter.toString())) { return false; } } return true; } /** * Matches syncMethod's signature with the signatures of the array of * asyncMethods passed * * @returns true if a match is found */ public static boolean isMethodSignatureEqual( final ExecutableElement syncMethod, final ExecutableElement[] asyncMethods){ for (final ExecutableElement async: asyncMethods){ if (isMethodMatched(syncMethod, async)) { return true; } } return false; } /** * Matches asyncMethod's signature with the signatures of the array of * syncMethods passed * * @returns true if a match is found */ public static boolean isMathcingServiceMethodFound( final ExecutableElement asyncMethod, final ExecutableElement[] syncMethods){ for(final ExecutableElement sync : syncMethods) { if(isMethodMatched(sync, asyncMethod)) { return true; } } return false; } /** * Method to compare the methods of clazzA with those of clazzB * * @returns list of element handles of methods in A but not in B */ @SuppressWarnings("rawtypes") public static List<ElementHandle> getUnmatchedMethods( TypeElement clazzA, TypeElement clazzB){ List<ElementHandle> unmatchedMethods = new ArrayList<ElementHandle>(); for (ExecutableElement aMethod: ElementFilter.methodsIn( clazzA.getEnclosedElements())){ String amethodName = aMethod.getSimpleName().toString(); ExecutableElement methodsWithMatchingName[] = JavaModelUtils.getMethod(clazzB,amethodName); if (methodsWithMatchingName.length==0){ ElementHandle aMethodHandle = ElementHandle.create(aMethod); unmatchedMethods.add(aMethodHandle); } else if (!JavaModelUtils.isMathcingServiceMethodFound(aMethod, methodsWithMatchingName)){ ElementHandle aMethodHandle = ElementHandle.create(aMethod); unmatchedMethods.add(aMethodHandle); } } return unmatchedMethods; } public static String extractClassNameFromType(TypeMirror type){ if (type.getKind() == TypeKind.DECLARED){ Element elem = ((DeclaredType)type).asElement(); if (elem.getKind() == ElementKind.CLASS || elem.getKind() == ElementKind.INTERFACE){ return ((TypeElement)elem).getQualifiedName().toString(); } } return null; } public static String getShortClassName(String qualifiedClassName){ return qualifiedClassName.substring( qualifiedClassName.lastIndexOf(".") + 1); // NOI18N } }