LCOV - code coverage report
Current view: top level - server/query/change - ChangeStatusPredicate.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 43 44 97.7 %
Date: 2022-11-19 15:00:39 Functions: 12 13 92.3 %

          Line data    Source code
       1             : // Copyright (C) 2009 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.query.change;
      16             : 
      17             : import static com.google.common.base.Preconditions.checkState;
      18             : import static java.util.Objects.requireNonNull;
      19             : 
      20             : import com.google.gerrit.common.Nullable;
      21             : import com.google.gerrit.entities.Change;
      22             : import com.google.gerrit.entities.Change.Status;
      23             : import com.google.gerrit.index.query.HasCardinality;
      24             : import com.google.gerrit.index.query.Predicate;
      25             : import com.google.gerrit.index.query.QueryParseException;
      26             : import com.google.gerrit.server.index.change.ChangeField;
      27             : import java.util.ArrayList;
      28             : import java.util.List;
      29             : import java.util.Map;
      30             : import java.util.NavigableMap;
      31             : import java.util.Objects;
      32             : import java.util.TreeMap;
      33             : 
      34             : /**
      35             :  * Predicate for a {@link Status}.
      36             :  *
      37             :  * <p>The actual name of this operator can differ, it usually comes as {@code status:} but may also
      38             :  * be {@code is:} to help do-what-i-meanery for end-users searching for changes. Either operator
      39             :  * name has the same meaning.
      40             :  *
      41             :  * <p>Status names are looked up by prefix case-insensitively.
      42             :  */
      43             : public final class ChangeStatusPredicate extends ChangeIndexPredicate implements HasCardinality {
      44             :   private static final String INVALID_STATUS = "__invalid__";
      45         113 :   static final Predicate<ChangeData> NONE = new ChangeStatusPredicate(null);
      46             : 
      47             :   private static final TreeMap<String, Predicate<ChangeData>> PREDICATES;
      48             :   private static final Predicate<ChangeData> CLOSED;
      49             :   private static final Predicate<ChangeData> OPEN;
      50             : 
      51             :   static {
      52         113 :     PREDICATES = new TreeMap<>();
      53         113 :     List<Predicate<ChangeData>> open = new ArrayList<>();
      54         113 :     List<Predicate<ChangeData>> closed = new ArrayList<>();
      55             : 
      56         113 :     for (Change.Status s : Change.Status.values()) {
      57         113 :       ChangeStatusPredicate p = forStatus(s);
      58         113 :       String str = canonicalize(s);
      59         113 :       checkState(
      60         113 :           !INVALID_STATUS.equals(str),
      61             :           "invalid status sentinel %s cannot match canonicalized status string %s",
      62             :           INVALID_STATUS,
      63             :           str);
      64         113 :       PREDICATES.put(str, p);
      65         113 :       (s.isOpen() ? open : closed).add(p);
      66             :     }
      67             : 
      68         113 :     CLOSED = Predicate.or(closed);
      69         113 :     OPEN = Predicate.or(open);
      70             : 
      71         113 :     PREDICATES.put("closed", CLOSED);
      72         113 :     PREDICATES.put("open", OPEN);
      73         113 :     PREDICATES.put("pending", OPEN);
      74         113 :   }
      75             : 
      76             :   public static String canonicalize(Change.Status status) {
      77         113 :     return status.name().toLowerCase();
      78             :   }
      79             : 
      80             :   public static Predicate<ChangeData> parse(String value) throws QueryParseException {
      81          18 :     String lower = value.toLowerCase();
      82          18 :     NavigableMap<String, Predicate<ChangeData>> head = PREDICATES.tailMap(lower, true);
      83          18 :     if (!head.isEmpty()) {
      84             :       // Assume no statuses share a common prefix so we can only walk one entry.
      85          18 :       Map.Entry<String, Predicate<ChangeData>> e = head.entrySet().iterator().next();
      86          18 :       if (e.getKey().startsWith(lower)) {
      87          17 :         return e.getValue();
      88             :       }
      89             :     }
      90           5 :     throw new QueryParseException("Unrecognized value: " + value);
      91             :   }
      92             : 
      93             :   public static Predicate<ChangeData> open() {
      94         105 :     return OPEN;
      95             :   }
      96             : 
      97             :   public static Predicate<ChangeData> closed() {
      98           7 :     return CLOSED;
      99             :   }
     100             : 
     101             :   public static ChangeStatusPredicate forStatus(Change.Status status) {
     102         113 :     return new ChangeStatusPredicate(requireNonNull(status));
     103             :   }
     104             : 
     105             :   @Nullable private final Change.Status status;
     106             : 
     107             :   private ChangeStatusPredicate(@Nullable Change.Status status) {
     108         113 :     super(ChangeField.STATUS_SPEC, status != null ? canonicalize(status) : INVALID_STATUS);
     109         113 :     this.status = status;
     110         113 :   }
     111             : 
     112             :   /**
     113             :    * Get the status for this predicate.
     114             :    *
     115             :    * @return the status, or null if this predicate is intended to never match any changes.
     116             :    */
     117             :   @Nullable
     118             :   public Change.Status getStatus() {
     119           9 :     return status;
     120             :   }
     121             : 
     122             :   @Override
     123             :   public boolean match(ChangeData object) {
     124          81 :     Change change = object.change();
     125          81 :     return change != null && Objects.equals(status, change.getStatus());
     126             :   }
     127             : 
     128             :   @Override
     129             :   public int hashCode() {
     130           0 :     return Objects.hashCode(status);
     131             :   }
     132             : 
     133             :   @Override
     134             :   public boolean equals(Object other) {
     135           1 :     return (other instanceof ChangeStatusPredicate)
     136           1 :         && Objects.equals(status, ((ChangeStatusPredicate) other).status);
     137             :   }
     138             : 
     139             :   @Override
     140             :   public String toString() {
     141           8 :     return getOperator() + ":" + getValue();
     142             :   }
     143             : 
     144             :   @Override
     145             :   public int getCardinality() {
     146           8 :     if (getStatus() == null) {
     147           2 :       return 0;
     148             :     }
     149           8 :     switch (getStatus()) {
     150             :       case MERGED:
     151           7 :         return 50_000;
     152             :       case ABANDONED:
     153           7 :         return 50_000;
     154             :       case NEW:
     155             :       default:
     156           8 :         return 2000;
     157             :     }
     158             :   }
     159             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750