<commit_before>package com.typesafe.sbt
package packager
package windows

import Keys._
import sbt._

import collection.mutable.ArrayBuffer

case class WindowsProductInfo(
  id: String, // UUID of the package
  title: String, // Human readable name of the package
  version: String, // Windows version
  maintainer: String,
  description: String,
  upgradeId: String, // UUID for upgrading
  comments: String = "",
  installScope: String = "perMachine",
  installerVersion: String = "200",
  compressed: Boolean = true)

sealed trait FeatureComponent
/** Define a new feature, that will be selectable in the default MSI. */
case class WindowsFeature(
  id: String,
  title: String,
  desc: String,
  absent: String = "allow",
  level: String = "1",
  display: String = "collapse",
  components: Seq[FeatureComponent] = Seq.empty) extends FeatureComponent {}
/** Adds a file into a given windows feature. */
case class ComponentFile(
  source: String,
  editable: Boolean = false) extends FeatureComponent
/**
 * Will add the directory to the windows path.  NOTE: Only one of these
 * per MSI.
 */
case class AddDirectoryToPath(dir: String = "") extends FeatureComponent
case class AddShortCuts(
  target: Seq[String],
  workingDir: String = "INSTALLDIR") extends FeatureComponent

// TODO - Shortcut as a component element.

/** Helper functions to deal with Wix/CAB craziness. */
object WixHelper {
  /** Generates a windows friendly GUID for use in random locations in the build. */
  def makeGUID: String = java.util.UUID.randomUUID.toString

  // TODO - Fragment out this function a bit so it's not so ugly/random.  
  def makeWixProductConfig(name: String, product: WindowsProductInfo, features: Seq[WindowsFeature], license: Option[File] = None): scala.xml.Node = {
    // TODO - First we find directories...
    // Adds all subdirectories... there was a bug when there were directories with only subdirs and no files in the tree,
    // so there was a gap and dirXml failed to create some directories
    def allParentDirs(f: File): Seq[File] = Option(f).toSeq.flatMap(f => f +: allParentDirs(f.getParentFile))
    val filenamesPrep =
      for {
        f <- features
        ComponentFile(name, _) <- f.components
      } yield allParentDirs(file(name))
    val filenames = filenamesPrep.flatten.map(_.toString.replaceAll("\\\\", "/")).filter(_ != "")
    // Now for directories...
    def parentDir(filename: String) = { filename take (filename lastIndexOf '/') }
    def simpleName(filename: String) = {
      val lastSlash =
        if (filename contains '/') filename lastIndexOf '/'
        else filename lastIndexOf '\\'
      filename drop (lastSlash + 1)
    }
    val dirs = (filenames map parentDir).distinct;
    // Now we need our directory tree xml?
    val dirToChildren = dirs groupBy parentDir;
    def dirXml(currentDir: String): scala.xml.Node = if (!currentDir.isEmpty) {
      val children = dirToChildren.getOrElse(currentDir, Seq.empty)
      <Directory Id={ cleanStringForId(currentDir) } Name={ simpleName(currentDir) }>
        {
          children map dirXml
        }
      </Directory>
    } else <!-- -->

    // We need component helpers...
    case class ComponentInfo(id: String, xml: scala.xml.Node)
    def makeComponentInfo(c: FeatureComponent): ComponentInfo = c match {
      case w: WindowsFeature => sys.error("Nested windows features currently unsupported!")
      case AddDirectoryToPath(dir) =>
        val dirRef = if (dir.isEmpty) "INSTALLDIR" else cleanStringForId(dir)
        val homeEnvVar = archetypes.JavaAppBatScript.makeEnvFriendlyName(name) + "_HOME"
        val pathAddition =
          if (dir.isEmpty) "%" + homeEnvVar + "%"
          else "[INSTALLDIR]" + dir.replaceAll("\\/", "\\\\")
        val id = cleanStringForId(dir).takeRight(65) + "PathC"
        val guid = makeGUID
        val xml =
          <DirectoryRef Id={ dirRef }>
            <Component Id={ id } Guid={ guid }>
              <CreateFolder/>
              <Environment Id={ homeEnvVar } Name={ homeEnvVar } Value="[INSTALLDIR]" Permanent="no" Action="set" System="yes"/>
              <Environment Id="PATH" Name="PATH" Value={ pathAddition } Permanent="no" Part="last" Action="set" System="yes"/>
            </Component>
          </DirectoryRef>
        ComponentInfo(id, xml)
      case ComponentFile(name, editable) =>
        val uname = name.replaceAll("\\\\", "/")
        val dir = parentDir(uname).replaceAll("//", "/").stripSuffix("/").stripSuffix("/")
        val dirRef = if (dir.isEmpty) "INSTALLDIR" else cleanStringForId(dir)
        val fname = simpleName(uname)
        val id = cleanStringForId(uname).takeRight(67) // Room for "fl_"
        val xml =
          <DirectoryRef Id={ dirRef }>
            <Component Id={ id } Guid={ makeGUID }>
              <File Id={ "fl_" + id } Name={ cleanFileName(fname) } DiskId='1' Source={ cleanFileName(uname) }>
                {
                  if (editable) {
                    <xml:group>
                      <util:PermissionEx User="Administrators" GenericAll="yes"/>
                      <util:PermissionEx User="Users" GenericAll="yes"/>
                    </xml:group>
                  } else Seq.empty
                }
              </File>
            </Component>
          </DirectoryRef>
        ComponentInfo(id, xml)
      // TODO - To have shortcuts, you MUST put something in the registry.  Here,
      // We should have shortcuts actually provide us with what they want in the registry,
      // rather than forcing it to be something.
      // Also, we need some mechanism to ensure the start menu folder is removed in the event
      // that we remove all menu items.
      case AddShortCuts(targets, workingDir) =>
        val id = cleanStringForId("shortcut_" + makeGUID).takeRight(70 - targets.size.toString().size - 3) // Room for "_SC"+incremental number
        val xml =
          <DirectoryRef Id="ApplicationProgramsFolder">
            <Component Id={ id } Guid={ makeGUID }>
              {
                var i = 0
                for (target <- targets) yield {
                  i = i + 1
                  val name = simpleName(target)
                  val desc = "Edit configuration file: " + name
                  val cleanName = name.replaceAll("[\\.-\\\\//]+", "_")
                  <Shortcut Id={ id + "_SC" + ("%0" + targets.size.toString().size + "d").format(i) } Name={ cleanName } Description={ desc } Target={ "[INSTALLDIR]\\" + target.replaceAll("\\/", "\\\\") } WorkingDirectory="INSTALLDIR"/>
                }<commit_msg>small maths<commit_after>package com.typesafe.sbt
package packager
package windows

import Keys._
import sbt._

import collection.mutable.ArrayBuffer

case class WindowsProductInfo(
  id: String, // UUID of the package
  title: String, // Human readable name of the package
  version: String, // Windows version
  maintainer: String,
  description: String,
  upgradeId: String, // UUID for upgrading
  comments: String = "",
  installScope: String = "perMachine",
  installerVersion: String = "200",
  compressed: Boolean = true)

sealed trait FeatureComponent
/** Define a new feature, that will be selectable in the default MSI. */
case class WindowsFeature(
  id: String,
  title: String,
  desc: String,
  absent: String = "allow",
  level: String = "1",
  display: String = "collapse",
  components: Seq[FeatureComponent] = Seq.empty) extends FeatureComponent {}
/** Adds a file into a given windows feature. */
case class ComponentFile(
  source: String,
  editable: Boolean = false) extends FeatureComponent
/**
 * Will add the directory to the windows path.  NOTE: Only one of these
 * per MSI.
 */
case class AddDirectoryToPath(dir: String = "") extends FeatureComponent
case class AddShortCuts(
  target: Seq[String],
  workingDir: String = "INSTALLDIR") extends FeatureComponent

// TODO - Shortcut as a component element.

/** Helper functions to deal with Wix/CAB craziness. */
object WixHelper {
  /** Generates a windows friendly GUID for use in random locations in the build. */
  def makeGUID: String = java.util.UUID.randomUUID.toString

  // TODO - Fragment out this function a bit so it's not so ugly/random.  
  def makeWixProductConfig(name: String, product: WindowsProductInfo, features: Seq[WindowsFeature], license: Option[File] = None): scala.xml.Node = {
    // TODO - First we find directories...
    // Adds all subdirectories... there was a bug when there were directories with only subdirs and no files in the tree,
    // so there was a gap and dirXml failed to create some directories
    def allParentDirs(f: File): Seq[File] = Option(f).toSeq.flatMap(f => f +: allParentDirs(f.getParentFile))
    val filenamesPrep =
      for {
        f <- features
        ComponentFile(name, _) <- f.components
      } yield allParentDirs(file(name))
    val filenames = filenamesPrep.flatten.map(_.toString.replaceAll("\\\\", "/")).filter(_ != "")
    // Now for directories...
    def parentDir(filename: String) = { filename take (filename lastIndexOf '/') }
    def simpleName(filename: String) = {
      val lastSlash =
        if (filename contains '/') filename lastIndexOf '/'
        else filename lastIndexOf '\\'
      filename drop (lastSlash + 1)
    }
    val dirs = (filenames map parentDir).distinct;
    // Now we need our directory tree xml?
    val dirToChildren = dirs groupBy parentDir;
    def dirXml(currentDir: String): scala.xml.Node = if (!currentDir.isEmpty) {
      val children = dirToChildren.getOrElse(currentDir, Seq.empty)
      <Directory Id={ cleanStringForId(currentDir) } Name={ simpleName(currentDir) }>
        {
          children map dirXml
        }
      </Directory>
    } else <!-- -->

    // We need component helpers...
    case class ComponentInfo(id: String, xml: scala.xml.Node)
    def makeComponentInfo(c: FeatureComponent): ComponentInfo = c match {
      case w: WindowsFeature => sys.error("Nested windows features currently unsupported!")
      case AddDirectoryToPath(dir) =>
        val dirRef = if (dir.isEmpty) "INSTALLDIR" else cleanStringForId(dir)
        val homeEnvVar = archetypes.JavaAppBatScript.makeEnvFriendlyName(name) + "_HOME"
        val pathAddition =
          if (dir.isEmpty) "%" + homeEnvVar + "%"
          else "[INSTALLDIR]" + dir.replaceAll("\\/", "\\\\")
        val id = cleanStringForId(dir).takeRight(65) + "PathC"
        val guid = makeGUID
        val xml =
          <DirectoryRef Id={ dirRef }>
            <Component Id={ id } Guid={ guid }>
              <CreateFolder/>
              <Environment Id={ homeEnvVar } Name={ homeEnvVar } Value="[INSTALLDIR]" Permanent="no" Action="set" System="yes"/>
              <Environment Id="PATH" Name="PATH" Value={ pathAddition } Permanent="no" Part="last" Action="set" System="yes"/>
            </Component>
          </DirectoryRef>
        ComponentInfo(id, xml)
      case ComponentFile(name, editable) =>
        val uname = name.replaceAll("\\\\", "/")
        val dir = parentDir(uname).replaceAll("//", "/").stripSuffix("/").stripSuffix("/")
        val dirRef = if (dir.isEmpty) "INSTALLDIR" else cleanStringForId(dir)
        val fname = simpleName(uname)
        val id = cleanStringForId(uname).takeRight(67) // Room for "fl_"
        val xml =
          <DirectoryRef Id={ dirRef }>
            <Component Id={ id } Guid={ makeGUID }>
              <File Id={ "fl_" + id } Name={ cleanFileName(fname) } DiskId='1' Source={ cleanFileName(uname) }>
                {
                  if (editable) {
                    <xml:group>
                      <util:PermissionEx User="Administrators" GenericAll="yes"/>
                      <util:PermissionEx User="Users" GenericAll="yes"/>
                    </xml:group>
                  } else Seq.empty
                }
              </File>
            </Component>
          </DirectoryRef>
        ComponentInfo(id, xml)
      // TODO - To have shortcuts, you MUST put something in the registry.  Here,
      // We should have shortcuts actually provide us with what they want in the registry,
      // rather than forcing it to be something.
      // Also, we need some mechanism to ensure the start menu folder is removed in the event
      // that we remove all menu items.
      case AddShortCuts(targets, workingDir) =>
        val id = cleanStringForId("shortcut_" + makeGUID).takeRight(67 - targets.size.toString().size) // Room for "_SC"+incremental number
        val xml =
          <DirectoryRef Id="ApplicationProgramsFolder">
            <Component Id={ id } Guid={ makeGUID }>
              {
                var i = 0
                for (target <- targets) yield {
                  i = i + 1
                  val name = simpleName(target)
                  val desc = "Edit configuration file: " + name
                  val cleanName = name.replaceAll("[\\.-\\\\//]+", "_")
                  <Shortcut Id={ id + "_SC" + ("%0" + targets.size.toString().size + "d").format(i) } Name={ cleanName } Description={ desc } Target={ "[INSTALLDIR]\\" + target.replaceAll("\\/", "\\\\") } WorkingDirectory="INSTALLDIR"/>
                }
{'fraction_non_alphanumeric': 0.12245363332319854, 'fraction_numerical': 0.002356339312861052, 'mean_word_length': 2.5339779747515445, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 24}, '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': '6614254', 'n_tokens_mistral': 3758, 'n_tokens_neox': 3621, 'n_words': 1154}