private static String buildAnnotationText(PsiAnnotation annotation) { final StringBuilder out = new StringBuilder("@"); final PsiJavaCodeReferenceElement nameReferenceElement = annotation.getNameReferenceElement(); assert nameReferenceElement != null; out.append(nameReferenceElement.getText()); final PsiAnnotationParameterList parameterList = annotation.getParameterList(); final PsiNameValuePair[] attributes = parameterList.getAttributes(); if (attributes.length == 0) { return out.toString(); } out.append('('); if (attributes.length == 1) { final PsiNameValuePair attribute = attributes[0]; @NonNls final String name = attribute.getName(); if (name != null && !PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME.equals(name)) { out.append(name).append('='); } buildAttributeValueText(attribute.getValue(), out); } else { for (int i = 0; i < attributes.length; i++) { final PsiNameValuePair attribute = attributes[i]; if (i > 0) { out.append(','); } out.append(attribute.getName()).append('='); buildAttributeValueText(attribute.getValue(), out); } } out.append(')'); return out.toString(); }
@Nullable private ProblemDescriptor[] checkMember( final PsiDocCommentOwner docCommentOwner, final InspectionManager manager, final boolean isOnTheFly) { final ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>(); final PsiDocComment docComment = docCommentOwner.getDocComment(); if (docComment == null) return null; final Set<PsiJavaCodeReferenceElement> references = new HashSet<PsiJavaCodeReferenceElement>(); docComment.accept(getVisitor(references, docCommentOwner, problems, manager, isOnTheFly)); for (PsiJavaCodeReferenceElement reference : references) { final List<PsiClass> classesToImport = new ImportClassFix(reference).getClassesToImport(); final PsiElement referenceNameElement = reference.getReferenceNameElement(); problems.add( manager.createProblemDescriptor( referenceNameElement != null ? referenceNameElement : reference, cannotResolveSymbolMessage("<code>" + reference.getText() + "</code>"), !isOnTheFly || classesToImport.isEmpty() ? null : new AddImportFix(classesToImport), ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, isOnTheFly)); } return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]); }
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { super.visitReferenceElement(reference); if (referenceFound) { return; } final String text = reference.getText(); if (text.indexOf((int) '.') >= 0 || !name.equals(text)) { return; } final PsiElement element = reference.resolve(); if (!(element instanceof PsiClass) || element instanceof PsiTypeParameter) { return; } final PsiClass aClass = (PsiClass) element; final String testClassName = aClass.getName(); final String testClassQualifiedName = aClass.getQualifiedName(); if (testClassQualifiedName == null || testClassName == null || testClassQualifiedName.equals(fullyQualifiedName) || !testClassName.equals(name)) { return; } referenceFound = true; }
public InspectionGadgetsFix buildFix(Object... infos) { final PsiNewExpression expression = (PsiNewExpression) infos[0]; final PsiJavaCodeReferenceElement classReference = expression.getClassReference(); assert classReference != null; final String className = classReference.getText(); return new CachedNumberConstructorCallFix(className); }
private static boolean newExpressionsAreEquivalent( @NotNull PsiNewExpression newExpression1, @NotNull PsiNewExpression newExpression2) { final PsiJavaCodeReferenceElement classReference1 = newExpression1.getClassReference(); final PsiJavaCodeReferenceElement classReference2 = newExpression2.getClassReference(); if (classReference1 == null || classReference2 == null) { return false; } final String text = classReference1.getText(); if (!text.equals(classReference2.getText())) { return false; } final PsiExpression[] arrayDimensions1 = newExpression1.getArrayDimensions(); final PsiExpression[] arrayDimensions2 = newExpression2.getArrayDimensions(); if (!expressionListsAreEquivalent(arrayDimensions1, arrayDimensions2)) { return false; } final PsiArrayInitializerExpression arrayInitializer1 = newExpression1.getArrayInitializer(); final PsiArrayInitializerExpression arrayInitializer2 = newExpression2.getArrayInitializer(); if (!expressionsAreEquivalent(arrayInitializer1, arrayInitializer2)) { return false; } final PsiExpression qualifier1 = newExpression1.getQualifier(); final PsiExpression qualifier2 = newExpression2.getQualifier(); if (!expressionsAreEquivalent(qualifier1, qualifier2)) { return false; } final PsiExpressionList argumentList1 = newExpression1.getArgumentList(); final PsiExpression[] args1; if (argumentList1 == null) { args1 = null; } else { args1 = argumentList1.getExpressions(); } final PsiExpressionList argumentList2 = newExpression2.getArgumentList(); final PsiExpression[] args2; if (argumentList2 == null) { args2 = null; } else { args2 = argumentList2.getExpressions(); } return expressionListsAreEquivalent(args1, args2); }
@Nullable static HighlightInfo checkVariableMustBeFinal( PsiVariable variable, PsiJavaCodeReferenceElement context, @NotNull LanguageLevel languageLevel) { if (variable.hasModifierProperty(PsiModifier.FINAL)) return null; final PsiClass innerClass = getInnerClassVariableReferencedFrom(variable, context); if (innerClass != null) { if (variable instanceof PsiParameter) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression && notAccessedForWriting( variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) { return null; } } if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && isEffectivelyFinal(variable, innerClass, context)) { return null; } final String description = JavaErrorMessages.message("variable.must.be.final", context.getText()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass)); return highlightInfo; } final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(context, PsiLambdaExpression.class); if (lambdaExpression != null && !PsiTreeUtil.isAncestor(lambdaExpression, variable, true)) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() == lambdaExpression) { return null; } if (!isEffectivelyFinal(variable, lambdaExpression, context)) { return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip("Variable used in lambda expression should be effectively final") .create(); } } return null; }
/** * @param strict if strict is true this method checks if the conflicting class which is imported * is actually used in the file. If it isn't the on demand import can be overridden with an * exact import for the fqName without breaking stuff. */ private static boolean hasOnDemandImportConflict( @NotNull String fqName, @NotNull PsiJavaFile file, boolean strict) { final PsiImportList imports = file.getImportList(); if (imports == null) { return false; } final PsiImportStatement[] importStatements = imports.getImportStatements(); final String shortName = ClassUtil.extractClassName(fqName); final String packageName = ClassUtil.extractPackageName(fqName); for (final PsiImportStatement importStatement : importStatements) { if (!importStatement.isOnDemand()) { continue; } final PsiJavaCodeReferenceElement importReference = importStatement.getImportReference(); if (importReference == null) { continue; } final String packageText = importReference.getText(); if (packageText.equals(packageName)) { continue; } final PsiElement element = importReference.resolve(); if (element == null || !(element instanceof PsiPackage)) { continue; } final PsiPackage aPackage = (PsiPackage) element; final PsiClass[] classes = aPackage.getClasses(); for (final PsiClass aClass : classes) { final String className = aClass.getName(); if (!shortName.equals(className)) { continue; } if (!strict) { return true; } final String qualifiedClassName = aClass.getQualifiedName(); if (qualifiedClassName == null || fqName.equals(qualifiedClassName)) { continue; } return containsReferenceToConflictingClass(file, qualifiedClassName); } } return hasJavaLangImportConflict(fqName, file); }
@NotNull public static PsiAnnotationMemberValue getMemberValue( final PsiElement element, final ClsElementImpl parent) { if (element instanceof PsiExpression) { return psiToClsExpression((PsiExpression) element, parent); } else if (element instanceof PsiArrayInitializerMemberValue) { PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue) element).getInitializers(); PsiAnnotationMemberValue[] clsInitializers = new PsiAnnotationMemberValue[initializers.length]; ClsArrayInitializerMemberValueImpl arrayValue = new ClsArrayInitializerMemberValueImpl(parent, clsInitializers); for (int i = 0; i < initializers.length; i++) { clsInitializers[i] = getMemberValue(initializers[i], arrayValue); } return arrayValue; } else if (element instanceof PsiAnnotation) { final PsiAnnotation psiAnnotation = (PsiAnnotation) element; final PsiJavaCodeReferenceElement referenceElement = psiAnnotation.getNameReferenceElement(); assert referenceElement != null : psiAnnotation; final String canonicalText = referenceElement.getText(); // class file has FQNs return new ClsAnnotationValueImpl(parent) { @Override protected ClsJavaCodeReferenceElementImpl createReference() { return new ClsJavaCodeReferenceElementImpl(this, canonicalText); } @Override protected ClsAnnotationParameterListImpl createParameterList() { PsiNameValuePair[] psiAttributes = psiAnnotation.getParameterList().getAttributes(); return new ClsAnnotationParameterListImpl(this, psiAttributes); } @Override public PsiAnnotationOwner getOwner() { return (PsiAnnotationOwner) getParent(); } }; } else { LOG.error("Unexpected source element for annotation member value: " + element); return null; } }
@Nullable private static PsiImportStaticStatement findOnDemandImportStaticStatement( PsiImportList importList, String qualifierClass) { final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements(); for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { if (!importStaticStatement.isOnDemand()) { continue; } final PsiJavaCodeReferenceElement importReference = importStaticStatement.getImportReference(); if (importReference == null) { continue; } final String text = importReference.getText(); if (qualifierClass.equals(text)) { return importStaticStatement; } } return null; }
@Nullable static HighlightInfo checkVariableMustBeFinal( @NotNull PsiVariable variable, @NotNull PsiJavaCodeReferenceElement context, @NotNull LanguageLevel languageLevel) { if (variable.hasModifierProperty(PsiModifier.FINAL)) return null; final PsiElement innerClass = getInnerClassVariableReferencedFrom(variable, context); if (innerClass instanceof PsiClass) { if (variable instanceof PsiParameter) { final PsiElement parent = variable.getParent(); if (parent instanceof PsiParameterList && parent.getParent() instanceof PsiLambdaExpression && notAccessedForWriting( variable, new LocalSearchScope(((PsiParameter) variable).getDeclarationScope()))) { return null; } } final boolean isToBeEffectivelyFinal = languageLevel.isAtLeast(LanguageLevel.JDK_1_8); if (isToBeEffectivelyFinal && isEffectivelyFinal(variable, innerClass, context)) { return null; } final String description = JavaErrorMessages.message( isToBeEffectivelyFinal ? "variable.must.be.final.or.effectively.final" : "variable.must.be.final", context.getText()); final HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR) .range(context) .descriptionAndTooltip(description) .create(); QuickFixAction.registerQuickFixAction( highlightInfo, QUICK_FIX_FACTORY.createVariableAccessFromInnerClassFix(variable, innerClass)); return highlightInfo; } return checkWriteToFinalInsideLambda(variable, context); }
@Nullable public ProblemDescriptor[] checkFile( @NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { // does not work in tests since CodeInsightTestCase copies file into temporary location if (ApplicationManager.getApplication().isUnitTestMode()) return null; if (file instanceof PsiJavaFile) { if (JspPsiUtil.isInJspFile(file)) return null; PsiJavaFile javaFile = (PsiJavaFile) file; PsiDirectory directory = javaFile.getContainingDirectory(); if (directory == null) return null; PsiPackage dirPackage = JavaDirectoryService.getInstance().getPackage(directory); if (dirPackage == null) return null; PsiPackageStatement packageStatement = javaFile.getPackageStatement(); // highlight the first class in the file only PsiClass[] classes = javaFile.getClasses(); if (classes.length == 0 && packageStatement == null) return null; String packageName = dirPackage.getQualifiedName(); if (!Comparing.strEqual(packageName, "", true) && packageStatement == null) { String description = JavaErrorMessages.message("missing.package.statement", packageName); return new ProblemDescriptor[] { manager.createProblemDescriptor( classes[0].getNameIdentifier(), description, new AdjustPackageNameFix(javaFile, null, dirPackage), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly) }; } if (packageStatement != null) { final PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference(); PsiPackage classPackage = (PsiPackage) packageReference.resolve(); List<LocalQuickFix> availableFixes = new ArrayList<LocalQuickFix>(); if (classPackage == null) { availableFixes.add(new AdjustPackageNameFix(javaFile, packageStatement, dirPackage)); } else if (!Comparing.equal( dirPackage.getQualifiedName(), packageReference.getText(), true)) { availableFixes.add(new AdjustPackageNameFix(javaFile, packageStatement, dirPackage)); MoveToPackageFix moveToPackageFix = new MoveToPackageFix(file, classPackage); if (moveToPackageFix.isAvailable()) { availableFixes.add(moveToPackageFix); } } if (!availableFixes.isEmpty()) { String description = JavaErrorMessages.message( "package.name.file.path.mismatch", packageReference.getText(), dirPackage.getQualifiedName()); return new ProblemDescriptor[] { manager.createProblemDescriptor( packageStatement.getPackageReference(), description, isOnTheFly, availableFixes.toArray(new LocalQuickFix[availableFixes.size()]), ProblemHighlightType.GENERIC_ERROR_OR_WARNING) }; } } } return null; }
@Nullable public static HighlightInfo checkApplicability( @NotNull PsiAnnotation annotation, @NotNull LanguageLevel languageLevel, @NotNull PsiFile containingFile) { if (ANY_ANNOTATION_ALLOWED.accepts(annotation)) { return null; } PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement(); if (nameRef == null) return null; PsiAnnotationOwner owner = annotation.getOwner(); PsiAnnotation.TargetType[] targets = PsiImplUtil.getTargetsForLocation(owner); if (owner == null || targets.length == 0) { String message = JavaErrorMessages.message("annotation.not.allowed.here"); return annotationError(annotation, message); } if (!(owner instanceof PsiModifierList)) { HighlightInfo info = HighlightUtil.checkTypeAnnotationFeature(annotation, languageLevel, containingFile); if (info != null) return info; } PsiAnnotation.TargetType applicable = PsiImplUtil.findApplicableTarget(annotation, targets); if (applicable == PsiAnnotation.TargetType.UNKNOWN) return null; if (applicable == null) { String target = JavaErrorMessages.message("annotation.target." + targets[0]); String message = JavaErrorMessages.message("annotation.not.applicable", nameRef.getText(), target); return annotationError(annotation, message); } if (applicable == PsiAnnotation.TargetType.TYPE_USE) { if (owner instanceof PsiClassReferenceType) { PsiJavaCodeReferenceElement ref = ((PsiClassReferenceType) owner).getReference(); HighlightInfo info = checkReferenceTarget(annotation, ref); if (info != null) return info; } else if (owner instanceof PsiModifierList) { PsiElement nextElement = PsiTreeUtil.skipSiblingsForward( (PsiModifierList) owner, PsiComment.class, PsiWhiteSpace.class, PsiTypeParameterList.class); if (nextElement instanceof PsiTypeElement) { PsiTypeElement typeElement = (PsiTypeElement) nextElement; PsiType type = typeElement.getType(); if (PsiType.VOID.equals(type)) { String message = JavaErrorMessages.message("annotation.not.allowed.void"); return annotationError(annotation, message); } if (!(type instanceof PsiPrimitiveType)) { PsiJavaCodeReferenceElement ref = getOutermostReferenceElement(typeElement.getInnermostComponentReferenceElement()); HighlightInfo info = checkReferenceTarget(annotation, ref); if (info != null) return info; } } } else if (owner instanceof PsiTypeElement) { PsiElement context = PsiTreeUtil.skipParentsOfType((PsiTypeElement) owner, PsiTypeElement.class); if (context instanceof PsiClassObjectAccessExpression) { String message = JavaErrorMessages.message("annotation.not.allowed.class"); return annotationError(annotation, message); } } } return null; }
private void generatorLayoutCode(String contextName, String findPre) { List<Element> editTextElements = new ArrayList<>(); List<Element> clickableElements = new ArrayList<>(); List<Element> itemClickableElements = new ArrayList<>(); // generator findViewById code in initView() method StringBuilder initView = new StringBuilder(); if (TextUtils.isEmpty(findPre)) { initView.append("private void initView() {\n"); } else { initView.append("private void initView(View " + findPre + ") {\n"); } for (Element element : mElements) { String pre = TextUtils.isEmpty(findPre) ? "" : findPre + "."; initView.append( element.getFieldName() + " = (" + element.name + ") " + pre + "findViewById(" + element.getFullID() + ");\n"); // set flag if (element.isEditText) { editTextElements.add(element); } if (element.isClickable) { clickableElements.add(element); } if (element.isItemClickable) { itemClickableElements.add(element); } } // generator EditText validate code if need StringBuilder sbEditText = new StringBuilder(); if (editTextElements.size() > 0) { sbEditText.append("private void submit() {\n"); sbEditText.append("\t\t// validate\n"); for (Element element : editTextElements) { // generator EditText string name String idName = element.id; int index = idName.lastIndexOf("_"); String name = index == -1 ? idName : idName.substring(index + 1); if (name.equals(idName)) { name += "String"; } sbEditText.append("String " + name + " = " + idName + ".getText().toString().trim();\n"); sbEditText.append("if(TextUtils.isEmpty(" + name + ")) {\n"); // 提示的toast为EditText的hint文字,无hint时格式为"name不能为空" String emptyTint = name + "不能为空"; String hint = element.xml.getAttributeValue("android:hint"); if (!TextUtils.isEmpty(hint) && !hint.startsWith("@string")) { emptyTint = hint; } sbEditText.append( "Toast.makeText(" + contextName + ", \"" + emptyTint + "\", Toast.LENGTH_SHORT).show();\n"); sbEditText.append("return;\n"); sbEditText.append("}\n"); sbEditText.append("\n"); } sbEditText.append("\t\t// TODO validate success, do something\n"); sbEditText.append("\n\n}\n"); } // generator clickable code if need StringBuilder sbClickable = new StringBuilder(); if (clickableElements.size() > 0) { // let class implement OnClickListener PsiReferenceList implementsList = mClass.getImplementsList(); if (implementsList != null) { PsiJavaCodeReferenceElement[] referenceElements = implementsList.getReferenceElements(); boolean hasImpl = false; for (PsiJavaCodeReferenceElement re : referenceElements) { hasImpl = re.getText().contains("OnClickListener"); } // add implement if not exist if (!hasImpl) { PsiJavaCodeReferenceElement pjcre = mFactory.createReferenceElementByFQClassName( "android.view.View.OnClickListener", mClass.getResolveScope()); implementsList.add(pjcre); } } initView.append("\n"); sbClickable .append("@Override public void onClick(View v) {\n") .append("switch (v.getId()) {\n"); for (Element element : clickableElements) { // generator setOnClickListener code in initView() initView.append(element.getFieldName() + ".setOnClickListener(this);\n"); // generator override public void onClick(View v) method sbClickable.append("case " + element.getFullID() + " :\n\nbreak;\n"); } sbClickable.append("}\n}"); } // generator itemClickable code if need for (Element element : itemClickableElements) { // generator setOnClickListener code in initView() initView.append( element.getFieldName() + ".setOnItemClickListener(new AdapterView.OnItemClickListener() {\n"); initView.append("@Override\n"); initView.append( "public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n\n"); initView.append("}\n"); initView.append("});\n"); } initView.append("}\n"); PsiMethod[] initViewMethods = mClass.findMethodsByName("initView", false); if (initViewMethods.length > 0 && initViewMethods[0].getBody() != null) { // already have method // append non-repeated field PsiCodeBlock initViewMethodBody = initViewMethods[0].getBody(); for (Element element : mElements) { // append findViewById String pre = TextUtils.isEmpty(findPre) ? "" : findPre + "."; String s2 = element.getFieldName() + " = (" + element.name + ") " + pre + "findViewById(" + element.getFullID() + ");"; initViewMethodBody.add(mFactory.createStatementFromText(s2, initViewMethods[0])); // append setOnClickListener String s1 = element.getFieldName() + ".setOnClickListener(this);"; initViewMethodBody.add(mFactory.createStatementFromText(s1, initViewMethods[0])); } } else { // new method mClass.add(mFactory.createMethodFromText(initView.toString(), mClass)); } if (clickableElements.size() > 0) { PsiMethod[] onClickMethods = mClass.findMethodsByName("onClick", false); if (onClickMethods.length > 0 && onClickMethods[0].getBody() != null) { // already have method // append non-repeated field PsiCodeBlock onClickMethodBody = onClickMethods[0].getBody(); for (PsiElement element : onClickMethodBody.getChildren()) { if (element instanceof PsiSwitchStatement) { PsiSwitchStatement switchStatement = (PsiSwitchStatement) element; PsiCodeBlock body = switchStatement.getBody(); if (body != null) { for (Element clickableElement : clickableElements) { String caseStr = "case " + clickableElement.getFullID() + " :"; body.add(mFactory.createStatementFromText(caseStr, body)); body.add(mFactory.createStatementFromText("break;", body)); } } break; } } } else { // new method mClass.add(mFactory.createMethodFromText(sbClickable.toString(), mClass)); } } if (editTextElements.size() > 0) { mClass.add(mFactory.createMethodFromText(sbEditText.toString(), mClass)); } }