Package com.google.gerrit.server.query.change

Source Code of com.google.gerrit.server.query.change.ListChanges$LabelInfo

// 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 com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
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.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.OutputFormat;
import com.google.gerrit.server.events.AccountAttribute;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gson.reflect.TypeToken;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;

import org.kohsuke.args4j.Option;

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.List;
import java.util.Map;

public class ListChanges {
  private final QueryProcessor imp;
  private final Provider<ReviewDb> db;
  private final ApprovalTypes approvalTypes;
  private final CurrentUser user;
  private final ChangeControl.Factory changeControlFactory;
  private boolean reverse;
  private Map<Account.Id, AccountAttribute> accounts;

  @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")
  void setLimit(int limit) {
    imp.setLimit(limit);
  }

  @Option(name = "-P", metaVar = "SORTKEY", usage = "Previous changes before SORTKEY")
  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")
  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,
      ChangeControl.Factory cf) {
    this.imp = qp;
    this.db = db;
    this.approvalTypes = at;
    this.user = u;
    this.changeControlFactory = cf;

    accounts = Maps.newHashMap();
  }

  public OutputFormat getFormat() {
    return format;
  }

  public ListChanges setFormat(OutputFormat fmt) {
    this.format = fmt;
    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 = labelsFor(cd);
    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 Map<String, LabelInfo> labelsFor(ChangeData cd) throws OrmException {
    Change in = cd.change(db);
    ChangeControl ctl = cd.changeControl();
    if (ctl == null || ctl.getCurrentUser() != user) {
      try {
        ctl = changeControlFactory.controlFor(in);
      } catch (NoSuchChangeException e) {
        return null;
      }
    }

    PatchSet ps = cd.currentPatchSet(db);
    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;
  }

  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;
    Boolean _moreChanges;
  }

  static class LabelInfo {
    transient SubmitRecord.Label.Status _status;
    AccountAttribute approved;
    AccountAttribute rejected;

    AccountAttribute recommended;
    AccountAttribute disliked;
    Short value;
    Boolean optional;
  }
}
TOP

Related Classes of com.google.gerrit.server.query.change.ListChanges$LabelInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.