public Integer runCopyTrans(
      HLocale targetLocale,
      HCopyTransOptions options,
      HDocument document,
      boolean requireTranslationReview,
      List<HTextFlow> copyTargets) {
    int numCopied = 0;
    boolean checkContext = false, checkProject = false, checkDocument = false;

    // Only outright reject copies if the options say so
    if (options.getDocIdMismatchAction() == HCopyTransOptions.ConditionRuleAction.REJECT) {
      checkDocument = true;
    }
    if (options.getProjectMismatchAction() == HCopyTransOptions.ConditionRuleAction.REJECT) {
      checkProject = true;
    }
    if (options.getContextMismatchAction() == HCopyTransOptions.ConditionRuleAction.REJECT) {
      checkContext = true;
    }

    Long actorId = authenticatedAccount.getPerson().getId();
    for (HTextFlow textFlow : copyTargets) {
      if (shouldFindMatch(textFlow, targetLocale, requireTranslationReview)) {

        Optional<HTextFlowTarget> bestMatch =
            translationFinder.searchBestMatchTransMemory(
                textFlow,
                targetLocale.getLocaleId(),
                document.getLocale().getLocaleId(),
                checkContext,
                checkDocument,
                checkProject);
        if (bestMatch.isPresent()) {
          numCopied++;

          saveCopyTransMatch(actorId, bestMatch.get(), textFlow, options, requireTranslationReview);
        }
      }
    }
    return numCopied;
  }
  @Override
  public String call() throws Exception {
    // Needed Components
    DocumentDAO documentDAO = (DocumentDAO) Component.getInstance(DocumentDAO.class);
    LocaleDAO localeDAO = (LocaleDAO) Component.getInstance(LocaleDAO.class);
    ResourceUtils resourceUtils = (ResourceUtils) Component.getInstance(ResourceUtils.class);
    TextFlowTargetDAO textFlowTargetDAO =
        (TextFlowTargetDAO) Component.getInstance(TextFlowTargetDAO.class);
    FileSystemService fileSystemService =
        (FileSystemService) Component.getInstance(FileSystemServiceImpl.class);
    ConfigurationService configurationService =
        (ConfigurationService) Component.getInstance(ConfigurationServiceImpl.class);

    final String projectDirectory = projectSlug + "-" + iterationSlug + "/";
    final HLocale hLocale = localeDAO.findByLocaleId(new LocaleId(localeId));
    final String mappedLocale = hLocale.getLocaleId().getId();
    final String localeDirectory = projectDirectory + mappedLocale + "/";

    final File downloadFile = fileSystemService.createDownloadStagingFile("zip");
    final FileOutputStream output = new FileOutputStream(downloadFile);
    final ZipOutputStream zipOutput = new ZipOutputStream(output);
    zipOutput.setMethod(ZipOutputStream.DEFLATED);
    final PoWriter2 poWriter = new PoWriter2(false, !isPoProject);
    final Set<String> extensions = new HashSet<String>();

    extensions.add("gettext");
    extensions.add("comment");

    // Generate the download descriptor file
    String downloadId =
        fileSystemService.createDownloadDescriptorFile(
            downloadFile, projectSlug + "_" + iterationSlug + "_" + localeId + ".zip", userName);

    // Add the config file at the root of the project directory
    String configFilename = projectDirectory + configurationService.getConfigurationFileName();
    zipOutput.putNextEntry(new ZipEntry(configFilename));
    zipOutput.write(
        configurationService
            .getConfigForOfflineTranslation(projectSlug, iterationSlug, hLocale)
            .getBytes());
    zipOutput.closeEntry();
    getHandle().increaseProgress(1);

    final List<HDocument> allIterationDocs =
        documentDAO.getAllByProjectIteration(projectSlug, iterationSlug);
    for (HDocument document : allIterationDocs) {
      // Stop the process if signaled to do so
      if (getHandle().isCancelled()) {
        zipOutput.close();
        downloadFile.delete();
        fileSystemService.deleteDownloadDescriptorFile(downloadId);
        return null;
      }

      TranslationsResource translationResource = new TranslationsResource();
      List<HTextFlowTarget> hTargets = textFlowTargetDAO.findTranslations(document, hLocale);
      resourceUtils.transferToTranslationsResource(
          translationResource, document, hLocale, extensions, hTargets, Optional.<String>absent());

      Resource res = resourceUtils.buildResource(document);

      String filename = localeDirectory + document.getDocId() + ".po";
      zipOutput.putNextEntry(new ZipEntry(filename));
      poWriter.writePo(zipOutput, "UTF-8", res, translationResource);
      zipOutput.closeEntry();

      getHandle().increaseProgress(1);
    }

    zipOutput.flush();
    zipOutput.close();

    return downloadId;
  }
  private void saveCopyTransMatch(
      Long actorId,
      final HTextFlowTarget matchingTarget,
      final HTextFlow originalTf,
      final HCopyTransOptions options,
      final boolean requireTranslationReview) {
    final HProjectIteration matchingTargetProjectIteration =
        matchingTarget.getTextFlow().getDocument().getProjectIteration();
    // lazy evaluation of some conditions
    Supplier<Boolean> contextMatches =
        new Supplier<Boolean>() {
          @Override
          public Boolean get() {
            return originalTf.getResId().equals(matchingTarget.getTextFlow().getResId());
          }
        };
    Supplier<Boolean> projectMatches =
        new Supplier<Boolean>() {
          public Boolean get() {
            return originalTf
                .getDocument()
                .getProjectIteration()
                .getProject()
                .getId()
                .equals(matchingTargetProjectIteration.getProject().getId());
          }
        };
    Supplier<Boolean> docIdMatches =
        new Supplier<Boolean>() {
          @Override
          public Boolean get() {
            return originalTf
                .getDocument()
                .getDocId()
                .equals(matchingTarget.getTextFlow().getDocument().getDocId());
          }
        };
    final ContentState copyState =
        determineContentState(
            contextMatches,
            projectMatches,
            docIdMatches,
            options,
            requireTranslationReview,
            matchingTarget.getState());

    boolean hasValidationError =
        validationTranslations(
            copyState,
            matchingTargetProjectIteration,
            originalTf.getContents(),
            matchingTarget.getContents());

    if (hasValidationError) {
      return;
    }

    HTextFlowTarget hTarget =
        textFlowTargetDAO.getOrCreateTarget(originalTf, matchingTarget.getLocale());
    ContentState prevState = hTarget.getId() == null ? ContentState.New : hTarget.getState();
    if (shouldOverwrite(hTarget, copyState)) {
      // NB we don't touch creationDate
      hTarget.setTextFlowRevision(originalTf.getRevision());
      hTarget.setLastChanged(matchingTarget.getLastChanged());
      hTarget.setLastModifiedBy(matchingTarget.getLastModifiedBy());
      hTarget.setTranslator(matchingTarget.getTranslator());
      // TODO rhbz953734 - will need a new copyTran option for
      // review state
      if (copyState == ContentState.Approved) {
        hTarget.setReviewer(matchingTarget.getReviewer());
      }
      hTarget.setContents(matchingTarget.getContents());
      hTarget.setState(copyState);
      if (matchingTarget.getComment() == null) {
        hTarget.setComment(null);
      } else {
        HSimpleComment hComment = hTarget.getComment();
        if (hComment == null) {
          hComment = new HSimpleComment();
          hTarget.setComment(hComment);
        }
        hComment.setComment(matchingTarget.getComment().getComment());
      }
      hTarget.setRevisionComment(TranslationUtil.getCopyTransMessage(matchingTarget));
      hTarget.setSourceType(TranslationSourceType.COPY_TRANS);

      TranslationUtil.copyEntity(matchingTarget, hTarget);

      // TODO Maybe we should think about registering a Hibernate
      // integrator for these updates
      signalCopiedTranslation(hTarget, prevState, originalTf.getWordCount());
    }
  }