<commit_before>package org.jetbrains.plugins.scala
package project.maven

import java.io.File
import java.util

import com.intellij.openapi.externalSystem.model.ExternalSystemException
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.impl.libraries.LibraryEx.ModifiableModelEx
import org.jdom.Element
import org.jetbrains.idea.maven.importing.{MavenImporter, MavenRootModelAdapter}
import org.jetbrains.idea.maven.model.{MavenArtifact, MavenArtifactInfo, MavenId, MavenPlugin}
import org.jetbrains.idea.maven.project._
import org.jetbrains.idea.maven.server.{MavenEmbedderWrapper, NativeMavenProjectHolder}
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.project.ScalaLanguageLevel.Scala_2_10
import org.jetbrains.plugins.scala.project._
import org.jetbrains.plugins.scala.project.maven.ScalaMavenImporter._
import scala.collection.JavaConverters._

/**
 * @author <NAME>
 */
class ScalaMavenImporter extends MavenImporter("org.scala-tools", "maven-scala-plugin") {
  override def collectSourceFolders(mavenProject: MavenProject, result: java.util.List[String]) {
    collectSourceOrTestFolders(mavenProject, "add-source", "sourceDir", "src/main/scala", result)
  }

  override def collectTestFolders(mavenProject: MavenProject, result: java.util.List[String]) {
    collectSourceOrTestFolders(mavenProject, "add-source", "testSourceDir", "src/test/scala", result)
  }

  private def collectSourceOrTestFolders(mavenProject: MavenProject, goal: String, goalPath: String,
                                         defaultDir: String, result: java.util.List[String]) {
    val goalConfigValue = findGoalConfigValue(mavenProject, goal, goalPath)
    result.add(if (goalConfigValue == null) defaultDir else goalConfigValue)
  }

  // exclude "default" plugins, should be done inside IDEA's MavenImporter itself
  override def isApplicable(mavenProject: MavenProject): Boolean = validConfigurationIn(mavenProject).isDefined

  override def preProcess(module: Module, mavenProject: MavenProject, changes: MavenProjectChanges, modelsProvider: IdeModifiableModelsProvider) {}

  override def process(modelsProvider: IdeModifiableModelsProvider, module: Module,
              rootModel: MavenRootModelAdapter, mavenModel: MavenProjectsTree, mavenProject: MavenProject,
              changes: MavenProjectChanges, mavenProjectToModuleName: util.Map[MavenProject, String],
              postTasks: util.List[MavenProjectsProcessorTask]) {

    validConfigurationIn(mavenProject).foreach { configuration =>
      // TODO configuration.vmOptions

      val compilerOptions = {
        val plugins = configuration.plugins.map(id => mavenProject.localPathTo(id).getPath)
        configuration.compilerOptions ++ plugins.map(path => "-Xplugin:" + path)
      }
      
      module.configureScalaCompilerSettingsFrom("Maven", compilerOptions)

      val compilerVersion = configuration.compilerVersion.get

      val scalaLibrary =modelsProvider.getAllLibraries.toSeq
              .filter(_.getName.contains("scala-library"))
              .find(_.scalaVersion.contains(compilerVersion))
              .getOrElse(throw new ExternalSystemException("Cannot find project Scala library " +
              compilerVersion.presentation + " for module " + module.getName))

      if (!scalaLibrary.isScalaSdk) {
        val languageLevel = compilerVersion.toLanguageLevel.getOrElse(ScalaLanguageLevel.Default)
        val compilerClasspath = configuration.compilerClasspath.map(mavenProject.localPathTo)

        val libraryModel = modelsProvider.getModifiableLibraryModel(scalaLibrary).asInstanceOf[LibraryEx.ModifiableModelEx]
        convertToScalaSdk(libraryModel, languageLevel, compilerClasspath)
      }
    }
  }

  // Can we reuse library.convertToScalaSdk? (dependency on modifiable model, cannot commit model)
  private def convertToScalaSdk(model: ModifiableModelEx, languageLevel: ScalaLanguageLevel, compilerClasspath: Seq[File]) {
    model.setKind(ScalaLibraryKind)

    val properties = new ScalaLibraryProperties()
    properties.languageLevel = languageLevel
    properties.compilerClasspath = compilerClasspath

    model.setProperties(properties)
  }

  override def resolve(project: Project, mavenProject: MavenProject, nativeMavenProject: NativeMavenProjectHolder,
                       embedder: MavenEmbedderWrapper) {
    validConfigurationIn(mavenProject).foreach { configuration =>
      val repositories = mavenProject.getRemoteRepositories

      def resolve(id: MavenId): MavenArtifact = {
        embedder.resolve(new MavenArtifactInfo(id, "pom", null), repositories)
        embedder.resolve(new MavenArtifactInfo(id, "jar", null), repositories)
      }

      // Scala Maven plugin can add scala-library to compilation classpath, without listing it as a project dependency.
      // Such an approach is probably incorrect, but we have to support that behaviour "as is".
      configuration.implicitScalaLibrary.foreach { scalaLibraryId =>
        mavenProject.addDependency(resolve(scalaLibraryId))
      }
      configuration.compilerClasspath.foreach(resolve)
      configuration.plugins.foreach(resolve)
    }
  }

  private def validConfigurationIn(project: MavenProject) = Some(new ScalaConfiguration(project)).filter(_.valid)
}

private object ScalaMavenImporter {
  implicit class RichMavenProject(val project: MavenProject) extends AnyVal {
    def localPathTo(id: MavenId): File = project.getLocalRepository / id.getGroupId.replaceAll("\\.", "/") /
            id.getArtifactId / id.getVersion / "%s-%s.jar".format(id.getArtifactId, id.getVersion)
  }

  implicit class RichFile(val file: File) extends AnyVal {
    def /(child: String): File = new File(file, child)
  }

  private class ScalaConfiguration(project: MavenProject) {
    private def versionNumber = compilerVersion.map(_.presentation).getOrElse("unknown")

    private def scalaCompilerId = new MavenId("org.scala-lang", "scala-compiler", versionNumber)

    private def scalaLibraryId = new MavenId("org.scala-lang", "scala-library", versionNumber)

    private def scalaReflectId = new MavenId("org.scala-lang", "scala-reflect", versionNumber)

    private def compilerPlugin: Option[MavenPlugin] =
      project.findPlugin("org.scala-tools", "maven-scala-plugin").toOption.filter(!_.isDefault).orElse(
        project.findPlugin("net.alchim31.maven", "scala-maven-plugin").toOption.filter(!_.isDefault))<commit_msg>Support our Maven plugin in the ScalaMavenImporter, Fix #20<commit_after>package org.jetbrains.plugins.scala
package project.maven

import java.io.File
import java.util

import com.intellij.openapi.externalSystem.model.ExternalSystemException
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.impl.libraries.LibraryEx
import com.intellij.openapi.roots.impl.libraries.LibraryEx.ModifiableModelEx
import org.jdom.Element
import org.jetbrains.idea.maven.importing.{MavenImporter, MavenRootModelAdapter}
import org.jetbrains.idea.maven.model.{MavenArtifact, MavenArtifactInfo, MavenId, MavenPlugin}
import org.jetbrains.idea.maven.project._
import org.jetbrains.idea.maven.server.{MavenEmbedderWrapper, NativeMavenProjectHolder}
import org.jetbrains.plugins.scala.extensions._
import org.jetbrains.plugins.scala.project.ScalaLanguageLevel.Scala_2_10
import org.jetbrains.plugins.scala.project._
import org.jetbrains.plugins.scala.project.maven.ScalaMavenImporter._
import scala.collection.JavaConverters._

/**
 * @author <NAME>
 */
class ScalaMavenImporter extends MavenImporter("org.scala-tools", "maven-scala-plugin") {
  override def collectSourceFolders(mavenProject: MavenProject, result: java.util.List[String]) {
    collectSourceOrTestFolders(mavenProject, "add-source", "sourceDir", "src/main/scala", result)
  }

  override def collectTestFolders(mavenProject: MavenProject, result: java.util.List[String]) {
    collectSourceOrTestFolders(mavenProject, "add-source", "testSourceDir", "src/test/scala", result)
  }

  private def collectSourceOrTestFolders(mavenProject: MavenProject, goal: String, goalPath: String,
                                         defaultDir: String, result: java.util.List[String]) {
    val goalConfigValue = findGoalConfigValue(mavenProject, goal, goalPath)
    result.add(if (goalConfigValue == null) defaultDir else goalConfigValue)
  }

  // exclude "default" plugins, should be done inside IDEA's MavenImporter itself
  override def isApplicable(mavenProject: MavenProject): Boolean = validConfigurationIn(mavenProject).isDefined

  override def preProcess(module: Module, mavenProject: MavenProject, changes: MavenProjectChanges, modelsProvider: IdeModifiableModelsProvider) {}

  override def process(modelsProvider: IdeModifiableModelsProvider, module: Module,
              rootModel: MavenRootModelAdapter, mavenModel: MavenProjectsTree, mavenProject: MavenProject,
              changes: MavenProjectChanges, mavenProjectToModuleName: util.Map[MavenProject, String],
              postTasks: util.List[MavenProjectsProcessorTask]) {

    validConfigurationIn(mavenProject).foreach { configuration =>
      // TODO configuration.vmOptions

      val compilerOptions = {
        val plugins = configuration.plugins.map(id => mavenProject.localPathTo(id).getPath)
        configuration.compilerOptions ++ plugins.map(path => "-Xplugin:" + path)
      }
      
      module.configureScalaCompilerSettingsFrom("Maven", compilerOptions)

      val compilerVersion = configuration.compilerVersion.get

      val scalaLibrary =modelsProvider.getAllLibraries.toSeq
              .filter(_.getName.contains("scala-library"))
              .find(_.scalaVersion.contains(compilerVersion))
              .getOrElse(throw new ExternalSystemException("Cannot find project Scala library " +
              compilerVersion.presentation + " for module " + module.getName))

      if (!scalaLibrary.isScalaSdk) {
        val languageLevel = compilerVersion.toLanguageLevel.getOrElse(ScalaLanguageLevel.Default)
        val compilerClasspath = configuration.compilerClasspath.map(mavenProject.localPathTo)

        val libraryModel = modelsProvider.getModifiableLibraryModel(scalaLibrary).asInstanceOf[LibraryEx.ModifiableModelEx]
        convertToScalaSdk(libraryModel, languageLevel, compilerClasspath)
      }
    }
  }

  // Can we reuse library.convertToScalaSdk? (dependency on modifiable model, cannot commit model)
  private def convertToScalaSdk(model: ModifiableModelEx, languageLevel: ScalaLanguageLevel, compilerClasspath: Seq[File]) {
    model.setKind(ScalaLibraryKind)

    val properties = new ScalaLibraryProperties()
    properties.languageLevel = languageLevel
    properties.compilerClasspath = compilerClasspath

    model.setProperties(properties)
  }

  override def resolve(project: Project, mavenProject: MavenProject, nativeMavenProject: NativeMavenProjectHolder,
                       embedder: MavenEmbedderWrapper) {
    validConfigurationIn(mavenProject).foreach { configuration =>
      val repositories = mavenProject.getRemoteRepositories

      def resolve(id: MavenId): MavenArtifact = {
        embedder.resolve(new MavenArtifactInfo(id, "pom", null), repositories)
        embedder.resolve(new MavenArtifactInfo(id, "jar", null), repositories)
      }

      // Scala Maven plugin can add scala-library to compilation classpath, without listing it as a project dependency.
      // Such an approach is probably incorrect, but we have to support that behaviour "as is".
      configuration.implicitScalaLibrary.foreach { scalaLibraryId =>
        mavenProject.addDependency(resolve(scalaLibraryId))
      }
      configuration.compilerClasspath.foreach(resolve)
      configuration.plugins.foreach(resolve)
    }
  }

  private def validConfigurationIn(project: MavenProject) = Some(new ScalaConfiguration(project)).filter(_.valid)
}

private object ScalaMavenImporter {
  implicit class RichMavenProject(val project: MavenProject) extends AnyVal {
    def localPathTo(id: MavenId): File = project.getLocalRepository / id.getGroupId.replaceAll("\\.", "/") /
            id.getArtifactId / id.getVersion / "%s-%s.jar".format(id.getArtifactId, id.getVersion)
  }

  implicit class RichFile(val file: File) extends AnyVal {
    def /(child: String): File = new File(file, child)
  }

  private class ScalaConfiguration(project: MavenProject) {
    private def versionNumber = compilerVersion.map(_.presentation).getOrElse("unknown")

    private def scalaCompilerId = new MavenId("org.scala-lang", "scala-compiler", versionNumber)

    private def scalaLibraryId = new MavenId("org.scala-lang", "scala-library", versionNumber)

    private def scalaReflectId = new MavenId("org.scala-lang", "scala-reflect", versionNumber)

    private def compilerPlugin: Option[MavenPlugin] =
      project.findPlugin("org.scala-tools", "maven-scala-plugin").toOption.filter(!_.isDefault).orElse(
        project.findPlugin("net.alchim31.maven", "scala-maven-plugin").toOption.filter(!_.isDefault)).orElse(
        project.findPlugin("com.triplequote.maven", "scala-maven-plugin").toOption.filter(!_.isDefault))
{'fraction_non_alphanumeric': 0.09601247493873914, 'fraction_numerical': 0.0008910670527957229, 'mean_word_length': 4.606994171523731, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5632745', 'n_tokens_mistral': 3890, 'n_tokens_neox': 3680, 'n_words': 735}