@NotNull
  private List<AnnotationData> doCollect(
      @NotNull PsiModifierListOwner listOwner, boolean onlyWritable) {
    final List<PsiFile> files = findExternalAnnotationsFiles(listOwner);
    if (files == null) {
      return NO_DATA;
    }
    SmartList<AnnotationData> result = new SmartList<AnnotationData>();
    String externalName = getExternalName(listOwner, false);
    if (externalName == null) return NO_DATA;

    for (PsiFile file : files) {
      if (!file.isValid()) continue;
      if (onlyWritable && !file.isWritable()) continue;

      MostlySingularMultiMap<String, AnnotationData> fileData = getDataFromFile(file);

      ContainerUtil.addAll(result, fileData.get(externalName));
    }
    if (result.isEmpty()) {
      return NO_DATA;
    }
    result.trimToSize();
    return result;
  }
  @NotNull
  private MostlySingularMultiMap<String, AnnotationData> getDataFromFile(
      @NotNull final PsiFile file) {
    Pair<MostlySingularMultiMap<String, AnnotationData>, Long> cached =
        annotationFileToDataAndModStamp.get(file);
    final long fileModificationStamp = file.getModificationStamp();
    if (cached != null && cached.getSecond() == fileModificationStamp) {
      return cached.getFirst();
    }
    DataParsingSaxHandler handler = new DataParsingSaxHandler(file);
    try {
      SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
      saxParser.parse(new InputSource(new StringReader(escapeAttributes(file.getText()))), handler);
    } catch (IOException e) {
      LOG.error(e);
    } catch (ParserConfigurationException e) {
      LOG.error(e);
    } catch (SAXException e) {
      LOG.error(e);
    }

    Pair<MostlySingularMultiMap<String, AnnotationData>, Long> pair =
        Pair.create(handler.getResult(), file.getModificationStamp());
    annotationFileToDataAndModStamp.put(file, pair);

    return pair.first;
  }
 protected void cacheExternalAnnotations(
     @NotNull String packageName,
     @NotNull PsiFile fromFile,
     @NotNull List<PsiFile> annotationFiles) {
   VirtualFile virtualFile = fromFile.getVirtualFile();
   if (virtualFile != null) {
     myExternalAnnotations.put(virtualFile, annotationFiles);
   }
 }
  @Override
  @Nullable
  public List<PsiFile> findExternalAnnotationsFiles(@NotNull PsiModifierListOwner listOwner) {
    final PsiFile containingFile = listOwner.getContainingFile();
    if (!(containingFile instanceof PsiJavaFile)) {
      return null;
    }
    final PsiJavaFile javaFile = (PsiJavaFile) containingFile;
    final String packageName = javaFile.getPackageName();
    final VirtualFile virtualFile = containingFile.getVirtualFile();
    if (virtualFile == null) return null;

    final List<PsiFile> files = myExternalAnnotations.get(virtualFile);
    if (files == NULL_LIST) return null;
    if (files != null) {
      boolean allValid = true;
      for (PsiFile file : files) {
        allValid &= file.isValid();
      }
      if (allValid) {
        return files;
      }
    }

    if (virtualFile == null) {
      return null;
    }

    Set<PsiFile> possibleAnnotationsXmls = new THashSet<PsiFile>();
    for (VirtualFile root : getExternalAnnotationsRoots(virtualFile)) {
      final VirtualFile ext =
          root.findFileByRelativePath(packageName.replace('.', '/') + "/" + ANNOTATIONS_XML);
      if (ext == null) continue;
      final PsiFile psiFile = myPsiManager.findFile(ext);
      if (psiFile == null) continue;
      possibleAnnotationsXmls.add(psiFile);
    }
    List<PsiFile> result;
    if (possibleAnnotationsXmls.isEmpty()) {
      myExternalAnnotations.put(virtualFile, NULL_LIST);
      result = null;
    } else {
      result = new SmartList<PsiFile>(possibleAnnotationsXmls);
      // sorting by writability: writable go first
      Collections.sort(
          result,
          new Comparator<PsiFile>() {
            @Override
            public int compare(PsiFile f1, PsiFile f2) {
              boolean w1 = f1.isWritable();
              boolean w2 = f2.isWritable();
              if (w1 == w2) {
                return 0;
              }
              return w1 ? -1 : 1;
            }
          });

      myExternalAnnotations.put(virtualFile, result);
    }
    return result;
  }
 protected void duplicateError(
     @NotNull PsiFile file, @NotNull String externalName, @NotNull String text) {
   LOG.error(text + "; for signature: '" + externalName + "' in the " + file.getVirtualFile());
 }