<commit_before>/*
 * Copyright 2011 Typesafe Inc.
 *
 * This work is based on the original contribution of WeigleWilczek.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.typesafe.sbteclipse

import EclipsePlugin.{ ClasspathEntry, EclipseExecutionEnvironment, EclipseKeys }
import java.io.FileWriter
import java.util.Properties
import sbt.{ Command, Configuration, Configurations, File, IO, Keys, Project, ProjectRef, ResolvedProject, State, ThisBuild, richFile }
import sbt.CommandSupport.logger
import scala.collection.JavaConverters
import scala.xml.{ Elem, NodeSeq, PrettyPrinter }
import scalaz.{ Failure, Success }
import scalaz.Scalaz._

private object Eclipse {

  val SettingFormat = """-?([^:]*):?(.*)""".r

  val FileSep = System.getProperty("file.separator")

  def eclipseCommand(
    executionEnvironment: Option[EclipseExecutionEnvironment.Value],
    skipParents: Boolean,
    withSource: Boolean,
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry],
    commandName: String): Command =
    Command(commandName)(_ => parser)((state, args) =>
      action(executionEnvironment, skipParents, withSource, classpathEntryCollector, args.toMap)(state)
    )

  def parser = {
    import EclipseOpts._
    (boolOpt(ExecutionEnvironment) | boolOpt(SkipParents) | boolOpt(WithSource)).*
  }

  def action(
    executionEnvironment: Option[EclipseExecutionEnvironment.Value],
    skipParents: Boolean,
    withSource: Boolean,
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry],
    args: Map[String, Boolean])(
      implicit state: State) = {

    logger(state).info("About to create Eclipse project files for your project(s).")
    import EclipseOpts._
    val ee = args get ExecutionEnvironment getOrElse executionEnvironment
    val sp = args get SkipParents getOrElse skipParents
    val ws = args get WithSource getOrElse withSource

    contentsForAllProjects(sp, classpathEntryCollector).fold(onFailure, onSuccess)
  }

  def contentsForAllProjects(
    skipParents: Boolean,
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry])(
      implicit state: State) = {
    val contents = for {
      ref <- structure.allProjectRefs
      project <- Project.getProject(ref, structure) if project.aggregate.isEmpty || !skipParents
    } yield {
      val cs = configurations(ref)
      (name(ref) |@|
        buildDirectory(ref) |@|
        baseDirectory(ref) |@|
        ((cs map srcDirectories(ref)).sequence[ValidationNELS, Seq[(File, File)]] map (_.flatten.distinct)) |@|
        target(ref) |@|
        scalacOptions(ref) |@|
        ((cs map externalDependencies(ref)).sequence[ValidationNELS, Seq[File]] map (_.flatten.distinct)) |@|
        ((cs map projectDependencies(ref, project)).sequence[ValidationNELS, Seq[String]] map (_.flatten.distinct)))(content(classpathEntryCollector))
    }
    contents.sequence[ValidationNELS, Content]
  }

  def onFailure(errors: NELS)(implicit state: State) = {
    logger(state).error("Could not create Eclipse project files: %s" format (errors.list mkString ", "))
    state.fail
  }

  def onSuccess(contents: Seq[Content])(implicit state: State) = {
    if (contents.isEmpty)
      logger(state).warn("There was no project to create Eclipse project files for!")
    else {
      val names = contents map writeContent
      logger(state).info("Successfully created Eclipse project files for project(s): %s" format (names mkString ", "))
    }
    state
  }

  def content(
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry])(
      name: String,
      buildDirectory: File,
      baseDirectory: File,
      srcDirectories: Seq[(File, File)],
      target: File,
      scalacOptions: Seq[(String, String)],
      externalDependencies: Seq[File],
      projectDependencies: Seq[String])(
        implicit state: State) =
    Content(
      name,
      baseDirectory,
      projectXml(name),
      classpath(
        classpathEntryCollector,
        buildDirectory,
        baseDirectory,
        srcDirectories,
        target,
        externalDependencies,
        projectDependencies
      ),
      scalacOptions)

  def projectXml(name: String) =
    <projectDescription>
      <name>{ name }</name>
      <buildSpec>
        <buildCommand>
          <name>org.scala-ide.sdt.core.scalabuilder</name>
        </buildCommand>
      </buildSpec>
      <natures>
        <nature>org.scala-ide.sdt.core.scalanature</nature>
        <nature>org.eclipse.jdt.core.javanature</nature>
      </natures>
    </projectDescription>

  def classpath(
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry],
    buildDirectory: File,
    baseDirectory: File,
    srcDirectories: Seq[(File, File)],
    target: File,
    externalDependencies: Seq[File],
    projectDependencies: Seq[String])(
      implicit state: State) = {
    val entries = Seq(
      (for ((dir, output) <- srcDirectories) yield srcEntry(baseDirectory, output)(dir)).flatten.distinct,
      externalDependencies map libEntry(buildDirectory, baseDirectory),
      projectDependencies map ClasspathEntry.Project,
      Seq("org.eclipse.jdt.launching.JRE_CONTAINER") map ClasspathEntry.Con, // TODO Optionally use execution env!
      Seq(output(baseDirectory, target)) map ClasspathEntry.Output
    ).flatten collect classpathEntryCollector map (_.toXml)
    <classpath>{ entries }</classpath>
  }

  def srcEntry(baseDirectory: File, classDirectory: File)(srcDirectory: File)(implicit state: State) = {
    if (srcDirectory.exists()) {
      logger(state).debug("Creating src entry for directory '%s'." format srcDirectory)
      Some(ClasspathEntry.Src(relativize(baseDirectory, srcDirectory), output(baseDirectory, classDirectory)))
    } else {
      logger(state).debug("Skipping src entry for not-existing directory '%s'." format srcDirectory)
      None
    }
  }

  def libEntry(buildDirectory: File, baseDirectory: File)(file: File)(implicit state: State) = {
    val path =
      (IO.relativize(buildDirectory, baseDirectory) |@| IO.relativize(buildDirectory, file))((buildToBase, buildToFile) =>
        "%s/%s".format(buildToBase split FileSep map (_ => "..") mkString FileSep, buildToFile)
      ) getOrElse file.getAbsolutePath
    ClasspathEntry.Lib(path)
  }

  // Getting and transforming settings and task results<commit_msg>Refactored configuration support.<commit_after>/*
 * Copyright 2011 Typesafe Inc.
 *
 * This work is based on the original contribution of WeigleWilczek.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.typesafe.sbteclipse

import EclipsePlugin.{ ClasspathEntry, EclipseExecutionEnvironment, EclipseKeys }
import java.io.FileWriter
import java.util.Properties
import sbt.{ Command, Configuration, Configurations, File, IO, Keys, Project, ProjectRef, ResolvedProject, State, ThisBuild, richFile }
import sbt.CommandSupport.logger
import scala.collection.JavaConverters
import scala.xml.{ Elem, NodeSeq, PrettyPrinter }
import scalaz.{ Failure, Success }
import scalaz.Scalaz._

private object Eclipse {

  val SettingFormat = """-?([^:]*):?(.*)""".r

  val FileSep = System.getProperty("file.separator")

  def eclipseCommand(
    executionEnvironment: Option[EclipseExecutionEnvironment.Value],
    skipParents: Boolean,
    withSource: Boolean,
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry],
    commandName: String): Command =
    Command(commandName)(_ => parser)((state, args) =>
      action(executionEnvironment, skipParents, withSource, classpathEntryCollector, args.toMap)(state)
    )

  def parser = {
    import EclipseOpts._
    (boolOpt(ExecutionEnvironment) | boolOpt(SkipParents) | boolOpt(WithSource)).*
  }

  def action(
    executionEnvironment: Option[EclipseExecutionEnvironment.Value],
    skipParents: Boolean,
    withSource: Boolean,
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry],
    args: Map[String, Boolean])(
      implicit state: State) = {

    logger(state).info("About to create Eclipse project files for your project(s).")
    import EclipseOpts._
    val ee = args get ExecutionEnvironment getOrElse executionEnvironment
    val sp = args get SkipParents getOrElse skipParents
    val ws = args get WithSource getOrElse withSource

    contentsForAllProjects(sp, classpathEntryCollector).fold(onFailure, onSuccess)
  }

  def contentsForAllProjects(
    skipParents: Boolean,
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry])(
      implicit state: State) = {
    val contents = for {
      ref <- structure.allProjectRefs
      project <- Project.getProject(ref, structure) if project.aggregate.isEmpty || !skipParents
    } yield {
      val configs = configurations(ref)
      (name(ref) |@|
        buildDirectory(ref) |@|
        baseDirectory(ref) |@|
        mapConfigs(configs, srcDirectories(ref)) |@|
        target(ref) |@|
        scalacOptions(ref) |@|
        mapConfigs(configs, externalDependencies(ref)) |@|
        mapConfigs(configs, projectDependencies(ref, project)))(content(classpathEntryCollector))
    }
    contents.sequence[ValidationNELS, Content]
  }

  def onFailure(errors: NELS)(implicit state: State) = {
    logger(state).error("Could not create Eclipse project files: %s" format (errors.list mkString ", "))
    state.fail
  }

  def onSuccess(contents: Seq[Content])(implicit state: State) = {
    if (contents.isEmpty)
      logger(state).warn("There was no project to create Eclipse project files for!")
    else {
      val names = contents map writeContent
      logger(state).info("Successfully created Eclipse project files for project(s): %s" format (names mkString ", "))
    }
    state
  }

  def mapConfigs[A](configurations: Seq[Configuration], f: Configuration => ValidationNELS[Seq[A]]) =
    (configurations map f).sequence map (_.flatten.distinct)

  def content(
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry])(
      name: String,
      buildDirectory: File,
      baseDirectory: File,
      srcDirectories: Seq[(File, File)],
      target: File,
      scalacOptions: Seq[(String, String)],
      externalDependencies: Seq[File],
      projectDependencies: Seq[String])(
        implicit state: State) =
    Content(
      name,
      baseDirectory,
      projectXml(name),
      classpath(
        classpathEntryCollector,
        buildDirectory,
        baseDirectory,
        srcDirectories,
        target,
        externalDependencies,
        projectDependencies
      ),
      scalacOptions)

  def projectXml(name: String) =
    <projectDescription>
      <name>{ name }</name>
      <buildSpec>
        <buildCommand>
          <name>org.scala-ide.sdt.core.scalabuilder</name>
        </buildCommand>
      </buildSpec>
      <natures>
        <nature>org.scala-ide.sdt.core.scalanature</nature>
        <nature>org.eclipse.jdt.core.javanature</nature>
      </natures>
    </projectDescription>

  def classpath(
    classpathEntryCollector: PartialFunction[ClasspathEntry, ClasspathEntry],
    buildDirectory: File,
    baseDirectory: File,
    srcDirectories: Seq[(File, File)],
    target: File,
    externalDependencies: Seq[File],
    projectDependencies: Seq[String])(
      implicit state: State) = {
    val entries = Seq(
      (for ((dir, output) <- srcDirectories) yield srcEntry(baseDirectory, output)(dir)).flatten,
      externalDependencies map libEntry(buildDirectory, baseDirectory),
      projectDependencies map ClasspathEntry.Project,
      Seq("org.eclipse.jdt.launching.JRE_CONTAINER") map ClasspathEntry.Con, // TODO Optionally use execution env!
      Seq(output(baseDirectory, target)) map ClasspathEntry.Output
    ).flatten collect classpathEntryCollector map (_.toXml)
    <classpath>{ entries }</classpath>
  }

  def srcEntry(baseDirectory: File, classDirectory: File)(srcDirectory: File)(implicit state: State) = {
    if (srcDirectory.exists()) {
      logger(state).debug("Creating src entry for directory '%s'." format srcDirectory)
      Some(ClasspathEntry.Src(relativize(baseDirectory, srcDirectory), output(baseDirectory, classDirectory)))
    } else {
      logger(state).debug("Skipping src entry for not-existing directory '%s'." format srcDirectory)
      None
    }
  }

  def libEntry(buildDirectory: File, baseDirectory: File)(file: File)(implicit state: State) = {
    val path =
      (IO.relativize(buildDirectory, baseDirectory) |@| IO.relativize(buildDirectory, file))((buildToBase, buildToFile) =>
        "%s/%s".format(buildToBase split FileSep map (_ => "..") mkString FileSep, buildToFile)
      ) getOrElse file.getAbsolutePath
    ClasspathEntry.Lib(path)
  }

  // Getting and transforming settings and task results
{'fraction_non_alphanumeric': 0.10630291627469426, 'fraction_numerical': 0.0011578261813445256, 'mean_word_length': 3.838935574229692, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6266777', 'n_tokens_mistral': 3982, 'n_tokens_neox': 3856, 'n_words': 1009}