LCOV - code coverage report
Current view: top level - server/patch - PatchList.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 0 71 0.0 %
Date: 2022-11-19 15:00:39 Functions: 0 16 0.0 %

          Line data    Source code
       1             : // Copyright (C) 2009 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.patch;
      16             : 
      17             : import static com.google.gerrit.server.ioutil.BasicSerialization.readBytes;
      18             : import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
      19             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
      20             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
      21             : import static org.eclipse.jgit.lib.ObjectIdSerializer.read;
      22             : import static org.eclipse.jgit.lib.ObjectIdSerializer.readWithoutMarker;
      23             : import static org.eclipse.jgit.lib.ObjectIdSerializer.write;
      24             : import static org.eclipse.jgit.lib.ObjectIdSerializer.writeWithoutMarker;
      25             : 
      26             : import com.google.common.annotations.VisibleForTesting;
      27             : import com.google.common.collect.ImmutableList;
      28             : import com.google.gerrit.common.Nullable;
      29             : import com.google.gerrit.entities.Patch;
      30             : import com.google.gerrit.entities.Patch.ChangeType;
      31             : import com.google.gerrit.git.ObjectIds;
      32             : import java.io.ByteArrayInputStream;
      33             : import java.io.ByteArrayOutputStream;
      34             : import java.io.IOException;
      35             : import java.io.ObjectInputStream;
      36             : import java.io.ObjectOutputStream;
      37             : import java.io.Serializable;
      38             : import java.util.Arrays;
      39             : import java.util.Collections;
      40             : import java.util.Comparator;
      41             : import java.util.List;
      42             : import java.util.zip.DeflaterOutputStream;
      43             : import java.util.zip.InflaterInputStream;
      44             : import org.eclipse.jgit.lib.AnyObjectId;
      45             : import org.eclipse.jgit.lib.ObjectId;
      46             : 
      47             : public class PatchList implements Serializable {
      48             :   private static final long serialVersionUID = PatchListKey.serialVersionUID;
      49             : 
      50             :   @VisibleForTesting
      51           0 :   static final Comparator<String> FILE_PATH_CMP =
      52           0 :       Comparator.comparing(Patch::isMagic).reversed().thenComparing(Comparator.naturalOrder());
      53             : 
      54             :   /**
      55             :    * We use the ChangeType comparator for a rare case when PatchList contains two entries for the
      56             :    * same file, e.g. {ADDED, DELETED}. We return a single entry according to the following order.
      57             :    * Check the following bug for an example case:
      58             :    * https://bugs.chromium.org/p/gerrit/issues/detail?id=13914.
      59             :    */
      60             :   @VisibleForTesting
      61           0 :   static class ChangeTypeCmp implements Comparator<ChangeType> {
      62           0 :     static final List<ChangeType> order =
      63           0 :         ImmutableList.of(
      64             :             ChangeType.ADDED,
      65             :             ChangeType.RENAMED,
      66             :             ChangeType.MODIFIED,
      67             :             ChangeType.COPIED,
      68             :             ChangeType.REWRITE,
      69             :             ChangeType.DELETED);
      70             : 
      71             :     @Override
      72             :     public int compare(ChangeType o1, ChangeType o2) {
      73           0 :       int idx1 = priority(o1);
      74           0 :       int idx2 = priority(o2);
      75           0 :       return idx1 - idx2;
      76             :     }
      77             : 
      78             :     private int priority(ChangeType changeType) {
      79           0 :       int idx = order.indexOf(changeType);
      80             :       // Return least priority if the element is not in the order list.
      81           0 :       return idx == -1 ? order.size() : idx;
      82             :     }
      83             :   }
      84             : 
      85           0 :   @VisibleForTesting static final Comparator<ChangeType> CHANGE_TYPE_CMP = new ChangeTypeCmp();
      86             : 
      87           0 :   private static final Comparator<PatchListEntry> PATCH_CMP =
      88           0 :       Comparator.comparing(PatchListEntry::getNewName, FILE_PATH_CMP)
      89           0 :           .thenComparing(PatchListEntry::getChangeType, CHANGE_TYPE_CMP);
      90             : 
      91             :   @Nullable private transient ObjectId oldId;
      92             :   private transient ObjectId newId;
      93             :   private transient boolean isMerge;
      94             :   private transient ComparisonType comparisonType;
      95             :   private transient int insertions;
      96             :   private transient int deletions;
      97             :   private transient PatchListEntry[] patches;
      98             : 
      99             :   public PatchList(
     100             :       @Nullable AnyObjectId oldId,
     101             :       AnyObjectId newId,
     102             :       boolean isMerge,
     103             :       ComparisonType comparisonType,
     104           0 :       PatchListEntry[] patches) {
     105           0 :     this.oldId = ObjectIds.copyOrNull(oldId);
     106           0 :     this.newId = newId.copy();
     107           0 :     this.isMerge = isMerge;
     108           0 :     this.comparisonType = comparisonType;
     109             : 
     110           0 :     Arrays.sort(patches, 0, patches.length, PATCH_CMP);
     111             : 
     112             :     // Skip magic files
     113           0 :     int i = 0;
     114           0 :     for (; i < patches.length; i++) {
     115           0 :       if (!Patch.isMagic(patches[i].getNewName())) {
     116           0 :         break;
     117             :       }
     118             :     }
     119           0 :     for (; i < patches.length; i++) {
     120           0 :       insertions += patches[i].getInsertions();
     121           0 :       deletions += patches[i].getDeletions();
     122             :     }
     123             : 
     124           0 :     this.patches = patches;
     125           0 :   }
     126             : 
     127             :   /** Old side tree or commit; null only if this is a combined diff. */
     128             :   @Nullable
     129             :   public ObjectId getOldId() {
     130           0 :     return oldId;
     131             :   }
     132             : 
     133             :   /** New side commit. */
     134             :   public ObjectId getNewId() {
     135           0 :     return newId;
     136             :   }
     137             : 
     138             :   /** Get a sorted, unmodifiable list of all files in this list. */
     139             :   public List<PatchListEntry> getPatches() {
     140           0 :     return Collections.unmodifiableList(Arrays.asList(patches));
     141             :   }
     142             : 
     143             :   /** Returns the comparison type */
     144             :   public ComparisonType getComparisonType() {
     145           0 :     return comparisonType;
     146             :   }
     147             : 
     148             :   /** Returns total number of new lines added. */
     149             :   public int getInsertions() {
     150           0 :     return insertions;
     151             :   }
     152             : 
     153             :   /** Returns total number of lines removed. */
     154             :   public int getDeletions() {
     155           0 :     return deletions;
     156             :   }
     157             : 
     158             :   /** Find an entry by name, returning an empty entry if not present. */
     159             :   public PatchListEntry get(String fileName) {
     160           0 :     int index = search(fileName);
     161           0 :     if (index >= 0) {
     162           0 :       return patches[index];
     163             :     }
     164             :     // If index is negative, it marks the insertion point of the object in the list.
     165             :     // index = (-(insertion point) - 1).
     166             :     // Since we use the ChangeType in the comparison, the object that we are using in the lookup
     167             :     // (which has a ADDED ChangeType) may have a different ChangeType than the object in the list.
     168             :     // For this reason, we look at the file name of the object at the insertion point and return it
     169             :     // if it has the same name.
     170           0 :     index = -1 * (index + 1);
     171           0 :     if (index < patches.length && patches[index].getNewName().equals(fileName)) {
     172           0 :       return patches[index];
     173             :     }
     174           0 :     return PatchListEntry.empty(fileName);
     175             :   }
     176             : 
     177             :   private int search(String fileName) {
     178           0 :     PatchListEntry want = PatchListEntry.empty(fileName, ChangeType.ADDED);
     179           0 :     return Arrays.binarySearch(patches, 0, patches.length, want, PATCH_CMP);
     180             :   }
     181             : 
     182             :   private void writeObject(ObjectOutputStream output) throws IOException {
     183           0 :     final ByteArrayOutputStream buf = new ByteArrayOutputStream();
     184           0 :     try (DeflaterOutputStream out = new DeflaterOutputStream(buf)) {
     185           0 :       write(out, oldId);
     186           0 :       writeWithoutMarker(out, newId);
     187           0 :       writeVarInt32(out, isMerge ? 1 : 0);
     188           0 :       comparisonType.writeTo(out);
     189           0 :       writeVarInt32(out, insertions);
     190           0 :       writeVarInt32(out, deletions);
     191           0 :       writeVarInt32(out, patches.length);
     192           0 :       for (PatchListEntry p : patches) {
     193           0 :         p.writeTo(out);
     194             :       }
     195             :     }
     196           0 :     writeBytes(output, buf.toByteArray());
     197           0 :   }
     198             : 
     199             :   private void readObject(ObjectInputStream input) throws IOException {
     200           0 :     final ByteArrayInputStream buf = new ByteArrayInputStream(readBytes(input));
     201           0 :     try (InflaterInputStream in = new InflaterInputStream(buf)) {
     202           0 :       oldId = read(in);
     203           0 :       newId = readWithoutMarker(in);
     204           0 :       isMerge = readVarInt32(in) != 0;
     205           0 :       comparisonType = ComparisonType.readFrom(in);
     206           0 :       insertions = readVarInt32(in);
     207           0 :       deletions = readVarInt32(in);
     208           0 :       final int cnt = readVarInt32(in);
     209           0 :       final PatchListEntry[] all = new PatchListEntry[cnt];
     210           0 :       for (int i = 0; i < all.length; i++) {
     211           0 :         all[i] = PatchListEntry.readFrom(in);
     212             :       }
     213           0 :       patches = all;
     214             :     }
     215           0 :   }
     216             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750