summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java
blob: aabc0664213e9650e196ac5b852869a9f4d7f40d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright (C) 2009 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;

import static com.google.common.base.Preconditions.checkState;

import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * An abstract predicate tree for any form of query.
 *
 * <p>Implementations should be immutable, such that the meaning of a predicate never changes once
 * constructed. They should ensure their immutable promise by defensively copying any structures
 * which might be modified externally, but was passed into the object's constructor.
 *
 * <p>However, implementations <i>may</i> retain non-thread-safe caches internally, to speed up
 * evaluation operations within the context of one thread's evaluation of the predicate. As a
 * result, callers should assume predicates are not thread-safe, but that two predicate graphs
 * produce the same results given the same inputs if they are {@link #equals(Object)}.
 *
 * <p>Predicates should support deep inspection whenever possible, so that generic algorithms can be
 * written to operate against them. Predicates which contain other predicates should override {@link
 * #getChildren()} to return the list of children nested within the predicate.
 *
 * @param <T> type of object the predicate can evaluate in memory.
 */
public abstract class Predicate<T> {
  /** A predicate that matches any input, always, with no cost. */
  @SuppressWarnings("unchecked")
  public static <T> Predicate<T> any() {
    return (Predicate<T>) Any.INSTANCE;
  }

  /** Combine the passed predicates into a single AND node. */
  @SafeVarargs
  public static <T> Predicate<T> and(final Predicate<T>... that) {
    if (that.length == 1) {
      return that[0];
    }
    return new AndPredicate<>(that);
  }

  /** Combine the passed predicates into a single AND node. */
  public static <T> Predicate<T> and(final Collection<? extends Predicate<T>> that) {
    if (that.size() == 1) {
      return Iterables.getOnlyElement(that);
    }
    return new AndPredicate<>(that);
  }

  /** Combine the passed predicates into a single OR node. */
  @SafeVarargs
  public static <T> Predicate<T> or(final Predicate<T>... that) {
    if (that.length == 1) {
      return that[0];
    }
    return new OrPredicate<>(that);
  }

  /** Combine the passed predicates into a single OR node. */
  public static <T> Predicate<T> or(final Collection<? extends Predicate<T>> that) {
    if (that.size() == 1) {
      return Iterables.getOnlyElement(that);
    }
    return new OrPredicate<>(that);
  }

  /** Invert the passed node. */
  public static <T> Predicate<T> not(final Predicate<T> that) {
    if (that instanceof NotPredicate) {
      // Negate of a negate is the original predicate.
      //
      return that.getChild(0);
    }
    return new NotPredicate<>(that);
  }

  /** Get the children of this predicate, if any. */
  public List<Predicate<T>> getChildren() {
    return Collections.emptyList();
  }

  /** Same as {@code getChildren().size()} */
  public int getChildCount() {
    return getChildren().size();
  }

  /** Same as {@code getChildren().get(i)} */
  public Predicate<T> getChild(final int i) {
    return getChildren().get(i);
  }

  /** Create a copy of this predicate, with new children. */
  public abstract Predicate<T> copy(Collection<? extends Predicate<T>> children);

  public boolean isMatchable() {
    return this instanceof Matchable;
  }

  @SuppressWarnings("unchecked")
  public Matchable<T> asMatchable() {
    checkState(isMatchable(), "not matchable");
    return (Matchable<T>) this;
  }

  /** @return a cost estimate to run this predicate, higher figures cost more. */
  public int estimateCost() {
    if (!isMatchable()) {
      return 1;
    }
    return asMatchable().getCost();
  }

  @Override
  public abstract int hashCode();

  @Override
  public abstract boolean equals(Object other);

  private static class Any<T> extends Predicate<T> implements Matchable<T> {
    private static final Any<Object> INSTANCE = new Any<>();

    private Any() {}

    @Override
    public Predicate<T> copy(Collection<? extends Predicate<T>> children) {
      return this;
    }

    @Override
    public boolean match(T object) {
      return true;
    }

    @Override
    public int getCost() {
      return 0;
    }

    @Override
    public int hashCode() {
      return System.identityHashCode(this);
    }

    @Override
    public boolean equals(Object other) {
      return other == this;
    }
  }
}