public static void showNow() {
   if (ourInstance == null) {
     IdeFrame frame =
         EP.getExtensions().length == 0 ? new WelcomeFrame() : EP.getExtensions()[0].createFrame();
     if (SystemInfo.isLinux) {
       ApplicationMenu.tryInstall(((JFrame) frame));
     }
     ((JFrame) frame).setVisible(true);
     ourInstance = frame;
   }
 }
  public static void showDialogAndAddLibraryToDependencies(
      final Library library, final Project project, boolean allowEmptySelection) {
    for (ProjectStructureValidator validator : EP_NAME.getExtensions()) {
      if (validator.addLibraryToDependencies(library, project, allowEmptySelection)) {
        return;
      }
    }

    final ModuleStructureConfigurable moduleStructureConfigurable =
        ModuleStructureConfigurable.getInstance(project);
    final List<Module> modules =
        LibraryEditingUtil.getSuitableModules(
            moduleStructureConfigurable, ((LibraryEx) library).getKind(), library);
    if (modules.isEmpty()) return;
    final ChooseModulesDialog dlg =
        new ChooseModulesDialog(
            moduleStructureConfigurable.getProject(),
            modules,
            ProjectBundle.message("choose.modules.dialog.title"),
            ProjectBundle.message("choose.modules.dialog.description", library.getName()));
    if (dlg.showAndGet()) {
      final List<Module> chosenModules = dlg.getChosenElements();
      for (Module module : chosenModules) {
        moduleStructureConfigurable.addLibraryOrderEntry(module, library);
      }
    }
  }
/**
 * Base class and extension point for custom folding providers.
 *
 * @author Rustam Vishnyakov
 */
public abstract class CustomFoldingProvider {
  public static final ExtensionPointName<CustomFoldingProvider> EP_NAME =
      ExtensionPointName.create("com.intellij.customFoldingProvider");

  public static CustomFoldingProvider[] getAllProviders() {
    return Extensions.getExtensions(EP_NAME);
  }

  public abstract boolean isCustomRegionStart(String elementText);

  public abstract boolean isCustomRegionEnd(String elementText);

  public abstract String getPlaceholderText(String elementText);

  /** @return A description string shown in "Surround With" action. */
  public abstract String getDescription();

  public abstract String getStartString();

  public abstract String getEndString();

  public boolean isCollapsedByDefault(String text) {
    return false;
  }
}
/** @author Max Medvedev */
public abstract class CustomAnnotationChecker {
  public static final ExtensionPointName<CustomAnnotationChecker> EP_NAME =
      ExtensionPointName.create("org.intellij.groovy.customAnnotationChecker");

  public boolean checkArgumentList(
      @NotNull AnnotationHolder holder, @NotNull GrAnnotation annotation) {
    return false;
  }

  public boolean checkApplicability(
      @NotNull AnnotationHolder holder, @NotNull GrAnnotation annotation) {
    return false;
  }

  @Nullable
  public static String isAnnotationApplicable(
      @NotNull GrAnnotation annotation, final PsiElement parent) {
    PsiElement owner = parent.getParent();

    final PsiElement ownerToUse = parent instanceof PsiModifierList ? owner : parent;

    String[] elementTypeFields = GrAnnotationImpl.getApplicableElementTypeFields(ownerToUse);
    if (elementTypeFields != null
        && !GrAnnotationImpl.isAnnotationApplicableTo(annotation, false, elementTypeFields)) {
      final String annotationTargetText =
          JavaErrorMessages.message("annotation.target." + elementTypeFields[0]);
      GrCodeReferenceElement ref = annotation.getClassReference();
      return JavaErrorMessages.message(
          "annotation.not.applicable", ref.getText(), annotationTargetText);
    }

    return null;
  }
}
/** @author egor */
public abstract class JvmSteppingCommandProvider {
  public static final ExtensionPointName<JvmSteppingCommandProvider> EP_NAME =
      ExtensionPointName.create("com.intellij.debugger.jvmSteppingCommandProvider");

  /** @return null if can not handle */
  public DebugProcessImpl.ResumeCommand getStepIntoCommand(
      SuspendContextImpl suspendContext,
      boolean ignoreFilters,
      final MethodFilter smartStepFilter,
      int stepSize) {
    return null;
  }

  /** @return null if can not handle */
  public DebugProcessImpl.ResumeCommand getStepOutCommand(
      SuspendContextImpl suspendContext, int stepSize) {
    return null;
  }

  /** @return null if can not handle */
  public DebugProcessImpl.ResumeCommand getStepOverCommand(
      SuspendContextImpl suspendContext, boolean ignoreBreakpoints, int stepSize) {
    return null;
  }
}
示例#6
0
/**
 * Factory to instantiate {@link jetbrains.mps.openapi.editor.Editor editor} for a node. It's {@link
 * EditorOpenHandler} done right - no IOperationContext, and it doesn't open editors, it
 * instantiates (hence factory) component, and actual code to open an editor is platform's
 * responsibility.
 *
 * <p>Instances of the factory are registered with {@link #EXT_POINT} extension point, within
 * Project area (thus can access {@link com.intellij.openapi.project.Project} or {@link
 * jetbrains.mps.project.MPSProject} as constructor argument.
 *
 * <p>To avoid code breakage in the future MPS versions, do not implement this interface directly
 * (outside of MPS), rather subclass {@link NodeEditorFactoryBase}.
 *
 * @author Artem Tikhomirov
 * @since 3.4
 */
public interface NodeEditorFactory {
  ExtensionPointName<NodeEditorFactory> EXT_POINT =
      ExtensionPointName.create("jetbrains.mps.NodeEditorFactory");

  boolean canCreate(@NotNull Context context);

  @Nullable
  Editor create(@NotNull Context context);

  /**
   * This is dubious method with unclear contract. Used to carry "todo split into base node getter &
   * TabbedEditorHandler" comment Meanwhile is just a copy of {@link
   * EditorOpenHandler#getBaseNode(IOperationContext, SNode)}, without IOperationContext.
   */
  SNode getBaseNode(@NotNull SNode aspect);

  /**
   * Set of parameters for the factory. At the moment, we don't need/pass anything but node,
   * interface introduced for ease of future extension.
   */
  interface Context {
    @NotNull
    SNode getNode();
  }
}
/**
 * Allows to provide a custom list of keyword arguments for a function that uses **kwargs.
 *
 * @author yole
 */
public interface PyKeywordArgumentProvider {
  ExtensionPointName<PyKeywordArgumentProvider> EP_NAME =
      ExtensionPointName.create("Pythonid.keywordArgumentProvider");

  @NotNull
  List<String> getKeywordArguments(PyFunction function, PyCallExpression callExpr);
}
示例#8
0
public interface TreeGenerator {
  ExtensionPointName<TreeGenerator> EP_NAME =
      ExtensionPointName.create("com.intellij.treeGenerator");

  @Nullable
  TreeElement generateTreeFor(PsiElement original, CharTable table, final PsiManager manager);
}
/** @author Eugene.Kudelevsky */
public interface AndroidRefactoringContextProvider {
  ExtensionPointName<AndroidRefactoringContextProvider> EP_NAME =
      ExtensionPointName.create("org.jetbrains.android.refactoringContextProvider");

  @Nullable
  XmlTag getComponentTag(@NotNull DataContext dataContext);
}
/**
 * Provides {@link com.intellij.execution.ExecutionTarget ExecutionTargets} for run configurations.
 */
public abstract class ExecutionTargetProvider {
  public static final ExtensionPointName<ExecutionTargetProvider> EXTENSION_NAME =
      ExtensionPointName.create("com.intellij.executionTargetProvider");

  @NotNull
  public abstract List<ExecutionTarget> getTargets(
      @NotNull Project project, @NotNull RunnerAndConfigurationSettings configuration);
}
/**
 * Named component which provides a configuration user interface.
 *
 * <p>
 *
 * <p>Use {@code com.intellij.projectConfigurable} and {@code com.intellij.applicationConfigurable}
 * extensions to provide items for "Project Settings" and "IDE Settings" groups correspondingly in
 * the "Settings" dialog. There are two ways to declare such extension:
 *
 * <ul>
 *   <li>an extension element with 'instance' attribute <br>
 *       &lt;extensions defaultExtensionNs="com.intellij"&gt;<br>
 *       &nbsp;&nbsp;&lt;projectConfigurable instance="class-name"/&gt;<br>
 *       &lt;/extensions&gt;<br>
 *       where 'class-name' implements {@link Configurable} means that a new instance of the
 *       specified class will be created each time when the dialog is opened.
 *       <p>
 *   <li>an extension with 'provider' attribute<br>
 *       &lt;extensions defaultExtensionNs="com.intellij"&gt;<br>
 *       &nbsp;&nbsp;&lt;projectConfigurable provider="class-name"/&gt;<br>
 *       &lt;/extensions&gt;<br>
 *       where 'class-name' implements {@link ConfigurableProvider} means that method {@link
 *       ConfigurableProvider#createConfigurable()} will be used to create instance each time when
 *       the dialog is opened.
 * </ul>
 *
 * @see SearchableConfigurable
 */
public interface Configurable extends UnnamedConfigurable {

  ExtensionPointName<ConfigurableEP<Configurable>> APPLICATION_CONFIGURABLE =
      ExtensionPointName.create("com.intellij.applicationConfigurable");

  ExtensionPointName<ConfigurableEP<Configurable>> PROJECT_CONFIGURABLE =
      ExtensionPointName.create("com.intellij.projectConfigurable");

  /**
   * Returns the user-visible name of the settings component.
   *
   * @return the visible name of the component.
   */
  @Nls
  String getDisplayName();

  /**
   * Returns the topic in the help file which is shown when help for the configurable is requested.
   *
   * @return the help topic, or null if no help is available.
   */
  @Nullable
  @NonNls
  String getHelpTopic();

  /**
   * @deprecated this marker interface was used to hide a Configurable declared as
   *     applicationConfigurable or projectConfigurable extension from the Settings dialog. However
   *     it makes no sense to register a Configurable as extension if you don't want to see it in
   *     the Settings dialog
   */
  interface Assistant extends Configurable {}

  interface Composite {
    Configurable[] getConfigurables();
  }

  /**
   * Forbids wrapping the content of the configurable in a scroll pane. Required when the
   * configurable contains its own scrollable components.
   */
  interface NoScroll {}

  /** Forbids setting an empty border to the content of the configurable. */
  interface NoMargin {}
}
/** @author Vladislav.Kaznacheev */
public abstract class ProjectImportBuilder<T> extends ProjectBuilder {
  public static final ExtensionPointName<ProjectImportBuilder> EXTENSIONS_POINT_NAME =
      ExtensionPointName.create("com.intellij.projectImportBuilder");

  private boolean myUpdate;
  private String myFileToImport;

  @NotNull
  public abstract String getName();

  public abstract Icon getIcon();

  public abstract List<T> getList();

  public abstract boolean isMarked(final T element);

  public abstract void setList(List<T> list) throws ConfigurationException;

  public abstract void setOpenProjectSettingsAfter(boolean on);

  @Override
  public List<Module> commit(
      @NotNull Project project, ModifiableModuleModel model, ModulesProvider modulesProvider) {
    return commit(project, model, modulesProvider, null);
  }

  @Nullable
  public abstract List<Module> commit(
      Project project,
      ModifiableModuleModel model,
      ModulesProvider modulesProvider,
      ModifiableArtifactModel artifactModel);

  public void setFileToImport(String path) {
    myFileToImport = path;
  }

  public String getFileToImport() {
    return myFileToImport;
  }

  @Nullable
  public static Project getCurrentProject() {
    return CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext());
  }

  protected String getTitle() {
    return IdeBundle.message("project.import.wizard.title", getName());
  }

  public boolean isUpdate() {
    return myUpdate;
  }

  public void setUpdate(final boolean update) {
    myUpdate = update;
  }
}
示例#13
0
 @Nullable
 public static MvcFramework findCommonPluginModuleFramework(Module module) {
   for (MvcFramework framework : EP_NAME.getExtensions()) {
     if (framework.isCommonPluginsModule(module)) {
       return framework;
     }
   }
   return null;
 }
示例#14
0
 @Nullable
 public static MvcFramework getInstanceBySdk(@NotNull Module module) {
   for (final MvcFramework framework : EP_NAME.getExtensions()) {
     if (framework.getSdkRoot(module) != null) {
       return framework;
     }
   }
   return null;
 }
/**
 * IntelliJ external systems integration is built using GoF Bridge pattern, i.e. 'external-system'
 * module defines external system-specific extension (current interface) and an api which is used by
 * all extensions. Most of the codebase is built on top of that api and provides generic actions
 * like 'sync ide project with external project'; 'import library dependencies which are configured
 * at external system but not at the ide' etc.
 *
 * <p>That makes it relatively easy to add a new external system integration.
 *
 * @author Denis Zhdanov
 * @since 4/4/13 4:05 PM
 */
public interface ExternalSystemManager<
        ProjectSettings extends ExternalProjectSettings,
        SettingsListener extends ExternalSystemSettingsListener<ProjectSettings>,
        Settings extends AbstractExternalSystemSettings<ProjectSettings, SettingsListener>,
        LocalSettings extends AbstractExternalSystemLocalSettings,
        ExecutionSettings extends ExternalSystemExecutionSettings>
    extends ParametersEnhancer {

  ExtensionPointName<ExternalSystemManager> EP_NAME =
      ExtensionPointName.create("com.intellij.externalSystemManager");

  /** @return id of the external system represented by the current manager */
  @NotNull
  ProjectSystemId getSystemId();

  /**
   * @return a strategy which can be queried for external system settings to use with the given
   *     project
   */
  @NotNull
  Function<Project, Settings> getSettingsProvider();

  /**
   * @return a strategy which can be queried for external system local settings to use with the
   *     given project
   */
  @NotNull
  Function<Project, LocalSettings> getLocalSettingsProvider();

  /**
   * @return a strategy which can be queried for external system execution settings to use with the
   *     given project
   */
  @NotNull
  Function<Pair<Project, String /*linked project path*/>, ExecutionSettings>
      getExecutionSettingsProvider();

  /**
   * Allows to retrieve information about {@link ExternalSystemProjectResolver project resolver} to
   * use for the target external system.
   *
   * <p><b>Note:</b> we return a class instance instead of resolver object here because there is a
   * possible case that the resolver is used at external (non-ide) process, so, it needs information
   * which is enough for instantiating it there. That implies the requirement that target resolver
   * class is expected to have a no-args constructor
   *
   * @return class of the project resolver to use for the target external system
   */
  @NotNull
  Class<? extends ExternalSystemProjectResolver<ExecutionSettings>> getProjectResolverClass();

  /**
   * @return class of the build manager to use for the target external system
   * @see #getProjectResolverClass()
   */
  Class<? extends ExternalSystemBuildManager<ExecutionSettings>> getBuildManagerClass();
}
 public static List<RenamePsiElementProcessor> allForElement(@NotNull PsiElement element) {
   final List<RenamePsiElementProcessor> result = new ArrayList<>();
   for (RenamePsiElementProcessor processor : EP_NAME.getExtensions()) {
     if (processor.canProcessElement(element)) {
       result.add(processor);
     }
   }
   return result;
 }
 public static void check(
     ProjectStructureElement element, ProjectStructureProblemsHolder problemsHolder) {
   for (ProjectStructureValidator validator : EP_NAME.getExtensions()) {
     if (validator.checkElement(element, problemsHolder)) {
       return;
     }
   }
   element.check(problemsHolder);
 }
示例#18
0
/** @author yole */
public abstract class FindUsagesHandlerFactory {
  public static final ExtensionPointName<FindUsagesHandlerFactory> EP_NAME =
      ExtensionPointName.create("com.intellij.findUsagesHandlerFactory");

  public abstract boolean canFindUsages(PsiElement element);

  @Nullable
  public abstract FindUsagesHandler createFindUsagesHandler(
      PsiElement element, final boolean forHighlightUsages);
}
/** User: anna Date: Jan 30, 2005 */
public abstract class ProductivityFeaturesProvider {
  public static ExtensionPointName<ProductivityFeaturesProvider> EP_NAME =
      ExtensionPointName.create("com.intellij.productivityFeaturesProvider");

  public abstract FeatureDescriptor[] getFeatureDescriptors();

  public abstract GroupDescriptor[] getGroupDescriptors();

  public abstract ApplicabilityFilter[] getApplicabilityFilters();
}
 public static List<ProjectStructureElementUsage> getUsagesInElement(
     final ProjectStructureElement element) {
   for (ProjectStructureValidator validator : EP_NAME.getExtensions()) {
     List<ProjectStructureElementUsage> usages = validator.getUsagesIn(element);
     if (usages != null) {
       return usages;
     }
   }
   return element.getUsagesInElement();
 }
 /**
  * Finds extensions supporting the given <code>schemeClass</code>
  *
  * @param schemeClass The class of the scheme to search extensions for.
  * @return A collection of importers capable of importing schemes of the given class. An empty
  *     collection is returned if there are no matching importers.
  */
 @NotNull
 public static <S extends Scheme> Collection<SchemeImporterEP<S>> getExtensions(
     Class<S> schemeClass) {
   List<SchemeImporterEP<S>> importers = new ArrayList<SchemeImporterEP<S>>();
   for (SchemeImporterEP<?> importerEP : EP_NAME.getExtensions()) {
     if (schemeClass.getName().equals(importerEP.schemeClass)) {
       //noinspection unchecked
       importers.add((SchemeImporterEP<S>) importerEP);
     }
   }
   return importers;
 }
  public static List<MavenImporter> getSuitableImporters(MavenProject p) {
    List<MavenImporter> result = null;
    Set<ModuleType> moduleTypes = null;

    for (MavenImporter importer : EXTENSION_POINT_NAME.getExtensions()) {
      if (importer.isApplicable(p)) {
        if (result == null) {
          result = new ArrayList<MavenImporter>();
          moduleTypes = new THashSet<ModuleType>();
        }

        result.add(importer);
        moduleTypes.add(importer.getModuleType());
      }
    }

    if (result == null) {
      return Collections.emptyList();
    }

    if (moduleTypes.size() <= 1) {
      return result;
    }

    // This code is reached when several importers say that they are applicable but they want to
    // have different module types.
    // Now we select one module type and return only those importers that are ok with it.
    // If possible - return at least one importer that explicitly supports packaging of the given
    // maven project.
    ModuleType moduleType = result.get(0).getModuleType();
    List<String> supportedPackagings = new ArrayList<String>();
    for (MavenImporter importer : result) {
      supportedPackagings.clear();
      importer.getSupportedPackagings(supportedPackagings);
      if (supportedPackagings.contains(p.getPackaging())) {
        moduleType = importer.getModuleType();
        break;
      }
    }

    final ModuleType finalModuleType = moduleType;
    return ContainerUtil.filter(
        result,
        new Condition<MavenImporter>() {
          public boolean value(final MavenImporter importer) {
            return importer.getModuleType() == finalModuleType;
          }
        });
  }
  private static void ensureInit() {
    if (ourClassSpecifiedContributors != null) return;

    MultiMap<String, NonCodeMembersContributor> contributorMap =
        new MultiMap<String, NonCodeMembersContributor>();

    for (final NonCodeMembersContributor contributor : EP_NAME.getExtensions()) {
      contributorMap.putValue(contributor.getParentClassName(), contributor);
    }

    Collection<NonCodeMembersContributor> allTypeContributors = contributorMap.remove(null);
    ourAllTypeContributors =
        allTypeContributors.toArray(new NonCodeMembersContributor[allTypeContributors.size()]);
    ourClassSpecifiedContributors = contributorMap;
  }
 public static <T> void registerExtension(
     final ExtensionsArea area,
     final ExtensionPointName<T> name,
     final T t,
     final Disposable parentDisposable) {
   final ExtensionPoint<T> extensionPoint = area.getExtensionPoint(name.getName());
   extensionPoint.registerExtension(t);
   Disposer.register(
       parentDisposable,
       new Disposable() {
         @Override
         public void dispose() {
           extensionPoint.unregisterExtension(t);
         }
       });
 }
/**
 * Implement this class to provide settings page for debugger. Settings page will be placed under
 * 'Debugger' node in the 'Settings' dialog. An implementation should be registered in plugin.xml:
 *
 * <p>&lt;extensions defaultExtensionNs="com.intellij"&gt;<br>
 * &nbsp;&nbsp;&lt;xdebugger.settings implementation="qualified-class-name"/&gt;<br>
 * &lt;/extensions&gt;
 *
 * @author nik
 */
public abstract class XDebuggerSettings<T> implements PersistentStateComponent<T> {
  public static final ExtensionPointName<XDebuggerSettings> EXTENSION_POINT =
      ExtensionPointName.create("com.intellij.xdebugger.settings");
  private final String myId;

  protected XDebuggerSettings(final @NotNull @NonNls String id) {
    myId = id;
  }

  protected static <S extends XDebuggerSettings<?>> S getInstance(Class<S> aClass) {
    return XDebuggerUtil.getInstance().getDebuggerSettings(aClass);
  }

  public final String getId() {
    return myId;
  }

  @NotNull
  public abstract Configurable createConfigurable();
}
/** @author peter */
public class DomExtenderEP extends AbstractExtensionPointBean {
  private static final Logger LOG =
      Logger.getInstance("#com.intellij.util.xml.reflect.DomExtenderEP");
  public static final ExtensionPointName<DomExtenderEP> EP_NAME =
      ExtensionPointName.create("com.intellij.dom.extender");

  @Attribute("domClass")
  public String domClassName;

  @Attribute("extenderClass")
  public String extenderClassName;

  private Class<?> myDomClass;
  private DomExtender myExtender;

  @Nullable
  public DomExtensionsRegistrarImpl extend(
      @NotNull final Project project,
      @NotNull final DomInvocationHandler handler,
      @Nullable DomExtensionsRegistrarImpl registrar) {
    if (myExtender == null) {
      try {
        myDomClass = findClass(domClassName);
        myExtender = instantiate(extenderClassName, project.getPicoContainer());
      } catch (Exception e) {
        LOG.error(e);
        return null;
      }
    }
    if (myDomClass.isAssignableFrom(handler.getRawType())) {
      if (!myExtender.supportsStubs() && XmlUtil.isStubBuilding()) return registrar;
      if (registrar == null) {
        registrar = new DomExtensionsRegistrarImpl();
      }
      //noinspection unchecked
      myExtender.registerExtensions(handler.getProxy(), registrar);
    }
    return registrar;
  }
}
 private void notifyCheckoutListeners(
     final File directory, final ExtensionPointName<CheckoutListener> epName) {
   final CheckoutListener[] listeners = Extensions.getExtensions(epName);
   for (CheckoutListener listener : listeners) {
     myFoundProject = listener.processCheckedOutDirectory(myProject, directory);
     if (myFoundProject) break;
   }
   if (!myFoundProject && !epName.equals(CheckoutListener.COMPLETED_EP_NAME)) {
     final VcsAwareCheckoutListener[] vcsAwareExtensions =
         Extensions.getExtensions(VcsAwareCheckoutListener.EP_NAME);
     for (VcsAwareCheckoutListener extension : vcsAwareExtensions) {
       myFoundProject = extension.processCheckedOutDirectory(myProject, directory, myVcsKey);
       if (myFoundProject) break;
     }
   }
   final Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
   if (openProjects.length > 0) {
     final Project lastOpenedProject = openProjects[openProjects.length - 1];
     for (CheckoutListener listener : listeners) {
       listener.processOpenedProject(lastOpenedProject);
     }
   }
 }
/** @author Rustam Vishnyakov */
public class SchemeImporterEP<S extends Scheme> extends AbstractExtensionPointBean {

  public static ExtensionPointName<SchemeImporterEP> EP_NAME =
      ExtensionPointName.create("com.intellij.schemeImporter");

  @Attribute("name")
  public String name;

  @Attribute("schemeClass")
  public String schemeClass;

  @Attribute("implementationClass")
  public String implementationClass;

  private final LazyInstance<SchemeImporter<S>> myImporterInstance =
      new LazyInstance<SchemeImporter<S>>() {
        @Override
        protected Class<SchemeImporter<S>> getInstanceClass() throws ClassNotFoundException {
          return findClass(implementationClass);
        }
      };

  public SchemeImporter<S> getInstance() {
    return myImporterInstance.getValue();
  }

  /**
   * Finds extensions supporting the given <code>schemeClass</code>
   *
   * @param schemeClass The class of the scheme to search extensions for.
   * @return A collection of importers capable of importing schemes of the given class. An empty
   *     collection is returned if there are no matching importers.
   */
  @NotNull
  public static <S extends Scheme> Collection<SchemeImporterEP<S>> getExtensions(
      Class<S> schemeClass) {
    List<SchemeImporterEP<S>> importers = new ArrayList<SchemeImporterEP<S>>();
    for (SchemeImporterEP<?> importerEP : EP_NAME.getExtensions()) {
      if (schemeClass.getName().equals(importerEP.schemeClass)) {
        //noinspection unchecked
        importers.add((SchemeImporterEP<S>) importerEP);
      }
    }
    return importers;
  }

  /**
   * Find an importer for the given name and scheme class. It is allowed for importers to have the
   * same name but different scheme classes.
   *
   * @param name The importer name as defined in plug-in configuration.
   * @param schemeClass The scheme class the importer has to support.
   * @return The found importer or null if there are no importers for the given name and scheme
   *     class.
   */
  @Nullable
  public static <S extends Scheme> SchemeImporter<S> getImporter(
      @NotNull String name, Class<S> schemeClass) {
    for (SchemeImporterEP<S> importerEP : getExtensions(schemeClass)) {
      if (name.equals(importerEP.name)) {
        return importerEP.getInstance();
      }
    }
    return null;
  }
}
class PsiChangeHandler extends PsiTreeChangeAdapter implements Disposable {
  private static final ExtensionPointName<ChangeLocalityDetector> EP_NAME =
      ExtensionPointName.create("com.intellij.daemon.changeLocalityDetector");
  private /*NOT STATIC!!!*/ final Key<Boolean> UPDATE_ON_COMMIT_ENGAGED =
      Key.create("UPDATE_ON_COMMIT_ENGAGED");

  private final Project myProject;
  private final Map<Document, List<Pair<PsiElement, Boolean>>> changedElements =
      new WeakHashMap<>();
  private final FileStatusMap myFileStatusMap;

  PsiChangeHandler(
      @NotNull Project project,
      @NotNull final PsiDocumentManagerImpl documentManager,
      @NotNull EditorFactory editorFactory,
      @NotNull MessageBusConnection connection,
      @NotNull FileStatusMap fileStatusMap) {
    myProject = project;
    myFileStatusMap = fileStatusMap;
    editorFactory
        .getEventMulticaster()
        .addDocumentListener(
            new DocumentAdapter() {
              @Override
              public void beforeDocumentChange(DocumentEvent e) {
                final Document document = e.getDocument();
                if (documentManager.getSynchronizer().isInSynchronization(document)) return;
                if (documentManager.getCachedPsiFile(document) == null) return;
                if (document.getUserData(UPDATE_ON_COMMIT_ENGAGED) == null) {
                  document.putUserData(UPDATE_ON_COMMIT_ENGAGED, Boolean.TRUE);
                  PsiDocumentManagerBase.addRunOnCommit(
                      document,
                      () -> {
                        if (document.getUserData(UPDATE_ON_COMMIT_ENGAGED) != null) {
                          updateChangesForDocument(document);
                          document.putUserData(UPDATE_ON_COMMIT_ENGAGED, null);
                        }
                      });
                }
              }
            },
            this);

    connection.subscribe(
        PsiDocumentTransactionListener.TOPIC,
        new PsiDocumentTransactionListener() {
          @Override
          public void transactionStarted(
              @NotNull final Document doc, @NotNull final PsiFile file) {}

          @Override
          public void transactionCompleted(
              @NotNull final Document document, @NotNull final PsiFile file) {
            updateChangesForDocument(document);
            document.putUserData(
                UPDATE_ON_COMMIT_ENGAGED,
                null); // ensure we don't call updateChangesForDocument() twice which can lead to
                       // whole file re-highlight
          }
        });
  }

  @Override
  public void dispose() {}

  private void updateChangesForDocument(@NotNull final Document document) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    if (DaemonListeners.isUnderIgnoredAction(null) || myProject.isDisposed()) return;
    List<Pair<PsiElement, Boolean>> toUpdate = changedElements.get(document);
    if (toUpdate == null) {
      // The document has been changed, but psi hasn't
      // We may still need to rehighlight the file if there were changes inside highlighted ranges.
      if (UpdateHighlightersUtil.isWhitespaceOptimizationAllowed(document)) return;

      // don't create PSI for files in other projects
      PsiElement file = PsiDocumentManager.getInstance(myProject).getCachedPsiFile(document);
      if (file == null) return;

      toUpdate = Collections.singletonList(Pair.create(file, true));
    }
    Application application = ApplicationManager.getApplication();
    final Editor editor = FileEditorManager.getInstance(myProject).getSelectedTextEditor();
    if (editor != null && !application.isUnitTestMode()) {
      application.invokeLater(
          () -> {
            if (!editor.isDisposed()) {
              EditorMarkupModel markupModel = (EditorMarkupModel) editor.getMarkupModel();
              PsiFile file =
                  PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
              TrafficLightRenderer.setOrRefreshErrorStripeRenderer(
                  markupModel, myProject, editor.getDocument(), file);
            }
          },
          ModalityState.stateForComponent(editor.getComponent()),
          myProject.getDisposed());
    }

    for (Pair<PsiElement, Boolean> changedElement : toUpdate) {
      PsiElement element = changedElement.getFirst();
      Boolean whiteSpaceOptimizationAllowed = changedElement.getSecond();
      updateByChange(element, document, whiteSpaceOptimizationAllowed);
    }
    changedElements.remove(document);
  }

  @Override
  public void childAdded(@NotNull PsiTreeChangeEvent event) {
    queueElement(event.getParent(), true, event);
  }

  @Override
  public void childRemoved(@NotNull PsiTreeChangeEvent event) {
    queueElement(event.getParent(), true, event);
  }

  @Override
  public void childReplaced(@NotNull PsiTreeChangeEvent event) {
    queueElement(event.getNewChild(), typesEqual(event.getNewChild(), event.getOldChild()), event);
  }

  private static boolean typesEqual(final PsiElement newChild, final PsiElement oldChild) {
    return newChild != null && oldChild != null && newChild.getClass() == oldChild.getClass();
  }

  @Override
  public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
    if (((PsiTreeChangeEventImpl) event).isGenericChange()) {
      return;
    }
    queueElement(event.getParent(), true, event);
  }

  @Override
  public void beforeChildMovement(@NotNull PsiTreeChangeEvent event) {
    queueElement(event.getOldParent(), true, event);
    queueElement(event.getNewParent(), true, event);
  }

  @Override
  public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) {
    // this event sent always before every PSI change, even not significant one (like after quick
    // typing/backspacing char)
    // mark file dirty just in case
    PsiFile psiFile = event.getFile();
    if (psiFile != null) {
      myFileStatusMap.markFileScopeDirtyDefensively(psiFile, event);
    }
  }

  @Override
  public void propertyChanged(@NotNull PsiTreeChangeEvent event) {
    String propertyName = event.getPropertyName();
    if (!propertyName.equals(PsiTreeChangeEvent.PROP_WRITABLE)) {
      Object oldValue = event.getOldValue();
      if (oldValue instanceof VirtualFile && shouldBeIgnored((VirtualFile) oldValue)) {
        // ignore workspace.xml
        return;
      }
      myFileStatusMap.markAllFilesDirty(event);
    }
  }

  private void queueElement(
      @NotNull PsiElement child,
      final boolean whitespaceOptimizationAllowed,
      @NotNull PsiTreeChangeEvent event) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    PsiFile file = event.getFile();
    if (file == null) file = child.getContainingFile();
    if (file == null) {
      myFileStatusMap.markAllFilesDirty(child);
      return;
    }

    if (!child.isValid()) return;

    PsiDocumentManagerImpl pdm = (PsiDocumentManagerImpl) PsiDocumentManager.getInstance(myProject);
    Document document = pdm.getCachedDocument(file);
    if (document != null) {
      if (pdm.getSynchronizer().getTransaction(document) == null) {
        // content reload, language level change or some other big change
        myFileStatusMap.markAllFilesDirty(child);
        return;
      }

      List<Pair<PsiElement, Boolean>> toUpdate = changedElements.get(document);
      if (toUpdate == null) {
        toUpdate = new SmartList<>();
        changedElements.put(document, toUpdate);
      }
      toUpdate.add(Pair.create(child, whitespaceOptimizationAllowed));
    }
  }

  private void updateByChange(
      @NotNull PsiElement child,
      @NotNull final Document document,
      final boolean whitespaceOptimizationAllowed) {
    ApplicationManager.getApplication().assertIsDispatchThread();
    final PsiFile file;
    try {
      file = child.getContainingFile();
    } catch (PsiInvalidElementAccessException e) {
      myFileStatusMap.markAllFilesDirty(e);
      return;
    }
    if (file == null || file instanceof PsiCompiledElement) {
      myFileStatusMap.markAllFilesDirty(child);
      return;
    }
    VirtualFile virtualFile = file.getVirtualFile();
    if (virtualFile != null && shouldBeIgnored(virtualFile)) {
      // ignore workspace.xml
      return;
    }

    int fileLength = file.getTextLength();
    if (!file.getViewProvider().isPhysical()) {
      myFileStatusMap.markFileScopeDirty(
          document, new TextRange(0, fileLength), fileLength, "Non-physical file update: " + file);
      return;
    }

    PsiElement element =
        whitespaceOptimizationAllowed
                && UpdateHighlightersUtil.isWhitespaceOptimizationAllowed(document)
            ? child
            : child.getParent();
    while (true) {
      if (element == null || element instanceof PsiFile || element instanceof PsiDirectory) {
        myFileStatusMap.markAllFilesDirty("Top element: " + element);
        return;
      }

      final PsiElement scope = getChangeHighlightingScope(element);
      if (scope != null) {
        myFileStatusMap.markFileScopeDirty(
            document, scope.getTextRange(), fileLength, "Scope: " + scope);
        return;
      }

      element = element.getParent();
    }
  }

  private boolean shouldBeIgnored(@NotNull VirtualFile virtualFile) {
    return ProjectCoreUtil.isProjectOrWorkspaceFile(virtualFile)
        || ProjectRootManager.getInstance(myProject).getFileIndex().isExcluded(virtualFile);
  }

  @Nullable
  private static PsiElement getChangeHighlightingScope(@NotNull PsiElement element) {
    DefaultChangeLocalityDetector defaultDetector = null;
    for (ChangeLocalityDetector detector : Extensions.getExtensions(EP_NAME)) {
      if (detector instanceof DefaultChangeLocalityDetector) {
        // run default detector last
        assert defaultDetector == null : defaultDetector;
        defaultDetector = (DefaultChangeLocalityDetector) detector;
        continue;
      }
      final PsiElement scope = detector.getChangeHighlightingDirtyScopeFor(element);
      if (scope != null) return scope;
    }
    assert defaultDetector != null
        : "com.intellij.codeInsight.daemon.impl.DefaultChangeLocalityDetector is unregistered";
    return defaultDetector.getChangeHighlightingDirtyScopeFor(element);
  }
}
示例#30
0
/** @author peter */
@Deprecated // TODO [VISTALL] rewrit it using ModuleExtensions
public abstract class MvcFramework {
  protected static final ExtensionPointName<MvcFramework> EP_NAME =
      ExtensionPointName.create("org.intellij.groovy.mvc.framework");

  private static final Logger LOG =
      Logger.getInstance("#org.jetbrains.plugins.groovy.mvc.MvcFramework");
  public static final Key<Boolean> CREATE_APP_STRUCTURE = Key.create("CREATE_MVC_APP_STRUCTURE");
  public static final Key<Boolean> UPGRADE = Key.create("UPGRADE");
  @NonNls public static final String GROOVY_STARTER_CONF = "/conf/groovy-starter.conf";
  @NonNls public static final String XMX_JVM_PARAMETER = "-Xmx";

  public abstract boolean hasSupport(@NotNull Module module);

  public boolean isAuxModule(@NotNull Module module) {
    return isCommonPluginsModule(module) || isGlobalPluginModule(module);
  }

  public boolean hasFrameworkJar(@NotNull Module module) {
    GlobalSearchScope scope =
        GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, false);
    return JavaPsiFacade.getInstance(module.getProject()).findClass(getSomeFrameworkClass(), scope)
        != null;
  }

  public boolean isCommonPluginsModule(@NotNull Module module) {
    return module.getName().endsWith(getCommonPluginSuffix());
  }

  public List<Module> reorderModulesForMvcView(List<Module> modules) {
    return modules;
  }

  public abstract String getApplicationDirectoryName();

  public void syncSdkAndLibrariesInPluginsModule(@NotNull Module module) {
    final Module pluginsModule = findCommonPluginsModule(module);
    if (pluginsModule != null) {
      MvcModuleStructureUtil.syncAuxModuleSdk(module, pluginsModule, this);
    }
  }

  public boolean isInteractiveConsoleSupported(@NotNull Module module) {
    return false;
  }

  public void runInteractiveConsole(@NotNull Module module) {
    throw new UnsupportedOperationException();
  }

  public abstract void upgradeFramework(@NotNull Module module);

  public void createApplicationIfNeeded(@NotNull final Module module) {
    if (findAppRoot(module) == null && module.getUserData(CREATE_APP_STRUCTURE) == Boolean.TRUE) {
      while (ModuleUtilCore.getSdk(module, JavaModuleExtensionImpl.class) == null) {
        if (Messages.showYesNoDialog(
                module.getProject(),
                "Cannot generate "
                    + getDisplayName()
                    + " project structure because JDK is not specified for module \""
                    + module.getName()
                    + "\".\n"
                    + getDisplayName()
                    + " project will not be created if you don't specify JDK.\nDo you want to specify JDK?",
                "Error",
                Messages.getErrorIcon())
            == 1) {
          return;
        }
        ProjectSettingsService.getInstance(module.getProject())
            .showModuleConfigurationDialog(module.getName(), ClasspathEditor.NAME);
      }
      module.putUserData(CREATE_APP_STRUCTURE, null);
      final GeneralCommandLine commandLine = getCreationCommandLine(module);
      if (commandLine == null) return;

      MvcConsole.executeProcess(
          module,
          commandLine,
          new Runnable() {
            public void run() {
              VirtualFile root = findAppRoot(module);
              if (root == null) return;

              PsiDirectory psiDir = PsiManager.getInstance(module.getProject()).findDirectory(root);
              IdeView ide =
                  LangDataKeys.IDE_VIEW.getData(DataManager.getInstance().getDataContext());
              if (ide != null) ide.selectElement(psiDir);

              // also here comes fileCreated(application.properties) which manages roots and run
              // configuration
            }
          },
          true);
    }
  }

  @Nullable
  protected GeneralCommandLine getCreationCommandLine(Module module) {
    String message =
        "Create default "
            + getDisplayName()
            + " directory structure in module '"
            + module.getName()
            + "'?";
    final int result =
        Messages.showDialog(
            module.getProject(),
            message,
            "Create " + getDisplayName() + " application",
            new String[] {"Run 'create-&app'", "Run 'create-&plugin'", "&Cancel"},
            0,
            getIcon());
    if (result < 0 || result > 1) {
      return null;
    }

    return createCommandAndShowErrors(
        null, module, true, new MvcCommand(result == 0 ? "create-app" : "create-plugin"));
  }

  public abstract void updateProjectStructure(@NotNull final Module module);

  public abstract void ensureRunConfigurationExists(@NotNull Module module);

  @Nullable
  public VirtualFile findAppRoot(@Nullable Module module) {
    if (module == null) return null;

    String appDirName = getApplicationDirectoryName();

    for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) {
      if (root.findChild(appDirName) != null) return root;
    }

    return null;
  }

  @Nullable
  public VirtualFile findAppRoot(@Nullable PsiElement element) {
    VirtualFile appDirectory = findAppDirectory(element);
    return appDirectory == null ? null : appDirectory.getParent();
  }

  @Nullable
  public VirtualFile findAppDirectory(@Nullable Module module) {
    if (module == null) return null;

    String appDirName = getApplicationDirectoryName();

    for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) {
      VirtualFile res = root.findChild(appDirName);
      if (res != null) return res;
    }

    return null;
  }

  @Nullable
  public VirtualFile findAppDirectory(@Nullable PsiElement element) {
    if (element == null) return null;

    PsiFile containingFile = element.getContainingFile().getOriginalFile();
    VirtualFile file = containingFile.getVirtualFile();
    if (file == null) return null;

    ProjectFileIndex index =
        ProjectRootManager.getInstance(containingFile.getProject()).getFileIndex();

    VirtualFile root = index.getContentRootForFile(file);
    if (root == null) return null;

    return root.findChild(getApplicationDirectoryName());
  }

  @Nullable
  public abstract VirtualFile getSdkRoot(@Nullable Module module);

  public abstract String getUserLibraryName();

  protected List<File> getImplicitClasspathRoots(@NotNull Module module) {
    final List<File> toExclude = new ArrayList<File>();

    VirtualFile sdkRoot = getSdkRoot(module);
    if (sdkRoot != null) toExclude.add(VfsUtil.virtualToIoFile(sdkRoot));

    ContainerUtil.addIfNotNull(getCommonPluginsDir(module), toExclude);
    final VirtualFile appRoot = findAppRoot(module);
    if (appRoot != null) {
      VirtualFile pluginDir = appRoot.findChild(MvcModuleStructureUtil.PLUGINS_DIRECTORY);
      if (pluginDir != null) toExclude.add(VfsUtil.virtualToIoFile(pluginDir));

      VirtualFile libDir = appRoot.findChild("lib");
      if (libDir != null) toExclude.add(VfsUtil.virtualToIoFile(libDir));
    }

    final Library library = MvcModuleStructureUtil.findUserLibrary(module, getUserLibraryName());
    if (library != null) {
      for (VirtualFile file : library.getFiles(OrderRootType.CLASSES)) {
        toExclude.add(VfsUtil.virtualToIoFile(PathUtil.getLocalFile(file)));
      }
    }
    return toExclude;
  }

  private PathsList removeFrameworkStuff(Module module, List<VirtualFile> rootFiles) {
    final List<File> toExclude = getImplicitClasspathRoots(module);
    if (LOG.isDebugEnabled()) {
      LOG.debug("Before removing framework stuff: " + rootFiles);
      LOG.debug("Implicit roots:" + toExclude);
    }

    PathsList scriptClassPath = new PathsList();
    eachRoot:
    for (VirtualFile file : rootFiles) {
      for (final File excluded : toExclude) {
        if (VfsUtil.isAncestor(excluded, VfsUtil.virtualToIoFile(file), false)) {
          continue eachRoot;
        }
      }
      scriptClassPath.add(file);
    }
    return scriptClassPath;
  }

  public PathsList getApplicationClassPath(Module module) {
    final List<VirtualFile> classPath =
        OrderEnumerator.orderEntries(module)
            .recursively()
            .withoutSdk()
            .getPathsList()
            .getVirtualFiles();

    retainOnlyJarsAndDirectories(classPath);

    removeModuleOutput(module, classPath);

    final Module pluginsModule = findCommonPluginsModule(module);
    if (pluginsModule != null) {
      removeModuleOutput(pluginsModule, classPath);
    }

    return removeFrameworkStuff(module, classPath);
  }

  public abstract boolean updatesWholeProject();

  private static void retainOnlyJarsAndDirectories(List<VirtualFile> woSdk) {
    for (Iterator<VirtualFile> iterator = woSdk.iterator(); iterator.hasNext(); ) {
      VirtualFile file = iterator.next();
      final VirtualFile local = ArchiveVfsUtil.getVirtualFileForJar(file);
      final boolean dir = file.isDirectory();
      final String name = file.getName();
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            "Considering: "
                + file.getPath()
                + "; local="
                + local
                + "; dir="
                + dir
                + "; name="
                + name);
      }
      if (dir || local != null) {
        continue;
      }
      if (name.endsWith(".jar")) {
        continue;
      }
      LOG.debug("Removing");
      iterator.remove();
    }
  }

  private static void removeModuleOutput(Module module, List<VirtualFile> from) {
    CompilerPathsManager compilerPathsManager =
        CompilerPathsManager.getInstance(module.getProject());
    for (ContentFolderType contentFolderType : ContentFolderType.ALL_SOURCE_ROOTS) {
      from.remove(compilerPathsManager.getCompilerOutput(module, contentFolderType));
    }
  }

  public abstract JavaParameters createJavaParameters(
      @NotNull Module module,
      boolean forCreation,
      boolean forTests,
      boolean classpathFromDependencies,
      @Nullable String jvmParams,
      @NotNull MvcCommand command)
      throws ExecutionException;

  protected static void ensureRunConfigurationExists(
      Module module, ConfigurationType configurationType, String name) {
    final RunManagerEx runManager = RunManagerEx.getInstanceEx(module.getProject());
    for (final RunConfiguration runConfiguration :
        runManager.getConfigurations(configurationType)) {
      if (runConfiguration instanceof MvcRunConfiguration
          && ((MvcRunConfiguration) runConfiguration).getModule() == module) {
        return;
      }
    }

    final ConfigurationFactory factory = configurationType.getConfigurationFactories()[0];
    final RunnerAndConfigurationSettings runSettings =
        runManager.createRunConfiguration(name, factory);
    final MvcRunConfiguration configuration = (MvcRunConfiguration) runSettings.getConfiguration();
    configuration.setModule(module);
    runManager.addConfiguration(runSettings, false);
    runManager.setActiveConfiguration(runSettings);

    RunManagerEx.disableTasks(
        module.getProject(),
        configuration,
        CompileStepBeforeRun.ID,
        CompileStepBeforeRunNoErrorCheck.ID);
  }

  public abstract String getFrameworkName();

  public String getDisplayName() {
    return getFrameworkName();
  }

  public abstract Icon getIcon(); // 16*16

  public abstract Icon getToolWindowIcon(); // 13*13

  public abstract String getSdkHomePropertyName();

  @Nullable
  public GeneralCommandLine createCommandAndShowErrors(
      @NotNull Module module, @NotNull String command, String... args) {
    return createCommandAndShowErrors(null, module, new MvcCommand(command, args));
  }

  @Nullable
  public GeneralCommandLine createCommandAndShowErrors(
      @NotNull Module module, @NotNull MvcCommand command) {
    return createCommandAndShowErrors(null, module, command);
  }

  @Nullable
  public GeneralCommandLine createCommandAndShowErrors(
      @Nullable String vmOptions, @NotNull Module module, @NotNull MvcCommand command) {
    return createCommandAndShowErrors(vmOptions, module, false, command);
  }

  @Nullable
  public GeneralCommandLine createCommandAndShowErrors(
      @Nullable String vmOptions,
      @NotNull Module module,
      final boolean forCreation,
      @NotNull MvcCommand command) {
    try {
      return createCommand(module, vmOptions, forCreation, command);
    } catch (ExecutionException e) {
      Messages.showErrorDialog(e.getMessage(), "Failed to run grails command: " + command);
      return null;
    }
  }

  @NotNull
  public GeneralCommandLine createCommand(
      @NotNull Module module,
      @Nullable String jvmParams,
      boolean forCreation,
      @NotNull MvcCommand command)
      throws ExecutionException {
    final JavaParameters params =
        createJavaParameters(module, forCreation, false, true, jvmParams, command);
    addJavaHome(params, module);

    final GeneralCommandLine commandLine = createCommandLine(params);

    final VirtualFile griffonHome = getSdkRoot(module);
    if (griffonHome != null) {
      commandLine
          .getEnvironment()
          .put(getSdkHomePropertyName(), FileUtil.toSystemDependentName(griffonHome.getPath()));
    }

    final VirtualFile root = findAppRoot(module);
    final File ioRoot =
        root != null ? VfsUtilCore.virtualToIoFile(root) : new File(module.getModuleDirPath());
    commandLine.setWorkDirectory(forCreation ? ioRoot.getParentFile() : ioRoot);

    return commandLine;
  }

  public static void addJavaHome(@NotNull JavaParameters params, @NotNull Module module) {
    final Sdk sdk = ModuleUtilCore.getSdk(module, JavaModuleExtensionImpl.class);
    if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) {
      String path = StringUtil.trimEnd(sdk.getHomePath(), File.separator);
      if (StringUtil.isNotEmpty(path)) {
        Map<String, String> env = params.getEnv();
        if (env == null) {
          env = new HashMap<String, String>();
          params.setEnv(env);
        }

        env.put("JAVA_HOME", FileUtil.toSystemDependentName(path));
      }
    }
  }

  public static GeneralCommandLine createCommandLine(@NotNull JavaParameters params)
      throws CantRunException {
    return CommandLineBuilder.createFromJavaParameters(params);
  }

  private void extractPlugins(
      Project project, @Nullable VirtualFile pluginRoot, Map<String, VirtualFile> res) {
    if (pluginRoot != null) {
      VirtualFile[] children = pluginRoot.getChildren();
      if (children != null) {
        for (VirtualFile child : children) {
          String pluginName = getInstalledPluginNameByPath(project, child);
          if (pluginName != null) {
            res.put(pluginName, child);
          }
        }
      }
    }
  }

  public Collection<VirtualFile> getAllPluginRoots(@NotNull Module module, boolean refresh) {
    return getCommonPluginRoots(module, refresh);
  }

  public void collectCommonPluginRoots(
      Map<String, VirtualFile> result, @NotNull Module module, boolean refresh) {
    if (isCommonPluginsModule(module)) {
      for (VirtualFile root : ModuleRootManager.getInstance(module).getContentRoots()) {
        String pluginName = getInstalledPluginNameByPath(module.getProject(), root);
        if (pluginName != null) {
          result.put(pluginName, root);
        }
      }
    } else {
      VirtualFile root = findAppRoot(module);
      if (root == null) return;

      extractPlugins(
          module.getProject(), root.findChild(MvcModuleStructureUtil.PLUGINS_DIRECTORY), result);
      extractPlugins(
          module.getProject(),
          MvcModuleStructureUtil.findFile(getCommonPluginsDir(module), refresh),
          result);
      extractPlugins(
          module.getProject(),
          MvcModuleStructureUtil.findFile(getGlobalPluginsDir(module), refresh),
          result);
    }
  }

  public Collection<VirtualFile> getCommonPluginRoots(@NotNull Module module, boolean refresh) {
    Map<String, VirtualFile> result = new HashMap<String, VirtualFile>();
    collectCommonPluginRoots(result, module, refresh);
    return result.values();
  }

  @Nullable
  public Module findCommonPluginsModule(@NotNull Module module) {
    return ModuleManager.getInstance(module.getProject())
        .findModuleByName(getCommonPluginsModuleName(module));
  }

  public boolean isGlobalPluginModule(@NotNull Module module) {
    return module.getName().startsWith(getGlobalPluginsModuleName());
  }

  @Nullable
  public File getSdkWorkDir(@NotNull Module module) {
    return getDefaultSdkWorkDir(module);
  }

  @Nullable
  public abstract File getDefaultSdkWorkDir(@NotNull Module module);

  @Nullable
  public File getGlobalPluginsDir(@NotNull Module module) {
    final File sdkWorkDir = getSdkWorkDir(module);
    return sdkWorkDir == null ? null : new File(sdkWorkDir, "global-plugins");
  }

  @Nullable
  public File getCommonPluginsDir(@NotNull Module module) {
    final File grailsWorkDir = getSdkWorkDir(module);
    if (grailsWorkDir == null) return null;

    final String applicationName = getApplicationName(module);
    if (applicationName == null) return null;

    return new File(grailsWorkDir, "projects/" + applicationName + "/plugins");
  }

  public String getApplicationName(Module module) {
    final VirtualFile root = findAppRoot(module);
    if (root == null) return null;
    return root.getName();
  }

  protected abstract String getCommonPluginSuffix();

  public abstract String getGlobalPluginsModuleName();

  public String getCommonPluginsModuleName(Module module) {
    return module.getName() + getCommonPluginSuffix();
  }

  public abstract boolean isSDKLibrary(Library library);

  public abstract MvcProjectStructure createProjectStructure(
      @NotNull Module module, boolean auxModule);

  public abstract LibraryKind getLibraryKind();

  public abstract String getSomeFrameworkClass();

  public static void addAvailableSystemScripts(
      final Collection<String> result, @NotNull Module module) {
    VirtualFile scriptRoot = null;

    GlobalSearchScope searchScope =
        GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, false);

    for (PsiClass aClass :
        JavaPsiFacade.getInstance(module.getProject()).findClasses("CreateApp_", searchScope)) {
      PsiClass superClass = aClass.getSuperClass();
      if (superClass != null
          && GroovyCommonClassNames.GROOVY_LANG_SCRIPT.equals(superClass.getQualifiedName())) {
        PsiFile psiFile = aClass.getContainingFile();
        if (psiFile != null) {
          VirtualFile file = psiFile.getVirtualFile();
          if (file != null && file.getFileSystem() instanceof ArchiveFileSystem) {
            VirtualFile parent = file.getParent();
            if (parent != null && parent.findChild("Console.class") != null) {
              scriptRoot = parent;
              break;
            }
          }
        }
      }
    }

    if (scriptRoot == null) return;

    Pattern scriptPattern = Pattern.compile("([A-Za-z0-9]+)_?\\.class");

    for (VirtualFile file : scriptRoot.getChildren()) {
      Matcher matcher = scriptPattern.matcher(file.getName());
      if (matcher.matches()) {
        result.add(GroovyNamesUtil.camelToSnake(matcher.group(1)));
      }
    }
  }

  public abstract boolean isToReformatOnCreation(VirtualFile file);

  public static void addAvailableScripts(
      final Collection<String> result, @Nullable final VirtualFile root) {
    if (root == null || !root.isDirectory()) {
      return;
    }

    final VirtualFile scripts = root.findChild("scripts");

    if (scripts == null || !scripts.isDirectory()) {
      return;
    }

    for (VirtualFile child : scripts.getChildren()) {
      if (isScriptFile(child)) {
        result.add(GroovyNamesUtil.camelToSnake(child.getNameWithoutExtension()));
      }
    }
  }

  @Nullable
  public static MvcFramework findCommonPluginModuleFramework(Module module) {
    for (MvcFramework framework : EP_NAME.getExtensions()) {
      if (framework.isCommonPluginsModule(module)) {
        return framework;
      }
    }
    return null;
  }

  public static boolean isScriptFileName(String fileName) {
    return fileName.endsWith(GroovyFileType.DEFAULT_EXTENSION) && fileName.charAt(0) != '_';
  }

  private static boolean isScriptFile(VirtualFile virtualFile) {
    return !virtualFile.isDirectory() && isScriptFileName(virtualFile.getName());
  }

  @Nullable
  public String getInstalledPluginNameByPath(Project project, @NotNull VirtualFile pluginPath) {
    VirtualFile pluginXml = pluginPath.findChild("plugin.xml");
    if (pluginXml == null) return null;

    PsiFile pluginXmlPsi = PsiManager.getInstance(project).findFile(pluginXml);
    if (!(pluginXmlPsi instanceof XmlFile)) return null;

    XmlTag rootTag = ((XmlFile) pluginXmlPsi).getRootTag();
    if (rootTag == null || !"plugin".equals(rootTag.getName())) return null;

    XmlAttribute attrName = rootTag.getAttribute("name");
    if (attrName == null) return null;

    String res = attrName.getValue();
    if (res == null) return null;

    res = res.trim();
    if (res.length() == 0) return null;

    return res;
  }

  @Nullable
  public static MvcFramework getInstance(@Nullable final Module module) {
    if (module == null) {
      return null;
    }

    final Project project = module.getProject();

    return CachedValuesManager.getManager(project)
        .getCachedValue(
            module,
            new CachedValueProvider<MvcFramework>() {
              @Override
              public Result<MvcFramework> compute() {
                final ModificationTracker tracker =
                    MvcModuleStructureSynchronizer.getInstance(project)
                        .getFileAndRootsModificationTracker();
                for (final MvcFramework framework : EP_NAME.getExtensions()) {
                  if (framework.hasSupport(module)) {
                    return Result.create(framework, tracker);
                  }
                }
                return Result.create(null, tracker);
              }
            });
  }

  @Nullable
  public static MvcFramework getInstanceBySdk(@NotNull Module module) {
    for (final MvcFramework framework : EP_NAME.getExtensions()) {
      if (framework.getSdkRoot(module) != null) {
        return framework;
      }
    }
    return null;
  }
}