ChangeListServiceImpl.java :  » android-core » tools-gerrit » com » google » gerrit » httpd » rpc » Android Open Source

Android Open Source » android core » tools gerrit 
tools gerrit » com » google » gerrit » httpd » rpc » ChangeListServiceImpl.java
// 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.httpd.rpc;

import com.google.gerrit.common.data.AccountDashboardInfo;
import com.google.gerrit.common.data.ChangeInfo;
import com.google.gerrit.common.data.ChangeListService;
import com.google.gerrit.common.data.SingleListChangeInfo;
import com.google.gerrit.common.data.ToggleStarRequest;
import com.google.gerrit.common.errors.InvalidQueryException;
import com.google.gerrit.common.errors.NoSuchEntityException;
import com.google.gerrit.reviewdb.Account;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.ChangeAccess;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.reviewdb.StarredChange;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountInfoCacheFactory;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeDataSource;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwtjsonrpc.client.VoidResult;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.ResultSet;
import com.google.gwtorm.client.impl.ListResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ChangeListServiceImpl extends BaseServiceImplementation implements
    ChangeListService {
  private static final Comparator<ChangeInfo> ID_COMP =
      new Comparator<ChangeInfo>() {
        public int compare(final ChangeInfo o1, final ChangeInfo o2) {
          return o1.getId().get() - o2.getId().get();
        }
      };
  private static final Comparator<ChangeInfo> SORT_KEY_COMP =
      new Comparator<ChangeInfo>() {
        public int compare(final ChangeInfo o1, final ChangeInfo o2) {
          return o2.getSortKey().compareTo(o1.getSortKey());
        }
      };
  private static final Comparator<Change> QUERY_PREV =
      new Comparator<Change>() {
        public int compare(final Change a, final Change b) {
          return a.getSortKey().compareTo(b.getSortKey());
        }
      };
  private static final Comparator<Change> QUERY_NEXT =
      new Comparator<Change>() {
        public int compare(final Change a, final Change b) {
          return b.getSortKey().compareTo(a.getSortKey());
        }
      };

  private static final int MAX_PER_PAGE = 100;

  private static int safePageSize(final int pageSize) {
    return 0 < pageSize && pageSize <= MAX_PER_PAGE ? pageSize : MAX_PER_PAGE;
  }

  private final Provider<CurrentUser> currentUser;
  private final ChangeControl.Factory changeControlFactory;
  private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;

  private final ChangeQueryBuilder.Factory queryBuilder;
  private final Provider<ChangeQueryRewriter> queryRewriter;

  @Inject
  ChangeListServiceImpl(final Provider<ReviewDb> schema,
      final Provider<CurrentUser> currentUser,
      final ChangeControl.Factory changeControlFactory,
      final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
      final ChangeQueryBuilder.Factory queryBuilder,
      final Provider<ChangeQueryRewriter> queryRewriter) {
    super(schema, currentUser);
    this.currentUser = currentUser;
    this.changeControlFactory = changeControlFactory;
    this.accountInfoCacheFactory = accountInfoCacheFactory;
    this.queryBuilder = queryBuilder;
    this.queryRewriter = queryRewriter;
  }

  private boolean canRead(final Change c) {
    try {
      return changeControlFactory.controlFor(c).isVisible();
    } catch (NoSuchChangeException e) {
      return false;
    }
  }

  @Override
  public void allQueryPrev(final String query, final String pos,
      final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
    run(callback, new QueryPrev(pageSize, pos) {
      @Override
      ResultSet<Change> query(ReviewDb db, int lim, String key)
          throws OrmException, InvalidQueryException {
        return searchQuery(db, query, lim, key, QUERY_PREV);
      }
    });
  }

  @Override
  public void allQueryNext(final String query, final String pos,
      final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
    run(callback, new QueryNext(pageSize, pos) {
      @Override
      ResultSet<Change> query(ReviewDb db, int lim, String key)
          throws OrmException, InvalidQueryException {
        return searchQuery(db, query, lim, key, QUERY_NEXT);
      }
    });
  }

  @SuppressWarnings("unchecked")
  private ResultSet<Change> searchQuery(final ReviewDb db, String query,
      final int limit, final String key, final Comparator<Change> cmp)
      throws OrmException, InvalidQueryException {
    try {
      final ChangeQueryBuilder builder = queryBuilder.create(currentUser.get());
      final Predicate<ChangeData> visibleToMe = builder.is_visible();
      Predicate<ChangeData> q = builder.parse(query);
      q = Predicate.and(q, //
          cmp == QUERY_PREV //
              ? builder.sortkey_after(key) //
              : builder.sortkey_before(key), //
          builder.limit(limit), //
          visibleToMe //
          );

      ChangeQueryRewriter rewriter = queryRewriter.get();
      Predicate<ChangeData> s = rewriter.rewrite(q);
      if (!(s instanceof ChangeDataSource)) {
        s = rewriter.rewrite(Predicate.and(builder.status_open(), q));
      }

      if (s instanceof ChangeDataSource) {
        ArrayList<Change> r = new ArrayList();
        HashSet<Change.Id> want = new HashSet<Change.Id>();
        for (ChangeData d : ((ChangeDataSource) s).read()) {
          if (d.hasChange()) {
            // Checking visibleToMe here should be unnecessary, the
            // query should have already performed it.  But we don't
            // want to trust the query rewriter that much yet.
            //
            if (visibleToMe.match(d)) {
              r.add(d.getChange());
            }
          } else {
            want.add(d.getId());
          }
        }

        // Here we have to check canRead. Its impossible to
        // do that test without the change object, and it being
        // missing above means we have to compute it ourselves.
        //
        if (!want.isEmpty()) {
          for (Change c : db.changes().get(want)) {
            if (canRead(c)) {
              r.add(c);
            }
          }
        }

        Collections.sort(r, cmp);
        return new ListResultSet<Change>(r);
      } else {
        throw new InvalidQueryException("Not Supported", s.toString());
      }
    } catch (QueryParseException e) {
      throw new InvalidQueryException(e.getMessage(), query);
    }
  }

  public void forAccount(final Account.Id id,
      final AsyncCallback<AccountDashboardInfo> callback) {
    final Account.Id me = getAccountId();
    final Account.Id target = id != null ? id : me;
    if (target == null) {
      callback.onFailure(new NoSuchEntityException());
      return;
    }

    run(callback, new Action<AccountDashboardInfo>() {
      public AccountDashboardInfo run(final ReviewDb db) throws OrmException,
          Failure {
        final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
        final Account user = ac.get(target);
        if (user == null) {
          throw new Failure(new NoSuchEntityException());
        }

        final Set<Change.Id> stars = currentUser.get().getStarredChanges();
        final ChangeAccess changes = db.changes();
        final AccountDashboardInfo d;

        final Set<Change.Id> openReviews = new HashSet<Change.Id>();
        final Set<Change.Id> closedReviews = new HashSet<Change.Id>();
        for (final PatchSetApproval ca : db.patchSetApprovals().openByUser(id)) {
          openReviews.add(ca.getPatchSetId().getParentKey());
        }
        for (final PatchSetApproval ca : db.patchSetApprovals()
            .closedByUser(id)) {
          closedReviews.add(ca.getPatchSetId().getParentKey());
        }

        d = new AccountDashboardInfo(target);
        d.setByOwner(filter(changes.byOwnerOpen(target), stars, ac));
        d.setClosed(filter(changes.byOwnerClosed(target), stars, ac));

        for (final ChangeInfo c : d.getByOwner()) {
          openReviews.remove(c.getId());
        }
        d.setForReview(filter(changes.get(openReviews), stars, ac));
        Collections.sort(d.getForReview(), ID_COMP);

        for (final ChangeInfo c : d.getClosed()) {
          closedReviews.remove(c.getId());
        }
        if (!closedReviews.isEmpty()) {
          d.getClosed().addAll(filter(changes.get(closedReviews), stars, ac));
          Collections.sort(d.getClosed(), SORT_KEY_COMP);
        }

        d.setAccounts(ac.create());
        return d;
      }
    });
  }

  public void toggleStars(final ToggleStarRequest req,
      final AsyncCallback<VoidResult> callback) {
    run(callback, new Action<VoidResult>() {
      public VoidResult run(final ReviewDb db) throws OrmException {
        final Account.Id me = getAccountId();
        final Set<Change.Id> existing = currentUser.get().getStarredChanges();
        List<StarredChange> add = new ArrayList<StarredChange>();
        List<StarredChange.Key> remove = new ArrayList<StarredChange.Key>();

        if (req.getAddSet() != null) {
          for (final Change.Id id : req.getAddSet()) {
            if (!existing.contains(id)) {
              add.add(new StarredChange(new StarredChange.Key(me, id)));
            }
          }
        }

        if (req.getRemoveSet() != null) {
          for (final Change.Id id : req.getRemoveSet()) {
            remove.add(new StarredChange.Key(me, id));
          }
        }

        db.starredChanges().insert(add);
        db.starredChanges().deleteKeys(remove);
        return VoidResult.INSTANCE;
      }
    });
  }

  public void myStarredChangeIds(final AsyncCallback<Set<Change.Id>> callback) {
    callback.onSuccess(currentUser.get().getStarredChanges());
  }

  private List<ChangeInfo> filter(final ResultSet<Change> rs,
      final Set<Change.Id> starred, final AccountInfoCacheFactory accts) {
    final ArrayList<ChangeInfo> r = new ArrayList<ChangeInfo>();
    for (final Change c : rs) {
      if (canRead(c)) {
        final ChangeInfo ci = new ChangeInfo(c);
        accts.want(ci.getOwner());
        ci.setStarred(starred.contains(ci.getId()));
        r.add(ci);
      }
    }
    return r;
  }

  private abstract class QueryNext implements Action<SingleListChangeInfo> {
    protected final String pos;
    protected final int limit;
    protected final int slim;

    QueryNext(final int pageSize, final String pos) {
      this.pos = pos;
      this.limit = safePageSize(pageSize);
      this.slim = limit + 1;
    }

    public SingleListChangeInfo run(final ReviewDb db) throws OrmException,
        InvalidQueryException {
      final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
      final SingleListChangeInfo d = new SingleListChangeInfo();
      final Set<Change.Id> starred = currentUser.get().getStarredChanges();

      final ArrayList<ChangeInfo> list = new ArrayList<ChangeInfo>();
      final ResultSet<Change> rs = query(db, slim, pos);
      for (final Change c : rs) {
        final ChangeInfo ci = new ChangeInfo(c);
        ac.want(ci.getOwner());
        ci.setStarred(starred.contains(ci.getId()));
        list.add(ci);
        if (list.size() == slim) {
          rs.close();
          break;
        }
      }

      final boolean atEnd = finish(list);
      d.setChanges(list, atEnd);
      d.setAccounts(ac.create());
      return d;
    }

    boolean finish(final ArrayList<ChangeInfo> list) {
      final boolean atEnd = list.size() <= limit;
      if (list.size() == slim) {
        list.remove(limit);
      }
      return atEnd;
    }

    abstract ResultSet<Change> query(final ReviewDb db, final int slim,
        String sortKey) throws OrmException, InvalidQueryException;
  }

  private abstract class QueryPrev extends QueryNext {
    QueryPrev(int pageSize, String pos) {
      super(pageSize, pos);
    }

    @Override
    boolean finish(final ArrayList<ChangeInfo> list) {
      final boolean atEnd = super.finish(list);
      Collections.reverse(list);
      return atEnd;
    }
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.