Package com.google.gerrit.httpd.rpc.patch

Source Code of com.google.gerrit.httpd.rpc.patch.PatchScriptFactory

// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.httpd.rpc.patch;

import com.google.gerrit.common.data.CommentDetail;
import com.google.gerrit.common.data.PatchScript;
import com.google.gerrit.httpd.rpc.Handler;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountDiffPreference;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
import com.google.gerrit.reviewdb.client.Patch.ChangeType;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.LargeObjectException;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.patch.PatchListKey;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;

import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;


class PatchScriptFactory extends Handler<PatchScript> {
  interface Factory {
    PatchScriptFactory create(Patch.Key patchKey,
        @Assisted("patchSetA") PatchSet.Id patchSetA,
        @Assisted("patchSetB") PatchSet.Id patchSetB,
        AccountDiffPreference diffPrefs);
  }

  private static final Logger log =
      LoggerFactory.getLogger(PatchScriptFactory.class);

  private final GitRepositoryManager repoManager;
  private final Provider<PatchScriptBuilder> builderFactory;
  private final PatchListCache patchListCache;
  private final ReviewDb db;
  private final ChangeControl.Factory changeControlFactory;
  private final AccountInfoCacheFactory.Factory aicFactory;

  private final Patch.Key patchKey;
  @Nullable
  private final PatchSet.Id psa;
  private final PatchSet.Id psb;
  private final AccountDiffPreference diffPrefs;

  private final PatchSet.Id patchSetId;
  private final Change.Id changeId;

  private Change change;
  private PatchSet patchSet;
  private Project.NameKey projectKey;
  private ChangeControl control;
  private ObjectId aId;
  private ObjectId bId;
  private List<Patch> history;
  private CommentDetail comments;

  @Inject
  PatchScriptFactory(final GitRepositoryManager grm,
      Provider<PatchScriptBuilder> builderFactory,
      final PatchListCache patchListCache, final ReviewDb db,
      final ChangeControl.Factory changeControlFactory,
      final AccountInfoCacheFactory.Factory aicFactory,
      @Assisted final Patch.Key patchKey,
      @Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
      @Assisted("patchSetB") final PatchSet.Id patchSetB,
      @Assisted final AccountDiffPreference diffPrefs) {
    this.repoManager = grm;
    this.builderFactory = builderFactory;
    this.patchListCache = patchListCache;
    this.db = db;
    this.changeControlFactory = changeControlFactory;
    this.aicFactory = aicFactory;

    this.patchKey = patchKey;
    this.psa = patchSetA;
    this.psb = patchSetB;
    this.diffPrefs = diffPrefs;

    patchSetId = patchKey.getParentKey();
    changeId = patchSetId.getParentKey();
  }

  @Override
  public PatchScript call() throws OrmException, NoSuchChangeException,
      LargeObjectException {
    validatePatchSetId(psa);
    validatePatchSetId(psb);

    control = changeControlFactory.validateFor(changeId);
    change = control.getChange();
    projectKey = change.getProject();
    patchSet = db.patchSets().get(patchSetId);
    if (patchSet == null) {
      throw new NoSuchChangeException(changeId);
    }

    aId = psa != null ? toObjectId(db, psa) : null;
    bId = toObjectId(db, psb);

    final Repository git;
    try {
      git = repoManager.openRepository(projectKey);
    } catch (RepositoryNotFoundException e) {
      log.error("Repository " + projectKey + " not found", e);
      throw new NoSuchChangeException(changeId, e);
    } catch (IOException e) {
      log.error("Cannot open repository " + projectKey, e);
      throw new NoSuchChangeException(changeId, e);
    }
    try {
      final PatchList list = listFor(keyFor(diffPrefs.getIgnoreWhitespace()));
      final PatchScriptBuilder b = newBuilder(list, git);
      final PatchListEntry content = list.get(patchKey.getFileName());

      loadCommentsAndHistory(content.getChangeType(), //
          content.getOldName(), //
          content.getNewName());

        return b.toPatchScript(content, comments, history);
    } catch (PatchListNotAvailableException e) {
      throw new NoSuchChangeException(changeId, e);
    } catch (IOException e) {
      log.error("File content unavailable", e);
      throw new NoSuchChangeException(changeId, e);
    } catch (org.eclipse.jgit.errors.LargeObjectException err) {
      throw new LargeObjectException("File content is too large", err);
    } finally {
      git.close();
    }
  }

  private PatchListKey keyFor(final Whitespace whitespace) {
    return new PatchListKey(projectKey, aId, bId, whitespace);
  }

  private PatchList listFor(final PatchListKey key)
      throws PatchListNotAvailableException {
    return patchListCache.get(key);
  }

  private PatchScriptBuilder newBuilder(final PatchList list, Repository git) {
    final AccountDiffPreference dp = new AccountDiffPreference(diffPrefs);
    final PatchScriptBuilder b = builderFactory.get();
    b.setRepository(git, projectKey);
    b.setChange(change);
    b.setDiffPrefs(dp);
    b.setTrees(list.isAgainstParent(), list.getOldId(), list.getNewId());
    return b;
  }

  private ObjectId toObjectId(final ReviewDb db, final PatchSet.Id psId)
      throws OrmException, NoSuchChangeException {
    if (!changeId.equals(psId.getParentKey())) {
      throw new NoSuchChangeException(changeId);
    }

    final PatchSet ps = db.patchSets().get(psId);
    if (ps == null || ps.getRevision() == null
        || ps.getRevision().get() == null) {
      throw new NoSuchChangeException(changeId);
    }

    try {
      return ObjectId.fromString(ps.getRevision().get());
    } catch (IllegalArgumentException e) {
      log.error("Patch set " + psId + " has invalid revision");
      throw new NoSuchChangeException(changeId, e);
    }
  }

  private void validatePatchSetId(final PatchSet.Id psId)
      throws NoSuchChangeException {
    if (psId == null) { // OK, means use base;
    } else if (changeId.equals(psId.getParentKey())) { // OK, same change;
    } else {
      throw new NoSuchChangeException(changeId);
    }
  }

  private void loadCommentsAndHistory(final ChangeType changeType,
      final String oldName, final String newName) throws OrmException {
    history = new ArrayList<Patch>();
    comments = new CommentDetail(psa, psb);

    final Map<Patch.Key, Patch> byKey = new HashMap<Patch.Key, Patch>();
    final AccountInfoCacheFactory aic = aicFactory.create();

    // This seems like a cheap trick. It doesn't properly account for a
    // file that gets renamed between patch set 1 and patch set 2. We
    // will wind up packing the wrong Patch object because we didn't do
    // proper rename detection between the patch sets.
    //
    for (final PatchSet ps : db.patchSets().byChange(changeId)) {
      String name = patchKey.get();
      if (psa != null) {
        switch (changeType) {
          case COPIED:
          case RENAMED:
            if (ps.getId().equals(psa)) {
              name = oldName;
            }
            break;
        }
      }

      final Patch p = new Patch(new Patch.Key(ps.getId(), name));
      history.add(p);
      byKey.put(p.getKey(), p);
    }

    switch (changeType) {
      case ADDED:
      case MODIFIED:
        loadPublished(byKey, aic, newName);
        break;

      case DELETED:
        loadPublished(byKey, aic, newName);
        break;

      case COPIED:
      case RENAMED:
        if (psa != null) {
          loadPublished(byKey, aic, oldName);
        }
        loadPublished(byKey, aic, newName);
        break;
    }

    final CurrentUser user = control.getCurrentUser();
    if (user instanceof IdentifiedUser) {
      final Account.Id me = ((IdentifiedUser) user).getAccountId();
      switch (changeType) {
        case ADDED:
        case MODIFIED:
          loadDrafts(byKey, aic, me, newName);
          break;

        case DELETED:
          loadDrafts(byKey, aic, me, newName);
          break;

        case COPIED:
        case RENAMED:
          if (psa != null) {
            loadDrafts(byKey, aic, me, oldName);
          }
          loadDrafts(byKey, aic, me, newName);
          break;
      }
    }

    comments.setAccountInfoCache(aic.create());
  }

  private void loadPublished(final Map<Patch.Key, Patch> byKey,
      final AccountInfoCacheFactory aic, final String file) throws OrmException {
    for (PatchLineComment c : db.patchComments().publishedByChangeFile(changeId, file)) {
      if (comments.include(c)) {
        aic.want(c.getAuthor());
      }

      final Patch.Key pKey = c.getKey().getParentKey();
      final Patch p = byKey.get(pKey);
      if (p != null) {
        p.setCommentCount(p.getCommentCount() + 1);
      }
    }
  }

  private void loadDrafts(final Map<Patch.Key, Patch> byKey,
      final AccountInfoCacheFactory aic, final Account.Id me, final String file)
      throws OrmException {
    for (PatchLineComment c : db.patchComments().draftByChangeFileAuthor(changeId, file, me)) {
      if (comments.include(c)) {
        aic.want(me);
      }

      final Patch.Key pKey = c.getKey().getParentKey();
      final Patch p = byKey.get(pKey);
      if (p != null) {
        p.setDraftCount(p.getDraftCount() + 1);
      }
    }
  }
}
TOP

Related Classes of com.google.gerrit.httpd.rpc.patch.PatchScriptFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.