| 1 | package com.reallifedeveloper.tools.test.database.inmemory; | |
| 2 | ||
| 3 | import java.lang.reflect.Field; | |
| 4 | import java.lang.reflect.InvocationHandler; | |
| 5 | import java.lang.reflect.Method; | |
| 6 | import java.lang.reflect.Proxy; | |
| 7 | import java.math.BigDecimal; | |
| 8 | import java.util.AbstractMap; | |
| 9 | import java.util.ArrayList; | |
| 10 | import java.util.Arrays; | |
| 11 | import java.util.Collection; | |
| 12 | import java.util.HashMap; | |
| 13 | import java.util.List; | |
| 14 | import java.util.Locale; | |
| 15 | import java.util.Map; | |
| 16 | import java.util.Objects; | |
| 17 | import java.util.Optional; | |
| 18 | import java.util.concurrent.ConcurrentHashMap; | |
| 19 | import java.util.function.Function; | |
| 20 | ||
| 21 | import org.checkerframework.checker.nullness.qual.Nullable; | |
| 22 | import org.slf4j.Logger; | |
| 23 | import org.slf4j.LoggerFactory; | |
| 24 | import org.springframework.data.jpa.domain.PredicateSpecification; | |
| 25 | ||
| 26 | import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | |
| 27 | import jakarta.persistence.criteria.CriteriaBuilder; | |
| 28 | import jakarta.persistence.criteria.Expression; | |
| 29 | import jakarta.persistence.criteria.From; | |
| 30 | import jakarta.persistence.criteria.Join; | |
| 31 | import jakarta.persistence.criteria.JoinType; | |
| 32 | import jakarta.persistence.criteria.MapJoin; | |
| 33 | import jakarta.persistence.criteria.Path; | |
| 34 | import jakarta.persistence.criteria.Predicate; | |
| 35 | import lombok.Getter; | |
| 36 | import lombok.experimental.Accessors; | |
| 37 | ||
| 38 | /** | |
| 39 | * An evaluator of {@code org.springframework.data.jpa.domain.PredicateSpecification} instances. | |
| 40 | * <p> | |
| 41 | * This implementation is maintained in a single, large, file. This is because, a) the functionatlity has a clear focus with a small public | |
| 42 | * API, and b) the code was written for the main part by ChatGPT, and I want to make it clear what code was generated by AI. | |
| 43 | * | |
| 44 | * @param <T> the type of entity for which the specification | |
| 45 | * | |
| 46 | * @see <a href= "https://docs.spring.io/spring-data/jpa/reference/jpa/specifications.html#predicate-specification">Spring Data JPA | |
| 47 | * documentation</a> | |
| 48 | * | |
| 49 | * @author ChatGPT, RealLifeDeveloper | |
| 50 | */ | |
| 51 | @SuppressWarnings({ "InnerTypeLast", "PMD" }) | |
| 52 | @SuppressFBWarnings(value = "CRLF_INJECTION_LOGS", justification = "This code is only intended for testing") | |
| 53 | public final class PredicateSpecificationEvaluator<T> { | |
| 54 | ||
| 55 | private static final Logger LOG = LoggerFactory.getLogger(PredicateSpecificationEvaluator.class); | |
| 56 | ||
| 57 | private final Map<String, RegisteredFunction> functions = new HashMap<>(); | |
| 58 | ||
| 59 | /** | |
| 60 | * Checks if the given entity matches the given specification. | |
| 61 | * | |
| 62 | * @param specification the {@code PredicateSpecification} to use | |
| 63 | * @param entity the entity to check | |
| 64 | * | |
| 65 | * @return {@code true} if {@code specification} matches {@code entity}, {@code false} otherwise | |
| 66 | */ | |
| 67 | public boolean matches(PredicateSpecification<T> specification, T entity) { | |
| 68 | Objects.requireNonNull(specification); | |
| 69 | Objects.requireNonNull(entity); | |
| 70 | ||
| 71 | RecordingState state = new RecordingState(); | |
| 72 | ||
| 73 | From<?, T> root = Proxies.root(state); | |
| 74 | CriteriaBuilder cb = Proxies.criteriaBuilder(); | |
| 75 | ||
| 76 | Predicate predicate = specification.toPredicate(root, cb); | |
| 77 | ||
| 78 | // PredicateSpecification.unrestricted() and similar specifications are represented by a null predicate. | |
| 79 |
1
1. matches : negated conditional → KILLED |
if (predicate == null) { |
| 80 |
1
1. matches : replaced boolean return with false for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::matches → KILLED |
return true; |
| 81 | } | |
| 82 | ||
| 83 | Expr expr = Proxies.expressionOf(predicate); | |
| 84 | ||
| 85 | LOG.debug("matches: expr={}, entity={}", expr, entity); | |
| 86 | LOG.trace("matches: joins={}", state.joins()); | |
| 87 | ||
| 88 | List<EvaluationContext> rows = expandJoins(state, entity); | |
| 89 | ||
| 90 | LOG.trace("matches: rows={}", rows); | |
| 91 | ||
| 92 |
4
1. matches : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::matches → KILLED 2. matches : replaced boolean return with false for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::matches → KILLED 3. lambda$matches$0 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$matches$0 → KILLED 4. lambda$matches$0 : negated conditional → KILLED |
return rows.stream().anyMatch(context -> evaluateBoolean(expr, context) == Truth.TRUE); |
| 93 | } | |
| 94 | ||
| 95 | /** | |
| 96 | * Filters a collection of entities based on if they match the given specification or not. | |
| 97 | * | |
| 98 | * @param specification the {@code PredicateSpecification} to use | |
| 99 | * @param entities the collection of entities to filter | |
| 100 | * | |
| 101 | * @return a list of the entities from the {@code entities} collection that match {@code specification} | |
| 102 | */ | |
| 103 | public List<T> filter(PredicateSpecification<T> specification, Collection<T> entities) { | |
| 104 |
3
1. lambda$filter$1 : replaced boolean return with false for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$filter$1 → KILLED 2. filter : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::filter → KILLED 3. lambda$filter$1 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$filter$1 → KILLED |
return entities.stream().filter(entity -> matches(specification, entity)).toList(); |
| 105 | } | |
| 106 | ||
| 107 | /** | |
| 108 | * Registers a function that can be used by {@code CriteriaBuilder.function}. | |
| 109 | * <p> | |
| 110 | * An example of registering a function: | |
| 111 | * | |
| 112 | * <pre>{@code | |
| 113 | * evaluator.registerFunction("concat_with_separator", String.class, arguments -> { | |
| 114 | * String separator = (String) arguments.get(0); | |
| 115 | * String left = (String) arguments.get(1); | |
| 116 | * String right = (String) arguments.get(2); | |
| 117 | * if (separator == null || left == null || right == null) { | |
| 118 | * return null; | |
| 119 | * } | |
| 120 | * return left + separator + right; | |
| 121 | * }); | |
| 122 | * }</pre> | |
| 123 | * | |
| 124 | * @param name the name of the function to register | |
| 125 | * @param resultType the class representing the return type of the function to register | |
| 126 | * @param parameterTypes a list of class objects representing the type of the parameters | |
| 127 | * @param function the function to register | |
| 128 | * @param <R> the return type of the function to register | |
| 129 | * | |
| 130 | * @return the {@code PredicationSpecificationEvaluator} itself, in order to support nested (fluent) calls | |
| 131 | */ | |
| 132 | public <R> PredicateSpecificationEvaluator<T> registerFunction(String name, Class<R> resultType, List<Class<?>> parameterTypes, | |
| 133 | EvaluationFunction function) { | |
| 134 | ||
| 135 | Objects.requireNonNull(name); | |
| 136 | Objects.requireNonNull(resultType); | |
| 137 | Objects.requireNonNull(parameterTypes); | |
| 138 | Objects.requireNonNull(function); | |
| 139 | ||
| 140 | functions.put(normalizeFunctionName(name), new RegisteredFunction(resultType, List.copyOf(parameterTypes), function)); | |
| 141 | ||
| 142 |
1
1. registerFunction : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::registerFunction → KILLED |
return this; |
| 143 | } | |
| 144 | ||
| 145 | /** | |
| 146 | * A function that can be registered using the {@link #registerFunction(String, Class, List, EvaluationFunction)} method. | |
| 147 | */ | |
| 148 | @FunctionalInterface | |
| 149 | public interface EvaluationFunction { | |
| 150 | /** | |
| 151 | * Applies the function to its arguments. | |
| 152 | * | |
| 153 | * @param arguments the list of arguments | |
| 154 | * | |
| 155 | * @return the return values of the function | |
| 156 | */ | |
| 157 | Object apply(List<Object> arguments); | |
| 158 | } | |
| 159 | ||
| 160 | /** | |
| 161 | * Registers a single-parameter function thet can be used by {@code CriteriaBuilder.function}. | |
| 162 | * <p> | |
| 163 | * This is a convenience method so that you can register functions with a single parameter like this: | |
| 164 | * | |
| 165 | * <pre>{@code | |
| 166 | * evaluator.registerFunction("lower", String.class, String.class, value -> value == null ? null : value.toLowerCase(Locale.ROOT)); | |
| 167 | * }</pre> | |
| 168 | * | |
| 169 | * @param name the name of the function to register | |
| 170 | * @param resultType the class representing the return type of the function to register | |
| 171 | * @param argumentType the class representing the type of the only function parameter | |
| 172 | * @param function the function to register | |
| 173 | * @param <A> the type of the only function parameter | |
| 174 | * @param <R> the return type of the function to register | |
| 175 | * | |
| 176 | * @return the {@code PredicationSpecificationEvaluator} itself, in order to support nested (fluent) calls | |
| 177 | */ | |
| 178 | public <A, R> PredicateSpecificationEvaluator<T> registerFunction(String name, Class<R> resultType, Class<A> argumentType, | |
| 179 | Function<A, R> function) { | |
| 180 | ||
| 181 |
2
1. lambda$registerFunction$2 : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$registerFunction$2 → KILLED 2. registerFunction : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::registerFunction → KILLED |
return registerFunction(name, resultType, List.of(argumentType), arguments -> function.apply(argumentType.cast(arguments.get(0)))); |
| 182 | } | |
| 183 | ||
| 184 | // ============================================================ | |
| 185 | // Expression Model | |
| 186 | // ============================================================ | |
| 187 | ||
| 188 | private sealed interface Expr { | |
| 189 | } | |
| 190 | ||
| 191 | private record Constant(@Nullable Object value) implements Expr { | |
| 192 | } | |
| 193 | ||
| 194 | private sealed interface PathExpression extends Expr { | |
| 195 | } | |
| 196 | ||
| 197 | private record PathExpr(Source source, List<String> attributes) implements PathExpression { | |
| 198 | } | |
| 199 | ||
| 200 | private record AttributePath(Expr parent, String attribute) implements PathExpression { | |
| 201 | } | |
| 202 | ||
| 203 | private record MapKeyExpr(Source source) implements PathExpression { | |
| 204 | } | |
| 205 | ||
| 206 | private record MapValueExpr(Source source) implements PathExpression { | |
| 207 | } | |
| 208 | ||
| 209 | private record Equal(Expr left, Expr right) implements Expr { | |
| 210 | } | |
| 211 | ||
| 212 | private record NotEqual(Expr left, Expr right) implements Expr { | |
| 213 | } | |
| 214 | ||
| 215 | private record GreaterThan(Expr left, Expr right) implements Expr { | |
| 216 | } | |
| 217 | ||
| 218 | private record GreaterThanOrEqual(Expr left, Expr right) implements Expr { | |
| 219 | } | |
| 220 | ||
| 221 | private record LessThan(Expr left, Expr right) implements Expr { | |
| 222 | } | |
| 223 | ||
| 224 | private record LessThanOrEqual(Expr left, Expr right) implements Expr { | |
| 225 | } | |
| 226 | ||
| 227 | private record IsNull(Expr expression) implements Expr { | |
| 228 | } | |
| 229 | ||
| 230 | private record IsNotNull(Expr expression) implements Expr { | |
| 231 | } | |
| 232 | ||
| 233 | private record And(List<Expr> expressions) implements Expr { | |
| 234 | } | |
| 235 | ||
| 236 | private record Or(List<Expr> expressions) implements Expr { | |
| 237 | } | |
| 238 | ||
| 239 | private record Not(Expr expression) implements Expr { | |
| 240 | } | |
| 241 | ||
| 242 | private record Product(Expr left, Expr right) implements Expr { | |
| 243 | } | |
| 244 | ||
| 245 | private record In(Expr expression, List<Expr> values) implements Expr { | |
| 246 | } | |
| 247 | ||
| 248 | private record TrueExpr() implements Expr { | |
| 249 | } | |
| 250 | ||
| 251 | private record FalseExpr() implements Expr { | |
| 252 | } | |
| 253 | ||
| 254 | private record MapEntryExpr(Source source) implements Expr { | |
| 255 | } | |
| 256 | ||
| 257 | private record IsEmpty(Expr expression) implements Expr { | |
| 258 | } | |
| 259 | ||
| 260 | private record IsNotEmpty(Expr expression) implements Expr { | |
| 261 | } | |
| 262 | ||
| 263 | private record FunctionCall(String name, Class<?> resultType, List<Expr> arguments) implements Expr { | |
| 264 | } | |
| 265 | ||
| 266 | private interface ExpressionProvider { | |
| 267 | Expr expression(); | |
| 268 | } | |
| 269 | ||
| 270 | // ============================================================ | |
| 271 | // Sources and Joins | |
| 272 | // ============================================================ | |
| 273 | ||
| 274 | private sealed interface Source { | |
| 275 | } | |
| 276 | ||
| 277 | private record RootSource() implements Source { | |
| 278 | } | |
| 279 | ||
| 280 | private record JoinSource(int id) implements Source { | |
| 281 | } | |
| 282 | ||
| 283 | private enum JoinKind { | |
| 284 | STANDARD, MAP | |
| 285 | } | |
| 286 | ||
| 287 | private record JoinDefinition(int id, Source parent, String attribute, JoinType joinType, JoinKind kind) { | |
| 288 | } | |
| 289 | ||
| 290 | @Getter | |
| 291 | @Accessors(fluent = true) | |
| 292 | private static final class RecordingState { | |
| 293 | ||
| 294 | private int nextJoinId; | |
| 295 | private final List<JoinDefinition> joins = new ArrayList<>(); | |
| 296 | ||
| 297 | JoinSource addJoin(Source parent, String attribute, JoinType joinType) { | |
| 298 |
1
1. addJoin : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$RecordingState::addJoin → KILLED |
return addJoin(parent, attribute, joinType, JoinKind.STANDARD); |
| 299 | } | |
| 300 | ||
| 301 | JoinSource addMapJoin(Source parent, String attribute, JoinType joinType) { | |
| 302 |
1
1. addMapJoin : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$RecordingState::addMapJoin → KILLED |
return addJoin(parent, attribute, joinType, JoinKind.MAP); |
| 303 | } | |
| 304 | ||
| 305 | private JoinSource addJoin(Source parent, String attribute, JoinType joinType, JoinKind kind) { | |
| 306 |
1
1. addJoin : Replaced integer addition with subtraction → SURVIVED |
int id = nextJoinId++; |
| 307 | ||
| 308 | joins.add(new JoinDefinition(id, parent, attribute, joinType, kind)); | |
| 309 |
1
1. addJoin : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$RecordingState::addJoin → KILLED |
return new JoinSource(id); |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | // ============================================================ | |
| 314 | // Evaluation Context | |
| 315 | // ============================================================ | |
| 316 | ||
| 317 | private record EvaluationContext(Object root, Map<Integer, Object> joins) { | |
| 318 | ||
| 319 | EvaluationContext bind(int joinId, @Nullable Object value) { | |
| 320 | ||
| 321 | Map<Integer, Object> copy = new HashMap<>(joins); | |
| 322 | ||
| 323 | copy.put(joinId, value); | |
| 324 | ||
| 325 |
1
1. bind : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$EvaluationContext::bind → KILLED |
return new EvaluationContext(root, copy); |
| 326 | } | |
| 327 | ||
| 328 | @Nullable | |
| 329 | Object source(Source source) { | |
| 330 |
1
1. source : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$EvaluationContext::source → KILLED |
return switch (source) { |
| 331 | case RootSource ignored -> root; | |
| 332 | ||
| 333 | case JoinSource join -> joins.get(join.id()); | |
| 334 | }; | |
| 335 | } | |
| 336 | } | |
| 337 | ||
| 338 | // ============================================================ | |
| 339 | // Join Expansion | |
| 340 | // ============================================================ | |
| 341 | ||
| 342 | private List<EvaluationContext> expandJoins(RecordingState state, Object entity) { | |
| 343 | ||
| 344 | List<EvaluationContext> rows = List.of(new EvaluationContext(entity, Map.of())); | |
| 345 | ||
| 346 | for (JoinDefinition join : state.joins) { | |
| 347 | List<EvaluationContext> next = new ArrayList<>(); | |
| 348 | for (EvaluationContext row : rows) { | |
| 349 | Object parent = resolveJoinParent(row, join.parent()); | |
| 350 |
1
1. expandJoins : negated conditional → KILLED |
Object value = parent == null ? null : PropertyAccess.read(parent, join.attribute()); |
| 351 | List<?> joinedValues = normalizeJoinValue(value, join.kind()); | |
| 352 |
1
1. expandJoins : negated conditional → KILLED |
if (joinedValues.isEmpty()) { |
| 353 |
1
1. expandJoins : negated conditional → KILLED |
if (join.joinType() == JoinType.LEFT) { |
| 354 | next.add(row.bind(join.id(), null)); | |
| 355 | } | |
| 356 | continue; | |
| 357 | } | |
| 358 | ||
| 359 | for (Object joinedValue : joinedValues) { | |
| 360 | next.add(row.bind(join.id(), joinedValue)); | |
| 361 | } | |
| 362 | } | |
| 363 | rows = next; | |
| 364 | } | |
| 365 |
1
1. expandJoins : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::expandJoins → KILLED |
return rows; |
| 366 | } | |
| 367 | ||
| 368 | private record MapBinding(Object key, Object value) { | |
| 369 | } | |
| 370 | ||
| 371 | private static @Nullable Object resolveJoinParent(EvaluationContext context, Source source) { | |
| 372 | ||
| 373 | Object value = context.source(source); | |
| 374 | ||
| 375 |
1
1. resolveJoinParent : negated conditional → KILLED |
if (value instanceof MapBinding mapBinding) { |
| 376 |
1
1. resolveJoinParent : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::resolveJoinParent → KILLED |
return mapBinding.value(); |
| 377 | } | |
| 378 | ||
| 379 |
1
1. resolveJoinParent : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::resolveJoinParent → KILLED |
return value; |
| 380 | } | |
| 381 | ||
| 382 | private static List<?> normalizeJoinValue(@Nullable Object value, JoinKind kind) { | |
| 383 | ||
| 384 |
1
1. normalizeJoinValue : negated conditional → KILLED |
if (value == null) { |
| 385 | return List.of(); | |
| 386 | } | |
| 387 | ||
| 388 |
1
1. normalizeJoinValue : negated conditional → KILLED |
if (kind == JoinKind.MAP) { |
| 389 | ||
| 390 |
1
1. normalizeJoinValue : negated conditional → KILLED |
if (!(value instanceof Map<?, ?> map)) { |
| 391 | throw new IllegalArgumentException("Map join requires a Map value, but got " + value.getClass().getName()); | |
| 392 | } | |
| 393 | ||
| 394 |
2
1. lambda$normalizeJoinValue$3 : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$normalizeJoinValue$3 → KILLED 2. normalizeJoinValue : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::normalizeJoinValue → KILLED |
return map.entrySet().stream().map(entry -> new MapBinding(entry.getKey(), entry.getValue())).toList(); |
| 395 | } | |
| 396 | ||
| 397 |
1
1. normalizeJoinValue : negated conditional → KILLED |
if (value instanceof Collection<?> collection) { |
| 398 |
1
1. normalizeJoinValue : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::normalizeJoinValue → KILLED |
return new ArrayList<>(collection); |
| 399 | } | |
| 400 | ||
| 401 |
1
1. normalizeJoinValue : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::normalizeJoinValue → KILLED |
return List.of(value); |
| 402 | } | |
| 403 | ||
| 404 | // ============================================================ | |
| 405 | // Expression Evaluation | |
| 406 | // ============================================================ | |
| 407 | ||
| 408 | private enum Truth { | |
| 409 | TRUE, FALSE, UNKNOWN | |
| 410 | } | |
| 411 | ||
| 412 | private Truth evaluateBoolean(Expr expr, EvaluationContext context) { | |
| 413 | ||
| 414 | LOG.trace("evaluateBoolean: expr={}", expr); | |
| 415 | ||
| 416 |
1
1. evaluateBoolean : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateBoolean → KILLED |
return switch (expr) { |
| 417 | ||
| 418 | case TrueExpr ignored -> Truth.TRUE; | |
| 419 | ||
| 420 | case FalseExpr ignored -> Truth.FALSE; | |
| 421 | ||
| 422 | case Equal e -> equal(evaluateValue(e.left(), context), evaluateValue(e.right(), context)); | |
| 423 | ||
| 424 | case NotEqual e -> not(equal(evaluateValue(e.left(), context), evaluateValue(e.right(), context))); | |
| 425 | ||
| 426 |
3
1. lambda$evaluateBoolean$4 : changed conditional boundary → KILLED 2. lambda$evaluateBoolean$4 : negated conditional → KILLED 3. lambda$evaluateBoolean$4 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$evaluateBoolean$4 → KILLED |
case GreaterThan e -> compare(e.left(), e.right(), context, result -> result > 0); |
| 427 | ||
| 428 |
3
1. lambda$evaluateBoolean$5 : changed conditional boundary → KILLED 2. lambda$evaluateBoolean$5 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$evaluateBoolean$5 → KILLED 3. lambda$evaluateBoolean$5 : negated conditional → KILLED |
case GreaterThanOrEqual e -> compare(e.left(), e.right(), context, result -> result >= 0); |
| 429 | ||
| 430 |
3
1. lambda$evaluateBoolean$6 : changed conditional boundary → KILLED 2. lambda$evaluateBoolean$6 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$evaluateBoolean$6 → KILLED 3. lambda$evaluateBoolean$6 : negated conditional → KILLED |
case LessThan e -> compare(e.left(), e.right(), context, result -> result < 0); |
| 431 | ||
| 432 |
3
1. lambda$evaluateBoolean$7 : negated conditional → KILLED 2. lambda$evaluateBoolean$7 : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$evaluateBoolean$7 → KILLED 3. lambda$evaluateBoolean$7 : changed conditional boundary → KILLED |
case LessThanOrEqual e -> compare(e.left(), e.right(), context, result -> result <= 0); |
| 433 | ||
| 434 |
1
1. evaluateBoolean : negated conditional → KILLED |
case IsNull e -> evaluateValue(e.expression(), context) == null ? Truth.TRUE : Truth.FALSE; |
| 435 | ||
| 436 |
1
1. evaluateBoolean : negated conditional → KILLED |
case IsNotNull e -> evaluateValue(e.expression(), context) != null ? Truth.TRUE : Truth.FALSE; |
| 437 | ||
| 438 | case And e -> evaluateAnd(e.expressions(), context); | |
| 439 | ||
| 440 | case Or e -> evaluateOr(e.expressions(), context); | |
| 441 | ||
| 442 | case Not e -> not(evaluateBoolean(e.expression(), context)); | |
| 443 | ||
| 444 | case In e -> evaluateIn(e, context); | |
| 445 | ||
| 446 | case IsEmpty e -> evaluateIsEmpty(e, context); | |
| 447 | ||
| 448 | case IsNotEmpty e -> evaluateIsNotEmpty(e, context); | |
| 449 | ||
| 450 | default -> throw new IllegalArgumentException("Not a boolean expression: " + expr); | |
| 451 | }; | |
| 452 | } | |
| 453 | ||
| 454 | private @Nullable Object evaluateValue(Expr expr, EvaluationContext context) { | |
| 455 | ||
| 456 | LOG.trace("evaluateValue: expr={}", expr); | |
| 457 | ||
| 458 |
1
1. evaluateValue : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateValue → KILLED |
return switch (expr) { |
| 459 | ||
| 460 | case Constant constant -> constant.value(); | |
| 461 | ||
| 462 | case PathExpr path -> evaluatePath(path, context); | |
| 463 | ||
| 464 | case AttributePath path -> { | |
| 465 | Object parent = evaluateValue(path.parent(), context); | |
| 466 |
1
1. evaluateValue : negated conditional → KILLED |
yield parent == null ? null : PropertyAccess.read(parent, path.attribute()); |
| 467 | } | |
| 468 | ||
| 469 | case MapKeyExpr mapKey -> mapBinding(mapKey.source(), context).key(); | |
| 470 | ||
| 471 | case MapValueExpr mapValue -> mapBinding(mapValue.source(), context).value(); | |
| 472 | ||
| 473 | case MapEntryExpr mapEntry -> { | |
| 474 | MapBinding binding = mapBinding(mapEntry.source(), context); | |
| 475 | yield new AbstractMap.SimpleImmutableEntry<>(binding.key(), binding.value()); | |
| 476 | } | |
| 477 | ||
| 478 | case Product product -> multiply(evaluateValue(product.left(), context), evaluateValue(product.right(), context)); | |
| 479 | ||
| 480 | case FunctionCall function -> evaluateFunction(function, context); | |
| 481 | ||
| 482 | default -> throw new IllegalArgumentException("Not a value expression: " + expr); | |
| 483 | }; | |
| 484 | } | |
| 485 | ||
| 486 | @SuppressWarnings("noReturnNull") | |
| 487 | private @Nullable Object evaluatePath(PathExpr path, EvaluationContext context) { | |
| 488 | ||
| 489 | Object current = context.source(path.source()); | |
| 490 | ||
| 491 |
1
1. evaluatePath : negated conditional → KILLED |
if (current instanceof MapBinding mapBinding) { |
| 492 | current = mapBinding.value(); | |
| 493 | } | |
| 494 | ||
| 495 | for (String attribute : path.attributes()) { | |
| 496 |
1
1. evaluatePath : negated conditional → NO_COVERAGE |
if (current == null) { |
| 497 | return null; | |
| 498 | } | |
| 499 | ||
| 500 | current = PropertyAccess.read(current, attribute); | |
| 501 | } | |
| 502 | ||
| 503 |
1
1. evaluatePath : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluatePath → KILLED |
return current; |
| 504 | } | |
| 505 | ||
| 506 | private static MapBinding mapBinding(Source source, EvaluationContext context) { | |
| 507 | ||
| 508 | Object value = context.source(source); | |
| 509 | ||
| 510 |
1
1. mapBinding : negated conditional → KILLED |
if (!(value instanceof MapBinding binding)) { |
| 511 | throw new IllegalStateException("Expected map binding for " + source); | |
| 512 | } | |
| 513 | ||
| 514 |
1
1. mapBinding : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::mapBinding → KILLED |
return binding; |
| 515 | } | |
| 516 | ||
| 517 | private static Truth equal(@Nullable Object left, @Nullable Object right) { | |
| 518 | ||
| 519 | // SQL semantics: | |
| 520 | // NULL = anything => UNKNOWN. | |
| 521 |
2
1. equal : negated conditional → KILLED 2. equal : negated conditional → KILLED |
if (left == null || right == null) { |
| 522 |
1
1. equal : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::equal → KILLED |
return Truth.UNKNOWN; |
| 523 | } | |
| 524 | ||
| 525 |
2
1. equal : negated conditional → KILLED 2. equal : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::equal → KILLED |
return Objects.equals(left, right) ? Truth.TRUE : Truth.FALSE; |
| 526 | } | |
| 527 | ||
| 528 | private Truth evaluateAnd(List<Expr> expressions, EvaluationContext context) { | |
| 529 | ||
| 530 | LOG.trace("evaluateAnd: expressions={}", expressions); | |
| 531 | ||
| 532 | Truth result = Truth.TRUE; | |
| 533 | ||
| 534 | for (Expr expr : expressions) { | |
| 535 | LOG.trace("evaluateAnd: expr={}", expr); | |
| 536 | Truth value = evaluateBoolean(expr, context); | |
| 537 | LOG.trace("evaluateAnd: value={}", value); | |
| 538 | ||
| 539 |
1
1. evaluateAnd : negated conditional → KILLED |
if (value == Truth.FALSE) { |
| 540 |
1
1. evaluateAnd : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateAnd → SURVIVED |
return Truth.FALSE; |
| 541 | } | |
| 542 | ||
| 543 |
1
1. evaluateAnd : negated conditional → KILLED |
if (value == Truth.UNKNOWN) { |
| 544 | result = Truth.UNKNOWN; | |
| 545 | } | |
| 546 | } | |
| 547 | ||
| 548 |
1
1. evaluateAnd : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateAnd → KILLED |
return result; |
| 549 | } | |
| 550 | ||
| 551 | private Truth evaluateOr(List<Expr> expressions, EvaluationContext context) { | |
| 552 | ||
| 553 | LOG.trace("evaluateOr: expressions={}", expressions); | |
| 554 | ||
| 555 | Truth result = Truth.FALSE; | |
| 556 | ||
| 557 | for (Expr expr : expressions) { | |
| 558 | LOG.trace("evaluateOr: expr={}", expr); | |
| 559 | Truth value = evaluateBoolean(expr, context); | |
| 560 | LOG.trace("evaluateOr: value={}", value); | |
| 561 | ||
| 562 |
1
1. evaluateOr : negated conditional → KILLED |
if (value == Truth.TRUE) { |
| 563 |
1
1. evaluateOr : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateOr → KILLED |
return Truth.TRUE; |
| 564 | } | |
| 565 | ||
| 566 |
1
1. evaluateOr : negated conditional → SURVIVED |
if (value == Truth.UNKNOWN) { |
| 567 | result = Truth.UNKNOWN; | |
| 568 | } | |
| 569 | } | |
| 570 | ||
| 571 |
1
1. evaluateOr : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateOr → SURVIVED |
return result; |
| 572 | } | |
| 573 | ||
| 574 | private static Truth not(Truth truth) { | |
| 575 |
1
1. not : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::not → KILLED |
return switch (truth) { |
| 576 | case TRUE -> Truth.FALSE; | |
| 577 | case FALSE -> Truth.TRUE; | |
| 578 | case UNKNOWN -> Truth.UNKNOWN; | |
| 579 | }; | |
| 580 | } | |
| 581 | ||
| 582 | private Truth evaluateIn(In in, EvaluationContext context) { | |
| 583 | ||
| 584 | Object testedValue = evaluateValue(in.expression(), context); | |
| 585 | ||
| 586 | boolean unknown = false; | |
| 587 | ||
| 588 | for (Expr valueExpr : in.values()) { | |
| 589 | Object candidate = evaluateValue(valueExpr, context); | |
| 590 | ||
| 591 | Truth comparison = equal(testedValue, candidate); | |
| 592 | ||
| 593 |
1
1. evaluateIn : negated conditional → KILLED |
if (comparison == Truth.TRUE) { |
| 594 |
1
1. evaluateIn : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateIn → KILLED |
return Truth.TRUE; |
| 595 | } | |
| 596 | ||
| 597 |
1
1. evaluateIn : negated conditional → SURVIVED |
if (comparison == Truth.UNKNOWN) { |
| 598 | unknown = true; | |
| 599 | } | |
| 600 | } | |
| 601 | ||
| 602 |
2
1. evaluateIn : negated conditional → SURVIVED 2. evaluateIn : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateIn → SURVIVED |
return unknown ? Truth.UNKNOWN : Truth.FALSE; |
| 603 | } | |
| 604 | ||
| 605 | private Truth evaluateIsEmpty(IsEmpty expression, EvaluationContext context) { | |
| 606 | Object value = evaluateValue(expression.expression(), context); | |
| 607 | ||
| 608 |
1
1. evaluateIsEmpty : negated conditional → KILLED |
if (value == null) { |
| 609 |
1
1. evaluateIsEmpty : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateIsEmpty → SURVIVED |
return Truth.UNKNOWN; |
| 610 | } | |
| 611 | ||
| 612 |
1
1. evaluateIsEmpty : negated conditional → KILLED |
if (!(value instanceof Collection<?> collection)) { |
| 613 | throw new IllegalArgumentException("isEmpty() requires a Collection, but got " + value.getClass().getName()); | |
| 614 | } | |
| 615 | ||
| 616 |
2
1. evaluateIsEmpty : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateIsEmpty → KILLED 2. evaluateIsEmpty : negated conditional → KILLED |
return collection.isEmpty() ? Truth.TRUE : Truth.FALSE; |
| 617 | } | |
| 618 | ||
| 619 | private Truth evaluateIsNotEmpty(IsNotEmpty expression, EvaluationContext context) { | |
| 620 | Object value = evaluateValue(expression.expression(), context); | |
| 621 | ||
| 622 |
1
1. evaluateIsNotEmpty : negated conditional → KILLED |
if (value == null) { |
| 623 |
1
1. evaluateIsNotEmpty : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateIsNotEmpty → SURVIVED |
return Truth.UNKNOWN; |
| 624 | } | |
| 625 | ||
| 626 |
1
1. evaluateIsNotEmpty : negated conditional → KILLED |
if (!(value instanceof Collection<?> collection)) { |
| 627 | throw new IllegalArgumentException("isNotEmpty() requires a Collection, but got " + value.getClass().getName()); | |
| 628 | } | |
| 629 | ||
| 630 |
2
1. evaluateIsNotEmpty : negated conditional → KILLED 2. evaluateIsNotEmpty : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateIsNotEmpty → KILLED |
return collection.isEmpty() ? Truth.FALSE : Truth.TRUE; |
| 631 | } | |
| 632 | ||
| 633 | private Truth compare(Expr leftExpr, Expr rightExpr, EvaluationContext context, java.util.function.IntPredicate condition) { | |
| 634 | ||
| 635 | Object left = evaluateValue(leftExpr, context); | |
| 636 | ||
| 637 | Object right = evaluateValue(rightExpr, context); | |
| 638 | ||
| 639 | LOG.trace("compare: left={}, right={}", left, right); | |
| 640 | ||
| 641 |
2
1. compare : negated conditional → KILLED 2. compare : negated conditional → KILLED |
if (left == null || right == null) { |
| 642 |
1
1. compare : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::compare → NO_COVERAGE |
return Truth.UNKNOWN; |
| 643 | } | |
| 644 | ||
| 645 | int result = compareValues(left, right); | |
| 646 | ||
| 647 |
2
1. compare : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::compare → KILLED 2. compare : negated conditional → KILLED |
return condition.test(result) ? Truth.TRUE : Truth.FALSE; |
| 648 | } | |
| 649 | ||
| 650 | @SuppressWarnings({ "rawtypes", "unchecked" }) | |
| 651 | private static int compareValues(Object left, Object right) { | |
| 652 | ||
| 653 |
2
1. compareValues : negated conditional → SURVIVED 2. compareValues : negated conditional → KILLED |
if (left instanceof Number l && right instanceof Number r) { |
| 654 | ||
| 655 |
1
1. compareValues : replaced int return with 0 for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::compareValues → KILLED |
return toBigDecimal(l).compareTo(toBigDecimal(r)); |
| 656 | } | |
| 657 | ||
| 658 |
1
1. compareValues : negated conditional → KILLED |
if (left instanceof Comparable comparable) { |
| 659 |
1
1. compareValues : replaced int return with 0 for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::compareValues → KILLED |
return comparable.compareTo(right); |
| 660 | } | |
| 661 | ||
| 662 | throw new IllegalArgumentException("Cannot compare " + left.getClass().getName() + " and " + right.getClass().getName()); | |
| 663 | } | |
| 664 | ||
| 665 | @SuppressWarnings("noReturnNull") | |
| 666 | private static @Nullable Object multiply(@Nullable Object left, @Nullable Object right) { | |
| 667 | ||
| 668 | LOG.trace("multiply: left={}, right={}", left, right); | |
| 669 | ||
| 670 |
2
1. multiply : negated conditional → KILLED 2. multiply : negated conditional → KILLED |
if (left == null || right == null) { |
| 671 | return null; | |
| 672 | } | |
| 673 | ||
| 674 |
2
1. multiply : negated conditional → KILLED 2. multiply : negated conditional → KILLED |
if (!(left instanceof Number l) || !(right instanceof Number r)) { |
| 675 | throw new IllegalArgumentException("prod() requires numeric operands"); | |
| 676 | } | |
| 677 | ||
| 678 |
1
1. multiply : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::multiply → KILLED |
return toBigDecimal(l).multiply(toBigDecimal(r)); |
| 679 | } | |
| 680 | ||
| 681 | private static BigDecimal toBigDecimal(Number value) { | |
| 682 | ||
| 683 |
1
1. toBigDecimal : negated conditional → KILLED |
if (value instanceof BigDecimal bd) { |
| 684 |
1
1. toBigDecimal : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::toBigDecimal → KILLED |
return bd; |
| 685 | } | |
| 686 | ||
| 687 |
1
1. toBigDecimal : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::toBigDecimal → KILLED |
return new BigDecimal(value.toString()); |
| 688 | } | |
| 689 | ||
| 690 | // ============================================================ | |
| 691 | // Criteria API Proxy Implementation | |
| 692 | // ============================================================ | |
| 693 | ||
| 694 | private static final class Proxies { | |
| 695 | ||
| 696 | private Proxies() { | |
| 697 | } | |
| 698 | ||
| 699 | @SuppressWarnings("unchecked") | |
| 700 | static <T> From<?, T> root(RecordingState state) { | |
| 701 | ||
| 702 |
1
1. root : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::root → KILLED |
return (From<?, T>) Proxy.newProxyInstance(From.class.getClassLoader(), new Class<?>[] { From.class }, |
| 703 | new FromHandler(state, new RootSource())); | |
| 704 | } | |
| 705 | ||
| 706 | static CriteriaBuilder criteriaBuilder() { | |
| 707 | ||
| 708 |
1
1. criteriaBuilder : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::criteriaBuilder → KILLED |
return (CriteriaBuilder) Proxy.newProxyInstance(CriteriaBuilder.class.getClassLoader(), |
| 709 | new Class<?>[] { CriteriaBuilder.class }, new CriteriaBuilderHandler()); | |
| 710 | } | |
| 711 | ||
| 712 | static Expr expressionOf(Object value) { | |
| 713 | ||
| 714 | LOG.trace("expressionOf: value={}", value); | |
| 715 | ||
| 716 |
1
1. expressionOf : negated conditional → KILLED |
if (value == null) { |
| 717 |
1
1. expressionOf : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::expressionOf → NO_COVERAGE |
return new Constant(null); |
| 718 | } | |
| 719 | ||
| 720 |
1
1. expressionOf : negated conditional → KILLED |
if (Proxy.isProxyClass(value.getClass())) { |
| 721 | ||
| 722 | InvocationHandler handler = Proxy.getInvocationHandler(value); | |
| 723 | ||
| 724 |
1
1. expressionOf : negated conditional → KILLED |
if (handler instanceof ExpressionProvider provider) { |
| 725 |
1
1. expressionOf : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::expressionOf → KILLED |
return provider.expression(); |
| 726 | } | |
| 727 | } | |
| 728 | ||
| 729 |
1
1. expressionOf : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::expressionOf → KILLED |
return new Constant(value); |
| 730 | } | |
| 731 | ||
| 732 | @SuppressWarnings("unchecked") | |
| 733 | static <X> Path<X> path(PathExpression expression) { | |
| 734 | ||
| 735 |
1
1. path : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::path → KILLED |
return (Path<X>) Proxy.newProxyInstance(Path.class.getClassLoader(), new Class<?>[] { Path.class }, |
| 736 | new ExpressionHandler(expression)); | |
| 737 | } | |
| 738 | ||
| 739 | static Predicate predicate(Expr expr) { | |
| 740 | ||
| 741 |
1
1. predicate : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::predicate → KILLED |
return (Predicate) Proxy.newProxyInstance(Predicate.class.getClassLoader(), new Class<?>[] { Predicate.class }, |
| 742 | new ExpressionHandler(expr)); | |
| 743 | } | |
| 744 | ||
| 745 | @SuppressWarnings("unchecked") | |
| 746 | static <X> Expression<X> expression(Expr expr) { | |
| 747 | ||
| 748 |
1
1. expression : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::expression → KILLED |
return (Expression<X>) Proxy.newProxyInstance(Expression.class.getClassLoader(), new Class<?>[] { Expression.class }, |
| 749 | new ExpressionHandler(expr)); | |
| 750 | } | |
| 751 | ||
| 752 | @SuppressWarnings("unchecked") | |
| 753 | static <X, Y> Join<X, Y> join(RecordingState state, JoinSource source) { | |
| 754 | ||
| 755 |
1
1. join : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::join → KILLED |
return (Join<X, Y>) Proxy.newProxyInstance(Join.class.getClassLoader(), new Class<?>[] { Join.class }, |
| 756 | new FromHandler(state, source)); | |
| 757 | } | |
| 758 | ||
| 759 | @SuppressWarnings("unchecked") | |
| 760 | static <X, K, V> MapJoin<X, K, V> mapJoin(RecordingState state, JoinSource source) { | |
| 761 | ||
| 762 |
1
1. mapJoin : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::mapJoin → KILLED |
return (MapJoin<X, K, V>) Proxy.newProxyInstance(MapJoin.class.getClassLoader(), new Class<?>[] { MapJoin.class }, |
| 763 | new MapJoinHandler(state, source)); | |
| 764 | } | |
| 765 | ||
| 766 | @SuppressWarnings("unchecked") | |
| 767 | static <T> CriteriaBuilder.In<T> in(Expr expression) { | |
| 768 | ||
| 769 |
1
1. in : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$Proxies::in → KILLED |
return (CriteriaBuilder.In<T>) Proxy.newProxyInstance(CriteriaBuilder.In.class.getClassLoader(), |
| 770 | new Class<?>[] { CriteriaBuilder.In.class }, new InHandler(expression)); | |
| 771 | } | |
| 772 | } | |
| 773 | ||
| 774 | // ============================================================ | |
| 775 | // From / Join Proxy | |
| 776 | // ============================================================ | |
| 777 | ||
| 778 | private static final class FromHandler implements InvocationHandler, ExpressionProvider { | |
| 779 | ||
| 780 | private final RecordingState state; | |
| 781 | private final Source source; | |
| 782 | ||
| 783 | private FromHandler(RecordingState state, Source source) { | |
| 784 | this.state = state; | |
| 785 | this.source = source; | |
| 786 | } | |
| 787 | ||
| 788 | @Override | |
| 789 | public Expr expression() { | |
| 790 |
1
1. expression : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$FromHandler::expression → KILLED |
return new PathExpr(source, List.of()); |
| 791 | } | |
| 792 | ||
| 793 | @Override | |
| 794 | public Object invoke(Object proxy, Method method, Object[] args) { | |
| 795 | ||
| 796 | String name = method.getName(); | |
| 797 | ||
| 798 |
1
1. invoke : negated conditional → KILLED |
if (isGet(method, args)) { |
| 799 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$FromHandler::invoke → KILLED |
return navigatePath(expression(), (String) args[0]); |
| 800 | } | |
| 801 | ||
| 802 |
5
1. invoke : negated conditional → KILLED 2. invoke : negated conditional → KILLED 3. invoke : changed conditional boundary → KILLED 4. invoke : negated conditional → KILLED 5. invoke : negated conditional → KILLED |
if (name.equals("join") && args != null && args.length >= 1 && args[0] instanceof String attribute) { |
| 803 |
3
1. invoke : negated conditional → KILLED 2. invoke : changed conditional boundary → KILLED 3. invoke : negated conditional → KILLED |
JoinType joinType = args.length >= 2 && args[1] instanceof JoinType jt ? jt : JoinType.INNER; |
| 804 | JoinSource join = state.addJoin(source, attribute, joinType); | |
| 805 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$FromHandler::invoke → KILLED |
return Proxies.join(state, join); |
| 806 | } | |
| 807 | ||
| 808 |
5
1. invoke : negated conditional → KILLED 2. invoke : negated conditional → KILLED 3. invoke : changed conditional boundary → KILLED 4. invoke : negated conditional → KILLED 5. invoke : negated conditional → KILLED |
if (method.getName().equals("joinMap") && args != null && args.length >= 1 && args[0] instanceof String attribute) { |
| 809 |
3
1. invoke : changed conditional boundary → SURVIVED 2. invoke : negated conditional → NO_COVERAGE 3. invoke : negated conditional → KILLED |
JoinType joinType = args.length >= 2 && args[1] instanceof JoinType jt ? jt : JoinType.INNER; |
| 810 | JoinSource join = state.addMapJoin(source, attribute, joinType); | |
| 811 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$FromHandler::invoke → KILLED |
return Proxies.mapJoin(state, join); |
| 812 | } | |
| 813 | ||
| 814 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$FromHandler::invoke → NO_COVERAGE |
return objectMethodOrUnsupported(proxy, method, args, "From[" + source + "]"); |
| 815 | } | |
| 816 | } | |
| 817 | ||
| 818 | private static boolean isGet(Method method, Object[] args) { | |
| 819 |
5
1. isGet : negated conditional → KILLED 2. isGet : replaced boolean return with true for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::isGet → KILLED 3. isGet : negated conditional → KILLED 4. isGet : negated conditional → KILLED 5. isGet : negated conditional → KILLED |
return method.getName().equals("get") && args != null && args.length == 1 && args[0] instanceof String; |
| 820 | } | |
| 821 | ||
| 822 | private static Object navigatePath(Expr parent, String attribute) { | |
| 823 |
1
1. navigatePath : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::navigatePath → KILLED |
return Proxies.path(new AttributePath(parent, attribute)); |
| 824 | } | |
| 825 | ||
| 826 | // ============================================================ | |
| 827 | // MapJoin Proxy | |
| 828 | // ============================================================ | |
| 829 | ||
| 830 | private static final class MapJoinHandler implements InvocationHandler, ExpressionProvider { | |
| 831 | ||
| 832 | private final RecordingState state; | |
| 833 | private final JoinSource source; | |
| 834 | ||
| 835 | private MapJoinHandler(RecordingState state, JoinSource source) { | |
| 836 | this.state = state; | |
| 837 | this.source = source; | |
| 838 | } | |
| 839 | ||
| 840 | @Override | |
| 841 | public Expr expression() { | |
| 842 |
1
1. expression : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::expression → KILLED |
return new PathExpr(source, List.of()); |
| 843 | } | |
| 844 | ||
| 845 | @Override | |
| 846 | public Object invoke(Object proxy, Method method, Object[] args) { | |
| 847 | ||
| 848 | String name = method.getName(); | |
| 849 | ||
| 850 |
1
1. invoke : negated conditional → KILLED |
if (isGet(method, args)) { |
| 851 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → KILLED |
return navigatePath(expression(), (String) args[0]); |
| 852 | } | |
| 853 | ||
| 854 |
2
1. invoke : negated conditional → KILLED 2. invoke : negated conditional → KILLED |
if (name.equals("key") && method.getParameterCount() == 0) { |
| 855 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → KILLED |
return Proxies.path(new MapKeyExpr(source)); |
| 856 | } | |
| 857 | ||
| 858 |
2
1. invoke : negated conditional → KILLED 2. invoke : negated conditional → KILLED |
if (name.equals("value") && method.getParameterCount() == 0) { |
| 859 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → KILLED |
return Proxies.path(new MapValueExpr(source)); |
| 860 | } | |
| 861 | ||
| 862 |
2
1. invoke : negated conditional → NO_COVERAGE 2. invoke : negated conditional → SURVIVED |
if (name.equals("entry") && method.getParameterCount() == 0) { |
| 863 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → NO_COVERAGE |
return Proxies.expression(new MapEntryExpr(source)); |
| 864 | } | |
| 865 | ||
| 866 | /* | |
| 867 | * A MapJoin is also a From, so allow nested joins against the map value. | |
| 868 | */ | |
| 869 |
5
1. invoke : negated conditional → KILLED 2. invoke : changed conditional boundary → KILLED 3. invoke : negated conditional → KILLED 4. invoke : negated conditional → KILLED 5. invoke : negated conditional → KILLED |
if (name.equals("join") && args != null && args.length >= 1 && args[0] instanceof String attribute) { |
| 870 |
3
1. invoke : changed conditional boundary → SURVIVED 2. invoke : negated conditional → NO_COVERAGE 3. invoke : negated conditional → KILLED |
JoinType joinType = args.length >= 2 && args[1] instanceof JoinType jt ? jt : JoinType.INNER; |
| 871 | JoinSource join = state.addJoin(source, attribute, joinType); | |
| 872 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → KILLED |
return Proxies.join(state, join); |
| 873 | } | |
| 874 | ||
| 875 |
5
1. invoke : negated conditional → NO_COVERAGE 2. invoke : negated conditional → NO_COVERAGE 3. invoke : changed conditional boundary → NO_COVERAGE 4. invoke : negated conditional → NO_COVERAGE 5. invoke : negated conditional → NO_COVERAGE |
if (name.equals("joinMap") && args != null && args.length >= 1 && args[0] instanceof String attribute) { |
| 876 |
3
1. invoke : changed conditional boundary → NO_COVERAGE 2. invoke : negated conditional → NO_COVERAGE 3. invoke : negated conditional → NO_COVERAGE |
JoinType joinType = args.length >= 2 && args[1] instanceof JoinType jt ? jt : JoinType.INNER; |
| 877 | JoinSource join = state.addMapJoin(source, attribute, joinType); | |
| 878 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → NO_COVERAGE |
return Proxies.mapJoin(state, join); |
| 879 | } | |
| 880 | ||
| 881 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$MapJoinHandler::invoke → NO_COVERAGE |
return objectMethodOrUnsupported(proxy, method, args, "MapJoin[" + source + "]"); |
| 882 | } | |
| 883 | } | |
| 884 | ||
| 885 | // ============================================================ | |
| 886 | // Path / Expression / Predicate Proxy | |
| 887 | // ============================================================ | |
| 888 | ||
| 889 | private static final class ExpressionHandler implements InvocationHandler, ExpressionProvider { | |
| 890 | ||
| 891 | private final Expr expression; | |
| 892 | ||
| 893 | private ExpressionHandler(Expr expression) { | |
| 894 | LOG.trace("ExpressionHandler: expression={}", expression); | |
| 895 | this.expression = expression; | |
| 896 | } | |
| 897 | ||
| 898 | @Override | |
| 899 | public Expr expression() { | |
| 900 |
1
1. expression : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$ExpressionHandler::expression → KILLED |
return expression; |
| 901 | } | |
| 902 | ||
| 903 | @Override | |
| 904 | public Object invoke(Object proxy, Method method, Object[] args) { | |
| 905 | ||
| 906 | /* | |
| 907 | * Path.get("attribute") | |
| 908 | */ | |
| 909 |
1
1. invoke : negated conditional → KILLED |
if (isGet(method, args)) { |
| 910 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$ExpressionHandler::invoke → KILLED |
return navigatePath(expression, (String) args[0]); |
| 911 | } | |
| 912 | ||
| 913 | /* | |
| 914 | * Expression.in(...) | |
| 915 | * | |
| 916 | * Handles: | |
| 917 | * | |
| 918 | * path.in("A", "B") path.in(List.of("A", "B")) path.in(expr1, expr2) | |
| 919 | */ | |
| 920 |
1
1. invoke : negated conditional → KILLED |
if (method.getName().equals("in")) { |
| 921 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$ExpressionHandler::invoke → KILLED |
return handleIn(expression, method, args); |
| 922 | } | |
| 923 | ||
| 924 | /* | |
| 925 | * Predicate.not() | |
| 926 | * | |
| 927 | * Note that cb.not(predicate) is handled by CriteriaBuilderHandler instead. | |
| 928 | */ | |
| 929 |
3
1. invoke : negated conditional → KILLED 2. invoke : negated conditional → KILLED 3. invoke : negated conditional → KILLED |
if (method.getName().equals("not") && method.getParameterCount() == 0 && proxy instanceof Predicate) { |
| 930 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$ExpressionHandler::invoke → KILLED |
return Proxies.predicate(new Not(expression)); |
| 931 | } | |
| 932 | ||
| 933 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$ExpressionHandler::invoke → KILLED |
return objectMethodOrUnsupported(proxy, method, args, expression.toString()); |
| 934 | } | |
| 935 | } | |
| 936 | ||
| 937 | private static Object handleIn(Expr expression, Method method, Object[] args) { | |
| 938 | ||
| 939 |
2
1. handleIn : negated conditional → KILLED 2. handleIn : negated conditional → KILLED |
if (args == null || args.length != 1) { |
| 940 | throw new UnsupportedOperationException("Unsupported Expression.in() overload: " + method); | |
| 941 | } | |
| 942 | ||
| 943 | LOG.trace("handleIn: expression={}, method={}, args={}", expression, method, Arrays.asList(args)); | |
| 944 | ||
| 945 | Object argument = args[0]; | |
| 946 | ||
| 947 | List<Expr> values; | |
| 948 | ||
| 949 |
1
1. handleIn : negated conditional → KILLED |
if (argument instanceof Collection<?> collection) { |
| 950 | values = collection.stream().map(Proxies::expressionOf).toList(); | |
| 951 | ||
| 952 |
1
1. handleIn : negated conditional → KILLED |
} else if (argument instanceof Object[] array) { |
| 953 | values = Arrays.stream(array).map(Proxies::expressionOf).toList(); | |
| 954 | ||
| 955 | } else { | |
| 956 | values = List.of(Proxies.expressionOf(argument)); | |
| 957 | } | |
| 958 | ||
| 959 |
1
1. handleIn : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::handleIn → KILLED |
return Proxies.predicate(new In(expression, values)); |
| 960 | } | |
| 961 | ||
| 962 | // ============================================================ | |
| 963 | // CriteriaBuilder Proxy | |
| 964 | // ============================================================ | |
| 965 | ||
| 966 | private static final class CriteriaBuilderHandler implements InvocationHandler { | |
| 967 | ||
| 968 | @Override | |
| 969 | public Object invoke(Object proxy, Method method, Object[] args) { | |
| 970 | ||
| 971 | String name = method.getName(); | |
| 972 | ||
| 973 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$CriteriaBuilderHandler::invoke → KILLED |
return switch (name) { |
| 974 | ||
| 975 | case "equal" -> Proxies.predicate(new Equal(expr(args[0]), expr(args[1]))); | |
| 976 | ||
| 977 | case "notEqual" -> Proxies.predicate(new NotEqual(expr(args[0]), expr(args[1]))); | |
| 978 | ||
| 979 | case "greaterThan" -> Proxies.predicate(new GreaterThan(expr(args[0]), expr(args[1]))); | |
| 980 | ||
| 981 | case "greaterThanOrEqualTo" -> Proxies.predicate(new GreaterThanOrEqual(expr(args[0]), expr(args[1]))); | |
| 982 | ||
| 983 | case "lessThan" -> Proxies.predicate(new LessThan(expr(args[0]), expr(args[1]))); | |
| 984 | ||
| 985 | case "lessThanOrEqualTo" -> Proxies.predicate(new LessThanOrEqual(expr(args[0]), expr(args[1]))); | |
| 986 | ||
| 987 | case "isNull" -> Proxies.predicate(new IsNull(expr(args[0]))); | |
| 988 | ||
| 989 | case "isNotNull" -> Proxies.predicate(new IsNotNull(expr(args[0]))); | |
| 990 | ||
| 991 | case "and" -> Proxies.predicate(new And(predicateArguments(args))); | |
| 992 | ||
| 993 | case "or" -> Proxies.predicate(new Or(predicateArguments(args))); | |
| 994 | ||
| 995 | case "not" -> Proxies.predicate(new Not(expr(args[0]))); | |
| 996 | ||
| 997 | case "conjunction" -> Proxies.predicate(new TrueExpr()); | |
| 998 | ||
| 999 | case "disjunction" -> Proxies.predicate(new FalseExpr()); | |
| 1000 | ||
| 1001 | case "literal" -> Proxies.expression(new Constant(args[0])); | |
| 1002 | ||
| 1003 | case "prod" -> Proxies.expression(new Product(expr(args[0]), expr(args[1]))); | |
| 1004 | ||
| 1005 | case "in" -> Proxies.in(expr(args[0])); | |
| 1006 | ||
| 1007 | case "isEmpty" -> Proxies.predicate(new IsEmpty(expr(args[0]))); | |
| 1008 | ||
| 1009 | case "isNotEmpty" -> Proxies.predicate(new IsNotEmpty(expr(args[0]))); | |
| 1010 | ||
| 1011 | case "function" -> handleFunction(args); | |
| 1012 | ||
| 1013 | default -> objectMethodOrUnsupported(proxy, method, args, "CriteriaBuilder"); | |
| 1014 | }; | |
| 1015 | } | |
| 1016 | ||
| 1017 | private static Expr expr(Object value) { | |
| 1018 |
1
1. expr : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$CriteriaBuilderHandler::expr → KILLED |
return Proxies.expressionOf(value); |
| 1019 | } | |
| 1020 | ||
| 1021 | private static List<Expr> predicateArguments(Object[] args) { | |
| 1022 | ||
| 1023 |
2
1. predicateArguments : negated conditional → KILLED 2. predicateArguments : negated conditional → KILLED |
if (args == null || args.length == 0) { |
| 1024 | return List.of(); | |
| 1025 | } | |
| 1026 | ||
| 1027 | /* | |
| 1028 | * For CriteriaBuilder.and(Predicate...) reflection sees one Predicate[] argument. | |
| 1029 | * | |
| 1030 | * For and(Expression<Boolean>, Expression<Boolean>) it sees two arguments. | |
| 1031 | */ | |
| 1032 |
2
1. predicateArguments : negated conditional → NO_COVERAGE 2. predicateArguments : negated conditional → SURVIVED |
if (args.length == 1 && args[0] instanceof Object[] array) { |
| 1033 | ||
| 1034 |
1
1. predicateArguments : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$CriteriaBuilderHandler::predicateArguments → NO_COVERAGE |
return Arrays.stream(array).map(Proxies::expressionOf).toList(); |
| 1035 | } | |
| 1036 | ||
| 1037 |
1
1. predicateArguments : replaced return value with Collections.emptyList for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$CriteriaBuilderHandler::predicateArguments → KILLED |
return Arrays.stream(args).map(Proxies::expressionOf).toList(); |
| 1038 | } | |
| 1039 | } | |
| 1040 | ||
| 1041 | // ============================================================ | |
| 1042 | // CriteriaBuilder.In Proxy | |
| 1043 | // ============================================================ | |
| 1044 | ||
| 1045 | private static final class InHandler implements InvocationHandler, ExpressionProvider { | |
| 1046 | ||
| 1047 | private final Expr expression; | |
| 1048 | ||
| 1049 | private final List<Expr> values = new ArrayList<>(); | |
| 1050 | ||
| 1051 | private InHandler(Expr expression) { | |
| 1052 | this.expression = expression; | |
| 1053 | } | |
| 1054 | ||
| 1055 | @Override | |
| 1056 | public Expr expression() { | |
| 1057 |
1
1. expression : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$InHandler::expression → KILLED |
return new In(expression, List.copyOf(values)); |
| 1058 | } | |
| 1059 | ||
| 1060 | @Override | |
| 1061 | public Object invoke(Object proxy, Method method, Object[] args) { | |
| 1062 | ||
| 1063 |
3
1. invoke : negated conditional → KILLED 2. invoke : negated conditional → KILLED 3. invoke : negated conditional → KILLED |
if (method.getName().equals("value") && args != null && args.length == 1) { |
| 1064 | ||
| 1065 | values.add(Proxies.expressionOf(args[0])); | |
| 1066 | ||
| 1067 | // CriteriaBuilder.In.value() returns itself. | |
| 1068 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$InHandler::invoke → SURVIVED |
return proxy; |
| 1069 | } | |
| 1070 | ||
| 1071 |
1
1. invoke : negated conditional → SURVIVED |
if (method.getName().equals("not")) { |
| 1072 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$InHandler::invoke → NO_COVERAGE |
return Proxies.predicate(new Not(expression())); |
| 1073 | } | |
| 1074 | ||
| 1075 |
1
1. invoke : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$InHandler::invoke → SURVIVED |
return objectMethodOrUnsupported(proxy, method, args, expression().toString()); |
| 1076 | } | |
| 1077 | } | |
| 1078 | ||
| 1079 | // ============================================================ | |
| 1080 | // Bean / Property Access | |
| 1081 | // ============================================================ | |
| 1082 | ||
| 1083 | private static final class PropertyAccess { | |
| 1084 | ||
| 1085 | private static final Map<Key, Accessor> CACHE = new ConcurrentHashMap<>(); | |
| 1086 | ||
| 1087 | @SuppressWarnings("noReturnNull") | |
| 1088 | static @Nullable Object read(@Nullable Object target, String property) { | |
| 1089 | ||
| 1090 | LOG.trace("PropertyAccess.read: target={}, property={}", target, property); | |
| 1091 | ||
| 1092 |
1
1. read : negated conditional → KILLED |
if (target == null) { |
| 1093 | return null; | |
| 1094 | } | |
| 1095 | ||
| 1096 | Accessor accessor = CACHE.computeIfAbsent(new Key(target.getClass(), property), PropertyAccess::findAccessor); | |
| 1097 | ||
| 1098 | Object value = accessor.read(target); | |
| 1099 |
1
1. read : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess::read → KILLED |
return unwrapOptionalIfNecessary(value); |
| 1100 | } | |
| 1101 | ||
| 1102 | private static @Nullable Object unwrapOptionalIfNecessary(Object value) { | |
| 1103 |
1
1. unwrapOptionalIfNecessary : negated conditional → KILLED |
if (value instanceof Optional<?> optional) { |
| 1104 |
1
1. unwrapOptionalIfNecessary : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess::unwrapOptionalIfNecessary → NO_COVERAGE |
return optional.orElse(null); |
| 1105 | } | |
| 1106 |
1
1. unwrapOptionalIfNecessary : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess::unwrapOptionalIfNecessary → KILLED |
return value; |
| 1107 | } | |
| 1108 | ||
| 1109 | private static Accessor findAccessor(Key key) { | |
| 1110 | ||
| 1111 | Class<?> type = key.type(); | |
| 1112 | String property = key.property(); | |
| 1113 | ||
| 1114 | /* | |
| 1115 | * Records and ordinary methods whose name exactly equals the property. | |
| 1116 | */ | |
| 1117 | try { | |
| 1118 | Method method = type.getMethod(property); | |
| 1119 | ||
| 1120 |
1
1. findAccessor : negated conditional → SURVIVED |
if (method.getParameterCount() == 0) { |
| 1121 |
1
1. findAccessor : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess::findAccessor → SURVIVED |
return new MethodAccessor(method); |
| 1122 | } | |
| 1123 | } catch (NoSuchMethodException ignored) { | |
| 1124 | // Empty | |
| 1125 | } | |
| 1126 | ||
| 1127 | String capitalized = Character.toUpperCase(property.charAt(0)) + property.substring(1); | |
| 1128 | ||
| 1129 | for (String name : List.of("get" + capitalized, "is" + capitalized)) { | |
| 1130 | ||
| 1131 | try { | |
| 1132 | Method method = type.getMethod(name); | |
| 1133 | ||
| 1134 |
1
1. findAccessor : negated conditional → SURVIVED |
if (method.getParameterCount() == 0) { |
| 1135 |
1
1. findAccessor : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess::findAccessor → SURVIVED |
return new MethodAccessor(method); |
| 1136 | } | |
| 1137 | } catch (NoSuchMethodException ignored) { | |
| 1138 | // Empty | |
| 1139 | } | |
| 1140 | } | |
| 1141 | ||
| 1142 | Class<?> current = type; | |
| 1143 | ||
| 1144 |
1
1. findAccessor : negated conditional → SURVIVED |
while (current != null) { |
| 1145 | try { | |
| 1146 | Field field = current.getDeclaredField(property); | |
| 1147 | ||
| 1148 | field.trySetAccessible(); | |
| 1149 | ||
| 1150 |
1
1. findAccessor : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess::findAccessor → SURVIVED |
return new FieldAccessor(field); |
| 1151 | ||
| 1152 | } catch (NoSuchFieldException ignored) { | |
| 1153 | current = current.getSuperclass(); | |
| 1154 | } | |
| 1155 | } | |
| 1156 | ||
| 1157 | throw new IllegalArgumentException("No readable property '" + property + "' on " + type.getName()); | |
| 1158 | } | |
| 1159 | ||
| 1160 | private record Key(Class<?> type, String property) { | |
| 1161 | } | |
| 1162 | ||
| 1163 | private interface Accessor { | |
| 1164 | Object read(Object target); | |
| 1165 | } | |
| 1166 | ||
| 1167 | private record MethodAccessor(Method method) implements Accessor { | |
| 1168 | ||
| 1169 | @Override | |
| 1170 | public Object read(Object target) { | |
| 1171 | try { | |
| 1172 |
1
1. read : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess$MethodAccessor::read → KILLED |
return method.invoke(target); |
| 1173 | } catch (ReflectiveOperationException e) { | |
| 1174 | throw new IllegalStateException(e); | |
| 1175 | } | |
| 1176 | } | |
| 1177 | } | |
| 1178 | ||
| 1179 | private record FieldAccessor(Field field) implements Accessor { | |
| 1180 | ||
| 1181 | @Override | |
| 1182 | public Object read(Object target) { | |
| 1183 | try { | |
| 1184 |
1
1. read : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator$PropertyAccess$FieldAccessor::read → KILLED |
return field.get(target); |
| 1185 | } catch (IllegalAccessException e) { | |
| 1186 | throw new IllegalStateException(e); | |
| 1187 | } | |
| 1188 | } | |
| 1189 | } | |
| 1190 | } | |
| 1191 | ||
| 1192 | // ============================================================ | |
| 1193 | // Function Support | |
| 1194 | // ============================================================ | |
| 1195 | ||
| 1196 | private record RegisteredFunction(Class<?> resultType, List<Class<?>> argumentTypes, EvaluationFunction function) { | |
| 1197 | } | |
| 1198 | ||
| 1199 | private static String normalizeFunctionName(String name) { | |
| 1200 |
1
1. normalizeFunctionName : replaced return value with "" for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::normalizeFunctionName → KILLED |
return name.toLowerCase(Locale.ROOT); |
| 1201 | } | |
| 1202 | ||
| 1203 | @SuppressWarnings("MagicNumber") | |
| 1204 | private static Object handleFunction(Object[] args) { | |
| 1205 |
4
1. handleFunction : negated conditional → KILLED 2. handleFunction : negated conditional → KILLED 3. handleFunction : negated conditional → KILLED 4. handleFunction : negated conditional → KILLED |
if (args == null || args.length != 3 || !(args[0] instanceof String name) || !(args[1] instanceof Class<?> resultType) |
| 1206 |
1
1. handleFunction : negated conditional → KILLED |
|| !(args[2] instanceof Object[] functionArguments)) { |
| 1207 | throw new UnsupportedOperationException("Unsupported CriteriaBuilder.function() invocation"); | |
| 1208 | } | |
| 1209 | ||
| 1210 | List<Expr> arguments = Arrays.stream(functionArguments).map(Proxies::expressionOf).toList(); | |
| 1211 | ||
| 1212 |
1
1. handleFunction : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::handleFunction → KILLED |
return Proxies.expression(new FunctionCall(name, resultType, arguments)); |
| 1213 | } | |
| 1214 | ||
| 1215 | private Object evaluateFunction(FunctionCall functionCall, EvaluationContext context) { | |
| 1216 | RegisteredFunction registered = functions.get(normalizeFunctionName(functionCall.name())); | |
| 1217 | ||
| 1218 |
1
1. evaluateFunction : negated conditional → KILLED |
if (registered == null) { |
| 1219 | throw new UnsupportedOperationException("No evaluator function registered for CriteriaBuilder.function(" + functionCall.name() | |
| 1220 | + ", " + functionCall.resultType().getName() + ")"); | |
| 1221 | } | |
| 1222 | ||
| 1223 |
1
1. evaluateFunction : removed call to com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::verifyCompatibleResultType → KILLED |
verifyCompatibleResultType(functionCall, registered); |
| 1224 |
1
1. lambda$evaluateFunction$8 : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::lambda$evaluateFunction$8 → KILLED |
List<Object> arguments = functionCall.arguments().stream().map(argument -> evaluateValue(argument, context)).toList(); |
| 1225 |
1
1. evaluateFunction : removed call to com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::validateFunctionArguments → KILLED |
validateFunctionArguments(functionCall.name(), registered, arguments); |
| 1226 | Object result = registered.function().apply(arguments); | |
| 1227 |
1
1. evaluateFunction : removed call to com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::verifyFunctionResult → KILLED |
verifyFunctionResult(functionCall, result); |
| 1228 | ||
| 1229 |
1
1. evaluateFunction : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::evaluateFunction → KILLED |
return result; |
| 1230 | } | |
| 1231 | ||
| 1232 | private static void verifyCompatibleResultType(FunctionCall call, RegisteredFunction registered) { | |
| 1233 |
1
1. verifyCompatibleResultType : negated conditional → KILLED |
if (!wrap(call.resultType()).isAssignableFrom(wrap(registered.resultType()))) { |
| 1234 | throw new IllegalArgumentException("Function '%s' was requested with result type %s, but is registered with result type %s" | |
| 1235 | .formatted(call.name(), call.resultType().getName(), registered.resultType().getName())); | |
| 1236 | } | |
| 1237 | } | |
| 1238 | ||
| 1239 | private static void validateFunctionArguments(String functionName, RegisteredFunction function, List<Object> arguments) { | |
| 1240 | List<Class<?>> expectedTypes = function.argumentTypes(); | |
| 1241 |
1
1. validateFunctionArguments : negated conditional → KILLED |
if (arguments.size() != expectedTypes.size()) { |
| 1242 | throw new IllegalArgumentException( | |
| 1243 | "Function '%s' expected %d arguments but received %d".formatted(functionName, expectedTypes.size(), arguments.size())); | |
| 1244 | } | |
| 1245 | ||
| 1246 |
2
1. validateFunctionArguments : negated conditional → KILLED 2. validateFunctionArguments : changed conditional boundary → KILLED |
for (int i = 0; i < arguments.size(); i++) { |
| 1247 | Object argument = arguments.get(i); | |
| 1248 | Class<?> expectedType = wrap(expectedTypes.get(i)); | |
| 1249 |
2
1. validateFunctionArguments : negated conditional → KILLED 2. validateFunctionArguments : negated conditional → KILLED |
if (argument != null && !expectedType.isInstance(argument)) { |
| 1250 | throw new IllegalArgumentException("Argument %d to function '%s' was %s, expected %s".formatted(i, functionName, | |
| 1251 | argument.getClass().getName(), expectedType.getName())); | |
| 1252 | } | |
| 1253 | } | |
| 1254 | } | |
| 1255 | ||
| 1256 | private static void verifyFunctionResult(FunctionCall call, Object result) { | |
| 1257 |
1
1. verifyFunctionResult : negated conditional → KILLED |
if (result == null) { |
| 1258 | return; | |
| 1259 | } | |
| 1260 | ||
| 1261 | Class<?> expected = wrap(call.resultType()); | |
| 1262 | ||
| 1263 |
1
1. verifyFunctionResult : negated conditional → KILLED |
if (!expected.isInstance(result)) { |
| 1264 | throw new IllegalArgumentException("Function '%s' returned %s, but CriteriaBuilder.function() declared %s" | |
| 1265 | .formatted(call.name(), result.getClass().getName(), expected.getName())); | |
| 1266 | } | |
| 1267 | } | |
| 1268 | ||
| 1269 | private static Class<?> wrap(Class<?> type) { | |
| 1270 |
1
1. wrap : negated conditional → KILLED |
if (!type.isPrimitive()) { |
| 1271 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return type; |
| 1272 | } | |
| 1273 |
1
1. wrap : negated conditional → KILLED |
if (type == int.class) { |
| 1274 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Integer.class; |
| 1275 | } | |
| 1276 |
1
1. wrap : negated conditional → KILLED |
if (type == long.class) { |
| 1277 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Long.class; |
| 1278 | } | |
| 1279 |
1
1. wrap : negated conditional → KILLED |
if (type == double.class) { |
| 1280 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Double.class; |
| 1281 | } | |
| 1282 |
1
1. wrap : negated conditional → KILLED |
if (type == float.class) { |
| 1283 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Float.class; |
| 1284 | } | |
| 1285 |
1
1. wrap : negated conditional → KILLED |
if (type == short.class) { |
| 1286 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Short.class; |
| 1287 | } | |
| 1288 |
1
1. wrap : negated conditional → KILLED |
if (type == byte.class) { |
| 1289 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Byte.class; |
| 1290 | } | |
| 1291 |
1
1. wrap : negated conditional → KILLED |
if (type == boolean.class) { |
| 1292 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Boolean.class; |
| 1293 | } | |
| 1294 |
1
1. wrap : negated conditional → KILLED |
if (type == char.class) { |
| 1295 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → KILLED |
return Character.class; |
| 1296 | } | |
| 1297 |
1
1. wrap : negated conditional → NO_COVERAGE |
if (type == void.class) { |
| 1298 |
1
1. wrap : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::wrap → NO_COVERAGE |
return Void.class; |
| 1299 | } | |
| 1300 | throw new IllegalArgumentException("Unknown primitive type: " + type); | |
| 1301 | } | |
| 1302 | ||
| 1303 | // ============================================================ | |
| 1304 | // Proxy Utility for Unimplemented Methods | |
| 1305 | // ============================================================ | |
| 1306 | ||
| 1307 | private static Object objectMethodOrUnsupported(Object proxy, Method method, Object[] args, String description) { | |
| 1308 | ||
| 1309 |
2
1. objectMethodOrUnsupported : negated conditional → KILLED 2. objectMethodOrUnsupported : negated conditional → KILLED |
if (method.getName().equals("toString") && method.getParameterCount() == 0) { |
| 1310 |
1
1. objectMethodOrUnsupported : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::objectMethodOrUnsupported → KILLED |
return description; |
| 1311 |
2
1. objectMethodOrUnsupported : negated conditional → KILLED 2. objectMethodOrUnsupported : negated conditional → KILLED |
} else if (method.getName().equals("hashCode") && method.getParameterCount() == 0) { |
| 1312 |
1
1. objectMethodOrUnsupported : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::objectMethodOrUnsupported → KILLED |
return System.identityHashCode(proxy); |
| 1313 |
2
1. objectMethodOrUnsupported : negated conditional → NO_COVERAGE 2. objectMethodOrUnsupported : negated conditional → SURVIVED |
} else if (method.getName().equals("equals") && method.getParameterCount() == 1) { |
| 1314 |
1
1. objectMethodOrUnsupported : replaced return value with null for com/reallifedeveloper/tools/test/database/inmemory/PredicateSpecificationEvaluator::objectMethodOrUnsupported → NO_COVERAGE |
return proxy.equals(args[0]); |
| 1315 | } else { | |
| 1316 | throw new UnsupportedOperationException("Unsupported Criteria API operation: " + method); | |
| 1317 | } | |
| 1318 | } | |
| 1319 | } | |
Mutations | ||
| 79 |
1.1 |
|
| 80 |
1.1 |
|
| 92 |
1.1 2.2 3.3 4.4 |
|
| 104 |
1.1 2.2 3.3 |
|
| 142 |
1.1 |
|
| 181 |
1.1 2.2 |
|
| 298 |
1.1 |
|
| 302 |
1.1 |
|
| 306 |
1.1 |
|
| 309 |
1.1 |
|
| 325 |
1.1 |
|
| 330 |
1.1 |
|
| 350 |
1.1 |
|
| 352 |
1.1 |
|
| 353 |
1.1 |
|
| 365 |
1.1 |
|
| 375 |
1.1 |
|
| 376 |
1.1 |
|
| 379 |
1.1 |
|
| 384 |
1.1 |
|
| 388 |
1.1 |
|
| 390 |
1.1 |
|
| 394 |
1.1 2.2 |
|
| 397 |
1.1 |
|
| 398 |
1.1 |
|
| 401 |
1.1 |
|
| 416 |
1.1 |
|
| 426 |
1.1 2.2 3.3 |
|
| 428 |
1.1 2.2 3.3 |
|
| 430 |
1.1 2.2 3.3 |
|
| 432 |
1.1 2.2 3.3 |
|
| 434 |
1.1 |
|
| 436 |
1.1 |
|
| 458 |
1.1 |
|
| 466 |
1.1 |
|
| 491 |
1.1 |
|
| 496 |
1.1 |
|
| 503 |
1.1 |
|
| 510 |
1.1 |
|
| 514 |
1.1 |
|
| 521 |
1.1 2.2 |
|
| 522 |
1.1 |
|
| 525 |
1.1 2.2 |
|
| 539 |
1.1 |
|
| 540 |
1.1 |
|
| 543 |
1.1 |
|
| 548 |
1.1 |
|
| 562 |
1.1 |
|
| 563 |
1.1 |
|
| 566 |
1.1 |
|
| 571 |
1.1 |
|
| 575 |
1.1 |
|
| 593 |
1.1 |
|
| 594 |
1.1 |
|
| 597 |
1.1 |
|
| 602 |
1.1 2.2 |
|
| 608 |
1.1 |
|
| 609 |
1.1 |
|
| 612 |
1.1 |
|
| 616 |
1.1 2.2 |
|
| 622 |
1.1 |
|
| 623 |
1.1 |
|
| 626 |
1.1 |
|
| 630 |
1.1 2.2 |
|
| 641 |
1.1 2.2 |
|
| 642 |
1.1 |
|
| 647 |
1.1 2.2 |
|
| 653 |
1.1 2.2 |
|
| 655 |
1.1 |
|
| 658 |
1.1 |
|
| 659 |
1.1 |
|
| 670 |
1.1 2.2 |
|
| 674 |
1.1 2.2 |
|
| 678 |
1.1 |
|
| 683 |
1.1 |
|
| 684 |
1.1 |
|
| 687 |
1.1 |
|
| 702 |
1.1 |
|
| 708 |
1.1 |
|
| 716 |
1.1 |
|
| 717 |
1.1 |
|
| 720 |
1.1 |
|
| 724 |
1.1 |
|
| 725 |
1.1 |
|
| 729 |
1.1 |
|
| 735 |
1.1 |
|
| 741 |
1.1 |
|
| 748 |
1.1 |
|
| 755 |
1.1 |
|
| 762 |
1.1 |
|
| 769 |
1.1 |
|
| 790 |
1.1 |
|
| 798 |
1.1 |
|
| 799 |
1.1 |
|
| 802 |
1.1 2.2 3.3 4.4 5.5 |
|
| 803 |
1.1 2.2 3.3 |
|
| 805 |
1.1 |
|
| 808 |
1.1 2.2 3.3 4.4 5.5 |
|
| 809 |
1.1 2.2 3.3 |
|
| 811 |
1.1 |
|
| 814 |
1.1 |
|
| 819 |
1.1 2.2 3.3 4.4 5.5 |
|
| 823 |
1.1 |
|
| 842 |
1.1 |
|
| 850 |
1.1 |
|
| 851 |
1.1 |
|
| 854 |
1.1 2.2 |
|
| 855 |
1.1 |
|
| 858 |
1.1 2.2 |
|
| 859 |
1.1 |
|
| 862 |
1.1 2.2 |
|
| 863 |
1.1 |
|
| 869 |
1.1 2.2 3.3 4.4 5.5 |
|
| 870 |
1.1 2.2 3.3 |
|
| 872 |
1.1 |
|
| 875 |
1.1 2.2 3.3 4.4 5.5 |
|
| 876 |
1.1 2.2 3.3 |
|
| 878 |
1.1 |
|
| 881 |
1.1 |
|
| 900 |
1.1 |
|
| 909 |
1.1 |
|
| 910 |
1.1 |
|
| 920 |
1.1 |
|
| 921 |
1.1 |
|
| 929 |
1.1 2.2 3.3 |
|
| 930 |
1.1 |
|
| 933 |
1.1 |
|
| 939 |
1.1 2.2 |
|
| 949 |
1.1 |
|
| 952 |
1.1 |
|
| 959 |
1.1 |
|
| 973 |
1.1 |
|
| 1018 |
1.1 |
|
| 1023 |
1.1 2.2 |
|
| 1032 |
1.1 2.2 |
|
| 1034 |
1.1 |
|
| 1037 |
1.1 |
|
| 1057 |
1.1 |
|
| 1063 |
1.1 2.2 3.3 |
|
| 1068 |
1.1 |
|
| 1071 |
1.1 |
|
| 1072 |
1.1 |
|
| 1075 |
1.1 |
|
| 1092 |
1.1 |
|
| 1099 |
1.1 |
|
| 1103 |
1.1 |
|
| 1104 |
1.1 |
|
| 1106 |
1.1 |
|
| 1120 |
1.1 |
|
| 1121 |
1.1 |
|
| 1134 |
1.1 |
|
| 1135 |
1.1 |
|
| 1144 |
1.1 |
|
| 1150 |
1.1 |
|
| 1172 |
1.1 |
|
| 1184 |
1.1 |
|
| 1200 |
1.1 |
|
| 1205 |
1.1 2.2 3.3 4.4 |
|
| 1206 |
1.1 |
|
| 1212 |
1.1 |
|
| 1218 |
1.1 |
|
| 1223 |
1.1 |
|
| 1224 |
1.1 |
|
| 1225 |
1.1 |
|
| 1227 |
1.1 |
|
| 1229 |
1.1 |
|
| 1233 |
1.1 |
|
| 1241 |
1.1 |
|
| 1246 |
1.1 2.2 |
|
| 1249 |
1.1 2.2 |
|
| 1257 |
1.1 |
|
| 1263 |
1.1 |
|
| 1270 |
1.1 |
|
| 1271 |
1.1 |
|
| 1273 |
1.1 |
|
| 1274 |
1.1 |
|
| 1276 |
1.1 |
|
| 1277 |
1.1 |
|
| 1279 |
1.1 |
|
| 1280 |
1.1 |
|
| 1282 |
1.1 |
|
| 1283 |
1.1 |
|
| 1285 |
1.1 |
|
| 1286 |
1.1 |
|
| 1288 |
1.1 |
|
| 1289 |
1.1 |
|
| 1291 |
1.1 |
|
| 1292 |
1.1 |
|
| 1294 |
1.1 |
|
| 1295 |
1.1 |
|
| 1297 |
1.1 |
|
| 1298 |
1.1 |
|
| 1309 |
1.1 2.2 |
|
| 1310 |
1.1 |
|
| 1311 |
1.1 2.2 |
|
| 1312 |
1.1 |
|
| 1313 |
1.1 2.2 |
|
| 1314 |
1.1 |