summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java672
1 files changed, 0 insertions, 672 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java
deleted file mode 100644
index f0ed66a551..0000000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ListChanges.java
+++ /dev/null
@@ -1,672 +0,0 @@
-// Copyright (C) 2012 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.query.change;
-
-import static com.google.gerrit.common.changes.ListChangesOption.ALL_COMMITS;
-import static com.google.gerrit.common.changes.ListChangesOption.ALL_FILES;
-import static com.google.gerrit.common.changes.ListChangesOption.ALL_REVISIONS;
-import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_COMMIT;
-import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_FILES;
-import static com.google.gerrit.common.changes.ListChangesOption.CURRENT_REVISION;
-import static com.google.gerrit.common.changes.ListChangesOption.LABELS;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.gerrit.common.changes.ListChangesOption;
-import com.google.gerrit.common.data.ApprovalType;
-import com.google.gerrit.common.data.ApprovalTypes;
-import com.google.gerrit.common.data.SubmitRecord;
-import com.google.gerrit.reviewdb.client.Account;
-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.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
-import com.google.gerrit.reviewdb.client.PatchSetInfo.ParentInfo;
-import com.google.gerrit.reviewdb.client.UserIdentity;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.AnonymousUser;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.OutputFormat;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.events.AccountAttribute;
-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.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.query.QueryParseException;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.gson.reflect.TypeToken;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import com.jcraft.jsch.HostKey;
-
-import org.eclipse.jgit.lib.Config;
-import org.kohsuke.args4j.Option;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.Writer;
-import java.sql.Timestamp;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-
-public class ListChanges {
- private static final Logger log = LoggerFactory.getLogger(ListChanges.class);
-
- @Singleton
- static class Urls {
- final String git;
- final String http;
-
- @Inject
- Urls(@GerritServerConfig Config cfg) {
- this.git = ensureSlash(cfg.getString("gerrit", null, "canonicalGitUrl"));
- this.http = ensureSlash(cfg.getString("gerrit", null, "gitHttpUrl"));
- }
-
- private static String ensureSlash(String in) {
- if (in != null && !in.endsWith("/")) {
- return in + "/";
- }
- return in;
- }
- }
-
- private final QueryProcessor imp;
- private final Provider<ReviewDb> db;
- private final ApprovalTypes approvalTypes;
- private final CurrentUser user;
- private final AnonymousUser anonymous;
- private final ChangeControl.Factory changeControlFactory;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final PatchListCache patchListCache;
- private final SshInfo sshInfo;
- private final Provider<String> urlProvider;
- private final Urls urls;
- private boolean reverse;
- private Map<Account.Id, AccountAttribute> accounts;
- private Map<Change.Id, ChangeControl> controls;
- private EnumSet<ListChangesOption> options;
-
- @Option(name = "--format", metaVar = "FMT", usage = "Output display format")
- private OutputFormat format = OutputFormat.TEXT;
-
- @Option(name = "--query", aliases = {"-q"}, metaVar = "QUERY", multiValued = true, usage = "Query string")
- private List<String> queries;
-
- @Option(name = "--limit", aliases = {"-n"}, metaVar = "CNT", usage = "Maximum number of results to return")
- public void setLimit(int limit) {
- imp.setLimit(limit);
- }
-
- @Option(name = "-o", multiValued = true, usage = "Output options per change")
- public void addOption(ListChangesOption o) {
- options.add(o);
- }
-
- @Option(name = "-O", usage = "Output option flags, in hex")
- void setOptionFlagsHex(String hex) {
- options.addAll(ListChangesOption.fromBits(Integer.parseInt(hex, 16)));
- }
-
- @Option(name = "-P", metaVar = "SORTKEY", usage = "Previous changes before SORTKEY")
- public void setSortKeyAfter(String key) {
- // Querying for the prior page of changes requires sortkey_after predicate.
- // Changes are shown most recent->least recent. The previous page of
- // results contains changes that were updated after the given key.
- imp.setSortkeyAfter(key);
- reverse = true;
- }
-
- @Option(name = "-N", metaVar = "SORTKEY", usage = "Next changes after SORTKEY")
- public void setSortKeyBefore(String key) {
- // Querying for the next page of changes requires sortkey_before predicate.
- // Changes are shown most recent->least recent. The next page contains
- // changes that were updated before the given key.
- imp.setSortkeyBefore(key);
- }
-
- @Inject
- ListChanges(QueryProcessor qp,
- Provider<ReviewDb> db,
- ApprovalTypes at,
- CurrentUser u,
- AnonymousUser au,
- ChangeControl.Factory cf,
- PatchSetInfoFactory psi,
- PatchListCache plc,
- SshInfo sshInfo,
- @CanonicalWebUrl Provider<String> curl,
- Urls urls) {
- this.imp = qp;
- this.db = db;
- this.approvalTypes = at;
- this.user = u;
- this.anonymous = au;
- this.changeControlFactory = cf;
- this.patchSetInfoFactory = psi;
- this.patchListCache = plc;
- this.sshInfo = sshInfo;
- this.urlProvider = curl;
- this.urls = urls;
-
- accounts = Maps.newHashMap();
- controls = Maps.newHashMap();
- options = EnumSet.noneOf(ListChangesOption.class);
- }
-
- public OutputFormat getFormat() {
- return format;
- }
-
- public ListChanges setFormat(OutputFormat fmt) {
- this.format = fmt;
- return this;
- }
-
- public ListChanges addQuery(String query) {
- if (queries == null) {
- queries = Lists.newArrayList();
- }
- queries.add(query);
- return this;
- }
-
- public void query(Writer out)
- throws OrmException, QueryParseException, IOException {
- if (imp.isDisabled()) {
- throw new QueryParseException("query disabled");
- }
- if (queries == null || queries.isEmpty()) {
- queries = Collections.singletonList("status:open");
- } else if (queries.size() > 10) {
- // Hard-code a default maximum number of queries to prevent
- // users from submitting too much to the server in a single call.
- throw new QueryParseException("limit of 10 queries");
- }
-
- List<List<ChangeInfo>> res = Lists.newArrayListWithCapacity(queries.size());
- for (String query : queries) {
- List<ChangeData> changes = imp.queryChanges(query);
- boolean moreChanges = imp.getLimit() > 0 && changes.size() > imp.getLimit();
- if (moreChanges) {
- if (reverse) {
- changes = changes.subList(1, changes.size());
- } else {
- changes = changes.subList(0, imp.getLimit());
- }
- }
- ChangeData.ensureChangeLoaded(db, changes);
- ChangeData.ensureCurrentPatchSetLoaded(db, changes);
- ChangeData.ensureCurrentApprovalsLoaded(db, changes);
-
- List<ChangeInfo> info = Lists.newArrayListWithCapacity(changes.size());
- for (ChangeData cd : changes) {
- info.add(toChangeInfo(cd));
- }
- if (moreChanges && !info.isEmpty()) {
- if (reverse) {
- info.get(0)._moreChanges = true;
- } else {
- info.get(info.size() - 1)._moreChanges = true;
- }
- }
- res.add(info);
- }
-
- if (!accounts.isEmpty()) {
- for (Account account : db.get().accounts().get(accounts.keySet())) {
- AccountAttribute a = accounts.get(account.getId());
- a.name = Strings.emptyToNull(account.getFullName());
- }
- }
-
- if (format.isJson()) {
- format.newGson().toJson(
- res.size() == 1 ? res.get(0) : res,
- new TypeToken<List<ChangeInfo>>() {}.getType(),
- out);
- out.write('\n');
- } else {
- boolean firstQuery = true;
- for (List<ChangeInfo> info : res) {
- if (firstQuery) {
- firstQuery = false;
- } else {
- out.write('\n');
- }
- for (ChangeInfo c : info) {
- String id = new Change.Key(c.id).abbreviate();
- String subject = c.subject;
- if (subject.length() + id.length() > 80) {
- subject = subject.substring(0, 80 - id.length());
- }
- out.write(id);
- out.write(' ');
- out.write(subject.replace('\n', ' '));
- out.write('\n');
- }
- }
- }
- }
-
- private ChangeInfo toChangeInfo(ChangeData cd) throws OrmException {
- ChangeInfo out = new ChangeInfo();
- Change in = cd.change(db);
- out.project = in.getProject().get();
- out.branch = in.getDest().getShortName();
- out.topic = in.getTopic();
- out.id = in.getKey().get();
- out.subject = in.getSubject();
- out.status = in.getStatus();
- out.owner = asAccountAttribute(in.getOwner());
- out.created = in.getCreatedOn();
- out.updated = in.getLastUpdatedOn();
- out._number = in.getId().get();
- out._sortkey = in.getSortKey();
- out.starred = user.getStarredChanges().contains(in.getId()) ? true : null;
- out.reviewed = in.getStatus().isOpen() && isChangeReviewed(cd) ? true : null;
- out.labels = options.contains(LABELS) ? labelsFor(cd) : null;
-
- if (options.contains(ALL_REVISIONS) || options.contains(CURRENT_REVISION)) {
- out.revisions = revisions(cd);
- for (String commit : out.revisions.keySet()) {
- if (out.revisions.get(commit).isCurrent) {
- out.current_revision = commit;
- break;
- }
- }
- }
-
- return out;
- }
-
- private AccountAttribute asAccountAttribute(Account.Id user) {
- if (user == null) {
- return null;
- }
- AccountAttribute a = accounts.get(user);
- if (a == null) {
- a = new AccountAttribute();
- accounts.put(user, a);
- }
- return a;
- }
-
- private ChangeControl control(ChangeData cd) throws OrmException {
- ChangeControl ctrl = cd.changeControl();
- if (ctrl != null && ctrl.getCurrentUser() == user) {
- return ctrl;
- }
-
- ctrl = controls.get(cd.getId());
- if (ctrl != null) {
- return ctrl;
- }
-
- try {
- ctrl = changeControlFactory.controlFor(cd.change(db));
- } catch (NoSuchChangeException e) {
- return null;
- }
- controls.put(cd.getId(), ctrl);
- return ctrl;
- }
-
- private Map<String, LabelInfo> labelsFor(ChangeData cd) throws OrmException {
- ChangeControl ctl = control(cd);
- if (ctl == null) {
- return Collections.emptyMap();
- }
-
- PatchSet ps = cd.currentPatchSet(db);
- if (ps == null) {
- return Collections.emptyMap();
- }
-
- Map<String, LabelInfo> labels = Maps.newLinkedHashMap();
- for (SubmitRecord rec : ctl.canSubmit(db.get(), ps, cd, true, false)) {
- if (rec.labels == null) {
- continue;
- }
- for (SubmitRecord.Label r : rec.labels) {
- LabelInfo p = labels.get(r.label);
- if (p == null || p._status.compareTo(r.status) < 0) {
- LabelInfo n = new LabelInfo();
- n._status = r.status;
- switch (r.status) {
- case OK:
- n.approved = asAccountAttribute(r.appliedBy);
- break;
- case REJECT:
- n.rejected = asAccountAttribute(r.appliedBy);
- break;
- }
- n.optional = n._status == SubmitRecord.Label.Status.MAY ? true : null;
- labels.put(r.label, n);
- }
- }
- }
-
- Collection<PatchSetApproval> approvals = null;
- for (Map.Entry<String, LabelInfo> e : labels.entrySet()) {
- if (e.getValue().approved != null || e.getValue().rejected != null) {
- continue;
- }
-
- ApprovalType type = approvalTypes.byLabel(e.getKey());
- if (type == null || type.getMin() == null || type.getMax() == null) {
- // Unknown or misconfigured type can't have intermediate scores.
- continue;
- }
-
- short min = type.getMin().getValue();
- short max = type.getMax().getValue();
- if (-1 <= min && max <= 1) {
- // Types with a range of -1..+1 can't have intermediate scores.
- continue;
- }
-
- if (approvals == null) {
- approvals = cd.currentApprovals(db);
- }
- for (PatchSetApproval psa : approvals) {
- short val = psa.getValue();
- if (val != 0 && min < val && val < max
- && psa.getCategoryId().equals(type.getCategory().getId())) {
- if (0 < val) {
- e.getValue().recommended = asAccountAttribute(psa.getAccountId());
- e.getValue().value = val != 1 ? val : null;
- } else {
- e.getValue().disliked = asAccountAttribute(psa.getAccountId());
- e.getValue().value = val != -1 ? val : null;
- }
- }
- }
- }
- return labels;
- }
-
- private boolean isChangeReviewed(ChangeData cd) throws OrmException {
- if (user instanceof IdentifiedUser) {
- PatchSet currentPatchSet = cd.currentPatchSet(db);
- if (currentPatchSet == null) {
- return false;
- }
-
- List<ChangeMessage> messages =
- db.get().changeMessages().byPatchSet(currentPatchSet.getId()).toList();
-
- if (messages.isEmpty()) {
- return false;
- }
-
- // Sort messages to let the most recent ones at the beginning.
- Collections.sort(messages, new Comparator<ChangeMessage>() {
- @Override
- public int compare(ChangeMessage a, ChangeMessage b) {
- return b.getWrittenOn().compareTo(a.getWrittenOn());
- }
- });
-
- Account.Id currentUserId = ((IdentifiedUser) user).getAccountId();
- Account.Id changeOwnerId = cd.change(db).getOwner();
- for (ChangeMessage cm : messages) {
- if (currentUserId.equals(cm.getAuthor())) {
- return true;
- } else if (changeOwnerId.equals(cm.getAuthor())) {
- return false;
- }
- }
- }
- return false;
- }
-
- private Map<String, RevisionInfo> revisions(ChangeData cd) throws OrmException {
- ChangeControl ctl = control(cd);
- if (ctl == null) {
- return Collections.emptyMap();
- }
-
- Collection<PatchSet> src;
- if (options.contains(ALL_REVISIONS)) {
- src = cd.patches(db);
- } else {
- src = Collections.singletonList(cd.currentPatchSet(db));
- }
- Map<String, RevisionInfo> res = Maps.newLinkedHashMap();
- for (PatchSet in : src) {
- if (ctl.isPatchVisible(in, db.get())) {
- res.put(in.getRevision().get(), toRevisionInfo(cd, in));
- }
- }
- return res;
- }
-
- private RevisionInfo toRevisionInfo(ChangeData cd, PatchSet in)
- throws OrmException {
- RevisionInfo out = new RevisionInfo();
- out.isCurrent = in.getId().equals(cd.change(db).currentPatchSetId());
- out._number = in.getId().get();
- out.draft = in.isDraft() ? true : null;
- out.fetch = makeFetchMap(cd, in);
-
- if (options.contains(ALL_COMMITS)
- || (out.isCurrent && options.contains(CURRENT_COMMIT))) {
- try {
- PatchSetInfo info = patchSetInfoFactory.get(db.get(), in.getId());
- out.commit = new CommitInfo();
- out.commit.parents = Lists.newArrayListWithCapacity(info.getParents().size());
- out.commit.author = toGitPerson(info.getAuthor());
- out.commit.committer = toGitPerson(info.getCommitter());
- out.commit.subject = info.getSubject();
- out.commit.message = info.getMessage();
-
- for (ParentInfo parent : info.getParents()) {
- CommitInfo i = new CommitInfo();
- i.commit = parent.id.get();
- i.subject = parent.shortMessage;
- out.commit.parents.add(i);
- }
- } catch (PatchSetInfoNotAvailableException e) {
- log.warn("Cannot load PatchSetInfo " + in.getId(), e);
- }
- }
-
- if (options.contains(ALL_FILES)
- || (out.isCurrent && options.contains(CURRENT_FILES))) {
- PatchList list;
- try {
- list = patchListCache.get(cd.change(db), in);
- } catch (PatchListNotAvailableException e) {
- log.warn("Cannot load PatchList " + in.getId(), e);
- list = null;
- }
- if (list != null) {
- out.files = Maps.newTreeMap();
- for (PatchListEntry e : list.getPatches()) {
- if (Patch.COMMIT_MSG.equals(e.getNewName())) {
- continue;
- }
-
- FileInfo d = new FileInfo();
- d.status = e.getChangeType() != Patch.ChangeType.MODIFIED
- ? e.getChangeType().getCode()
- : null;
- d.oldPath = e.getOldName();
- if (e.getPatchType() == Patch.PatchType.BINARY) {
- d.binary = true;
- } else {
- d.linesInserted = e.getInsertions() > 0 ? e.getInsertions() : null;
- d.linesDeleted = e.getDeletions() > 0 ? e.getDeletions() : null;
- }
-
- FileInfo o = out.files.put(e.getNewName(), d);
- if (o != null) {
- // This should only happen on a delete-add break created by JGit
- // when the file was rewritten and too little content survived. Write
- // a single record with data from both sides.
- d.status = Patch.ChangeType.REWRITE.getCode();
- if (o.binary != null && o.binary) {
- d.binary = true;
- }
- if (o.linesInserted != null) {
- d.linesInserted = o.linesInserted;
- }
- if (o.linesDeleted != null) {
- d.linesDeleted = o.linesDeleted;
- }
- }
- }
- }
- }
- return out;
- }
-
- private Map<String, FetchInfo> makeFetchMap(ChangeData cd, PatchSet in)
- throws OrmException {
- Map<String, FetchInfo> r = Maps.newLinkedHashMap();
- String refName = in.getRefName();
- ChangeControl ctl = control(cd);
- if (ctl != null && ctl.forUser(anonymous).isPatchVisible(in, db.get())) {
- if (urls.git != null) {
- r.put("git", new FetchInfo(urls.git
- + cd.change(db).getProject().get(), refName));
- }
- }
- if (urls.http != null) {
- r.put("http", new FetchInfo(urls.http
- + cd.change(db).getProject().get(), refName));
- } else {
- String http = urlProvider.get();
- if (!Strings.isNullOrEmpty(http)) {
- r.put("http", new FetchInfo(http
- + cd.change(db).getProject().get(), refName));
- }
- }
- if (!sshInfo.getHostKeys().isEmpty()) {
- HostKey host = sshInfo.getHostKeys().get(0);
- r.put("ssh", new FetchInfo(String.format(
- "ssh://%s/%s",
- host.getHost(), cd.change(db).getProject().get()),
- refName));
- }
-
- return r;
- }
-
- private static GitPerson toGitPerson(UserIdentity committer) {
- GitPerson p = new GitPerson();
- p.name = committer.getName();
- p.email = committer.getEmail();
- p.date = committer.getDate();
- p.tz = committer.getTimeZone();
- return p;
- }
-
- static class ChangeInfo {
- String project;
- String branch;
- String topic;
- String id;
- String subject;
- Change.Status status;
- Timestamp created;
- Timestamp updated;
- Boolean starred;
- Boolean reviewed;
-
- String _sortkey;
- int _number;
-
- AccountAttribute owner;
- Map<String, LabelInfo> labels;
- String current_revision;
- Map<String, RevisionInfo> revisions;
-
- Boolean _moreChanges;
- }
-
- static class RevisionInfo {
- private transient boolean isCurrent;
- Boolean draft;
- int _number;
- Map<String, FetchInfo> fetch;
- CommitInfo commit;
- Map<String, FileInfo> files;
- }
-
- static class FetchInfo {
- String url;
- String ref;
-
- FetchInfo(String url, String ref) {
- this.url = url;
- this.ref = ref;
- }
- }
-
- static class GitPerson {
- String name;
- String email;
- Timestamp date;
- int tz;
- }
-
- static class CommitInfo {
- String commit;
- List<CommitInfo> parents;
- GitPerson author;
- GitPerson committer;
- String subject;
- String message;
- }
-
- static class FileInfo {
- Character status;
- Boolean binary;
- String oldPath;
- Integer linesInserted;
- Integer linesDeleted;
- }
-
- static class LabelInfo {
- transient SubmitRecord.Label.Status _status;
- AccountAttribute approved;
- AccountAttribute rejected;
-
- AccountAttribute recommended;
- AccountAttribute disliked;
- Short value;
- Boolean optional;
- }
-}