/**
   * @param idNoSlash
   * @param projectSlug
   * @param iterationSlug
   * @param locale
   * @param translatedDoc
   * @param extensions
   * @param merge
   * @param assignCreditToUploader
   * @return
   */
  @Override
  public ProcessStatus startTranslatedDocCreationOrUpdate(
      final String idNoSlash,
      final String projectSlug,
      final String iterationSlug,
      final LocaleId locale,
      final TranslationsResource translatedDoc,
      final Set<String> extensions,
      final String merge,
      final boolean assignCreditToUploader) {
    // check security (cannot be on @Restrict as it refers to method
    // parameters)
    identity.checkPermission(
        "modify-translation",
        this.localeServiceImpl.getByLocaleId(locale),
        this.getSecuredIteration(projectSlug, iterationSlug).getProject());

    MergeType mergeType;
    try {
      mergeType = MergeType.valueOf(merge.toUpperCase());
    } catch (Exception e) {
      ProcessStatus status = new ProcessStatus();
      status.setStatusCode(ProcessStatusCode.Failed);
      status.getMessages().add("bad merge type " + merge);
      return status;
    }

    final String id = URIHelper.convertFromDocumentURIId(idNoSlash);
    final MergeType finalMergeType = mergeType;

    AsyncTaskHandle<HDocument> handle = new AsyncTaskHandle<HDocument>();
    Serializable taskId = asyncTaskHandleManager.registerTaskHandle(handle);
    translationServiceImpl.translateAllInDocAsync(
        projectSlug,
        iterationSlug,
        id,
        locale,
        translatedDoc,
        extensions,
        finalMergeType,
        assignCreditToUploader,
        true,
        handle,
        TranslationSourceType.API_UPLOAD);

    return this.getProcessStatus(taskId.toString());
  }
  @Override
  public ProcessStatus startSourceDocCreation(
      final String idNoSlash,
      final String projectSlug,
      final String iterationSlug,
      final Resource resource,
      final Set<String> extensions,
      final boolean copytrans) {
    HProjectIteration hProjectIteration =
        retrieveAndCheckIteration(projectSlug, iterationSlug, true);

    resourceUtils.validateExtensions(extensions); // gettext, comment

    HDocument document = documentDAO.getByDocIdAndIteration(hProjectIteration, resource.getName());

    // already existing non-obsolete document.
    if (document != null) {
      if (!document.isObsolete()) {
        // updates must happen through PUT on the actual resource
        ProcessStatus status = new ProcessStatus();
        status.setStatusCode(ProcessStatusCode.Failed);
        status.getMessages().add("A document with name " + resource.getName() + " already exists.");
        return status;
      }
    }

    String name = "SourceDocCreation: " + projectSlug + "-" + iterationSlug + "-" + idNoSlash;
    AsyncTaskHandle<HDocument> handle = new AsyncTaskHandle<HDocument>();
    Serializable taskId = asyncTaskHandleManager.registerTaskHandle(handle);
    documentServiceImpl.saveDocumentAsync(
        projectSlug, iterationSlug, resource, extensions, copytrans, true, handle);

    return getProcessStatus(taskId.toString()); // TODO Change to return 202
    // Accepted,
    // with a url to get the
    // progress
  }
  @Override
  public ProcessStatus getProcessStatus(String processId) {
    AsyncTaskHandle handle = asyncTaskHandleManager.getHandleByKey(processId);

    if (handle == null) {
      throw new NotFoundException("A process was not found for id " + processId);
    }

    ProcessStatus status = new ProcessStatus();
    status.setStatusCode(handle.isDone() ? ProcessStatusCode.Finished : ProcessStatusCode.Running);
    int perComplete = 100;
    if (handle.getMaxProgress() > 0) {
      perComplete = (handle.getCurrentProgress() * 100 / handle.getMaxProgress());
    }
    status.setPercentageComplete(perComplete);
    status.setUrl("" + processId);

    if (handle.isDone()) {
      Object result = null;
      try {
        result = handle.getResult();
      } catch (InterruptedException e) {
        // The process was forcefully cancelled
        status.setStatusCode(ProcessStatusCode.Failed);
        status.setMessages(Lists.newArrayList(e.getMessage()));
      } catch (ExecutionException e) {
        // Exception thrown while running the task
        status.setStatusCode(ProcessStatusCode.Failed);
        status.setMessages(Lists.newArrayList(e.getCause().getMessage()));
      }

      // TODO Need to find a generic way of returning all object types.
      // Since the only current
      // scenario involves lists of strings, hardcoding to that
      if (result != null && result instanceof List) {
        status.getMessages().addAll((List) result);
      }
    }

    return status;
  }