private IIndexFragmentFile findContextFile(
      int linkageID,
      LinkageTask map,
      final FileVersionTask versionTask,
      LinkedHashSet<IIndexFile> safeGuard,
      IProgressMonitor monitor)
      throws CoreException, InterruptedException {
    IIndexFragmentFile ctxFile = versionTask.fIndexFile;
    while (true) {
      IIndexInclude ctxInclude = ctxFile.getParsedInContext();
      if (ctxInclude == null) return ctxFile;

      IIndexFragmentFile nextCtx = (IIndexFragmentFile) ctxInclude.getIncludedBy();
      if (nextCtx == null) return nextCtx;

      // Found a recursion.
      if (!safeGuard.add(nextCtx)) return null;

      final IIndexFileLocation ctxIfl = nextCtx.getLocation();
      LocationTask ctxTask = map.find(ctxIfl);
      if (ctxTask != null) {
        FileVersionTask ctxVersionTask = ctxTask.findVersion(nextCtx);
        if (ctxVersionTask != null && ctxVersionTask.fOutdated) {
          // Handle the context first.
          parseVersionInContext(
              linkageID, map, ctxIfl, ctxVersionTask, ctxTask.fTu, safeGuard, monitor);
          if (ctxVersionTask.fOutdated // This is unexpected.
              || !versionTask.fOutdated) // Our file was parsed.
          return null;

          // The file is no longer a context, look for a different one.
          nextCtx = ctxFile;
        }
      }
      ctxFile = nextCtx;
    }
  }
  private void extractFiles(
      HashMap<Integer, List<IIndexFileLocation>> files,
      List<IIndexFragmentFile> iFilesToRemove,
      IProgressMonitor monitor)
      throws CoreException {
    final boolean forceAll = (fUpdateFlags & IIndexManager.UPDATE_ALL) != 0;
    final boolean checkTimestamps = (fUpdateFlags & IIndexManager.UPDATE_CHECK_TIMESTAMPS) != 0;
    final boolean checkFileContentsHash =
        (fUpdateFlags & IIndexManager.UPDATE_CHECK_CONTENTS_HASH) != 0;
    final boolean forceUnresolvedIncludes =
        (fUpdateFlags & IIndexManager.UPDATE_UNRESOLVED_INCLUDES) != 0;
    final boolean both = fIndexHeadersWithoutContext == UnusedHeaderStrategy.useBoth;
    int count = 0;
    int forceFirst = fForceNumberFiles;
    BitSet linkages = new BitSet();
    for (final Object tu : fFilesToUpdate) {
      if (monitor.isCanceled()) return;

      final boolean force = forceAll || --forceFirst >= 0;
      final IIndexFileLocation ifl = fResolver.resolveFile(tu);
      if (ifl == null) continue;

      final IIndexFragmentFile[] indexFiles = fIndex.getWritableFiles(ifl);
      final boolean isSourceUnit = fResolver.isSourceUnit(tu);
      linkages.clear();
      final boolean regularContent = isRequiredInIndex(tu, ifl, isSourceUnit);
      final boolean indexedUnconditionally = fResolver.isIndexedUnconditionally(ifl);
      if (regularContent || indexedUnconditionally) {
        // Headers or sources required with a specific linkage
        final UpdateKind updateKind =
            isSourceUnit
                ? UpdateKind.REQUIRED_SOURCE
                : regularContent && both
                    ? UpdateKind.REQUIRED_HEADER
                    : UpdateKind.ONE_LINKAGE_HEADER;
        if (regularContent || indexFiles.length == 0) {
          AbstractLanguage[] langs = fResolver.getLanguages(tu, fIndexHeadersWithoutContext);
          for (AbstractLanguage lang : langs) {
            int linkageID = lang.getLinkageID();
            boolean foundInLinkage = false;
            for (int i = 0; i < indexFiles.length; i++) {
              IIndexFragmentFile ifile = indexFiles[i];
              if (ifile != null && ifile.getLinkageID() == linkageID && ifile.hasContent()) {
                foundInLinkage = true;
                indexFiles[i] = null; // Take the file.
                boolean update =
                    force
                        || (forceUnresolvedIncludes && ifile.hasUnresolvedInclude())
                        || isModified(checkTimestamps, checkFileContentsHash, ifl, tu, ifile);
                if (update && requestUpdate(linkageID, ifl, ifile, tu, updateKind)) {
                  count++;
                  linkages.set(linkageID);
                }
              }
            }
            if (!foundInLinkage && requestUpdate(linkageID, ifl, null, tu, updateKind)) {
              linkages.set(linkageID);
              count++;
            }
          }
        }
      }

      // Handle other files present in index.
      for (IIndexFragmentFile ifile : indexFiles) {
        if (ifile != null) {
          IIndexInclude ctx = ifile.getParsedInContext();
          if (ctx == null && !indexedUnconditionally && ifile.hasContent()) {
            iFilesToRemove.add(ifile);
            count++;
          } else {
            boolean update =
                force
                    || (forceUnresolvedIncludes && ifile.hasUnresolvedInclude())
                    || isModified(checkTimestamps, checkFileContentsHash, ifl, tu, ifile);
            final int linkageID = ifile.getLinkageID();
            if (update && requestUpdate(linkageID, ifl, ifile, tu, UpdateKind.OTHER_HEADER)) {
              count++;
              linkages.set(linkageID);
            }
          }
        }
      }
      for (int lid = linkages.nextSetBit(0); lid >= 0; lid = linkages.nextSetBit(lid + 1)) {
        addPerLinkage(lid, ifl, files);
      }
    }
    synchronized (this) {
      incrementRequestedFilesCount(count - fFilesToUpdate.length);
      fFilesToUpdate = null;
    }
  }