/**
  * Merges chunks found in a target translation Project that do not exist in the source translation
  * to a sibling chunk so that no data is lost.
  *
  * @param library
  * @param targetTranslation target translation to merge
  * @return
  */
 public static boolean migrateChunkChanges(
     final Library library, final TargetTranslation targetTranslation) {
   try {
     Logger.i(
         TargetTranslationMigrator.class.getName(),
         "Migrating chunks in target translation " + targetTranslation.getProjectId());
     final SourceTranslation sourceTranslation =
         library.getDefaultSourceTranslation(targetTranslation.getProjectId(), "en");
     if (sourceTranslation == null) {
       Logger.w(
           TargetTranslationMigrator.class.getName(),
           "Could not find a source translation for the target translation "
               + targetTranslation.getId());
       return false;
     }
     if (targetTranslation.getPath().exists()) {
       boolean migrationSuccess = true;
       // perform the chunk migration on each chapter of the target translation
       for (ChapterTranslation chapterTranslation : targetTranslation.getChapterTranslations()) {
         Chapter chapter = library.getChapter(sourceTranslation, chapterTranslation.getId());
         if (chapter != null) {
           boolean success =
               mergeInvalidChunksInChapter(library, sourceTranslation, targetTranslation, chapter);
           migrationSuccess = migrationSuccess && success;
         }
       }
       return migrationSuccess;
     }
   } catch (Exception e) {
     Logger.e(
         TargetTranslationMigrator.class.getName(),
         "Failed to merge the chunks in the target translation "
             + targetTranslation.getProjectId());
   }
   return false;
 }
  /**
   * Merges invalid chunks found in the target translation with a valid sibling chunk in order to
   * preserve translation data. Merged chunks are marked as not finished to force translators to
   * review the changes.
   *
   * @param library
   * @param sourceTranslation
   * @param targetTranslation
   * @param chapter
   * @return
   */
  private static boolean mergeInvalidChunksInChapter(
      final Library library,
      final SourceTranslation sourceTranslation,
      final TargetTranslation targetTranslation,
      final Chapter chapter) {
    boolean success = true;
    Logger.i(
        TargetTranslationMigrator.class.getName(),
        "Searching chapter " + chapter.getId() + " for invalid chunks ");
    // TRICKY: the translation format doesn't matter for migrating
    FrameTranslation[] frameTranslations =
        targetTranslation.getFrameTranslations(chapter.getId(), TranslationFormat.DEFAULT);
    String invalidChunks = "";
    Frame lastValidFrame = null;
    for (FrameTranslation frameTranslation : frameTranslations) {
      Frame frame = library.getFrame(sourceTranslation, chapter.getId(), frameTranslation.getId());
      if (frame != null) {
        lastValidFrame = frame;
        // merge invalid frames into the existing frame
        if (!invalidChunks.isEmpty()) {
          targetTranslation.applyFrameTranslation(
              frameTranslation, invalidChunks + frameTranslation.body);
          invalidChunks = "";
          targetTranslation.reopenFrame(frame);
        }
      } else if (!frameTranslation.body.trim().isEmpty()) {
        if (lastValidFrame == null) {
          // collect invalid frame
          invalidChunks += frameTranslation.body + CHUNK_MERGE_MARKER;
        } else { // if last frame is not null, then append invalid chunk to it
          FrameTranslation lastFrameTranslation =
              targetTranslation.getFrameTranslation(lastValidFrame);
          targetTranslation.applyFrameTranslation(
              lastFrameTranslation,
              lastFrameTranslation.body + CHUNK_MERGE_MARKER + frameTranslation.body);
          targetTranslation.reopenFrame(lastValidFrame);
        }
        targetTranslation.applyFrameTranslation(frameTranslation, ""); // clear out old data
      }
    }
    // clean up remaining invalid chunks
    if (!invalidChunks.isEmpty()) {
      if (lastValidFrame == null) {
        // push remaining invalid chunks onto the first available frame
        String[] frameslugs = library.getFrameSlugs(sourceTranslation, chapter.getId());
        if (frameslugs.length > 0) {
          lastValidFrame = library.getFrame(sourceTranslation, chapter.getId(), frameslugs[0]);
        } else {
          Logger.w(
              TargetTranslationMigrator.class.getName(),
              "No frames were found for chapter " + chapter.getId());
        }
      }

      if (lastValidFrame != null) {
        FrameTranslation frameTranslation = targetTranslation.getFrameTranslation(lastValidFrame);
        targetTranslation.applyFrameTranslation(
            frameTranslation, invalidChunks + CHUNK_MERGE_MARKER + frameTranslation.body);
        targetTranslation.reopenFrame(lastValidFrame);
      }
    }

    return success;
  }