@NotNull
  @Override
  public Runnable processFile(@NotNull PsiFile file) {
    if (!(file instanceof GoFile)) {
      return EmptyRunnable.getInstance();
    }
    MultiMap<String, GoImportSpec> importMap = ((GoFile) file).getImportMap();
    Set<PsiElement> importEntriesToDelete = ContainerUtil.newLinkedHashSet();
    Set<PsiElement> importIdentifiersToDelete = findRedundantImportIdentifiers(importMap);

    importEntriesToDelete.addAll(findDuplicatedEntries(importMap));
    importEntriesToDelete.addAll(filterUnusedImports(file, importMap).values());
    if (importEntriesToDelete.isEmpty() && importIdentifiersToDelete.isEmpty()) {
      return EmptyRunnable.getInstance();
    }

    return new CollectingInfoRunnable() {
      @Nullable
      @Override
      public String getUserNotificationInfo() {
        int entriesToDelete = importEntriesToDelete.size();
        int identifiersToDelete = importIdentifiersToDelete.size();
        String result = "";
        if (entriesToDelete > 0) {
          result = "Removed " + entriesToDelete + " import" + (entriesToDelete > 1 ? "s" : "");
        }
        if (identifiersToDelete > 0) {
          result += result.isEmpty() ? "Removed " : " and ";
          result += identifiersToDelete + " alias" + (identifiersToDelete > 1 ? "es" : "");
        }
        return StringUtil.nullize(result);
      }

      @Override
      public void run() {
        if (!importEntriesToDelete.isEmpty() || !importIdentifiersToDelete.isEmpty()) {
          PsiDocumentManager manager = PsiDocumentManager.getInstance(file.getProject());
          Document document = manager.getDocument(file);
          if (document != null) {
            manager.commitDocument(document);
          }
        }

        for (PsiElement importEntry : importEntriesToDelete) {
          if (importEntry != null && importEntry.isValid()) {
            deleteImportSpec(getImportSpec(importEntry));
          }
        }

        for (PsiElement identifier : importIdentifiersToDelete) {
          if (identifier != null && identifier.isValid()) {
            identifier.delete();
          }
        }
      }
    };
  }
示例#2
0
  public final void runBare() throws Throwable {
    final Throwable[] throwables = new Throwable[1];

    SwingUtilities.invokeAndWait(
        new Runnable() {
          public void run() {
            try {
              ourTestThread = Thread.currentThread();
              startRunAndTear();
            } catch (Throwable throwable) {
              throwables[0] = throwable;
            } finally {
              ourTestThread = null;
              try {
                cleanupApplicationCaches();
              } catch (Throwable e) {
                e.printStackTrace();
              }
            }
          }
        });

    if (throwables[0] != null) {
      throw throwables[0];
    }

    // just to make sure all deffered Runnable's to finish
    SwingUtilities.invokeAndWait(EmptyRunnable.getInstance());

    if (IdeaLogger.ourErrorsOccurred != null) {
      throw IdeaLogger.ourErrorsOccurred;
    }
  }
 @NotNull
 @Override
 public ListPopup createConfirmation(
     String title,
     final String yesText,
     String noText,
     final Runnable onYes,
     int defaultOptionIndex) {
   return createConfirmation(
       title, yesText, noText, onYes, EmptyRunnable.getInstance(), defaultOptionIndex);
 }
  public void testDelayedTasksReusePooledThreadIfExecuteAtDifferentTimes()
      throws InterruptedException, ExecutionException {
    final AppScheduledExecutorService service = new AppScheduledExecutorService(getName());
    final List<LogInfo> log = Collections.synchronizedList(new ArrayList<>());
    // pre-start one thread
    Future<?> future = service.submit(EmptyRunnable.getInstance());
    future.get();
    service.setBackendPoolCorePoolSize(1);
    assertEquals(1, service.getBackendPoolExecutorSize());

    int delay = 500;

    ScheduledFuture<?> f1 =
        service.schedule((Runnable) () -> log.add(new LogInfo(1)), delay, TimeUnit.MILLISECONDS);
    ScheduledFuture<?> f2 =
        service.schedule(
            (Runnable) () -> log.add(new LogInfo(2)), delay + 100, TimeUnit.MILLISECONDS);
    ScheduledFuture<?> f3 =
        service.schedule(
            (Runnable) () -> log.add(new LogInfo(3)), delay + 200, TimeUnit.MILLISECONDS);

    assertEquals(1, service.getBackendPoolExecutorSize());

    assertFalse(f1.isDone());
    assertFalse(f2.isDone());
    assertFalse(f3.isDone());

    TimeoutUtil.sleep(delay + 200 + 300);
    assertTrue(f1.isDone());
    assertTrue(f2.isDone());
    assertTrue(f3.isDone());
    assertEquals(1, service.getBackendPoolExecutorSize());

    assertEquals(3, log.size());
    Set<Thread> usedThreads =
        new HashSet<>(
            Arrays.asList(
                log.get(0).currentThread, log.get(1).currentThread, log.get(2).currentThread));
    if (usedThreads.size() != 1) {
      System.err.println(ThreadDumper.dumpThreadsToString());
    }
    assertEquals(usedThreads.toString(), 1, usedThreads.size()); // must be executed in same thread

    service.shutdownAppScheduledExecutorService();
    assertTrue(service.awaitTermination(10, TimeUnit.SECONDS));
  }
 private static void waitForAllLaters() throws InterruptedException, InvocationTargetException {
   for (int i = 0; i < 3; i++) {
     SwingUtilities.invokeAndWait(EmptyRunnable.getInstance());
   }
 }
 public final void updateUI() {
   updateUI(EmptyRunnable.getInstance());
 }
public class RefResolveServiceImpl extends RefResolveService implements Runnable, Disposable {
  private static final Logger LOG = Logger.getInstance(RefResolveServiceImpl.class);
  private final AtomicInteger fileCount = new AtomicInteger();
  private final AtomicLong bytesSize = new AtomicLong();
  private final AtomicLong refCount = new AtomicLong();
  private final PersistentIntList storage;
  private final Deque<VirtualFile> filesToResolve =
      new ArrayDeque<VirtualFile>(); // guarded by filesToResolve
  private final ConcurrentBitSet fileIsInQueue = new ConcurrentBitSet();
  private final ConcurrentBitSet fileIsResolved;
  private final ApplicationEx myApplication;
  private volatile boolean myDisposed;
  private volatile boolean upToDate;
  private final AtomicInteger enableVetoes =
      new AtomicInteger(); // number of disable() calls. To enable the service, there should be at
                           // least corresponding number of enable() calls.
  private final FileWriter log;
  private final ProjectFileIndex myProjectFileIndex;

  public RefResolveServiceImpl(
      final Project project,
      final MessageBus messageBus,
      final PsiManager psiManager,
      StartupManager startupManager,
      ApplicationEx application,
      ProjectFileIndex projectFileIndex)
      throws IOException {
    super(project);
    ((FutureTask) resolveProcess).run();
    myApplication = application;
    myProjectFileIndex = projectFileIndex;
    if (ENABLED) {
      log = new FileWriter(new File(getStorageDirectory(), "log.txt"));

      File dataFile = new File(getStorageDirectory(), "data");
      fileIsResolved = ConcurrentBitSet.readFrom(new File(getStorageDirectory(), "bitSet"));
      log("Read resolved file bitset: " + fileIsResolved);

      int maxId = FSRecords.getMaxId();
      PersistentIntList list = new PersistentIntList(dataFile, dataFile.exists() ? 0 : maxId);
      if (list.getSize() == maxId) {
        storage = list;
      } else {
        // just to be safe, re-resolve all if VFS files count changes since last restart
        list.dispose();
        storage = new PersistentIntList(dataFile, maxId);
        log(
            "VFS maxId changed: was "
                + list.getSize()
                + "; now: "
                + maxId
                + "; re-resolving everything");
        fileIsResolved.clear();
      }
      Disposer.register(this, storage);
      if (!application.isUnitTestMode()) {
        startupManager.runWhenProjectIsInitialized(
            () -> {
              initListeners(messageBus, psiManager);
              startThread();
            });
      }
      Disposer.register(
          this,
          new Disposable() {
            @Override
            public void dispose() {
              try {
                save();
                log.close();
              } catch (IOException e) {
                LOG.error(e);
              }
            }
          });
    } else {
      log = null;
      fileIsResolved = null;
      storage = null;
    }
  }

  @NotNull
  private static List<VirtualFile> toVf(@NotNull int[] ids) {
    List<VirtualFile> res = new ArrayList<VirtualFile>();
    for (int id : ids) {
      VirtualFile file = PersistentFS.getInstance().findFileById(id);
      if (file != null) {
        res.add(file);
      }
    }
    return res;
  }

  @NotNull
  private static String toVfString(@NotNull int[] backIds) {
    List<VirtualFile> list = toVf(backIds);
    return toVfString(list);
  }

  @NotNull
  private static String toVfString(@NotNull Collection<VirtualFile> list) {
    List<VirtualFile> sub = new ArrayList<VirtualFile>(list).subList(0, Math.min(list.size(), 100));
    return list.size()
        + " files: "
        + StringUtil.join(sub, file -> file.getName(), ", ")
        + (list.size() == sub.size() ? "" : "...");
  }

  private void initListeners(@NotNull MessageBus messageBus, @NotNull PsiManager psiManager) {
    messageBus
        .connect()
        .subscribe(
            VirtualFileManager.VFS_CHANGES,
            new BulkFileListener.Adapter() {
              @Override
              public void after(@NotNull List<? extends VFileEvent> events) {
                fileCount.set(0);
                List<VirtualFile> files =
                    ContainerUtil.mapNotNull(
                        events,
                        new Function<VFileEvent, VirtualFile>() {
                          @Override
                          public VirtualFile fun(VFileEvent event) {
                            return event.getFile();
                          }
                        });
                queue(files, "VFS events " + events.size());
              }
            });
    psiManager.addPsiTreeChangeListener(
        new PsiTreeChangeAdapter() {
          @Override
          public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
            PsiFile file = event.getFile();
            VirtualFile virtualFile = PsiUtilCore.getVirtualFile(file);
            if (virtualFile != null) {
              queue(Collections.singletonList(virtualFile), event);
            }
          }

          @Override
          public void propertyChanged(@NotNull PsiTreeChangeEvent event) {
            childrenChanged(event);
          }
        });

    messageBus
        .connect()
        .subscribe(
            DumbService.DUMB_MODE,
            new DumbService.DumbModeListener() {
              @Override
              public void enteredDumbMode() {
                disable();
              }

              @Override
              public void exitDumbMode() {
                enable();
              }
            });
    messageBus
        .connect()
        .subscribe(
            PowerSaveMode.TOPIC,
            new PowerSaveMode.Listener() {
              @Override
              public void powerSaveStateChanged() {
                if (PowerSaveMode.isEnabled()) {
                  enable();
                } else {
                  disable();
                }
              }
            });
    myApplication.addApplicationListener(
        new ApplicationAdapter() {
          @Override
          public void beforeWriteActionStart(@NotNull Object action) {
            disable();
          }

          @Override
          public void writeActionFinished(@NotNull Object action) {
            enable();
          }

          @Override
          public void applicationExiting() {
            disable();
          }
        },
        this);
    VirtualFileManager.getInstance()
        .addVirtualFileManagerListener(
            new VirtualFileManagerListener() {
              @Override
              public void beforeRefreshStart(boolean asynchronous) {
                disable();
              }

              @Override
              public void afterRefreshFinish(boolean asynchronous) {
                enable();
              }
            },
            this);
    HeavyProcessLatch.INSTANCE.addListener(
        new HeavyProcessLatch.HeavyProcessListener() {
          @Override
          public void processStarted() {}

          @Override
          public void processFinished() {
            wakeUp();
          }
        },
        this);
  }

  // return true if file was added to queue
  private boolean queueIfNeeded(VirtualFile virtualFile, @NotNull Project project) {
    return toResolve(virtualFile, project) && queueUpdate(virtualFile);
  }

  private boolean toResolve(VirtualFile virtualFile, @NotNull Project project) {
    if (virtualFile != null
        && virtualFile.isValid()
        && project.isInitialized()
        && myProjectFileIndex.isInSourceContent(virtualFile)
        && isSupportedFileType(virtualFile)) {
      return true;
    }

    // else mark it as resolved so we will not have to check it again
    if (virtualFile instanceof VirtualFileWithId) {
      int id = getAbsId(virtualFile);
      fileIsResolved.set(id);
    }

    return false;
  }

  public static boolean isSupportedFileType(@NotNull VirtualFile virtualFile) {
    if (virtualFile.isDirectory()) return true;
    if (virtualFile.getFileType() == StdFileTypes.JAVA) return true;
    if (virtualFile.getFileType() == StdFileTypes.XML
        && !ProjectCoreUtil.isProjectOrWorkspaceFile(virtualFile)) return true;
    if ("groovy".equals(virtualFile.getExtension())) return true;
    return false;
  }

  @NotNull
  private File getStorageDirectory() {
    String dirName =
        myProject.getName() + "." + Integer.toHexString(myProject.getPresentableUrl().hashCode());
    File dir = new File(PathManager.getSystemPath(), "refs/" + dirName);
    FileUtil.createDirectory(dir);
    return dir;
  }

  private void log(String m) {
    // System.out.println(m);
    logf(m);
  }

  private void logf(String m) {
    if (LOG.isDebugEnabled()) {
      try {
        log.write(
            DateFormat.getDateTimeInstance().format(new Date())
                + " "
                + m
                + /*"    ; gap="+storage.gap+*/ "\n");
      } catch (IOException e) {
        LOG.error(e);
      }
    }
  }

  private void flushLog() {
    try {
      log.flush();
    } catch (IOException e) {
      LOG.error(e);
    }
  }

  // return true if file was added to queue
  private boolean queueUpdate(@NotNull VirtualFile file) {
    synchronized (filesToResolve) {
      if (!(file instanceof VirtualFileWithId)) return false;
      int fileId = getAbsId(file);
      countAndMarkUnresolved(file, new LinkedHashSet<VirtualFile>(), true);
      boolean alreadyAdded = fileIsInQueue.set(fileId);
      if (!alreadyAdded) {
        filesToResolve.add(file);
      }
      upToDate = false;
      wakeUpUnderLock();
      return !alreadyAdded;
    }
  }

  private void wakeUp() {
    synchronized (filesToResolve) {
      wakeUpUnderLock();
    }
  }

  private void wakeUpUnderLock() {
    filesToResolve.notifyAll();
  }

  private void waitForQueue() throws InterruptedException {
    synchronized (filesToResolve) {
      filesToResolve.wait(1000);
    }
  }

  private void startThread() {
    new Thread(this, "Ref resolve service").start();
    upToDate = true;
    queueUnresolvedFilesSinceLastRestart();
  }

  private void queueUnresolvedFilesSinceLastRestart() {
    PersistentFS fs = PersistentFS.getInstance();
    int maxId = FSRecords.getMaxId();
    TIntArrayList list = new TIntArrayList();
    for (int id = fileIsResolved.nextClearBit(1);
        id >= 0 && id < maxId;
        id = fileIsResolved.nextClearBit(id + 1)) {
      int nextSetBit = fileIsResolved.nextSetBit(id);
      int endOfRun = Math.min(maxId, nextSetBit == -1 ? maxId : nextSetBit);
      do {
        VirtualFile virtualFile = fs.findFileById(id);
        if (queueIfNeeded(virtualFile, myProject)) {
          list.add(id);
        } else {
          fileIsResolved.set(id);
        }
      } while (++id < endOfRun);
    }
    log("Initially added to resolve " + toVfString(list.toNativeArray()));
  }

  @Override
  public void dispose() {
    myDisposed = true;
  }

  private void save() throws IOException {
    log("Saving resolved file bitset: " + fileIsResolved);
    fileIsResolved.writeTo(new File(getStorageDirectory(), "bitSet"));
    log("list.size = " + storage.getSize());
  }

  private volatile Future<?> resolveProcess =
      new FutureTask<Object>(EmptyRunnable.getInstance(), null); // write from EDT only

  @Override
  public void run() {
    while (!myDisposed) {
      boolean isEmpty;
      synchronized (filesToResolve) {
        isEmpty = filesToResolve.isEmpty();
      }
      if (enableVetoes.get() > 0
          || isEmpty
          || !resolveProcess.isDone()
          || HeavyProcessLatch.INSTANCE.isRunning()
          || PsiDocumentManager.getInstance(myProject).hasUncommitedDocuments()) {
        try {
          waitForQueue();
        } catch (InterruptedException e) {
          break;
        }
        continue;
      }
      final Set<VirtualFile> files = pollFilesToResolve();
      if (files.isEmpty()) continue;

      upToDate = false;

      myApplication.invokeLater(
          () -> {
            if (!resolveProcess.isDone()) return;
            log("Started to resolve " + files.size() + " files");

            Task.Backgroundable backgroundable =
                new Task.Backgroundable(myProject, "Resolving files...", false) {
                  @Override
                  public void run(@NotNull final ProgressIndicator indicator) {
                    if (!myApplication.isDisposed()) {
                      processBatch(indicator, files);
                    }
                  }
                };
            ProgressIndicator indicator;
            if (files.size() > 1) {
              // show progress
              indicator = new BackgroundableProcessIndicator(backgroundable);
            } else {
              indicator = new MyProgress();
            }
            resolveProcess =
                ((ProgressManagerImpl) ProgressManager.getInstance())
                    .runProcessWithProgressAsynchronously(backgroundable, indicator, null);
          },
          myProject.getDisposed());

      flushLog();
    }
  }

  private volatile int resolvedInPreviousBatch;

  private void processBatch(
      @NotNull final ProgressIndicator indicator, @NotNull Set<VirtualFile> files) {
    assert !myApplication.isDispatchThread();
    final int resolvedInPreviousBatch = this.resolvedInPreviousBatch;
    final int totalSize = files.size() + resolvedInPreviousBatch;
    final ConcurrentIntObjectMap<int[]> fileToForwardIds =
        ContainerUtil.createConcurrentIntObjectMap();
    final Set<VirtualFile> toProcess = Collections.synchronizedSet(files);
    indicator.setIndeterminate(false);
    ProgressIndicatorUtils.forceWriteActionPriority(indicator, (Disposable) indicator);
    long start = System.currentTimeMillis();
    Processor<VirtualFile> processor =
        file -> {
          double fraction = 1 - toProcess.size() * 1.0 / totalSize;
          indicator.setFraction(fraction);
          try {
            if (!file.isDirectory() && toResolve(file, myProject)) {
              int fileId = getAbsId(file);
              int i = totalSize - toProcess.size();
              indicator.setText(i + "/" + totalSize + ": Resolving " + file.getPresentableUrl());
              int[] forwardIds = processFile(file, fileId, indicator);
              if (forwardIds == null) {
                // queueUpdate(file);
                return false;
              }
              fileToForwardIds.put(fileId, forwardIds);
            }
            toProcess.remove(file);
            return true;
          } catch (RuntimeException e) {
            indicator.checkCanceled();
          }
          return true;
        };
    boolean success = true;
    try {
      success = processFilesConcurrently(files, indicator, processor);
    } finally {
      this.resolvedInPreviousBatch = toProcess.isEmpty() ? 0 : totalSize - toProcess.size();
      queue(toProcess, "re-added after fail. success=" + success);
      storeIds(fileToForwardIds);

      long end = System.currentTimeMillis();
      log(
          "Resolved batch of "
              + (totalSize - toProcess.size())
              + " from "
              + totalSize
              + " files in "
              + ((end - start) / 1000)
              + "sec. (Gap: "
              + storage.gap
              + ")");
      synchronized (filesToResolve) {
        upToDate = filesToResolve.isEmpty();
        log("upToDate = " + upToDate);
        if (upToDate) {
          for (Listener listener : myListeners) {
            listener.allFilesResolved();
          }
        }
      }
    }
  }

  private boolean processFilesConcurrently(
      @NotNull Set<VirtualFile> files,
      @NotNull final ProgressIndicator indicator,
      @NotNull final Processor<VirtualFile> processor) {
    final List<VirtualFile> fileList = new ArrayList<VirtualFile>(files);
    // fine but grabs all CPUs
    // return JobLauncher.getInstance().invokeConcurrentlyUnderProgress(fileList, indicator, false,
    // false, processor);

    int parallelism = CacheUpdateRunner.indexingThreadCount();
    final Callable<Boolean> processFileFromSet =
        () -> {
          final boolean[] result = {true};
          ProgressManager.getInstance()
              .executeProcessUnderProgress(
                  () -> {
                    while (true) {
                      ProgressManager.checkCanceled();
                      VirtualFile file;
                      synchronized (fileList) {
                        file = fileList.isEmpty() ? null : fileList.remove(fileList.size() - 1);
                      }
                      if (file == null) {
                        break;
                      }
                      if (!processor.process(file)) {
                        result[0] = false;
                        break;
                      }
                    }
                  },
                  indicator);
          return result[0];
        };
    List<Future<Boolean>> futures =
        ContainerUtil.map(
            Collections.nCopies(parallelism, ""),
            s -> myApplication.executeOnPooledThread(processFileFromSet));

    List<Boolean> results =
        ContainerUtil.map(
            futures,
            future -> {
              try {
                return future.get();
              } catch (Exception e) {
                LOG.error(e);
              }
              return false;
            });

    return !ContainerUtil.exists(
        results,
        result -> {
          return result != null && !result; // null means PCE
        });
  }

  @NotNull
  private Set<VirtualFile> pollFilesToResolve() {
    Set<VirtualFile> set;
    synchronized (filesToResolve) {
      int queuedSize = filesToResolve.size();
      set = new LinkedHashSet<VirtualFile>(queuedSize);
      // someone might have cleared this bit to mark file as processed
      for (VirtualFile file : filesToResolve) {
        if (fileIsInQueue.clear(getAbsId(file))) {
          set.add(file);
        }
      }
      filesToResolve.clear();
    }
    return countAndMarkUnresolved(set, false);
  }

  private static int getAbsId(@NotNull VirtualFile file) {
    return Math.abs(((VirtualFileWithId) file).getId());
  }

  @NotNull
  private Set<VirtualFile> countAndMarkUnresolved(
      @NotNull Collection<VirtualFile> files, boolean inDbOnly) {
    Set<VirtualFile> result = new LinkedHashSet<VirtualFile>();
    for (VirtualFile file : files) {
      countAndMarkUnresolved(file, result, inDbOnly);
    }
    return result;
  }

  private void countAndMarkUnresolved(
      @NotNull VirtualFile file, @NotNull final Set<VirtualFile> result, final boolean inDbOnly) {
    if (file.isDirectory()) {
      VfsUtilCore.visitChildrenRecursively(
          file,
          new VirtualFileVisitor() {
            @Override
            public boolean visitFile(@NotNull VirtualFile file) {
              return doCountAndMarkUnresolved(file, result);
            }

            @Nullable
            @Override
            public Iterable<VirtualFile> getChildrenIterable(@NotNull VirtualFile file) {
              return inDbOnly ? ((NewVirtualFile) file).iterInDbChildren() : null;
            }
          });
    } else {
      doCountAndMarkUnresolved(file, result);
    }
  }

  // return true if continue to process sub-directories of the {@code file}, false if the file is
  // already processed
  private boolean doCountAndMarkUnresolved(
      @NotNull VirtualFile file, @NotNull Set<VirtualFile> result) {
    if (file.isDirectory()) {
      fileIsResolved.set(getAbsId(file));
      return result.add(file);
    }
    if (toResolve(file, myProject)) {
      result.add(file);
      fileIsResolved.clear(getAbsId(file));
    }
    return true;
  }

  private void enable() {
    // decrement but only if it's positive
    int vetoes;
    do {
      vetoes = enableVetoes.get();
      if (vetoes == 0) break;
    } while (!enableVetoes.compareAndSet(vetoes, vetoes - 1));
    wakeUp();
  }

  private void disable() {
    enableVetoes.incrementAndGet();
    wakeUp();
  }

  // returns list of resolved files if updated successfully, or null if write action or dumb mode
  // started
  private int[] processFile(
      @NotNull final VirtualFile file, int fileId, @NotNull final ProgressIndicator indicator) {
    final TIntHashSet forward;
    try {
      forward = calcForwardRefs(file, indicator);
    } catch (IndexNotReadyException e) {
      return null;
    } catch (ApplicationUtil.CannotRunReadActionException e) {
      return null;
    } catch (ProcessCanceledException e) {
      throw e;
    } catch (Exception e) {
      log(ExceptionUtil.getThrowableText(e));
      flushLog();
      return null;
    }

    int[] forwardIds = forward.toArray();
    fileIsResolved.set(fileId);
    logf(
        "  ---- " + file.getPresentableUrl() + " processed. forwardIds: " + toVfString(forwardIds));
    for (Listener listener : myListeners) {
      listener.fileResolved(file);
    }
    return forwardIds;
  }

  private void storeIds(@NotNull ConcurrentIntObjectMap<int[]> fileToForwardIds) {
    int forwardSize = 0;
    int backwardSize = 0;
    final TIntObjectHashMap<TIntArrayList> fileToBackwardIds =
        new TIntObjectHashMap<TIntArrayList>(fileToForwardIds.size());
    for (ConcurrentIntObjectMap.IntEntry<int[]> entry : fileToForwardIds.entries()) {
      int fileId = entry.getKey();
      int[] forwardIds = entry.getValue();
      forwardSize += forwardIds.length;
      for (int forwardId : forwardIds) {
        TIntArrayList backIds = fileToBackwardIds.get(forwardId);
        if (backIds == null) {
          backIds = new TIntArrayList();
          fileToBackwardIds.put(forwardId, backIds);
        }
        backIds.add(fileId);
        backwardSize++;
      }
    }
    log("backwardSize = " + backwardSize);
    log("forwardSize = " + forwardSize);
    log("fileToForwardIds.size() = " + fileToForwardIds.size());
    log("fileToBackwardIds.size() = " + fileToBackwardIds.size());
    assert forwardSize == backwardSize;

    // wrap in read action so that sudden quit (in write action) would not interrupt us
    myApplication.runReadAction(
        () -> {
          if (!myApplication.isDisposed()) {
            fileToBackwardIds.forEachEntry(
                new TIntObjectProcedure<TIntArrayList>() {
                  @Override
                  public boolean execute(int fileId, TIntArrayList backIds) {
                    storage.addAll(fileId, backIds.toNativeArray());
                    return true;
                  }
                });
          }
        });
  }

  @NotNull
  private TIntHashSet calcForwardRefs(
      @NotNull final VirtualFile virtualFile, @NotNull final ProgressIndicator indicator)
      throws IndexNotReadyException, ApplicationUtil.CannotRunReadActionException {

    final TIntHashSet forward = new TIntHashSet();

    final PsiFile psiFile =
        ApplicationUtil.tryRunReadAction(
            () -> {
              if (myProject.isDisposed()) throw new ProcessCanceledException();
              if (fileCount.incrementAndGet() % 100 == 0) {
                PsiManager.getInstance(myProject).dropResolveCaches();
                try {
                  storage.flush();
                  log.flush();
                } catch (IOException e) {
                  LOG.error(e);
                }
              }

              return PsiManager.getInstance(myProject).findFile(virtualFile);
            });
    final int fileId = getAbsId(virtualFile);
    if (psiFile != null) {
      bytesSize.addAndGet(virtualFile.getLength());
      final Set<PsiElement> resolved = new THashSet<PsiElement>();
      ApplicationUtil.tryRunReadAction(
          new Runnable() {
            @Override
            public void run() {
              indicator.checkCanceled();

              if (psiFile instanceof PsiJavaFile) {
                psiFile.accept(
                    new JavaRecursiveElementWalkingVisitor() {
                      @Override
                      public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
                        indicator.checkCanceled();
                        resolveReference(reference, resolved);

                        super.visitReferenceElement(reference);
                      }
                    });
              } else {
                psiFile.accept(
                    new PsiRecursiveElementWalkingVisitor() {
                      @Override
                      public void visitElement(PsiElement element) {
                        for (PsiReference reference : element.getReferences()) {
                          indicator.checkCanceled();
                          resolveReference(reference, resolved);
                        }
                        super.visitElement(element);
                      }
                    });
              }

              indicator.checkCanceled();
              for (PsiElement element : resolved) {
                PsiFile file = element.getContainingFile();
                addIdAndSuperClasses(file, forward);
              }
            }
          });
    }

    forward.remove(fileId);
    return forward;
  }

  private void resolveReference(
      @NotNull PsiReference reference, @NotNull Set<PsiElement> resolved) {
    PsiElement element = reference.resolve();
    if (element != null) {
      resolved.add(element);
    }
    refCount.incrementAndGet();
  }

  private static void addIdAndSuperClasses(PsiFile file, @NotNull TIntHashSet forward) {
    if (file instanceof PsiJavaFile
        && file.getName().equals("Object.class")
        && ((PsiJavaFile) file).getPackageName().equals("java.lang")) {
      return;
    }
    VirtualFile virtualFile = PsiUtilCore.getVirtualFile(file);
    if (virtualFile instanceof VirtualFileWithId
        && forward.add(getAbsId(virtualFile))
        && file instanceof PsiClassOwner) {
      for (PsiClass aClass : ((PsiClassOwner) file).getClasses()) {
        for (PsiClass superClass : aClass.getSupers()) {
          addIdAndSuperClasses(superClass.getContainingFile(), forward);
        }
      }
    }
  }

  @Override
  @Nullable
  public int[] getBackwardIds(@NotNull VirtualFileWithId file) {
    if (!isUpToDate()) return null;
    int fileId = getAbsId((VirtualFile) file);
    return storage.get(fileId);
  }

  private String prevLog = "";
  private static final Set<JavaSourceRootType> SOURCE_ROOTS =
      ContainerUtil.newTroveSet(JavaSourceRootType.SOURCE, JavaSourceRootType.TEST_SOURCE);

  @NotNull
  @Override
  public GlobalSearchScope restrictByBackwardIds(
      @NotNull final VirtualFile virtualFile, @NotNull GlobalSearchScope scope) {
    final int[] backIds =
        RefResolveService.getInstance(myProject).getBackwardIds((VirtualFileWithId) virtualFile);
    if (backIds == null) {
      return scope;
    }
    String files = toVfString(backIds);
    String log = "Restricting scope of " + virtualFile.getName() + " to " + files;
    if (!log.equals(prevLog)) {
      log(log);
      flushLog();
      prevLog = log;
    }
    GlobalSearchScope restrictedByBackwardIds =
        new GlobalSearchScope() {
          @Override
          public boolean contains(@NotNull VirtualFile file) {
            if (!(file instanceof VirtualFileWithId)
                || file.equals(virtualFile)
                || ArrayUtil.indexOf(backIds, getAbsId(file)) != -1) return true;
            return false
                & !myProjectFileIndex.isUnderSourceRootOfType(
                    file,
                    SOURCE_ROOTS); // filter out source file which we know for sure does not
                                   // reference the element
          }

          @Override
          public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
            return 0;
          }

          @Override
          public boolean isSearchInModuleContent(@NotNull Module aModule) {
            return true;
          }

          @Override
          public boolean isSearchInLibraries() {
            return false;
          }
        };
    return scope.intersectWith(restrictedByBackwardIds);
  }

  @Override
  public boolean queue(@NotNull Collection<VirtualFile> files, @NotNull Object reason) {
    if (files.isEmpty()) {
      return false;
    }
    boolean queued = false;
    List<VirtualFile> added = new ArrayList<VirtualFile>(files.size());
    for (VirtualFile file : files) {
      boolean wasAdded = queueIfNeeded(file, myProject);
      if (wasAdded) {
        added.add(file);
      }
      queued |= wasAdded;
    }
    if (queued) {
      log("Queued to resolve (from " + reason + "): " + toVfString(added));
      flushLog();
    }
    return queued;
  }

  @Override
  public boolean isUpToDate() {
    return ENABLED && !myDisposed && upToDate;
  }

  @Override
  public int getQueueSize() {
    synchronized (filesToResolve) {
      return filesToResolve.size();
    }
  }

  private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();

  @Override
  public void addListener(@NotNull Disposable parent, @NotNull final Listener listener) {
    myListeners.add(listener);
    Disposer.register(
        parent,
        new Disposable() {
          @Override
          public void dispose() {
            myListeners.remove(listener);
          }
        });
  }

  private static class MyProgress extends ProgressIndicatorBase implements Disposable {
    @Override
    public void dispose() {}
  }
}
  @NotNull
  @Override
  public Runnable processFile(final PsiFile file) {
    VirtualFile vFile = file.getVirtualFile();
    if (vFile instanceof VirtualFileWindow) vFile = ((VirtualFileWindow) vFile).getDelegate();
    final Project project = file.getProject();
    if (vFile == null
        || !ProjectRootManager.getInstance(project).getFileIndex().isInSourceContent(vFile)) {
      return EmptyRunnable.INSTANCE;
    }
    final List<Pair<String, Boolean>> names = new ArrayList<Pair<String, Boolean>>();
    final Set<String> demandedForNested = new HashSet<>();
    collectNamesToImport(names, demandedForNested, (XmlFile) file);
    Collections.sort(names, (o1, o2) -> StringUtil.compare(o1.first, o2.first, true));
    final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project);
    final List<Pair<String, Boolean>> sortedNames =
        ImportHelper.sortItemsAccordingToSettings(names, settings);
    final HashSet<String> onDemand = new HashSet<String>();
    ImportHelper.collectOnDemandImports(sortedNames, onDemand, settings);
    onDemand.addAll(demandedForNested);
    final Set<String> imported = new HashSet<String>();
    final List<String> imports = new ArrayList<String>();
    for (Pair<String, Boolean> pair : sortedNames) {
      final String qName = pair.first;
      final String packageName = StringUtil.getPackageName(qName);
      if (imported.contains(packageName) || imported.contains(qName)) {
        continue;
      }
      if (onDemand.contains(packageName)) {
        imported.add(packageName);
        imports.add("<?import " + packageName + ".*?>");
      } else {
        imported.add(qName);
        imports.add("<?import " + qName + "?>");
      }
    }
    final PsiFileFactory factory = PsiFileFactory.getInstance(file.getProject());

    final XmlFile dummyFile =
        (XmlFile)
            factory.createFileFromText(
                "_Dummy_.fxml", StdFileTypes.XML, StringUtil.join(imports, "\n"));
    final XmlDocument document = dummyFile.getDocument();
    final XmlProlog newImportList = document != null ? document.getProlog() : null;
    if (newImportList == null) return EmptyRunnable.getInstance();
    return () -> {
      final XmlDocument xmlDocument = ((XmlFile) file).getDocument();
      final XmlProlog prolog = xmlDocument != null ? xmlDocument.getProlog() : null;
      if (prolog != null) {
        final Collection<XmlProcessingInstruction> instructions =
            PsiTreeUtil.findChildrenOfType(prolog, XmlProcessingInstruction.class);
        for (final XmlProcessingInstruction instruction : instructions) {
          final ASTNode node = instruction.getNode();
          final ASTNode nameNode = node.findChildByType(XmlTokenType.XML_NAME);
          if (nameNode != null && nameNode.getText().equals("import")) {
            instruction.delete();
          }
        }
        prolog.add(newImportList);
      } else {
        document.addBefore(newImportList, document.getRootTag());
      }
    };
  }
  public void testDelayedWorks() throws InterruptedException {
    final AppScheduledExecutorService service = new AppScheduledExecutorService(getName());
    final List<LogInfo> log = Collections.synchronizedList(new ArrayList<>());
    assertFalse(service.isShutdown());
    assertFalse(service.isTerminated());

    service.invokeAll(
        Collections.nCopies(
            service.getBackendPoolCorePoolSize() + 1,
            Executors.callable(EmptyRunnable.getInstance()))); // pre-start all threads

    int delay = 1000;
    long start = System.currentTimeMillis();
    ScheduledFuture<?> f1 =
        service.schedule(
            () -> {
              log.add(new LogInfo(1));
              TimeoutUtil.sleep(10);
            },
            delay,
            TimeUnit.MILLISECONDS);
    assertFalse(service.isShutdown());
    assertFalse(service.isTerminated());
    ScheduledFuture<?> f2 =
        service.schedule(
            () -> {
              log.add(new LogInfo(2));
              TimeoutUtil.sleep(10);
            },
            delay,
            TimeUnit.MILLISECONDS);
    assertFalse(service.isShutdown());
    assertFalse(service.isTerminated());
    ScheduledFuture<?> f3 =
        service.schedule(
            () -> {
              log.add(new LogInfo(3));
              TimeoutUtil.sleep(10);
            },
            delay,
            TimeUnit.MILLISECONDS);

    assertFalse(service.isShutdown());
    assertFalse(service.isTerminated());
    Future<?> f4 = service.submit((Runnable) () -> log.add(new LogInfo(4)));

    assertFalse(service.isShutdown());
    assertFalse(service.isTerminated());
    assertFalse(f1.isDone());
    assertFalse(f2.isDone());
    assertFalse(f3.isDone());

    TimeoutUtil.sleep(delay / 2);
    long elapsed = System.currentTimeMillis() - start; // can be > delay/2 on overloaded agent
    assertEquals(String.valueOf(f1.isDone()), elapsed > delay, f1.isDone());
    assertEquals(String.valueOf(f2.isDone()), elapsed > delay, f2.isDone());
    assertEquals(String.valueOf(f3.isDone()), elapsed > delay, f3.isDone());
    assertTrue(f4.isDone());

    TimeoutUtil.sleep(delay / 2 + 500);
    assertTrue(f1.isDone());
    assertTrue(f2.isDone());
    assertTrue(f3.isDone());
    assertTrue(f4.isDone());

    assertEquals(4, log.size());
    assertEquals(4, log.get(0).runnable);
    List<Thread> threads =
        Arrays.asList(log.get(1).currentThread, log.get(2).currentThread, log.get(3).currentThread);
    assertEquals(
        threads.toString(), 3, new HashSet<>(threads).size()); // must be executed in parallel

    service.shutdownAppScheduledExecutorService();
    assertTrue(service.awaitTermination(10, TimeUnit.SECONDS));
  }
示例#10
0
  public NewLanguageSettings(String projectPath) {
    super(new GridLayoutManager(5, 1, new Insets(0, 5, 5, 5), -1, -1));
    myProjectPath = projectPath;

    this.add(new JLabel("Language name:"), Util.getGridConstraints(0));
    myLanguageName = new JTextField();
    myLanguageName.setName("Name");
    myLanguageName
        .getDocument()
        .addDocumentListener(
            new DocumentAdapter() {
              protected void textChanged(DocumentEvent p0) {
                if ((myProjectPath == null || myProjectPath.length() == 0)) {
                  return;
                }
                String path = myProjectPath + File.separator + "languages" + File.separator;
                final String langName = getLanguageName();
                if (!(langName.equals(getLanguageLocation()))) {
                  path += langName;
                }
                if (!(myLangLocationChangedByUser)) {
                  setLanguageLocation(path);
                }
                fireChanged();
              }
            });
    this.add(myLanguageName, Util.getGridConstraints(1));

    myLanguageLocation = new JTextField();
    myLanguageLocation.setName("Path");
    myLanguageLocation
        .getDocument()
        .addDocumentListener(
            new DocumentAdapter() {
              protected void textChanged(DocumentEvent p0) {
                if (myLangLocationDocListenerEnabled) {
                  myLangLocationChangedByUser = true;
                }
              }
            });
    final FileChooserDescriptor descriptor =
        FileChooserDescriptorFactory.createSingleFolderDescriptor();
    InsertPathAction.addTo(myLanguageLocation, descriptor);
    BrowseFilesListener listener =
        new BrowseFilesListener(
            myLanguageLocation, "Choose Language Location Folder", "", descriptor);
    FieldPanel fieldPanel =
        new FieldPanel(
            myLanguageLocation, "Language location:", null, listener, EmptyRunnable.getInstance());
    FileChooserFactory.getInstance()
        .installFileCompletion(fieldPanel.getTextField(), descriptor, false, null);
    this.add(fieldPanel, Util.getGridConstraints(2));

    myRuntimeSolution = new JCheckBox("Create Runtime Solution");
    this.add(myRuntimeSolution, Util.getGridConstraints(3));

    mySandboxSolution = new JCheckBox("Create Sandbox Solution");
    this.add(mySandboxSolution, Util.getGridConstraints(4));

    this.setPreferredSize(new Dimension(400, 100));

    reset();
  }