void (@NotNull PsiFile psiFile, @NotNull String externalName, @NotNull String assumedPackage) { // 'item name="java.lang.ClassLoader java.net.URL getResource(java.lang.String) 0"' should have all FQNs String unescaped = StringUtil.unescapeXmlEntities(externalName); List<String> words = StringUtil.split(unescaped, " "); String className = words.get(0); if (words.size() == 1 && JavaPsiFacade.getInstance(getProject()).findPackage(className) != null) { return; } PsiClass aClass = assertClassFqn(className, psiFile, externalName, assumedPackage); if (words.size() == 1) return; String rest = unescaped.substring(className.length() + " ".length()); if (rest.indexOf('(') == -1) { // field String field = StringUtil.trim(rest); PsiField psiField = aClass.findFieldByName(field, false); if (psiField == null) { fail("Field '"+field+"' not found in class '"+aClass.getQualifiedName()+"'", psiFile, externalName); } return; } String methodName = ContainerUtil.getLastItem(StringUtil.getWordsIn(rest.substring(0, rest.indexOf('(')))); String methodSignature = rest.substring(0, rest.indexOf(')') + 1); String methodExternalName = className + " " + methodSignature; List<PsiMethod> methods = ContainerUtil.filter(aClass.getMethods(), method -> methodExternalName.equals( PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE))); if (methods.isEmpty()) { // Sometimes the method is overridden in later JDK versions, and inferred contract is not satisfactory, // thus having explicit subclass contract is desired. Thus we don't fail if the annotated method exists // in superclass only methods = ContainerUtil.filter( aClass.getAllMethods(), method -> (method.getContainingClass().getQualifiedName() + " " + methodSignature) .equals(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE))); } boolean found = !methods.isEmpty(); if (!found) { if (KNOWN_EXCEPTIONS.contains(methodSignature)) return; List<String> candidates = ContainerUtil.map(aClass.findMethodsByName(methodName, true), method -> XmlUtil.escape(PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE))); String additionalMsg = candidates.isEmpty() ? "" : "\nMaybe you have meant one of these methods instead:\n"+StringUtil.join(candidates, "\n")+"\n"; fail("This method was not found in class '"+aClass.getQualifiedName()+"':\n"+"'"+methodSignature+"'"+additionalMsg, psiFile, externalName); } String parameterNumberText = StringUtil.trim(rest.substring(rest.indexOf(')') + 1)); if (parameterNumberText.isEmpty()) return; try { int paramNumber = Integer.parseInt(parameterNumberText); PsiMethod method = methods.get(0); if (method.getParameterList().getParametersCount() <= paramNumber) { fail("Parameter number '"+paramNumber+"' is too big for a method '"+methodSignature+"'", psiFile, externalName); } } catch (NumberFormatException e) { fail("Parameter number is not an integer: '"+parameterNumberText+"'", psiFile, externalName); } }