Line data Source code
1 : // Copyright (C) 2020 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.edit; 16 : 17 : import com.google.gerrit.entities.Change; 18 : import com.google.gerrit.entities.PatchSet; 19 : import com.google.gerrit.server.project.InvalidChangeOperationException; 20 : import java.io.IOException; 21 : import org.eclipse.jgit.lib.Repository; 22 : import org.eclipse.jgit.revwalk.RevCommit; 23 : import org.eclipse.jgit.revwalk.RevWalk; 24 : 25 : /** 26 : * Target of the modification of a commit. 27 : * 28 : * <p>This is currently used in the context of change edits which involves both direct actions on 29 : * change edits (e.g. creating a change edit; modifying a file of a change edit) as well as indirect 30 : * creation/modification of them (e.g. via applying a suggested fix of a robot comment.) 31 : * 32 : * <p>Depending on the situation and exact action, either an existing {@link ChangeEdit} (-> {@link 33 : * EditCommit} or a specific patchset commit (-> {@link PatchsetCommit}) is the target of a 34 : * modification. 35 : */ 36 : public interface ModificationTarget { 37 : 38 : void ensureNewEditMayBeBasedOnTarget(Change change) throws InvalidChangeOperationException; 39 : 40 : void ensureTargetMayBeModifiedDespiteExistingEdit(ChangeEdit changeEdit) 41 : throws InvalidChangeOperationException; 42 : 43 : /** Commit to modify. */ 44 : RevCommit getCommit(Repository repository) throws IOException; 45 : 46 : /** 47 : * Patchset within whose context the modification happens. This also applies to change edits as 48 : * each change edit is based on a specific patchset. 49 : */ 50 : PatchSet getBasePatchset(); 51 : 52 : /** A specific patchset commit is the target of the modification. */ 53 : class PatchsetCommit implements ModificationTarget { 54 : 55 : private final PatchSet patchSet; 56 : 57 17 : PatchsetCommit(PatchSet patchSet) { 58 17 : this.patchSet = patchSet; 59 17 : } 60 : 61 : @Override 62 : public void ensureTargetMayBeModifiedDespiteExistingEdit(ChangeEdit changeEdit) 63 : throws InvalidChangeOperationException { 64 2 : if (!isBasedOn(changeEdit, patchSet)) { 65 2 : throw new InvalidChangeOperationException( 66 2 : String.format( 67 : "Only the patch set %s on which the existing change edit is based may be modified " 68 : + "(specified patch set: %s)", 69 2 : changeEdit.getBasePatchSet().id(), patchSet.id())); 70 : } 71 2 : } 72 : 73 : private static boolean isBasedOn(ChangeEdit changeEdit, PatchSet patchSet) { 74 2 : PatchSet editBasePatchSet = changeEdit.getBasePatchSet(); 75 2 : return editBasePatchSet.id().equals(patchSet.id()); 76 : } 77 : 78 : @Override 79 : public void ensureNewEditMayBeBasedOnTarget(Change change) 80 : throws InvalidChangeOperationException { 81 17 : PatchSet.Id patchSetId = patchSet.id(); 82 17 : PatchSet.Id currentPatchSetId = change.currentPatchSetId(); 83 17 : if (!patchSetId.equals(currentPatchSetId)) { 84 2 : throw new InvalidChangeOperationException( 85 2 : String.format( 86 : "A change edit may only be created for the current patch set %s (and not for %s)", 87 : currentPatchSetId, patchSetId)); 88 : } 89 17 : } 90 : 91 : @Override 92 : public RevCommit getCommit(Repository repository) throws IOException { 93 17 : try (RevWalk revWalk = new RevWalk(repository)) { 94 17 : return revWalk.parseCommit(patchSet.commitId()); 95 : } 96 : } 97 : 98 : @Override 99 : public PatchSet getBasePatchset() { 100 17 : return patchSet; 101 : } 102 : } 103 : 104 : /** An existing {@link ChangeEdit} commit is the target of the modification. */ 105 : class EditCommit implements ModificationTarget { 106 : 107 : private final ChangeEdit changeEdit; 108 : 109 12 : EditCommit(ChangeEdit changeEdit) { 110 12 : this.changeEdit = changeEdit; 111 12 : } 112 : 113 : @Override 114 : public void ensureNewEditMayBeBasedOnTarget(Change change) { 115 : // The current code will never create a new edit if one already exists. It would be a 116 : // programmer error if this changes in the future (without adjusting the storage of change 117 : // edits). 118 0 : throw new IllegalStateException( 119 0 : String.format( 120 : "Change %d already has a change edit for the calling user. A new change edit can't" 121 : + " be created.", 122 0 : changeEdit.getChange().getChangeId())); 123 : } 124 : 125 : @Override 126 : public void ensureTargetMayBeModifiedDespiteExistingEdit(ChangeEdit changeEdit) { 127 : // The target is the change edit and hence can be modified. 128 12 : } 129 : 130 : @Override 131 : public RevCommit getCommit(Repository repository) throws IOException { 132 12 : return changeEdit.getEditCommit(); 133 : } 134 : 135 : @Override 136 : public PatchSet getBasePatchset() { 137 11 : return changeEdit.getBasePatchSet(); 138 : } 139 : } 140 : }