private PrimaryVersionSpec resolvePrimaryVersionSpec(
      ProjectHistory projectHistory, PrimaryVersionSpec versionSpec)
      throws InvalidVersionSpecException {
    final int index = versionSpec.getIdentifier();
    final String branch = versionSpec.getBranch();
    final int versions = projectHistory.getVersions().size();
    if (0 > index || index >= versions || branch == null) {
      throw new InvalidVersionSpecException(
          MessageFormat.format(Messages.VersionSubInterfaceImpl_InvalidVersionRequested, index));
    }

    if (branch.equals(VersionSpec.GLOBAL)) {
      return projectHistory.getVersions().get(index).getPrimarySpec();
    }

    // Get biggest primary version of given branch which is equal or lower
    // to the given versionSpec
    for (int i = index; i >= 0; i--) {
      final Version version = projectHistory.getVersions().get(i);
      if (branch.equals(version.getPrimarySpec().getBranch())) {
        return version.getPrimarySpec();
      }
    }
    throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_PrimaryVersionNotFound);
  }
  /**
   * @param projectId
   * @param changePackage
   * @param projectHistory
   * @param newVersion
   * @param newProjectState
   * @throws FatalESException
   */
  private void trySave(
      ProjectId projectId,
      AbstractChangePackage changePackage,
      final ProjectHistory projectHistory,
      Version newVersion,
      final Project newProjectState)
      throws FatalESException {
    getResourceHelper()
        .createResourceForProject(
            newProjectState, newVersion.getPrimarySpec(), projectHistory.getProjectId());
    getResourceHelper()
        .createResourceForChangePackage(changePackage, newVersion.getPrimarySpec(), projectId);
    if (FileBasedChangePackage.class.isInstance(changePackage)) {
      try {
        /* move the temporary file to the project folder */
        final URI uri = changePackage.eResource().getURI();
        final URI normalizedUri =
            changePackage.eResource().getResourceSet().getURIConverter().normalize(uri);
        final String filePath = normalizedUri.toFileString() + ".1"; // $NON-NLS-1$
        FileBasedChangePackage.class.cast(changePackage).move(filePath);
        ModelUtil.saveResource(changePackage.eResource(), ModelUtil.getResourceLogger());
      } catch (final IOException ex) {
        throw new FatalESException(StorageException.NOSAVE, ex);
      }
    }

    getResourceHelper().createResourceForVersion(newVersion, projectHistory.getProjectId());

    newVersion.setProjectStateResource(newProjectState.eResource());
    newVersion.setChangeResource(changePackage.eResource());
  }
 /**
  * Helper method which retrieves the next version in the history tree. This method must be used in
  * reversed order. With the introduction of branches, the versions are organized in a tree
  * structure. Therefore, next versions are always searched for walking up the tree.
  *
  * @param currentVersion current version
  * @return version
  * @throws InvalidVersionSpecException if the path can't be followed further
  */
 public static Version findNextVersion(Version currentVersion) throws InvalidVersionSpecException {
   // find next version
   if (currentVersion.getPreviousVersion() != null) {
     currentVersion = currentVersion.getPreviousVersion();
   } else if (currentVersion.getAncestorVersion() != null) {
     currentVersion = currentVersion.getAncestorVersion();
   } else {
     throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_NextVersionInvalid);
   }
   return currentVersion;
 }
 private Version getVersion(ProjectHistory projectHistory, PrimaryVersionSpec baseVersionSpec) {
   if (0 > baseVersionSpec.getIdentifier()
       || baseVersionSpec.getIdentifier() > projectHistory.getVersions().size() - 1) {
     return null;
   }
   final Version version = projectHistory.getVersions().get(baseVersionSpec.getIdentifier());
   if (version == null || !version.getPrimarySpec().equals(baseVersionSpec)) {
     return null;
   }
   return version;
 }
 private PrimaryVersionSpec resolveTagVersionSpec(
     ProjectHistory projectHistory, TagVersionSpec versionSpec)
     throws InvalidVersionSpecException {
   for (final Version version : projectHistory.getVersions()) {
     for (final TagVersionSpec tag : version.getTagSpecs()) {
       if (versionSpec.equals(tag)) {
         return ModelUtil.clone(version.getPrimarySpec());
       }
     }
   }
   throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_TagVersionNotFound);
 }
 private PrimaryVersionSpec resolveDateVersionSpec(
     ProjectHistory projectHistory, DateVersionSpec versionSpec) {
   for (final Version version : projectHistory.getVersions()) {
     final LogMessage logMessage = version.getLogMessage();
     if (logMessage == null || logMessage.getDate() == null) {
       continue;
     }
     if (versionSpec.getDate().before(logMessage.getDate())) {
       final Version previousVersion = version.getPreviousVersion();
       if (previousVersion == null) {
         return VersioningFactory.eINSTANCE.createPrimaryVersionSpec();
       }
       return previousVersion.getPrimarySpec();
     }
   }
   return projectHistory.getLastVersion().getPrimarySpec();
 }
  private PrimaryVersionSpec resolvePagedUpdateVersionSpec(
      ProjectHistory projectHistory, PagedUpdateVersionSpec baseVersion) {

    int changes = 0;
    PrimaryVersionSpec resolvedSpec = baseVersion.getBaseVersionSpec();
    int maxChanges = baseVersion.getMaxChanges();

    int i = resolvedSpec.getIdentifier();

    AbstractChangePackage cp = projectHistory.getVersions().get(i).getChanges();

    if (i == projectHistory.getVersions().size() - 1) {
      return projectHistory.getVersions().get(i).getPrimarySpec();
    }

    do {
      cp = projectHistory.getVersions().get(++i).getChanges();
    } while (cp == null && i < projectHistory.getVersions().size());

    // pull at least one change package
    if (cp.leafSize() > maxChanges) {
      maxChanges = cp.leafSize();
    }

    while (changes < maxChanges && i < projectHistory.getVersions().size()) {
      resolvedSpec = projectHistory.getVersions().get(i).getPrimarySpec();
      final Version version = projectHistory.getVersions().get(i);
      final AbstractChangePackage changePackage = version.getChanges();

      if (changePackage != null) {
        final int size = changePackage.leafSize();
        if (changes + size >= maxChanges) {
          resolvedSpec = projectHistory.getVersions().get(i).getPrimarySpec();
          break;
        }
        changes += size;
      }
      i += 1;
    }

    return resolvedSpec;
  }
 private Version performRegularCommit(
     PrimaryVersionSpec baseVersionSpec,
     LogMessage logMessage,
     final ACUser user,
     final ProjectHistory projectHistory,
     final BranchInfo baseBranch,
     final Version baseVersion,
     final Project newProjectState)
     throws ESUpdateRequiredException, ESException {
   Version newVersion;
   // If branch is null or branch equals base branch, create new
   // version for specific branch
   if (!baseVersionSpec.equals(isHeadOfBranch(projectHistory, baseVersion.getPrimarySpec()))) {
     throw new ESUpdateRequiredException();
   }
   newVersion = createVersion(projectHistory, newProjectState, logMessage, user, baseVersion);
   newVersion.setPreviousVersion(baseVersion);
   baseBranch.setHead(ModelUtil.clone(newVersion.getPrimarySpec()));
   return newVersion;
 }
  private void rollback(
      final ProjectHistory projectHistory,
      final BranchInfo baseBranch,
      final Version baseVersion,
      Version newVersion,
      BranchInfo newBranch,
      final FatalESException e)
      throws StorageException {
    projectHistory.getVersions().remove(newVersion);

    if (newBranch == null) {
      // normal commit
      baseVersion.setNextVersion(null);
      baseBranch.setHead(ModelUtil.clone(baseVersion.getPrimarySpec()));
    } else {
      // branch commit
      baseVersion.getBranchedVersions().remove(newVersion);
      projectHistory.getBranches().remove(newBranch);
    }
    // TODO: delete obsolete project, change package and version files
    throw new StorageException(StorageException.NOSAVE, e);
  }
  /**
   * Whether a projectstate should be deleted according to the server configuration.
   *
   * @param projectId project id
   * @param previousHeadVersion last head version
   * @param resourceHelper the resource helper
   * @return <code>true</code> if project state should be deleted, <code>false</code> otherwise
   */
  static boolean shouldDeleteOldProjectStateAccordingToOptions(
      ProjectId projectId, Version previousHeadVersion, ResourceHelper resourceHelper) {

    final boolean keepBecauseOfTaggedVersion =
        ServerConfiguration.createProjectStateOnTag()
            && !previousHeadVersion.getTagSpecs().isEmpty();
    if (keepBecauseOfTaggedVersion) {
      return false;
    }

    final String property =
        ServerConfiguration.getProperties()
            .getProperty(
                ServerConfiguration.PROJECTSTATE_VERSION_PERSISTENCE,
                ServerConfiguration.PROJECTSPACE_VERSION_PERSISTENCE_DEFAULT);

    if (property.equals(ServerConfiguration.PROJECTSTATE_VERSION_PERSISTENCE_EVERYXVERSIONS)) {

      final int x =
          resourceHelper.getXFromPolicy(
              ServerConfiguration.PROJECTSTATE_VERSION_PERSISTENCE_EVERYXVERSIONS_X,
              ServerConfiguration.PROJECTSTATE_VERSION_PERSISTENCE_EVERYXVERSIONS_X_DEFAULT,
              false);

      // always save projecstate of first version
      final int lastVersion = previousHeadVersion.getPrimarySpec().getIdentifier();
      if (lastVersion != 0 && lastVersion % x != 0) {
        return true;
      }

    } else {
      return true;
    }

    return false;
  }
  private PrimaryVersionSpec resolveAncestorVersionSpec(
      ProjectHistory projectHistory, AncestorVersionSpec versionSpec)
      throws InvalidVersionSpecException {

    Version currentSource = getVersion(projectHistory, versionSpec.getSource());
    Version currentTarget = getVersion(projectHistory, versionSpec.getTarget());

    if (currentSource == null || currentTarget == null) {
      throw new InvalidVersionSpecException(
          Messages.VersionSubInterfaceImpl_Invalid_Source_Or_Target);
    }

    // The goal is to find the common ancestor version of the source and
    // target version from different branches. In
    // order to find the ancestor the algorithm starts at the specified
    // version and walks down the version tree in
    // parallel for source and target until the current versions are equal
    // and the ancestor is found. In Each step
    // only one version (of target and source) is decremented. To find the
    // global ancestor it is necessary that the
    // version with the higher version number is decremented.
    while (currentSource != null && currentTarget != null) {
      if (currentSource == currentTarget) {
        return currentSource.getPrimarySpec();
      }

      // Shortcut for most common merge usecase: If you have 2 parallel
      // branches, only seperated by one level and merge several times from the one branch into the
      // another.
      // This case is also supported by #getVersions
      if (currentSource.getMergedFromVersion().contains(currentTarget)) {
        return currentTarget.getPrimarySpec();
      }

      if (currentSource.getPrimarySpec().getIdentifier()
          >= currentTarget.getPrimarySpec().getIdentifier()) {
        currentSource = findNextVersion(currentSource);
      } else {
        currentTarget = findNextVersion(currentTarget);
      }
    }
    throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_NoAncestorFound);
  }
  /**
   * Returns a list of versions starting from source and ending with target. This method returns the
   * version always in an ascanding order. So if you need it ordered differently you have to reverse
   * the list.
   *
   * @param projectId project id
   * @param source source
   * @param target target
   * @return list of versions
   * @throws ESException if source or target are out of range or any other problem occurs
   */
  protected List<Version> getVersions(
      ProjectId projectId, PrimaryVersionSpec source, PrimaryVersionSpec target)
      throws ESException {
    if (source.compareTo(target) < 1) {
      final ProjectHistory projectHistory =
          getSubInterface(ProjectSubInterfaceImpl.class).getProject(projectId);

      final Version sourceVersion = getVersion(projectHistory, source);
      final Version targetVersion = getVersion(projectHistory, target);

      if (sourceVersion == null || targetVersion == null) {
        throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_NoSourceNorTarget);
      }
      final List<Version> result = new ArrayList<Version>();

      // since the introduction of branches the versions are collected
      // in different order.
      Version currentVersion = targetVersion;
      while (currentVersion != null) {
        result.add(currentVersion);
        if (currentVersion.equals(sourceVersion)) {
          break;
        }
        if (currentVersion.getPrimarySpec().compareTo(sourceVersion.getPrimarySpec()) < 0) {
          // walked too far, invalid path.
          throw new InvalidVersionSpecException(Messages.VersionSubInterfaceImpl_InvalidPath);
        }
        // Shortcut for most common merge usecase: If you have 2
        // parallel branches and merge several times
        // from the one branch into the another.
        if (currentVersion.getMergedFromVersion().contains(sourceVersion)) {
          // add sourceVersion because #getChanges always removes
          // the first version
          result.add(sourceVersion);
          break;
        }

        currentVersion = findNextVersion(currentVersion);
      }
      // versions are collected in descending order, so the result has to be reversed.
      Collections.reverse(result);
      return result;
    }

    return getVersions(projectId, target, source);
  }
  private Version createVersion(
      ProjectHistory projectHistory,
      Project projectState,
      LogMessage logMessage,
      ACUser user,
      Version previousVersion)
      throws ESException {
    final Version newVersion = VersioningFactory.eINSTANCE.createVersion();

    long computedChecksum = ModelUtil.NO_CHECKSUM;

    try {
      if (ServerConfiguration.isComputeChecksumOnCommitActive()) {
        computedChecksum = ModelUtil.computeChecksum(projectState);
      }
    } catch (final SerializationException exception) {
      // TODO: clarify what to do in case checksum computation fails + provide ext. point
      throw new ESException(
          MessageFormat.format(
              Messages.VersionSubInterfaceImpl_ChecksumComputationFailed,
              projectHistory.getProjectName()),
          exception);
    }

    // newVersion.setChanges(changePackage);

    logMessage.setDate(new Date());
    logMessage.setAuthor(user.getName());
    newVersion.setLogMessage(logMessage);

    // latest version == getVersion.size() (version start with index 0 as
    // the list), branch from previous is used.
    newVersion.setPrimarySpec(
        Versions.createPRIMARY(
            previousVersion.getPrimarySpec(), projectHistory.getVersions().size()));
    newVersion.getPrimarySpec().setProjectStateChecksum(computedChecksum);
    newVersion.setNextVersion(null);

    projectHistory.getVersions().add(newVersion);
    return newVersion;
  }
  /**
   * Returns all changes within the specified version range for a given project.
   *
   * @param projectId the ID of a project
   * @param source the source version
   * @param target the target version (inclusive)
   * @return a list of change packages containing all the changes for the specified version range
   * @throws InvalidVersionSpecException if an invalid version has been specified
   * @throws ESException in case of failure
   */
  @ESMethod(MethodId.GETCHANGES)
  public List<AbstractChangePackage> getChanges(
      ProjectId projectId, VersionSpec source, VersionSpec target)
      throws InvalidVersionSpecException, ESException {

    sanityCheckObjects(projectId, source, target);
    final PrimaryVersionSpec resolvedSource = resolveVersionSpec(projectId, source);
    final PrimaryVersionSpec resolvedTarget = resolveVersionSpec(projectId, target);
    // if target and source are equal return empty list
    if (resolvedSource.getIdentifier() == resolvedTarget.getIdentifier()) {
      return new ArrayList<AbstractChangePackage>();
    }

    synchronized (getMonitor()) {
      final boolean updateForward = resolvedTarget.getIdentifier() > resolvedSource.getIdentifier();

      // Example: if you want the changes to get from version 5 to 7, you
      // need the changes contained in version 6
      // and 7. The reason is that each version holds the changes which
      // occurred from the predecessor to the
      // version itself. Version 5 holds the changes to get from version 4
      // to 5 and therefore is irrelevant.
      // For that reason the first version is removed, since getVersions
      // always sorts ascending order.
      final List<Version> versions = getVersions(projectId, resolvedSource, resolvedTarget);
      if (versions.size() > 1) {
        versions.remove(0);
      }

      List<AbstractChangePackage> result = new ArrayList<AbstractChangePackage>();
      for (final Version version : versions) {
        final AbstractChangePackage changes = version.getChanges();
        if (changes != null) {
          changes.setLogMessage(ModelUtil.clone(version.getLogMessage()));
          result.add(changes);
        }
      }

      // if source is after target in time
      if (!updateForward) {
        // reverse list and change packages
        final List<AbstractChangePackage> resultReverse = new ArrayList<AbstractChangePackage>();
        for (final AbstractChangePackage changePackage : result) {

          final ChangePackage changePackageReverse =
              VersioningFactory.eINSTANCE.createChangePackage();
          final ESCloseableIterable<AbstractOperation> reversedOperations =
              changePackage.reversedOperations();
          final ArrayList<AbstractOperation> copiedReversedOperations =
              new ArrayList<AbstractOperation>();
          try {
            for (final AbstractOperation op : reversedOperations.iterable()) {
              copiedReversedOperations.add(op.reverse());
            }
          } finally {
            reversedOperations.close();
          }

          for (final AbstractOperation reversedOperation : copiedReversedOperations) {
            changePackageReverse.add(reversedOperation);
          }

          // copy again log message
          // reverse() created a new change package without copying
          // existent attributes
          changePackageReverse.setLogMessage(ModelUtil.clone(changePackage.getLogMessage()));
          resultReverse.add(changePackageReverse);
        }

        Collections.reverse(resultReverse);
        result = resultReverse;
      }

      return result;
    }
  }
 /**
  * @param targetBranch
  * @param baseVersion
  * @return
  */
 private boolean isRegularCommit(BranchVersionSpec targetBranch, final Version baseVersion) {
   return targetBranch == null
       || baseVersion.getPrimarySpec().getBranch().equals(targetBranch.getBranch());
 }
  private PrimaryVersionSpec internalCreateVersion(
      ProjectId projectId,
      PrimaryVersionSpec baseVersionSpec,
      AbstractChangePackage changePackage,
      BranchVersionSpec targetBranch,
      PrimaryVersionSpec sourceVersion,
      LogMessage logMessage,
      final ACUser user)
      throws ESException {
    synchronized (getMonitor()) {
      final long currentTimeMillis = System.currentTimeMillis();
      final ProjectHistory projectHistory =
          getSubInterface(ProjectSubInterfaceImpl.class).getProject(projectId);

      // Find branch
      final BranchInfo baseBranch = getBranchInfo(projectHistory, baseVersionSpec);
      final Version baseVersion = getVersion(projectHistory, baseVersionSpec);

      if (baseVersion == null || baseBranch == null) {
        throw new InvalidVersionSpecException(
            Messages.VersionSubInterfaceImpl_InvalidBranchOrVersion);
      }

      // defined here fore scoping reasons
      Version newVersion = null;
      BranchInfo newBranch = null;

      // copy project and apply changes
      final Project newProjectState =
          ((ProjectImpl) getSubInterface(ProjectSubInterfaceImpl.class).getProject(baseVersion))
              .copy();
      changePackage.apply(newProjectState);

      // regular commit
      if (isRegularCommit(targetBranch, baseVersion)) {

        newVersion =
            performRegularCommit(
                baseVersionSpec,
                logMessage,
                user,
                projectHistory,
                baseBranch,
                baseVersion,
                newProjectState);

        // case for new branch creation
      } else if (isNewBranchCommit(targetBranch, projectHistory)) {
        checkNewBranchCommitPreRequisites(targetBranch.getBranch());
        // when branch does NOT exist, create new branch
        newVersion = createVersion(projectHistory, newProjectState, logMessage, user, baseVersion);
        newBranch =
            createNewBranch(
                projectHistory,
                baseVersion.getPrimarySpec(),
                newVersion.getPrimarySpec(),
                targetBranch);
        newVersion.setAncestorVersion(baseVersion);

      } else {
        // This point only can be reached with invalid input
        throw new IllegalStateException(
            Messages.VersionSubInterfaceImpl_TargetBranchCombination_Invalid);
      }

      if (sourceVersion != null) {
        newVersion.getMergedFromVersion().add(getVersion(projectHistory, sourceVersion));
      }

      // try to save
      try {
        try {
          trySave(projectId, changePackage, projectHistory, newVersion, newProjectState);
        } catch (final FatalESException e) {
          // try to roll back. removing version is necessary in all cases
          rollback(projectHistory, baseBranch, baseVersion, newVersion, newBranch, e);
        }

        // if ancestor isn't null, a new branch was created. In this
        // case we want to keep the old base project
        // state
        if (newVersion.getAncestorVersion() == null && baseVersion.getProjectState() != null) {
          // delete projectstate from last revision depending on
          // persistence policy
          deleteOldProjectStateAccordingToOptions(projectId, baseVersion);
        }

        save(baseVersion);
        save(projectHistory);

      } catch (final FatalESException e) {
        // roll back failed
        EMFStoreController.getInstance().shutdown(e);
        throw new ESException(Messages.VersionSubInterfaceImpl_ShuttingServerDown);
      }

      ModelUtil.logInfo(
          Messages.VersionSubInterfaceImpl_TotalTimeForCommit
              + (System.currentTimeMillis() - currentTimeMillis));
      return newVersion.getPrimarySpec();
    }
  }