@Override
 public void cleanup() {
   myOpenProjectSettingsAfter = false;
   myProjectRoot = null;
   myFoundOtpApps = Collections.emptyList();
   mySelectedOtpApps = Collections.emptyList();
 }
 @NotNull
 public Collection<? extends PackagingSourceItem> getSourceItems(
     @NotNull ArtifactEditorContext editorContext,
     @NotNull Artifact artifact,
     PackagingSourceItem parent) {
   if (parent == null) {
     return createModuleItems(editorContext, artifact, ArrayUtil.EMPTY_STRING_ARRAY);
   } else if (parent instanceof ModuleGroupItem) {
     return createModuleItems(editorContext, artifact, ((ModuleGroupItem) parent).getPath());
   } else if (parent instanceof ModuleSourceItemGroup) {
     return createClasspathItems(
         editorContext, artifact, ((ModuleSourceItemGroup) parent).getModule());
   }
   return Collections.emptyList();
 }
 public static void addLibrary(
     Module module, ModifiableRootModel model, String libName, String libPath, String... jarArr) {
   List<VirtualFile> classesRoots = new ArrayList<>();
   for (String jar : jarArr) {
     if (!libPath.endsWith("/") && !jar.startsWith("/")) {
       jar = "/" + jar;
     }
     String path = libPath + jar;
     VirtualFile root;
     if (path.endsWith(".jar")) {
       root = JarFileSystem.getInstance().refreshAndFindFileByPath(path + "!/");
     } else {
       root = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
     }
     assert root != null : "Library root folder not found: " + path + "!/";
     classesRoots.add(root);
   }
   addProjectLibrary(module, model, libName, classesRoots, Collections.emptyList());
 }
  @NotNull
  private List<Pair<PsiClass, VirtualFile>> doFindClasses(
      @NotNull String qName, @NotNull final GlobalSearchScope scope) {
    final Collection<PsiClass> classes =
        JavaFullClassNameIndex.getInstance().get(qName.hashCode(), myManager.getProject(), scope);
    if (classes.isEmpty()) return Collections.emptyList();
    List<Pair<PsiClass, VirtualFile>> result = new ArrayList<>(classes.size());
    for (PsiClass aClass : classes) {
      final String qualifiedName = aClass.getQualifiedName();
      if (qualifiedName == null || !qualifiedName.equals(qName)) continue;

      PsiFile file = aClass.getContainingFile();
      if (file == null) {
        throw new AssertionError("No file for class: " + aClass + " of " + aClass.getClass());
      }
      final boolean valid = file.isValid();
      VirtualFile vFile = file.getVirtualFile();
      if (!valid) {
        LOG.error(
            "Invalid file "
                + file
                + "; virtualFile:"
                + vFile
                + (vFile != null && !vFile.isValid() ? " (invalid)" : "")
                + "; id="
                + (vFile == null ? 0 : ((VirtualFileWithId) vFile).getId()),
            new PsiInvalidElementAccessException(aClass));
        continue;
      }
      if (!hasAcceptablePackage(vFile)) continue;

      result.add(Pair.create(aClass, vFile));
    }

    return result;
  }
 public static void addProjectLibrary(Module module, String libName, VirtualFile... classesRoots) {
   addProjectLibrary(module, libName, Arrays.asList(classesRoots), Collections.emptyList());
 }
public class RebarProjectImportBuilder extends ProjectImportBuilder<ImportedOtpApp> {
  private static final Logger LOG = Logger.getInstance(RebarProjectImportBuilder.class);

  private boolean myOpenProjectSettingsAfter = false;
  @Nullable private VirtualFile myProjectRoot = null;
  @NotNull private List<ImportedOtpApp> myFoundOtpApps = Collections.emptyList();
  @NotNull private List<ImportedOtpApp> mySelectedOtpApps = Collections.emptyList();
  private boolean myImportExamples;
  @NotNull private String myRebarPath = "";
  private boolean myIsImportingProject;

  @NotNull
  @NonNls
  @Override
  public String getName() {
    return "Rebar";
  }

  @Override
  public Icon getIcon() {
    return ErlangIcons.REBAR;
  }

  @Override
  public boolean isSuitableSdkType(@NotNull SdkTypeId sdkType) {
    return sdkType == ErlangSdkType.getInstance();
  }

  @Override
  public List<ImportedOtpApp> getList() {
    return new ArrayList<ImportedOtpApp>(myFoundOtpApps);
  }

  @Override
  public void setList(@Nullable List<ImportedOtpApp> selectedOtpApps)
      throws ConfigurationException {
    if (selectedOtpApps != null) {
      mySelectedOtpApps = selectedOtpApps;
    }
  }

  @Override
  public boolean isMarked(@Nullable ImportedOtpApp importedOtpApp) {
    return importedOtpApp != null && mySelectedOtpApps.contains(importedOtpApp);
  }

  @Override
  public boolean isOpenProjectSettingsAfter() {
    return myOpenProjectSettingsAfter;
  }

  @Override
  public void setOpenProjectSettingsAfter(boolean openProjectSettingsAfter) {
    myOpenProjectSettingsAfter = openProjectSettingsAfter;
  }

  @Override
  public void cleanup() {
    myOpenProjectSettingsAfter = false;
    myProjectRoot = null;
    myFoundOtpApps = Collections.emptyList();
    mySelectedOtpApps = Collections.emptyList();
  }

  public boolean setProjectRoot(@NotNull final VirtualFile projectRoot) {
    if (projectRoot.equals(myProjectRoot)) {
      return true;
    }

    boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode();

    myProjectRoot = projectRoot;
    if (!unitTestMode && projectRoot instanceof VirtualDirectoryImpl) {
      ((VirtualDirectoryImpl) projectRoot).refreshAndFindChild("deps");
    }

    ProgressManager.getInstance()
        .run(
            new Task.Modal(getCurrentProject(), "Scanning Rebar projects", true) {
              public void run(@NotNull final ProgressIndicator indicator) {

                List<VirtualFile> rebarConfigFiles = findRebarConfigs(myProjectRoot, indicator);
                final LinkedHashSet<ImportedOtpApp> importedOtpApps =
                    new LinkedHashSet<ImportedOtpApp>(rebarConfigFiles.size());

                VfsUtilCore.visitChildrenRecursively(
                    projectRoot,
                    new VirtualFileVisitor() {
                      @Override
                      public boolean visitFile(@NotNull VirtualFile file) {
                        indicator.checkCanceled();

                        if (file.isDirectory()) {
                          indicator.setText2(file.getPath());
                          if (isExamplesDirectory(file)
                              || isRelDirectory(projectRoot.getPath(), file.getPath()))
                            return false;
                        }

                        ContainerUtil.addAllNotNull(importedOtpApps, createImportedOtpApp(file));
                        return true;
                      }
                    });

                myFoundOtpApps = ContainerUtil.newArrayList(importedOtpApps);
              }
            });

    Collections.sort(
        myFoundOtpApps,
        new Comparator<ImportedOtpApp>() {
          @Override
          public int compare(ImportedOtpApp o1, ImportedOtpApp o2) {
            int nameCompareResult =
                String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName());
            if (nameCompareResult == 0) {
              return String.CASE_INSENSITIVE_ORDER.compare(
                  o1.getRoot().getPath(), o2.getRoot().getPath());
            }
            return nameCompareResult;
          }
        });

    mySelectedOtpApps = myFoundOtpApps;
    return !myFoundOtpApps.isEmpty();
  }

  private static boolean isRelDirectory(String projectRootPath, String path) {
    return (projectRootPath + "/rel").equals(path);
  }

  @SuppressWarnings("DialogTitleCapitalization")
  @Override
  public boolean validate(Project current, Project dest) {
    if (!findIdeaModuleFiles(mySelectedOtpApps)) {
      return true;
    }
    int resultCode =
        Messages.showYesNoCancelDialog(
            ApplicationInfoEx.getInstanceEx().getFullApplicationName()
                + " module files found:\n\n"
                + StringUtil.join(
                    mySelectedOtpApps,
                    new Function<ImportedOtpApp, String>() {
                      public String fun(ImportedOtpApp importedOtpApp) {
                        VirtualFile ideaModuleFile = importedOtpApp.getIdeaModuleFile();
                        return ideaModuleFile != null
                            ? "    " + ideaModuleFile.getPath() + "\n"
                            : "";
                      }
                    },
                    "")
                + "\nWould you like to reuse them?",
            "Module files found",
            Messages.getQuestionIcon());
    if (resultCode == DialogWrapper.OK_EXIT_CODE) {
      return true;
    } else if (resultCode == DialogWrapper.CANCEL_EXIT_CODE) {
      try {
        deleteIdeaModuleFiles(mySelectedOtpApps);
        return true;
      } catch (IOException e) {
        LOG.error(e);
        return false;
      }
    } else {
      return false;
    }
  }

  @Override
  public List<Module> commit(
      @NotNull Project project,
      @Nullable ModifiableModuleModel moduleModel,
      @NotNull ModulesProvider modulesProvider,
      @Nullable ModifiableArtifactModel modifiableArtifactModel) {
    Set<String> selectedAppNames = ContainerUtil.newHashSet();
    for (ImportedOtpApp importedOtpApp : mySelectedOtpApps) {
      selectedAppNames.add(importedOtpApp.getName());
    }
    Sdk projectSdk = fixProjectSdk(project);
    List<Module> createdModules = new ArrayList<Module>();
    final List<ModifiableRootModel> createdRootModels = new ArrayList<ModifiableRootModel>();
    final ModifiableModuleModel obtainedModuleModel =
        moduleModel != null ? moduleModel : ModuleManager.getInstance(project).getModifiableModel();
    for (ImportedOtpApp importedOtpApp : mySelectedOtpApps) {
      VirtualFile ideaModuleDir = importedOtpApp.getRoot();
      String ideaModuleFile =
          ideaModuleDir.getCanonicalPath() + File.separator + importedOtpApp.getName() + ".iml";
      Module module =
          obtainedModuleModel.newModule(ideaModuleFile, ErlangModuleType.getInstance().getId());
      createdModules.add(module);
      importedOtpApp.setModule(module);
      if (importedOtpApp.getIdeaModuleFile() == null) {
        ModifiableRootModel rootModel = ModuleRootManager.getInstance(module).getModifiableModel();
        // Make it inherit SDK from the project.
        rootModel.inheritSdk();
        // Initialize source and test paths.
        ContentEntry content = rootModel.addContentEntry(importedOtpApp.getRoot());
        addSourceDirToContent(content, ideaModuleDir, "src", false);
        addSourceDirToContent(content, ideaModuleDir, "test", true);
        addIncludeDirectories(content, importedOtpApp);
        // Exclude standard folders
        excludeDirFromContent(content, ideaModuleDir, "doc");
        // Initialize output paths according to Rebar conventions.
        CompilerModuleExtension compilerModuleExt =
            rootModel.getModuleExtension(CompilerModuleExtension.class);
        compilerModuleExt.inheritCompilerOutputPath(false);
        compilerModuleExt.setCompilerOutputPath(ideaModuleDir + File.separator + "ebin");
        compilerModuleExt.setCompilerOutputPathForTests(ideaModuleDir + File.separator + ".eunit");
        createdRootModels.add(rootModel);
        // Set inter-module dependencies
        resolveModuleDeps(rootModel, importedOtpApp, projectSdk, selectedAppNames);
      }
    }
    // Commit project structure.
    LOG.info("Commit project structure");
    ApplicationManager.getApplication()
        .runWriteAction(
            new Runnable() {
              public void run() {
                for (ModifiableRootModel rootModel : createdRootModels) {
                  rootModel.commit();
                }
                obtainedModuleModel.commit();
              }
            });

    addErlangFacets(mySelectedOtpApps);
    RebarSettings.getInstance(project).setRebarPath(myRebarPath);
    if (myIsImportingProject) {
      ErlangCompilerSettings.getInstance(project).setUseRebarCompilerEnabled(true);
    }
    CompilerWorkspaceConfiguration.getInstance(project).CLEAR_OUTPUT_DIRECTORY = false;

    return createdModules;
  }

  private static void addErlangFacets(final List<ImportedOtpApp> apps) {
    ApplicationManager.getApplication()
        .runWriteAction(
            new Runnable() {
              @Override
              public void run() {
                for (ImportedOtpApp app : apps) {
                  Module module = app.getModule();
                  if (module == null) continue;
                  ErlangFacet facet = ErlangFacet.getFacet(module);
                  if (facet == null) {
                    ErlangFacet.createFacet(module);
                    facet = ErlangFacet.getFacet(module);
                  }
                  if (facet != null) {
                    ErlangFacetConfiguration configuration = facet.getConfiguration();
                    configuration.addParseTransforms(app.getParseTransforms());
                  }
                }
              }
            });
  }

  @Nullable
  private static Sdk fixProjectSdk(@NotNull Project project) {
    final ProjectRootManagerEx projectRootMgr = ProjectRootManagerEx.getInstanceEx(project);
    Sdk selectedSdk = projectRootMgr.getProjectSdk();
    if (selectedSdk == null || selectedSdk.getSdkType() != ErlangSdkType.getInstance()) {
      final Sdk moreSuitableSdk =
          ProjectJdkTable.getInstance().findMostRecentSdkOfType(ErlangSdkType.getInstance());
      ApplicationManager.getApplication()
          .runWriteAction(
              new Runnable() {
                @Override
                public void run() {
                  projectRootMgr.setProjectSdk(moreSuitableSdk);
                }
              });
      return moreSuitableSdk;
    }
    return selectedSdk;
  }

  private static void addSourceDirToContent(
      @NotNull ContentEntry content,
      @NotNull VirtualFile root,
      @NotNull String sourceDir,
      boolean test) {
    VirtualFile sourceDirFile = root.findChild(sourceDir);
    if (sourceDirFile != null) {
      content.addSourceFolder(sourceDirFile, test);
    }
  }

  private static void addIncludeDirectories(@NotNull ContentEntry content, ImportedOtpApp app) {
    for (VirtualFile includeDirectory : app.getIncludePaths()) {
      ErlangIncludeDirectoryUtil.markAsIncludeDirectory(content, includeDirectory);
    }
  }

  private static void excludeDirFromContent(
      ContentEntry content, VirtualFile root, String excludeDir) {
    VirtualFile excludeDirFile = root.findChild(excludeDir);
    if (excludeDirFile != null) {
      content.addExcludeFolder(excludeDirFile);
    }
  }

  @NotNull
  private List<VirtualFile> findRebarConfigs(
      @NotNull final VirtualFile root, @NotNull final ProgressIndicator indicator) {
    root.refresh(false, true);
    final List<VirtualFile> foundRebarConfigs = new ArrayList<VirtualFile>();
    VfsUtilCore.visitChildrenRecursively(
        root,
        new VirtualFileVisitor() {
          @Override
          public boolean visitFile(@NotNull VirtualFile file) {
            indicator.checkCanceled();
            if (file.isDirectory()) {
              if (isExamplesDirectory(file) || isRelDirectory(root.getPath(), file.getPath()))
                return false;
              indicator.setText2(file.getPath());
            } else if (file.getName().equalsIgnoreCase("rebar.config")) {
              foundRebarConfigs.add(file);
            }
            return true;
          }
        });

    return foundRebarConfigs;
  }

  private boolean isExamplesDirectory(VirtualFile virtualFile) {
    return "examples".equals(virtualFile.getName()) && !myImportExamples;
  }

  @Nullable
  private static ImportedOtpApp createImportedOtpApp(@NotNull VirtualFile appRoot) {
    VirtualFile appResourceFile = findAppResourceFile(appRoot);
    if (appResourceFile == null) {
      return null;
    }
    return new ImportedOtpApp(appRoot, appResourceFile);
  }

  @Nullable
  private static VirtualFile findAppResourceFile(@NotNull VirtualFile applicationRoot) {
    VirtualFile appResourceFile = null;
    VirtualFile sourceDir = applicationRoot.findChild("src");
    if (sourceDir != null) {
      appResourceFile = findFileByExtension(sourceDir, "app.src");
    }
    if (appResourceFile == null) {
      VirtualFile ebinDir = applicationRoot.findChild("ebin");
      if (ebinDir != null) {
        appResourceFile = findFileByExtension(ebinDir, "app");
      }
    }
    return appResourceFile;
  }

  @Nullable
  private static VirtualFile findFileByExtension(
      @NotNull VirtualFile dir, @NotNull String extension) {
    for (VirtualFile file : dir.getChildren()) {
      String fileName = file.getName();
      if (!file.isDirectory() && fileName.endsWith(extension)) {
        return file;
      }
    }
    return null; // To change body of created methods use File | Settings | File Templates.
  }

  private static void deleteIdeaModuleFiles(@NotNull final List<ImportedOtpApp> importedOtpApps)
      throws IOException {
    final IOException[] ex = new IOException[1];
    ApplicationManager.getApplication()
        .runWriteAction(
            new Runnable() {
              @Override
              public void run() {
                for (ImportedOtpApp importedOtpApp : importedOtpApps) {
                  VirtualFile ideaModuleFile = importedOtpApp.getIdeaModuleFile();
                  if (ideaModuleFile != null) {
                    try {
                      ideaModuleFile.delete(this);
                      importedOtpApp.setIdeaModuleFile(null);
                    } catch (IOException e) {
                      ex[0] = e;
                    }
                  }
                }
              }
            });
    if (ex[0] != null) {
      throw ex[0];
    }
  }

  private static boolean findIdeaModuleFiles(@NotNull List<ImportedOtpApp> importedOtpApps) {
    boolean ideaModuleFileExists = false;
    for (ImportedOtpApp importedOtpApp : importedOtpApps) {
      VirtualFile applicationRoot = importedOtpApp.getRoot();
      String ideaModuleName = importedOtpApp.getName();
      VirtualFile imlFile = applicationRoot.findChild(ideaModuleName + ".iml");
      if (imlFile != null) {
        ideaModuleFileExists = true;
        importedOtpApp.setIdeaModuleFile(imlFile);
      } else {
        VirtualFile emlFile = applicationRoot.findChild(ideaModuleName + ".eml");
        if (emlFile != null) {
          ideaModuleFileExists = true;
          importedOtpApp.setIdeaModuleFile(emlFile);
        }
      }
    }
    return ideaModuleFileExists;
  }

  @NotNull
  private static Set<String> resolveModuleDeps(
      @NotNull ModifiableRootModel rootModel,
      @NotNull ImportedOtpApp importedOtpApp,
      @Nullable Sdk projectSdk,
      @NotNull Set<String> allImportedAppNames) {
    HashSet<String> unresolvedAppNames = ContainerUtil.newHashSet();
    for (String depAppName : importedOtpApp.getDeps()) {
      if (allImportedAppNames.contains(depAppName)) {
        rootModel.addInvalidModuleEntry(depAppName);
      } else if (projectSdk != null && isSdkOtpApp(depAppName, projectSdk)) {
        // SDK is already a dependency
      } else {
        rootModel.addInvalidModuleEntry(depAppName);
        unresolvedAppNames.add(depAppName);
      }
    }
    return unresolvedAppNames;
  }

  private static boolean isSdkOtpApp(@NotNull String otpAppName, @NotNull Sdk sdk) {
    Pattern appDirNamePattern = Pattern.compile(otpAppName + "-.*");
    for (VirtualFile srcSdkDir : sdk.getRootProvider().getFiles(OrderRootType.SOURCES)) {
      for (VirtualFile child : srcSdkDir.getChildren()) {
        if (child.isDirectory() && appDirNamePattern.matcher(child.getName()).find()) {
          return true;
        }
      }
    }
    return false;
  }

  public void setImportExamples(boolean importExamples) {
    myImportExamples = importExamples;
  }

  public void setRebarPath(@NotNull String rebarPath) {
    myRebarPath = rebarPath;
  }

  public void setIsImportingProject(boolean isImportingProject) {
    myIsImportingProject = isImportingProject;
  }
}