Package com.squareup.ideaplugin.otto

Source Code of com.squareup.ideaplugin.otto.OttoProjectHandler

package com.squareup.ideaplugin.otto;

import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.find.FindManager;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesManager;
import com.intellij.find.findUsages.JavaClassFindUsagesOptions;
import com.intellij.find.impl.FindManagerImpl;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierList;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.intellij.psi.PsiTreeChangeAdapter;
import com.intellij.psi.PsiTreeChangeEvent;
import com.intellij.psi.PsiTypeElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageInfo2UsageAdapter;
import com.intellij.usages.UsageInfoToUsageConverter;
import com.intellij.util.Processor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.jetbrains.annotations.NotNull;

public class OttoProjectHandler extends AbstractProjectComponent {

  private static final Key<OttoProjectHandler> KEY = Key.create(OttoProjectHandler.class.getName());
  public static final Logger LOGGER = Logger.getInstance(OttoProjectHandler.class);

  private final FindUsagesManager findUsagesManager;
  private final PsiManager psiManager;
  private final Map<VirtualFile, Set<String>> fileToEventClasses = new HashMap<VirtualFile, Set<String>>();
  private final Set<VirtualFile> filesToScan = new HashSet<VirtualFile>();
  private final Set<String> allEventClasses = new HashSet<String>();
  private final AtomicInteger startupScanAttemptsLeft = new AtomicInteger(5);

  public PsiTreeChangeAdapter listener;

  protected OttoProjectHandler(Project project, PsiManager psiManager) {
    super(project);
    this.findUsagesManager =
        ((FindManagerImpl) FindManager.getInstance(project)).getFindUsagesManager();
    this.psiManager = psiManager;
    project.putUserData(KEY, this);
    //System.out.println("OttoProjectHandler initialized");
  }

  public static OttoProjectHandler get(Project project) {
    return project.getUserData(KEY);
  }

  public boolean isEventClass(String className) {
    maybeRecomputeEventClasses();

    synchronized (allEventClasses) {
      return allEventClasses.contains(className);
    }
  }

  @Override public void projectOpened() {
    final AtomicBoolean hasRun = new AtomicBoolean(false);

    ApplicationManager.getApplication().invokeLater(
        new Runnable() {
          @Override public void run() {
            if (myProject.isInitialized()) {
              hasRun.set(true);
              findEventsViaMethodsAnnotatedSubscribe();

              psiManager.addPsiTreeChangeListener(listener = new MyPsiTreeChangeAdapter());
            }
          }
        }, new Condition() {
          @Override public boolean value(Object o) {
            return hasRun.get();
          }
        }
    );
  }

  private void findEventsViaMethodsAnnotatedSubscribe() {
    GlobalSearchScope projectScope = ProjectScope.getProjectScope(myProject);
    for (SubscriberMetadata subscriberMetadata : SubscriberMetadata.getAllSubscribers()) {
      performSearch(projectScope, subscriberMetadata.getSubscriberAnnotationClassName());
    }
  }

  private void performSearch(final SearchScope searchScope, final String subscribeClassName) {
    JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(myProject);
    PsiClass subscribePsiClass = javaPsiFacade.findClass(subscribeClassName,
        GlobalSearchScope.allScope(myProject));
    if (subscribePsiClass == null) {
      // the guava or otto library isn't available in this project so do nothing
      return;
    }

    FindUsagesHandler handler = findUsagesManager.getNewFindUsagesHandler(subscribePsiClass, false);
    if (handler != null) {
      UsageInfoToUsageConverter.TargetElementsDescriptor descriptor =
          new UsageInfoToUsageConverter.TargetElementsDescriptor(handler.getPrimaryElements(),
              handler.getSecondaryElements());

      final long startTime = System.currentTimeMillis();
      Processor<Usage> processor = new Processor<Usage>() {
        @Override public boolean process(Usage usage) {
          if (usage instanceof UsageInfo2UsageAdapter) {
            PsiElement element = ((UsageInfo2UsageAdapter) usage).getElement();
            if ((element = element.getContext()) instanceof PsiAnnotation) {
              if ((element = element.getContext()) instanceof PsiModifierList) {
                if ((element = element.getContext()) instanceof PsiMethod) {
                  if (PsiConsultantImpl.hasAnnotation((PsiMethod) element, subscribeClassName)) {
                    maybeAddSubscriberMethod((PsiMethod) element);
                  }
                }
              }
            }
          }
          return true;
        }
      };
      JavaClassFindUsagesOptions options = new JavaClassFindUsagesOptions(myProject);
      options.searchScope = searchScope;
      FindUsagesManager.startProcessUsages(handler, handler.getPrimaryElements(), handler.getSecondaryElements(), processor, options, new Runnable() {
        @Override public void run() {
          int eventClassCount = optimizeEventClassIndex();
          if (eventClassCount > 0) {
            scheduleRefreshOfEventFiles();
          } else {
            maybeScheduleAnotherSearch();
          }

          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(String.format("Searched for @Subscribe in %s in %dms",
                searchScope, System.currentTimeMillis() - startTime));
          }
        }
      });
    }
  }

  private void maybeScheduleAnotherSearch() {
    if (startupScanAttemptsLeft.decrementAndGet() > 0) {
      ApplicationManager.getApplication().invokeLater(new Runnable() {
        @Override
        public void run() {
          findEventsViaMethodsAnnotatedSubscribe();
        }
      });
    }
  }

  private void maybeRecomputeEventClasses() {
    List<VirtualFile> myFilesToScan;
    synchronized (filesToScan) {
      if (filesToScan.isEmpty()) return;

      myFilesToScan = new ArrayList<VirtualFile>(filesToScan);
      filesToScan.clear();
    }

    for (VirtualFile virtualFile : myFilesToScan) {
      synchronized (fileToEventClasses) {
        getEventClasses(virtualFile).clear();
      }
      PsiFile psiFile = PsiManager.getInstance(myProject).findFile(virtualFile);
      if (psiFile == null) throw new IllegalStateException("huh? " + virtualFile);
      if (psiFile.getFileType() instanceof JavaFileType) {

        final long startTime = System.currentTimeMillis();
        psiFile.accept(new PsiRecursiveElementVisitor() {
          @Override public void visitElement(PsiElement element) {
            if (element instanceof PsiMethod
                && SubscriberMetadata.isAnnotatedWithSubscriber((PsiMethod) element)) {
              maybeAddSubscriberMethod((PsiMethod) element);
            } else {
              super.visitElement(element);
            }
          }
        });
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug(String.format("Searched for @Subscribe in %s in %dms",
              virtualFile, System.currentTimeMillis() - startTime));
        }
      }
    }

    optimizeEventClassIndex();
  }

  private int optimizeEventClassIndex() {
    synchronized (allEventClasses) {
      allEventClasses.clear();

      synchronized (fileToEventClasses) {
        for (Set<String> strings : fileToEventClasses.values()) {
          allEventClasses.addAll(strings);
        }
      }
      return allEventClasses.size();
    }
  }

  private void scheduleRefreshOfEventFiles() {
    ApplicationManager.getApplication().invokeLater(new Runnable() {
      @Override
      public void run() {
        Set<String> eventClasses;
        synchronized (allEventClasses) {
          eventClasses = new HashSet<String>(allEventClasses);
        }
        JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(myProject);
        DaemonCodeAnalyzer codeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject);
        for (String eventClass : eventClasses) {
          PsiClass eventPsiClass = javaPsiFacade.findClass(eventClass,
                GlobalSearchScope.allScope(myProject));
          if (eventPsiClass == null) continue;
          PsiFile psiFile = eventPsiClass.getContainingFile();
          if (psiFile == null) continue;
          codeAnalyzer.restart(psiFile);
        }
      }
    });
  }

  private void maybeAddSubscriberMethod(PsiMethod element) {
    PsiTypeElement methodParameter = OttoLineMarkerProvider.getMethodParameter(element);
    if (methodParameter != null) {
      String canonicalText = methodParameter.getType().getCanonicalText();
      PsiFile containingFile = methodParameter.getContainingFile();
      if (containingFile != null) {
        VirtualFile virtualFile = containingFile.getVirtualFile();
        if (virtualFile != null) {
          synchronized (fileToEventClasses) {
            Set<String> eventClasses = getEventClasses(virtualFile);
            eventClasses.add(canonicalText);
          }
        }
      }
    }
  }

  private Set<String> getEventClasses(VirtualFile virtualFile) {
    Set<String> eventClasses = fileToEventClasses.get(virtualFile);
    if (eventClasses == null) {
      eventClasses = new HashSet<String>();
      fileToEventClasses.put(virtualFile, eventClasses);
    }
    return eventClasses;
  }

  @Override public void projectClosed() {
    if (listener != null) psiManager.removePsiTreeChangeListener(listener);
  }

  private class MyPsiTreeChangeAdapter extends PsiTreeChangeAdapter {
    @Override public void childAdded(@NotNull PsiTreeChangeEvent event) {
      maybeInvalidate(event);
    }

    @Override public void childMoved(@NotNull PsiTreeChangeEvent event) {
      maybeInvalidate(event);
    }

    @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) {
      maybeInvalidate(event);
    }

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

    @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) {
      maybeInvalidate(event);
    }

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

  private void maybeInvalidate(PsiTreeChangeEvent event) {
    PsiFile file = event.getFile();
    if (file == null) {
      return;
    }

    VirtualFile virtualFile = file.getVirtualFile();
    synchronized (filesToScan) {
      filesToScan.add(virtualFile);
    }
  }
}
TOP

Related Classes of com.squareup.ideaplugin.otto.OttoProjectHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.