com.google.gerrit.server.events.EventFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.google.gerrit.server.events.EventFactory.java

Source

// Copyright (C) 2010 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.server.events;

import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.ChangeMessage;
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.PatchSetAncestor;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ApprovalAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.DependencyAttribute;
import com.google.gerrit.server.data.MessageAttribute;
import com.google.gerrit.server.data.PatchAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
import com.google.gerrit.server.data.PatchSetCommentAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.data.SubmitLabelAttribute;
import com.google.gerrit.server.data.SubmitRecordAttribute;
import com.google.gerrit.server.data.TrackingIdAttribute;
import com.google.gerrit.server.notedb.ChangeNotes;
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.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Singleton
public class EventFactory {
    private static final Logger log = LoggerFactory.getLogger(EventFactory.class);
    private final AccountCache accountCache;
    private final Provider<String> urlProvider;
    private final PatchListCache patchListCache;
    private final SchemaFactory<ReviewDb> schema;
    private final PatchSetInfoFactory psInfoFactory;
    private final PersonIdent myIdent;
    private final Provider<ReviewDb> db;
    private final ChangeData.Factory changeDataFactory;
    private final ApprovalsUtil approvalsUtil;
    private final ChangeKindCache changeKindCache;

    @Inject
    EventFactory(AccountCache accountCache, @CanonicalWebUrl @Nullable Provider<String> urlProvider,
            PatchSetInfoFactory psif, PatchListCache patchListCache, SchemaFactory<ReviewDb> schema,
            @GerritPersonIdent PersonIdent myIdent, Provider<ReviewDb> db, ChangeData.Factory changeDataFactory,
            ApprovalsUtil approvalsUtil, ChangeKindCache changeKindCache) {
        this.accountCache = accountCache;
        this.urlProvider = urlProvider;
        this.patchListCache = patchListCache;
        this.schema = schema;
        this.psInfoFactory = psif;
        this.myIdent = myIdent;
        this.db = db;
        this.changeDataFactory = changeDataFactory;
        this.approvalsUtil = approvalsUtil;
        this.changeKindCache = changeKindCache;
    }

    /**
     * Create a ChangeAttribute for the given change suitable for serialization to
     * JSON.
     *
     * @param change
     * @return object suitable for serialization to JSON
     */
    public ChangeAttribute asChangeAttribute(final Change change) {
        ChangeAttribute a = new ChangeAttribute();
        a.project = change.getProject().get();
        a.branch = change.getDest().getShortName();
        a.topic = change.getTopic();
        a.id = change.getKey().get();
        a.number = change.getId().toString();
        a.subject = change.getSubject();
        try {
            a.commitMessage = changeDataFactory.create(db.get(), change).commitMessage();
        } catch (Exception e) {
            log.error("Error while getting full commit message for" + " change " + a.number);
        }
        a.url = getChangeUrl(change);
        a.owner = asAccountAttribute(change.getOwner());
        a.status = change.getStatus();
        return a;
    }

    /**
     * Create a RefUpdateAttribute for the given old ObjectId, new ObjectId, and
     * branch that is suitable for serialization to JSON.
     *
     * @param oldId
     * @param newId
     * @param refName
     * @return object suitable for serialization to JSON
     */
    public RefUpdateAttribute asRefUpdateAttribute(final ObjectId oldId, final ObjectId newId,
            final Branch.NameKey refName) {
        RefUpdateAttribute ru = new RefUpdateAttribute();
        ru.newRev = newId != null ? newId.getName() : ObjectId.zeroId().getName();
        ru.oldRev = oldId != null ? oldId.getName() : ObjectId.zeroId().getName();
        ru.project = refName.getParentKey().get();
        ru.refName = refName.get();
        return ru;
    }

    /**
     * Extend the existing ChangeAttribute with additional fields.
     *
     * @param a
     * @param change
     */
    public void extend(ChangeAttribute a, Change change) {
        a.createdOn = change.getCreatedOn().getTime() / 1000L;
        a.lastUpdated = change.getLastUpdatedOn().getTime() / 1000L;
        a.open = change.getStatus().isOpen();
    }

    /**
     * Add allReviewers to an existing ChangeAttribute.
     *
     * @param a
     * @param notes
     */
    public void addAllReviewers(ChangeAttribute a, ChangeNotes notes) throws OrmException {
        Collection<Account.Id> reviewers = approvalsUtil.getReviewers(db.get(), notes).values();
        if (!reviewers.isEmpty()) {
            a.allReviewers = Lists.newArrayListWithCapacity(reviewers.size());
            for (Account.Id id : reviewers) {
                a.allReviewers.add(asAccountAttribute(id));
            }
        }
    }

    /**
     * Add submitRecords to an existing ChangeAttribute.
     *
     * @param ca
     * @param submitRecords
     */
    public void addSubmitRecords(ChangeAttribute ca, List<SubmitRecord> submitRecords) {
        ca.submitRecords = new ArrayList<>();

        for (SubmitRecord submitRecord : submitRecords) {
            SubmitRecordAttribute sa = new SubmitRecordAttribute();
            sa.status = submitRecord.status.name();
            if (submitRecord.status != SubmitRecord.Status.RULE_ERROR) {
                addSubmitRecordLabels(submitRecord, sa);
            }
            ca.submitRecords.add(sa);
        }
        // Remove empty lists so a confusing label won't be displayed in the output.
        if (ca.submitRecords.isEmpty()) {
            ca.submitRecords = null;
        }
    }

    private void addSubmitRecordLabels(SubmitRecord submitRecord, SubmitRecordAttribute sa) {
        if (submitRecord.labels != null && !submitRecord.labels.isEmpty()) {
            sa.labels = new ArrayList<>();
            for (SubmitRecord.Label lbl : submitRecord.labels) {
                SubmitLabelAttribute la = new SubmitLabelAttribute();
                la.label = lbl.label;
                la.status = lbl.status.name();
                if (lbl.appliedBy != null) {
                    Account a = accountCache.get(lbl.appliedBy).getAccount();
                    la.by = asAccountAttribute(a);
                }
                sa.labels.add(la);
            }
        }
    }

    public void addDependencies(ChangeAttribute ca, Change change) {
        ca.dependsOn = new ArrayList<>();
        ca.neededBy = new ArrayList<>();
        try (ReviewDb db = schema.open()) {
            final PatchSet.Id psId = change.currentPatchSetId();
            for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(psId)) {
                for (PatchSet p : db.patchSets().byRevision(a.getAncestorRevision())) {
                    Change c = db.changes().get(p.getId().getParentKey());
                    if (c == null) {
                        log.error("Error while generating the ancestor change for" + " revision "
                                + a.getAncestorRevision() + ": Cannot find" + " Change entry in database for "
                                + p.getId().getParentKey());
                        continue;
                    }
                    ca.dependsOn.add(newDependsOn(c, p));
                }
            }

            final PatchSet ps = db.patchSets().get(psId);
            if (ps == null) {
                log.error("Error while generating the list of descendants for" + " PatchSet " + psId
                        + ": Cannot find PatchSet entry in" + " database.");
            } else {
                final RevId revId = ps.getRevision();
                for (PatchSetAncestor a : db.patchSetAncestors().descendantsOf(revId)) {
                    final PatchSet p = db.patchSets().get(a.getPatchSet());
                    if (p == null) {
                        log.error("Error while generating the list of descendants for" + " revision " + revId.get()
                                + ": Cannot find PatchSet entry in" + " database for " + a.getPatchSet());
                        continue;
                    }
                    final Change c = db.changes().get(p.getId().getParentKey());
                    if (c == null) {
                        log.error("Error while generating the list of descendants for" + " revision " + revId.get()
                                + ": Cannot find Change entry in" + " database for " + p.getId().getParentKey());
                        continue;
                    }
                    ca.neededBy.add(newNeededBy(c, p));
                }
            }
        } catch (OrmException e) {
            // Squash DB exceptions and leave dependency lists partially filled.
        }
        // Remove empty lists so a confusing label won't be displayed in the output.
        if (ca.dependsOn.isEmpty()) {
            ca.dependsOn = null;
        }
        if (ca.neededBy.isEmpty()) {
            ca.neededBy = null;
        }
    }

    private DependencyAttribute newDependsOn(Change c, PatchSet ps) {
        DependencyAttribute d = newDependencyAttribute(c, ps);
        d.isCurrentPatchSet = ps.getId().equals(c.currentPatchSetId());
        return d;
    }

    private DependencyAttribute newNeededBy(Change c, PatchSet ps) {
        return newDependencyAttribute(c, ps);
    }

    private DependencyAttribute newDependencyAttribute(Change c, PatchSet ps) {
        DependencyAttribute d = new DependencyAttribute();
        d.number = c.getId().toString();
        d.id = c.getKey().toString();
        d.revision = ps.getRevision().get();
        d.ref = ps.getRefName();
        return d;
    }

    public void addTrackingIds(ChangeAttribute a, Multimap<String, String> set) {
        if (!set.isEmpty()) {
            a.trackingIds = new ArrayList<>(set.size());
            for (Map.Entry<String, Collection<String>> e : set.asMap().entrySet()) {
                for (String id : e.getValue()) {
                    TrackingIdAttribute t = new TrackingIdAttribute();
                    t.system = e.getKey();
                    t.id = id;
                    a.trackingIds.add(t);
                }
            }
        }
    }

    public void addCommitMessage(ChangeAttribute a, String commitMessage) {
        a.commitMessage = commitMessage;
    }

    public void addPatchSets(ChangeAttribute a, Collection<PatchSet> ps, LabelTypes labelTypes) {
        addPatchSets(a, ps, null, false, null, labelTypes);
    }

    public void addPatchSets(ChangeAttribute ca, Collection<PatchSet> ps,
            Map<PatchSet.Id, Collection<PatchSetApproval>> approvals, LabelTypes labelTypes) {
        addPatchSets(ca, ps, approvals, false, null, labelTypes);
    }

    public void addPatchSets(ChangeAttribute ca, Collection<PatchSet> ps,
            Map<PatchSet.Id, Collection<PatchSetApproval>> approvals, boolean includeFiles, Change change,
            LabelTypes labelTypes) {
        if (!ps.isEmpty()) {
            ca.patchSets = new ArrayList<>(ps.size());
            for (PatchSet p : ps) {
                PatchSetAttribute psa = asPatchSetAttribute(p);
                if (approvals != null) {
                    addApprovals(psa, p.getId(), approvals, labelTypes);
                }
                ca.patchSets.add(psa);
                if (includeFiles && change != null) {
                    addPatchSetFileNames(psa, change, p);
                }
            }
        }
    }

    public void addPatchSetComments(PatchSetAttribute patchSetAttribute,
            Collection<PatchLineComment> patchLineComments) {
        for (PatchLineComment comment : patchLineComments) {
            if (comment.getKey().getParentKey().getParentKey().get() == Integer
                    .parseInt(patchSetAttribute.number)) {
                if (patchSetAttribute.comments == null) {
                    patchSetAttribute.comments = new ArrayList<>();
                }
                patchSetAttribute.comments.add(asPatchSetLineAttribute(comment));
            }
        }
    }

    public void addPatchSetFileNames(PatchSetAttribute patchSetAttribute, Change change, PatchSet patchSet) {
        try {
            PatchList patchList = patchListCache.get(change, patchSet);
            for (PatchListEntry patch : patchList.getPatches()) {
                if (patchSetAttribute.files == null) {
                    patchSetAttribute.files = new ArrayList<>();
                }

                PatchAttribute p = new PatchAttribute();
                p.file = patch.getNewName();
                p.fileOld = patch.getOldName();
                p.type = patch.getChangeType();
                p.deletions -= patch.getDeletions();
                p.insertions = patch.getInsertions();
                patchSetAttribute.files.add(p);
            }
        } catch (PatchListNotAvailableException e) {
            log.warn("Cannot get patch list", e);
        }
    }

    public void addComments(ChangeAttribute ca, Collection<ChangeMessage> messages) {
        if (!messages.isEmpty()) {
            ca.comments = new ArrayList<>();
            for (ChangeMessage message : messages) {
                ca.comments.add(asMessageAttribute(message));
            }
        }
    }

    /**
     * Create a PatchSetAttribute for the given patchset suitable for
     * serialization to JSON.
     *
     * @param patchSet
     * @return object suitable for serialization to JSON
     */
    public PatchSetAttribute asPatchSetAttribute(final PatchSet patchSet) {
        PatchSetAttribute p = new PatchSetAttribute();
        p.revision = patchSet.getRevision().get();
        p.number = Integer.toString(patchSet.getPatchSetId());
        p.ref = patchSet.getRefName();
        p.uploader = asAccountAttribute(patchSet.getUploader());
        p.createdOn = patchSet.getCreatedOn().getTime() / 1000L;
        p.isDraft = patchSet.isDraft();
        final PatchSet.Id pId = patchSet.getId();
        try (ReviewDb db = schema.open()) {
            p.parents = new ArrayList<>();
            for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(patchSet.getId())) {
                p.parents.add(a.getAncestorRevision().get());
            }

            UserIdentity author = psInfoFactory.get(db, pId).getAuthor();
            if (author.getAccount() == null) {
                p.author = new AccountAttribute();
                p.author.email = author.getEmail();
                p.author.name = author.getName();
                p.author.username = "";
            } else {
                p.author = asAccountAttribute(author.getAccount());
            }

            Change change = db.changes().get(pId.getParentKey());
            List<Patch> list = patchListCache.get(change, patchSet).toPatchList(pId);
            for (Patch pe : list) {
                if (!Patch.COMMIT_MSG.equals(pe.getFileName())) {
                    p.sizeDeletions -= pe.getDeletions();
                    p.sizeInsertions += pe.getInsertions();
                }
            }
            p.kind = changeKindCache.getChangeKind(db, change, patchSet);
        } catch (OrmException e) {
            log.error("Cannot load patch set data for " + patchSet.getId(), e);
        } catch (PatchSetInfoNotAvailableException e) {
            log.error(String.format("Cannot get authorEmail for %s.", pId), e);
        } catch (PatchListNotAvailableException e) {
            log.error(String.format("Cannot get size information for %s.", pId), e);
        }
        return p;
    }

    public void addApprovals(PatchSetAttribute p, PatchSet.Id id,
            Map<PatchSet.Id, Collection<PatchSetApproval>> all, LabelTypes labelTypes) {
        Collection<PatchSetApproval> list = all.get(id);
        if (list != null) {
            addApprovals(p, list, labelTypes);
        }
    }

    public void addApprovals(PatchSetAttribute p, Collection<PatchSetApproval> list, LabelTypes labelTypes) {
        if (!list.isEmpty()) {
            p.approvals = new ArrayList<>(list.size());
            for (PatchSetApproval a : list) {
                if (a.getValue() != 0) {
                    p.approvals.add(asApprovalAttribute(a, labelTypes));
                }
            }
            if (p.approvals.isEmpty()) {
                p.approvals = null;
            }
        }
    }

    /**
     * Create an AuthorAttribute for the given account suitable for serialization
     * to JSON.
     *
     * @param id
     * @return object suitable for serialization to JSON
     */
    public AccountAttribute asAccountAttribute(Account.Id id) {
        if (id == null) {
            return null;
        }
        return asAccountAttribute(accountCache.get(id).getAccount());
    }

    /**
     * Create an AuthorAttribute for the given account suitable for serialization
     * to JSON.
     *
     * @param account
     * @return object suitable for serialization to JSON
     */
    public AccountAttribute asAccountAttribute(final Account account) {
        if (account == null) {
            return null;
        }

        AccountAttribute who = new AccountAttribute();
        who.name = account.getFullName();
        who.email = account.getPreferredEmail();
        who.username = account.getUserName();
        return who;
    }

    /**
     * Create an AuthorAttribute for the given person ident suitable for
     * serialization to JSON.
     *
     * @param ident
     * @return object suitable for serialization to JSON
     */
    public AccountAttribute asAccountAttribute(PersonIdent ident) {
        AccountAttribute who = new AccountAttribute();
        who.name = ident.getName();
        who.email = ident.getEmailAddress();
        return who;
    }

    /**
     * Create an ApprovalAttribute for the given approval suitable for
     * serialization to JSON.
     *
     * @param approval
     * @param labelTypes label types for the containing project
     * @return object suitable for serialization to JSON
     */
    public ApprovalAttribute asApprovalAttribute(PatchSetApproval approval, LabelTypes labelTypes) {
        ApprovalAttribute a = new ApprovalAttribute();
        a.type = approval.getLabelId().get();
        a.value = Short.toString(approval.getValue());
        a.by = asAccountAttribute(approval.getAccountId());
        a.grantedOn = approval.getGranted().getTime() / 1000L;

        LabelType lt = labelTypes.byLabel(approval.getLabelId());
        if (lt != null) {
            a.description = lt.getName();
        }
        return a;
    }

    public MessageAttribute asMessageAttribute(ChangeMessage message) {
        MessageAttribute a = new MessageAttribute();
        a.timestamp = message.getWrittenOn().getTime() / 1000L;
        a.reviewer = message.getAuthor() != null ? asAccountAttribute(message.getAuthor())
                : asAccountAttribute(myIdent);
        a.message = message.getMessage();
        return a;
    }

    public PatchSetCommentAttribute asPatchSetLineAttribute(PatchLineComment c) {
        PatchSetCommentAttribute a = new PatchSetCommentAttribute();
        a.reviewer = asAccountAttribute(c.getAuthor());
        a.file = c.getKey().getParentKey().get();
        a.line = c.getLine();
        a.message = c.getMessage();
        return a;
    }

    /** Get a link to the change; null if the server doesn't know its own address. */
    private String getChangeUrl(final Change change) {
        if (change != null && urlProvider.get() != null) {
            final StringBuilder r = new StringBuilder();
            r.append(urlProvider.get());
            r.append(change.getChangeId());
            return r.toString();
        }
        return null;
    }
}