<commit_before>/**
 * Copyright (C) 2019 Orbeon, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.fr.datamigration

import org.orbeon.dom.saxon.{DocumentWrapper, NodeWrapper}
import org.orbeon.oxf.fr.DataFormatVersion.MigrationVersion
import org.orbeon.oxf.fr.FormRunner._
import org.orbeon.oxf.fr.XMLNames._
import org.orbeon.oxf.fr.datamigration.MigrationSupport._
import org.orbeon.oxf.fr.{DataFormatVersion, FormRunner}
import org.orbeon.oxf.util.CoreUtils._
import org.orbeon.oxf.util.StaticXPath.DocumentNodeInfoType
import org.orbeon.oxf.util.StringUtils._
import org.orbeon.oxf.xforms.NodeInfoFactory.{attributeInfo, elementInfo}
import org.orbeon.oxf.xforms.action.XFormsAPI.{delete, insert}
import org.orbeon.saxon.om.NodeInfo
import org.orbeon.scaxon
import org.orbeon.scaxon.Implicits._
import org.orbeon.scaxon.SimplePath._
import org.orbeon.xforms.BasicNamespaceMapping

import scala.collection.compat._


case class Migration48(
  containerPath : List[PathElem],
  iterationElem : PathElem
) {
  require(containerPath.nonEmpty)
}

case class MigrationSet48(migrations: List[Migration48]) extends MigrationSet {
  val version: MigrationVersion = DataFormatVersion.V480
}

object MigrationOps48 extends MigrationOps {

  import Private._

  type M = MigrationSet48
  val version: MigrationVersion = DataFormatVersion.V480

  def buildMigrationSet(
    outerDocument        : DocumentNodeInfoType,
    availableXBLBindings : Option[DocumentNodeInfoType],
    legacyGridsOnly      : Boolean
  ): Option[MigrationSet48] = {

    def updateWithBindingPath(migration: Migration48, bindingPath: List[PathElem]): Migration48 =
      migration.copy(containerPath = bindingPath ::: migration.containerPath)

    val migrations =
      MigrationSupport.buildGridMigrations(
        outerDocument         = outerDocument,
        availableXBLBindings  = availableXBLBindings,
        migrationsForBinding  = (doc, _) => migrationsForBinding(doc, legacyGridsOnly),
        updateWithBindingPath = updateWithBindingPath
      )

    migrations.nonEmpty option MigrationSet48(migrations.to(List))
  }

  def encodeMigrationsToJson(
    migrationSet : MigrationSet48
  ): String = {

    def encodeOne(m: Migration48) =
      s"""{ "path": "${m.containerPath map (_.value) mkString "/"}", "iteration-name": "${m.iterationElem.value}" }"""

    migrationSet.migrations map encodeOne mkString ("[", ",", "]")
  }

  def decodeMigrationSetFromJson(
    jsonString : String
  ): MigrationSet48 = {

    import spray.json._
    val json = jsonString.parseJson

    // Ouch, a lot of boilerplate!
    // For `MigrationSet20191`, we use Circe's automatic serialization. We could do this here, but we'd need to support
    // the "old" format as well for backward compatibility.
    val migrations =
      Iterator(json) collect {
        case JsArray(m) =>
          m.iterator collect {
            case JsObject(fields) =>

              val path          = fields("path").asInstanceOf[JsString].value
              val iterationName = fields("iteration-name").asInstanceOf[JsString].value

              Migration48(
                path.splitTo[List]("/") map {
                  case PathElem.TrimPathElementRE(p) => PathElem(p)
                  case name                          => throw new IllegalArgumentException(s"invalid migration name: `$name`")
                },
                PathElem(iterationName)
              )
          }
      }

    MigrationSet48(migrations.flatten.to(List))
  }

  def migrateDataDown(
    dataRootElem : NodeWrapper,
    migrationSet : MigrationSet48
  ): MigrationResult = {

    var result: MigrationResult = MigrationResult.None

    partitionNodes(dataRootElem, migrationSet.migrations) foreach {
      case (_, Nil, _, _) =>
        // This can happen if data is pruned
        // https://github.com/orbeon/orbeon-forms/issues/3172
      case (_, container :: tail, _, _) =>
        //assert(tail.isEmpty)

        result = MigrationResult.Some

        val contentForEachIteration =
          (container / * toList) map (iteration => (iteration /@ @*) ++ (iteration / Node) toList) // force

        insert(<commit_msg>Use Circe to decode JSON<commit_after>/**
 * Copyright (C) 2019 Orbeon, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; either version
 * 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
 */
package org.orbeon.oxf.fr.datamigration

import org.orbeon.dom.saxon.{DocumentWrapper, NodeWrapper}
import org.orbeon.oxf.fr.DataFormatVersion.MigrationVersion
import org.orbeon.oxf.fr.FormRunner._
import org.orbeon.oxf.fr.XMLNames._
import org.orbeon.oxf.fr.datamigration.MigrationSupport._
import org.orbeon.oxf.fr.{DataFormatVersion, FormRunner}
import org.orbeon.oxf.util.CoreUtils._
import org.orbeon.oxf.util.StaticXPath.DocumentNodeInfoType
import org.orbeon.oxf.util.StringUtils._
import org.orbeon.oxf.xforms.NodeInfoFactory.{attributeInfo, elementInfo}
import org.orbeon.oxf.xforms.action.XFormsAPI.{delete, insert}
import org.orbeon.saxon.om.NodeInfo
import org.orbeon.scaxon
import org.orbeon.scaxon.Implicits._
import org.orbeon.scaxon.SimplePath._
import org.orbeon.xforms.BasicNamespaceMapping

import scala.collection.compat._


case class Migration48(
  containerPath : List[PathElem],
  iterationElem : PathElem
) {
  require(containerPath.nonEmpty)
}

case class MigrationSet48(migrations: List[Migration48]) extends MigrationSet {
  val version: MigrationVersion = DataFormatVersion.V480
}

object MigrationOps48 extends MigrationOps {

  import Private._

  type M = MigrationSet48
  val version: MigrationVersion = DataFormatVersion.V480

  def buildMigrationSet(
    outerDocument        : DocumentNodeInfoType,
    availableXBLBindings : Option[DocumentNodeInfoType],
    legacyGridsOnly      : Boolean
  ): Option[MigrationSet48] = {

    def updateWithBindingPath(migration: Migration48, bindingPath: List[PathElem]): Migration48 =
      migration.copy(containerPath = bindingPath ::: migration.containerPath)

    val migrations =
      MigrationSupport.buildGridMigrations(
        outerDocument         = outerDocument,
        availableXBLBindings  = availableXBLBindings,
        migrationsForBinding  = (doc, _) => migrationsForBinding(doc, legacyGridsOnly),
        updateWithBindingPath = updateWithBindingPath
      )

    migrations.nonEmpty option MigrationSet48(migrations.to(List))
  }

  def encodeMigrationsToJson(
    migrationSet : MigrationSet48
  ): String = {

    def encodeOne(m: Migration48) =
      s"""{ "path": "${m.containerPath map (_.value) mkString "/"}", "iteration-name": "${m.iterationElem.value}" }"""

    migrationSet.migrations map encodeOne mkString ("[", ",", "]")
  }

  def decodeMigrationSetFromJson(
    jsonString : String
  ): MigrationSet48 = {

    import io.circe.parser
    import io.circe.generic.auto._

    case class PathAndName(path: String, `iteration-name`: String)

    MigrationSet48(
      for {
        pathAnNames                      <- parser.decode[Iterable[PathAndName]](jsonString).toOption.toList
        PathAndName(path, iterationName) <- pathAnNames
      } yield
        Migration48(
          path.splitTo[List]("/") map {
            case PathElem.TrimPathElementRE(p) => PathElem(p)
            case name                          => throw new IllegalArgumentException(s"invalid migration name: `$name`")
          },
          PathElem(iterationName)
        )
    )
  }

  def migrateDataDown(
    dataRootElem : NodeWrapper,
    migrationSet : MigrationSet48
  ): MigrationResult = {

    var result: MigrationResult = MigrationResult.None

    partitionNodes(dataRootElem, migrationSet.migrations) foreach {
      case (_, Nil, _, _) =>
        // This can happen if data is pruned
        // https://github.com/orbeon/orbeon-forms/issues/3172
      case (_, container :: tail, _, _) =>
        //assert(tail.isEmpty)

        result = MigrationResult.Some

        val contentForEachIteration =
          (container / * toList) map (iteration => (iteration /@ @*) ++ (iteration / Node) toList) // force

        insert(
{'fraction_non_alphanumeric': 0.09640790479309969, 'fraction_numerical': 0.010590675837973578, 'mean_word_length': 3.328922495274102, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 17, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6893139', 'n_tokens_mistral': 2780, 'n_tokens_neox': 2652, 'n_words': 641}