| // Copyright (C) 2008 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.entities; | |
| import static com.google.common.base.Preconditions.checkArgument; | |
| import static com.google.common.collect.ImmutableList.toImmutableList; | |
| import static java.util.Objects.requireNonNull; | |
| import com.google.auto.value.AutoValue; | |
| import com.google.common.base.Splitter; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.common.primitives.Ints; | |
| import com.google.errorprone.annotations.InlineMe; | |
| import com.google.gerrit.common.Nullable; | |
| import java.time.Instant; | |
| import java.util.List; | |
| import java.util.Optional; | |
| import org.eclipse.jgit.lib.ObjectId; | |
| /** A single revision of a {@link Change}. */ | |
| @AutoValue | |
| public abstract class PatchSet { | |
| /** Is the reference name a change reference? */ | |
| public static boolean isChangeRef(String name) { | |
| return Id.fromRef(name) != null; | |
| } | |
| /** | |
| * Is the reference name a change reference? | |
| * | |
| * @deprecated use isChangeRef instead. | |
| */ | |
| @Deprecated | |
| @InlineMe( | |
| replacement = "PatchSet.isChangeRef(name)", | |
| imports = "com.google.gerrit.entities.PatchSet") | |
| public static boolean isRef(String name) { | |
| return isChangeRef(name); | |
| } | |
| public static String joinGroups(List<String> groups) { | |
| requireNonNull(groups); | |
| for (String group : groups) { | |
| checkArgument(!group.contains(","), "group may not contain ',': %s", group); | |
| } | |
| return String.join(",", groups); | |
| } | |
| public static ImmutableList<String> splitGroups(String joinedGroups) { | |
| return Splitter.on(',').splitToStream(joinedGroups).collect(toImmutableList()); | |
| } | |
| public static Id id(Change.Id changeId, int id) { | |
| return new AutoValue_PatchSet_Id(changeId, id); | |
| } | |
| @AutoValue | |
| public abstract static class Id implements Comparable<Id> { | |
| /** Parse a PatchSet.Id out of a string representation. */ | |
| public static Id parse(String str) { | |
| List<String> parts = Splitter.on(',').splitToList(str); | |
| checkIdFormat(parts.size() == 2, str); | |
| Integer changeId = Ints.tryParse(parts.get(0)); | |
| checkIdFormat(changeId != null, str); | |
| Integer id = Ints.tryParse(parts.get(1)); | |
| checkIdFormat(id != null, str); | |
| return PatchSet.id(Change.id(changeId), id); | |
| } | |
| private static void checkIdFormat(boolean test, String input) { | |
| checkArgument(test, "invalid patch set ID: %s", input); | |
| } | |
| /** Parse a PatchSet.Id from a {@link #refName()} result. */ | |
| @Nullable | |
| public static Id fromRef(String ref) { | |
| int cs = Change.Id.startIndex(ref); | |
| if (cs < 0) { | |
| return null; | |
| } | |
| int ce = Change.Id.nextNonDigit(ref, cs); | |
| int patchSetId = fromRef(ref, ce); | |
| if (patchSetId < 0) { | |
| return null; | |
| } | |
| int changeId = Integer.parseInt(ref.substring(cs, ce)); | |
| return PatchSet.id(Change.id(changeId), patchSetId); | |
| } | |
| /** Parse a PatchSet.Id from an edit ref. */ | |
| public static PatchSet.Id fromEditRef(String ref) { | |
| Change.Id changeId = Change.Id.fromEditRefPart(ref); | |
| return PatchSet.id(changeId, Ints.tryParse(ref.substring(ref.lastIndexOf('/') + 1))); | |
| } | |
| static int fromRef(String ref, int changeIdEnd) { | |
| // Patch set ID. | |
| int ps = changeIdEnd + 1; | |
| if (ps >= ref.length() || ref.charAt(ps) == '0') { | |
| return -1; | |
| } | |
| for (int i = ps; i < ref.length(); i++) { | |
| if (ref.charAt(i) < '0' || ref.charAt(i) > '9') { | |
| return -1; | |
| } | |
| } | |
| return Integer.parseInt(ref.substring(ps)); | |
| } | |
| public static String toId(int number) { | |
| return number == 0 ? "edit" : String.valueOf(number); | |
| } | |
| public String getId() { | |
| return toId(id()); | |
| } | |
| public abstract Change.Id changeId(); | |
| abstract int id(); | |
| public int get() { | |
| return id(); | |
| } | |
| public String getCommaSeparatedChangeAndPatchSetId() { | |
| return changeId().toString() + ',' + id(); | |
| } | |
| public String toRefName() { | |
| return changeId().refPrefixBuilder().append(id()).toString(); | |
| } | |
| @Override | |
| public final String toString() { | |
| return getCommaSeparatedChangeAndPatchSetId(); | |
| } | |
| @Override | |
| public int compareTo(Id other) { | |
| return Ints.compare(get(), other.get()); | |
| } | |
| } | |
| public static Builder builder() { | |
| return new AutoValue_PatchSet.Builder().groups(ImmutableList.of()); | |
| } | |
| @AutoValue.Builder | |
| public abstract static class Builder { | |
| public abstract Builder id(Id id); | |
| public abstract Id id(); | |
| public abstract Builder commitId(ObjectId commitId); | |
| public abstract Optional<ObjectId> commitId(); | |
| public abstract Builder branch(Optional<String> branch); | |
| public abstract Builder branch(String branch); | |
| public abstract Builder uploader(Account.Id uploader); | |
| public abstract Builder realUploader(Account.Id realUploader); | |
| public abstract Builder createdOn(Instant createdOn); | |
| public abstract Builder groups(Iterable<String> groups); | |
| public abstract ImmutableList<String> groups(); | |
| public abstract Builder pushCertificate(Optional<String> pushCertificate); | |
| public abstract Builder pushCertificate(String pushCertificate); | |
| public abstract Builder description(Optional<String> description); | |
| public abstract Builder description(String description); | |
| public abstract Optional<String> description(); | |
| public abstract PatchSet build(); | |
| } | |
| /** ID of the patch set. */ | |
| public abstract Id id(); | |
| /** | |
| * Commit ID of the patch set, also known as the revision. | |
| * | |
| * <p>If this is a deserialized instance that was originally serialized by an older version of | |
| * Gerrit, and the old data erroneously did not include a {@code commitId}, then this method will | |
| * return {@link ObjectId#zeroId()}. | |
| */ | |
| public abstract ObjectId commitId(); | |
| /** | |
| * Name of the target branch where this patch-set should be merged into. If the change is moved, | |
| * different patch-sets will have different target branches. | |
| */ | |
| public abstract Optional<String> branch(); | |
| /** | |
| * Account that uploaded the patch set. | |
| * | |
| * <p>If the upload was done on behalf of another user, the impersonated user on whom's behalf the | |
| * patch set was uploaded. | |
| * | |
| * <p>If this is a deserialized instance that was originally serialized by an older version of | |
| * Gerrit, and the old data erroneously did not include an {@code uploader}, then this method will | |
| * return an account ID of 0. | |
| */ | |
| public abstract Account.Id uploader(); | |
| /** | |
| * The real account that uploaded the patch set. | |
| * | |
| * <p>If this is a deserialized instance that was originally serialized by an older version of | |
| * Gerrit, and the old data did not include an {@code realUploader}, then this method will return | |
| * the {@code uploader}. | |
| */ | |
| public abstract Account.Id realUploader(); | |
| /** | |
| * When this patch set was first introduced onto the change. | |
| * | |
| * <p>If this is a deserialized instance that was originally serialized by an older version of | |
| * Gerrit, and the old data erroneously did not include a {@code createdOn}, then this method will | |
| * return a timestamp of 0. | |
| */ | |
| public abstract Instant createdOn(); | |
| /** | |
| * Opaque group identifier, usually assigned during creation. | |
| * | |
| * <p>This field is actually a comma-separated list of values, as in rare cases involving merge | |
| * commits a patch set may belong to multiple groups. | |
| * | |
| * <p>Changes on the same branch having patch sets with intersecting groups are considered | |
| * related, as in the "Related Changes" tab. | |
| */ | |
| public abstract ImmutableList<String> groups(); | |
| /** Certificate sent with a push that created this patch set. */ | |
| public abstract Optional<String> pushCertificate(); | |
| /** | |
| * Optional user-supplied description for this patch set. | |
| * | |
| * <p>When this field is an empty {@code Optional}, the description was never set on the patch | |
| * set. When this field is present but an empty string, the description was set and later cleared. | |
| */ | |
| public abstract Optional<String> description(); | |
| /** Patch set number. */ | |
| public int number() { | |
| return id().get(); | |
| } | |
| /** Name of the corresponding patch set ref. */ | |
| public String refName() { | |
| return id().toRefName(); | |
| } | |
| } |