@Override
  public Collection<Annotation> updatePreviousAnnotations(
      Collection<Annotation> annotationsToUpdate, AnnotationUpdate update)
      throws ZoomaUpdateException {

    List<Annotation> annotationsList = new ArrayList<>(annotationsToUpdate);
    Collections.sort(
        annotationsList,
        new Comparator<Annotation>() {
          @Override
          public int compare(Annotation o1, Annotation o2) {
            return o1.getProvenance()
                .getSource()
                .getURI()
                .compareTo(o2.getProvenance().getSource().getURI());
          }
        });

    Collection<Annotation> newAnnotations = new HashSet<>();

    try {

      AnnotationSource currentSource = annotationsList.get(0).getProvenance().getSource();
      getAnnotationFactory().acquire(currentSource);

      for (Annotation previousAnnotation : annotationsList) {

        if (!previousAnnotation
            .getProvenance()
            .getSource()
            .getURI()
            .equals(currentSource.getURI())) {
          getAnnotationFactory().release();
          currentSource = previousAnnotation.getProvenance().getSource();
          getAnnotationFactory().acquire(currentSource);
        }

        Property newProperty;
        if (update.getPropertyType() != null && update.getPropertyValue() != null) {
          // both fields have changed
          newProperty =
              new SimpleTypedProperty(update.getPropertyType(), update.getPropertyValue());
        } else if (update.getPropertyType() != null && update.getPropertyValue() == null) {
          // if just the property type has changed
          newProperty =
              new SimpleTypedProperty(
                  update.getPropertyType(),
                  previousAnnotation.getAnnotatedProperty().getPropertyValue());
        } else if (update.getPropertyValue() != null) {
          // if just the property has changed, get the old property value
          newProperty =
              previousAnnotation.getAnnotatedProperty() instanceof TypedProperty
                  ? new SimpleTypedProperty(
                      ((TypedProperty) previousAnnotation.getAnnotatedProperty()).getPropertyType(),
                      update.getPropertyValue())
                  : new SimpleUntypedProperty(update.getPropertyValue());
        } else {
          // if  both are null, use the old property as it is
          newProperty = previousAnnotation.getAnnotatedProperty();
        }

        Collection<URI> semanticTags = new HashSet<>();
        if (update.isRetainSemanticTags() && previousAnnotation.getSemanticTags() != null) {
          semanticTags.addAll(previousAnnotation.getSemanticTags());
        }

        if (update.getSemanticTags() != null) {
          semanticTags.addAll(update.getSemanticTags());
        }

        AnnotationProvenanceTemplate template =
            getAnnotationFactory()
                .getAnnotationLoadingSession()
                .getAnnotationProvenanceTemplate()
                .annotatorIs(ZoomaUsers.getAuthenticatedUser().getFullName())
                .annotationDateIs(new Date());

        Annotation newAnnotation =
            getAnnotationFactory()
                .createAnnotation(
                    previousAnnotation.getAnnotatedBiologicalEntities(),
                    newProperty,
                    template.build(),
                    semanticTags,
                    Collections.singleton(previousAnnotation.getURI()));
        newAnnotations.add(newAnnotation);

        crossLinkAnnotations(previousAnnotation, newAnnotation);
      }

      // save new and update old
      try {
        getAnnotationDAO().create(newAnnotations);
        getAnnotationDAO().update(annotationsToUpdate);
      } catch (ResourceAlreadyExistsException e) {
        throw new ZoomaUpdateException(
            "Couldn't create new annotation as the annotation URI already existed", e);
      }

    } catch (InterruptedException e) {
      throw new ZoomaUpdateException("Update previous annotation operation was interrupted", e);
    } catch (AnonymousUserNotAllowedException e) {
      throw new ZoomaUpdateException("You must be authenticated to perform update operations", e);
    } finally {
      getAnnotationFactory().release();
    }
    return newAnnotations;
  }
  @Override
  public Collection<Annotation> saveAnnotations(Collection<Annotation> annotations)
      throws ZoomaUpdateException {

    List<Annotation> annotationsList = new ArrayList<>(annotations);
    Collections.sort(
        annotationsList,
        new Comparator<Annotation>() {
          @Override
          public int compare(Annotation o1, Annotation o2) {
            return o1.getProvenance()
                .getSource()
                .getURI()
                .compareTo(o2.getProvenance().getSource().getURI());
          }
        });

    Collection<Annotation> newAnnotations = new HashSet<>();
    Collection<Annotation> previousAnnotations = new HashSet<>();

    try {

      AnnotationSource currentSource = annotationsList.get(0).getProvenance().getSource();
      getAnnotationFactory().acquire(currentSource);

      for (Annotation annotation : annotationsList) {

        if (!annotation.getProvenance().getSource().getURI().equals(currentSource.getURI())) {
          getAnnotationFactory().release();
          currentSource = annotation.getProvenance().getSource();
          getAnnotationFactory().acquire(currentSource);
        }

        String username;
        ZoomaUser user = ZoomaUsers.getAuthenticatedUser();
        if (annotation.getProvenance().getAnnotator() != null) {
          username = annotation.getProvenance().getAnnotator();
        } else {
          username = user.getFullName();
        }

        AnnotationProvenanceTemplate template =
            getAnnotationFactory()
                .getAnnotationLoadingSession()
                .getAnnotationProvenanceTemplate()
                .annotatorIs(username)
                .annotationDateIs(new Date());

        Annotation newAnnotation =
            getAnnotationFactory()
                .createAnnotation(
                    annotation.getAnnotatedBiologicalEntities(),
                    annotation.getAnnotatedProperty(),
                    template.build(),
                    annotation.getSemanticTags(),
                    annotation.getReplaces());
        newAnnotations.add(newAnnotation);

        for (URI previousAnnotationUri : annotation.getReplaces()) {
          Annotation previousAnnotation = getAnnotationDAO().read(previousAnnotationUri);
          crossLinkAnnotations(previousAnnotation, newAnnotation);
          previousAnnotations.add(previousAnnotation);
        }
      }

      // save new and update old
      try {
        getAnnotationDAO().create(newAnnotations);
        getAnnotationDAO().update(previousAnnotations);
      } catch (ResourceAlreadyExistsException e) {
        throw new ZoomaUpdateException(
            "Couldn't create new annotation as the annotation URI already existed", e);
      }

    } catch (InterruptedException e) {
      throw new ZoomaUpdateException("Update previous annotation operation was interrupted", e);
    } finally {
      getAnnotationFactory().release();
    }
    return newAnnotations;
  }