• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.97
/byte-buddy-dep/src/main/java/net/bytebuddy/build/HashCodeAndEqualsPlugin.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.build;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.description.annotation.AnnotationDescription;
20
import net.bytebuddy.description.field.FieldDescription;
21
import net.bytebuddy.description.method.MethodDescription;
22
import net.bytebuddy.description.method.MethodList;
23
import net.bytebuddy.description.type.TypeDefinition;
24
import net.bytebuddy.description.type.TypeDescription;
25
import net.bytebuddy.dynamic.ClassFileLocator;
26
import net.bytebuddy.dynamic.DynamicType;
27
import net.bytebuddy.implementation.EqualsMethod;
28
import net.bytebuddy.implementation.HashCodeMethod;
29
import net.bytebuddy.implementation.attribute.AnnotationValueFilter;
30
import net.bytebuddy.implementation.attribute.MethodAttributeAppender;
31
import net.bytebuddy.matcher.ElementMatcher;
32
import net.bytebuddy.matcher.ElementMatchers;
33
import net.bytebuddy.utility.nullability.MaybeNull;
34
import org.objectweb.asm.AnnotationVisitor;
35
import org.objectweb.asm.MethodVisitor;
36

37
import java.lang.annotation.Documented;
38
import java.lang.annotation.ElementType;
39
import java.lang.annotation.Retention;
40
import java.lang.annotation.RetentionPolicy;
41
import java.lang.annotation.Target;
42
import java.util.Comparator;
43

44
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
45
import static net.bytebuddy.matcher.ElementMatchers.isEquals;
46
import static net.bytebuddy.matcher.ElementMatchers.isHashCode;
47
import static net.bytebuddy.matcher.ElementMatchers.named;
48
import static net.bytebuddy.matcher.ElementMatchers.not;
49

50
/**
51
 * A build tool plugin that adds {@link Object#hashCode()} and {@link Object#equals(Object)} methods to a class if the
52
 * {@link Enhance} annotation is present and no explicit method declaration was added. This plugin does not need to be closed.
53
 */
54
@HashCodeAndEqualsPlugin.Enhance
55
public class HashCodeAndEqualsPlugin implements Plugin, Plugin.Factory, MethodAttributeAppender.Factory, MethodAttributeAppender {
56

57
    /**
58
     * A description of the {@link Enhance#invokeSuper()} method.
59
     */
60
    private static final MethodDescription.InDefinedShape ENHANCE_INVOKE_SUPER;
61

62
    /**
63
     * A description of the {@link Enhance#simpleComparisonsFirst()} method.
64
     */
65
    private static final MethodDescription.InDefinedShape ENHANCE_SIMPLE_COMPARISON_FIRST;
66

67
    /**
68
     * A description of the {@link Enhance#includeSyntheticFields()} method.
69
     */
70
    private static final MethodDescription.InDefinedShape ENHANCE_INCLUDE_SYNTHETIC_FIELDS;
71

72
    /**
73
     * A description of the {@link Enhance#permitSubclassEquality()} method.
74
     */
75
    private static final MethodDescription.InDefinedShape ENHANCE_PERMIT_SUBCLASS_EQUALITY;
76

77
    /**
78
     * A description of the {@link Enhance#useTypeHashConstant()} method.
79
     */
80
    private static final MethodDescription.InDefinedShape ENHANCE_USE_TYPE_HASH_CONSTANT;
81

82
    /**
83
     * A description of the {@link ValueHandling#value()} method.
84
     */
85
    private static final MethodDescription.InDefinedShape VALUE_HANDLING_VALUE;
86

87
    /**
88
     * A description of the {@link Sorted#value()} method.
89
     */
90
    private static final MethodDescription.InDefinedShape SORTED_VALUE;
91

92
    /*
93
     * Resolves diverse annotation properties.
94
     */
95
    static {
96
        MethodList<MethodDescription.InDefinedShape> enhanceMethods = TypeDescription.ForLoadedType.of(Enhance.class).getDeclaredMethods();
1✔
97
        ENHANCE_INVOKE_SUPER = enhanceMethods.filter(named("invokeSuper")).getOnly();
1✔
98
        ENHANCE_SIMPLE_COMPARISON_FIRST = enhanceMethods.filter(named("simpleComparisonsFirst")).getOnly();
1✔
99
        ENHANCE_INCLUDE_SYNTHETIC_FIELDS = enhanceMethods.filter(named("includeSyntheticFields")).getOnly();
1✔
100
        ENHANCE_PERMIT_SUBCLASS_EQUALITY = enhanceMethods.filter(named("permitSubclassEquality")).getOnly();
1✔
101
        ENHANCE_USE_TYPE_HASH_CONSTANT = enhanceMethods.filter(named("useTypeHashConstant")).getOnly();
1✔
102
        VALUE_HANDLING_VALUE = TypeDescription.ForLoadedType.of(ValueHandling.class).getDeclaredMethods().filter(named("value")).getOnly();
1✔
103
        SORTED_VALUE = TypeDescription.ForLoadedType.of(Sorted.class).getDeclaredMethods().filter(named("value")).getOnly();
1✔
104
    }
1✔
105

106
    /**
107
     * Defines the binary name of a runtime-visible annotation type that should be added to the parameter of the
108
     * {@link Object#equals(Object)} method, or {@code null} if no such name should be defined.
109
     */
110
    @MaybeNull
111
    @ValueHandling(ValueHandling.Sort.REVERSE_NULLABILITY)
112
    private final String annotationType;
113

114
    /**
115
     * Creates a new hash code equals plugin.
116
     */
117
    public HashCodeAndEqualsPlugin() {
118
        this(null);
1✔
119
    }
1✔
120

121
    /**
122
     * Creates a new hash code equals plugin.
123
     *
124
     * @param annotationType Defines the binary name of a runtime-visible annotation type that should be added to the
125
     *                       parameter of the {@link Object#equals(Object)} method, or {@code null} if no such name
126
     *                       should be defined.
127
     */
128
    public HashCodeAndEqualsPlugin(@MaybeNull String annotationType) {
1✔
129
        this.annotationType = annotationType;
1✔
130
    }
1✔
131

132
    /**
133
     * {@inheritDoc}
134
     */
135
    public Plugin make() {
136
        return this;
×
137
    }
138

139
    /**
140
     * {@inheritDoc}
141
     */
142
    public boolean matches(@MaybeNull TypeDescription target) {
143
        return target != null && target.getDeclaredAnnotations().isAnnotationPresent(Enhance.class);
1✔
144
    }
145

146
    /**
147
     * {@inheritDoc}
148
     */
149
    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Annotation presence is required by matcher.")
150
    public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
151
        AnnotationDescription.Loadable<Enhance> enhance = typeDescription.getDeclaredAnnotations().ofType(Enhance.class);
1✔
152
        if (typeDescription.getDeclaredMethods().filter(isHashCode()).isEmpty()) {
1✔
153
            builder = builder.method(isHashCode()).intercept(enhance.getValue(ENHANCE_INVOKE_SUPER).load(Enhance.class.getClassLoader()).resolve(Enhance.InvokeSuper.class)
1✔
154
                    .hashCodeMethod(typeDescription,
1✔
155
                            enhance.getValue(ENHANCE_USE_TYPE_HASH_CONSTANT).resolve(Boolean.class),
1✔
156
                            enhance.getValue(ENHANCE_PERMIT_SUBCLASS_EQUALITY).resolve(Boolean.class))
1✔
157
                    .withIgnoredFields(enhance.getValue(ENHANCE_INCLUDE_SYNTHETIC_FIELDS).resolve(Boolean.class)
1✔
158
                            ? ElementMatchers.<FieldDescription>none()
×
159
                            : ElementMatchers.<FieldDescription>isSynthetic())
1✔
160
                    .withIgnoredFields(new ValueMatcher(ValueHandling.Sort.IGNORE))
1✔
161
                    .withNonNullableFields(nonNullable(new ValueMatcher(ValueHandling.Sort.REVERSE_NULLABILITY)))
1✔
162
                    .withIdentityFields(isAnnotatedWith(Identity.class)));
1✔
163
        }
164
        if (typeDescription.getDeclaredMethods().filter(isEquals()).isEmpty()) {
1✔
165
            EqualsMethod equalsMethod = enhance.getValue(ENHANCE_INVOKE_SUPER).load(Enhance.class.getClassLoader()).resolve(Enhance.InvokeSuper.class)
1✔
166
                    .equalsMethod(typeDescription)
1✔
167
                    .withIgnoredFields(enhance.getValue(ENHANCE_INCLUDE_SYNTHETIC_FIELDS).resolve(Boolean.class)
1✔
168
                            ? ElementMatchers.<FieldDescription>none()
×
169
                            : ElementMatchers.<FieldDescription>isSynthetic())
1✔
170
                    .withIgnoredFields(new ValueMatcher(ValueHandling.Sort.IGNORE))
1✔
171
                    .withNonNullableFields(nonNullable(new ValueMatcher(ValueHandling.Sort.REVERSE_NULLABILITY)))
1✔
172
                    .withIdentityFields(isAnnotatedWith(Identity.class))
1✔
173
                    .withFieldOrder(AnnotationOrderComparator.INSTANCE);
1✔
174
            if (enhance.getValue(ENHANCE_SIMPLE_COMPARISON_FIRST).resolve(Boolean.class)) {
1✔
175
                equalsMethod = equalsMethod
1✔
176
                        .withPrimitiveTypedFieldsFirst()
1✔
177
                        .withEnumerationTypedFieldsFirst()
1✔
178
                        .withPrimitiveWrapperTypedFieldsFirst()
1✔
179
                        .withStringTypedFieldsFirst();
1✔
180
            }
181
            builder = builder.method(isEquals()).intercept(enhance.getValue(ENHANCE_PERMIT_SUBCLASS_EQUALITY).resolve(Boolean.class)
1✔
182
                    ? equalsMethod.withSubclassEquality()
1✔
183
                    : equalsMethod).attribute(this);
1✔
184
        }
185
        return builder;
1✔
186
    }
187

188
    /**
189
     * Resolves the matcher to identify non-nullable fields.
190
     *
191
     * @param matcher The matcher that identifies fields that are either nullable or non-nullable.
192
     * @return The actual matcher to identify non-nullable fields.
193
     */
194
    protected ElementMatcher<FieldDescription> nonNullable(ElementMatcher<FieldDescription> matcher) {
195
        return matcher;
1✔
196
    }
197

198
    /**
199
     * {@inheritDoc}
200
     */
201
    public void close() {
202
        /* do nothing */
203
    }
×
204

205
    /**
206
     * {@inheritDoc}
207
     */
208
    public MethodAttributeAppender make(TypeDescription typeDescription) {
209
        return this;
1✔
210
    }
211

212
    /**
213
     * {@inheritDoc}
214
     */
215
    public void apply(MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) {
216
        if (annotationType != null) {
1✔
217
            AnnotationVisitor annotationVisitor = methodVisitor.visitParameterAnnotation(0,
1✔
218
                    "L" + annotationType.replace('.', '/') + ";",
1✔
219
                    true);
220
            if (annotationVisitor != null) {
1✔
221
                annotationVisitor.visitEnd();
1✔
222
            }
223
        }
224
    }
1✔
225

226
    /**
227
     * A version of the {@link HashCodeAndEqualsPlugin} that assumes that all fields are non-nullable unless they are explicitly marked.
228
     */
229
    @HashCodeAndEqualsPlugin.Enhance
230
    public static class WithNonNullableFields extends HashCodeAndEqualsPlugin {
231

232
        /**
233
         * Creates a new hash code equals plugin where fields are assumed nullable by default.
234
         */
235
        public WithNonNullableFields() {
236
            this(null);
1✔
237
        }
1✔
238

239
        /**
240
         * Creates a new hash code equals plugin where fields are assumed nullable by default.
241
         *
242
         * @param annotationType Defines the binary name of a runtime-visible annotation type that should be added to the
243
         *                       parameter of the {@link Object#equals(Object)} method, or {@code null} if no such name
244
         *                       should be defined.
245
         */
246
        public WithNonNullableFields(@MaybeNull String annotationType) {
247
            super(annotationType);
1✔
248
        }
1✔
249

250
        /**
251
         * {@inheritDoc}
252
         */
253
        protected ElementMatcher<FieldDescription> nonNullable(ElementMatcher<FieldDescription> matcher) {
254
            return not(matcher);
1✔
255
        }
256
    }
257

258
    /**
259
     * Instructs the {@link HashCodeAndEqualsPlugin} to generate {@link Object#hashCode()} and {@link Object#equals(Object)} for the annotated
260
     * class unless these methods are already declared explicitly.
261
     */
262
    @Documented
263
    @Target(ElementType.TYPE)
264
    @Retention(RetentionPolicy.RUNTIME)
265
    public @interface Enhance {
266

267
        /**
268
         * Determines the base value of any added method, i.e. if hash code or equality is based on the super type or not.
269
         *
270
         * @return A strategy for determining the base value.
271
         */
272
        InvokeSuper invokeSuper() default InvokeSuper.IF_DECLARED;
273

274
        /**
275
         * Determines if fields with primitive types, then enumeration types, then primtive wrapper types and then {@link String} types
276
         * should be compared for equality before fields with other types. Before determining such a field order,
277
         * the {@link Sorted} property is always considered first if it is defined.
278
         *
279
         * @return {@code true} if fields with simple comparison methods should be compared first.
280
         */
281
        boolean simpleComparisonsFirst() default true;
282

283
        /**
284
         * Determines if synthetic fields should be included in the hash code and equality contract.
285
         *
286
         * @return {@code true} if synthetic fields should be included.
287
         */
288
        boolean includeSyntheticFields() default false;
289

290
        /**
291
         * Determines if instances subclasses of the instrumented type are accepted upon an equality check.
292
         *
293
         * @return {@code true} if instances subclasses of the instrumented type are accepted upon an equality check.
294
         */
295
        boolean permitSubclassEquality() default false;
296

297
        /**
298
         * Determines if the hash code constant should be derived of the instrumented type. If {@link Enhance#permitSubclassEquality()}
299
         * is set to {@code true}, this constant is derived of the declared class, otherwise the type hash is computed of the active instance.
300
         *
301
         * @return {@code true} if the hash code constant should be derived of the instrumented type.
302
         */
303
        boolean useTypeHashConstant() default true;
304

305
        /**
306
         * A strategy for determining the base value of a hash code or equality contract.
307
         */
308
        enum InvokeSuper {
1✔
309

310
            /**
311
             * Only invokes the super method's hash code and equality methods if any super class that is not {@link Object} explicitly defines such a method.
312
             */
313
            IF_DECLARED {
1✔
314
                @Override
315
                protected HashCodeMethod hashCodeMethod(TypeDescription instrumentedType, boolean typeHash, boolean subclassEquality) {
316
                    TypeDefinition typeDefinition = instrumentedType.getSuperClass();
1✔
317
                    while (typeDefinition != null && !typeDefinition.represents(Object.class)) {
1✔
318
                        if (typeDefinition.asErasure().getDeclaredAnnotations().isAnnotationPresent(Enhance.class)) {
×
319
                            return HashCodeMethod.usingSuperClassOffset();
×
320
                        }
321
                        MethodList<?> hashCode = typeDefinition.getDeclaredMethods().filter(isHashCode());
×
322
                        if (!hashCode.isEmpty()) {
×
323
                            return hashCode.getOnly().isAbstract()
×
324
                                    ? (typeHash ? HashCodeMethod.usingTypeHashOffset(!subclassEquality) : HashCodeMethod.usingDefaultOffset())
×
325
                                    : HashCodeMethod.usingSuperClassOffset();
×
326
                        }
327
                        typeDefinition = typeDefinition.getSuperClass();
×
328
                    }
×
329
                    return typeHash ? HashCodeMethod.usingTypeHashOffset(!subclassEquality) : HashCodeMethod.usingDefaultOffset();
1✔
330
                }
331

332
                @Override
333
                protected EqualsMethod equalsMethod(TypeDescription instrumentedType) {
334
                    TypeDefinition typeDefinition = instrumentedType.getSuperClass();
1✔
335
                    while (typeDefinition != null && !typeDefinition.represents(Object.class)) {
1✔
336
                        if (typeDefinition.asErasure().getDeclaredAnnotations().isAnnotationPresent(Enhance.class)) {
1✔
337
                            return EqualsMethod.requiringSuperClassEquality();
1✔
338
                        }
339
                        MethodList<?> hashCode = typeDefinition.getDeclaredMethods().filter(isHashCode());
1✔
340
                        if (!hashCode.isEmpty()) {
1✔
341
                            return hashCode.getOnly().isAbstract()
1✔
342
                                    ? EqualsMethod.isolated()
×
343
                                    : EqualsMethod.requiringSuperClassEquality();
1✔
344
                        }
345
                        typeDefinition = typeDefinition.getSuperClass();
×
346
                    }
×
347
                    return EqualsMethod.isolated();
1✔
348
                }
349
            },
350

351
            /**
352
             * Only invokes the super method's hash code and equality methods if the super class is also annotated with {@link Enhance}.
353
             */
354
            IF_ANNOTATED {
1✔
355
                @Override
356
                protected HashCodeMethod hashCodeMethod(TypeDescription instrumentedType, boolean typeHash, boolean subclassEquality) {
357
                    TypeDefinition superClass = instrumentedType.getSuperClass();
×
358
                    return superClass != null && superClass.asErasure().getDeclaredAnnotations().isAnnotationPresent(Enhance.class)
×
359
                            ? HashCodeMethod.usingSuperClassOffset()
×
360
                            : (typeHash ? HashCodeMethod.usingTypeHashOffset(!subclassEquality) : HashCodeMethod.usingDefaultOffset());
×
361
                }
362

363
                @Override
364
                protected EqualsMethod equalsMethod(TypeDescription instrumentedType) {
365
                    TypeDefinition superClass = instrumentedType.getSuperClass();
1✔
366
                    return superClass != null && superClass.asErasure().getDeclaredAnnotations().isAnnotationPresent(Enhance.class)
1✔
367
                            ? EqualsMethod.requiringSuperClassEquality()
1✔
368
                            : EqualsMethod.isolated();
1✔
369
                }
370
            },
371

372
            /**
373
             * Always invokes the super class's hash code and equality methods.
374
             */
375
            ALWAYS {
1✔
376
                @Override
377
                protected HashCodeMethod hashCodeMethod(TypeDescription instrumentedType, boolean typeHash, boolean subclassEquality) {
378
                    return HashCodeMethod.usingSuperClassOffset();
×
379
                }
380

381
                @Override
382
                protected EqualsMethod equalsMethod(TypeDescription instrumentedType) {
383
                    return EqualsMethod.requiringSuperClassEquality();
1✔
384
                }
385
            },
386

387
            /**
388
             * Never invokes the super class's hash code and equality methods.
389
             */
390
            NEVER {
1✔
391
                @Override
392
                protected HashCodeMethod hashCodeMethod(TypeDescription instrumentedType, boolean typeHash, boolean subclassEquality) {
393
                    return typeHash ? HashCodeMethod.usingTypeHashOffset(!subclassEquality) : HashCodeMethod.usingDefaultOffset();
×
394
                }
395

396
                @Override
397
                protected EqualsMethod equalsMethod(TypeDescription instrumentedType) {
398
                    return EqualsMethod.isolated();
1✔
399
                }
400
            };
401

402
            /**
403
             * Resolves the hash code method to use.
404
             *
405
             * @param instrumentedType The instrumented type.
406
             * @param typeHash         {@code true} if the base hash should be based on the instrumented class's type.
407
             * @param subclassEquality {@code true} if subclasses can be equal to their base classes.
408
             * @return The hash code method to use.
409
             */
410
            protected abstract HashCodeMethod hashCodeMethod(TypeDescription instrumentedType, boolean typeHash, boolean subclassEquality);
411

412
            /**
413
             * Resolves the equals method to use.
414
             *
415
             * @param instrumentedType The instrumented type.
416
             * @return The equals method to use.
417
             */
418
            protected abstract EqualsMethod equalsMethod(TypeDescription instrumentedType);
419
        }
420
    }
421

422
    /**
423
     * Determines how a field should be used within generated hash code and equality methods.
424
     */
425
    @Documented
426
    @Target(ElementType.FIELD)
427
    @Retention(RetentionPolicy.RUNTIME)
428
    public @interface ValueHandling {
429

430
        /**
431
         * Determines the handling of the annotated field.
432
         *
433
         * @return The handling of the annotated field.
434
         */
435
        Sort value();
436

437
        /**
438
         * Determines how a field should be handled.
439
         */
440
        enum Sort {
1✔
441

442
            /**
443
             * Excludes the field from hash code and equality methods.
444
             */
445
            IGNORE,
1✔
446

447
            /**
448
             * Reverses the nullability of the field, i.e. assumes this field to be non-null or {@code null} if {@link WithNonNullableFields} is used.
449
             */
450
            REVERSE_NULLABILITY
1✔
451
        }
452
    }
453

454
    /**
455
     * Determines the sort order of fields for the equality check when implementing the {@link Object#equals(Object)} method. Any field
456
     * that is not annotated is considered with a value of {@link Sorted#DEFAULT} where fields with a higher value are checked for equality
457
     * first. This sort order is applied first after which the type order is considered if {@link Enhance#simpleComparisonsFirst()} is considered
458
     * as additional sort criteria.
459
     */
460
    @Documented
461
    @Target(ElementType.FIELD)
462
    @Retention(RetentionPolicy.RUNTIME)
463
    public @interface Sorted {
464

465
        /**
466
         * The default sort weight.
467
         */
468
        int DEFAULT = 0;
469

470
        /**
471
         * The value for the sort order where fields with higher values are checked for equality first.
472
         *
473
         * @return The value for the sort order where fields with higher values are checked for equality first.
474
         */
475
        int value();
476
    }
477

478
    /**
479
     * Indicates that a field should be compared by identity. Hash codes are then determined by
480
     * {@link System#identityHashCode(Object)}. Fields that are compared by identity are implicitly null-safe.
481
     */
482
    @Documented
483
    @Target(ElementType.FIELD)
484
    @Retention(RetentionPolicy.RUNTIME)
485
    public @interface Identity {
486
        /* empty */
487
    }
488

489
    /**
490
     * A comparator that arranges fields in the order of {@link Sorted}.
491
     */
492
    protected enum AnnotationOrderComparator implements Comparator<FieldDescription.InDefinedShape> {
1✔
493

494
        /**
495
         * The singleton instance.
496
         */
497
        INSTANCE;
1✔
498

499
        /**
500
         * {@inheritDoc}
501
         */
502
        public int compare(FieldDescription.InDefinedShape left, FieldDescription.InDefinedShape right) {
503
            AnnotationDescription.Loadable<Sorted> leftAnnotation = left.getDeclaredAnnotations().ofType(Sorted.class);
1✔
504
            AnnotationDescription.Loadable<Sorted> rightAnnotation = right.getDeclaredAnnotations().ofType(Sorted.class);
1✔
505
            int leftValue = leftAnnotation == null ? Sorted.DEFAULT : leftAnnotation.getValue(SORTED_VALUE).resolve(Integer.class);
1✔
506
            int rightValue = rightAnnotation == null ? Sorted.DEFAULT : rightAnnotation.getValue(SORTED_VALUE).resolve(Integer.class);
1✔
507
            if (leftValue > rightValue) {
1✔
508
                return -1;
1✔
509
            } else if (leftValue < rightValue) {
1✔
510
                return 1;
1✔
511
            } else {
512
                return 0;
1✔
513
            }
514
        }
515
    }
516

517
    /**
518
     * An element matcher for a {@link ValueHandling} annotation.
519
     */
520
    @HashCodeAndEqualsPlugin.Enhance
521
    protected static class ValueMatcher extends ElementMatcher.Junction.ForNonNullValues<FieldDescription> {
522

523
        /**
524
         * The matched value.
525
         */
526
        private final ValueHandling.Sort sort;
527

528
        /**
529
         * Creates a new value matcher.
530
         *
531
         * @param sort The matched value.
532
         */
533
        protected ValueMatcher(ValueHandling.Sort sort) {
1✔
534
            this.sort = sort;
1✔
535
        }
1✔
536

537
        /**
538
         * {@inheritDoc}
539
         */
540
        protected boolean doMatch(FieldDescription target) {
541
            AnnotationDescription.Loadable<ValueHandling> annotation = target.getDeclaredAnnotations().ofType(ValueHandling.class);
1✔
542
            return annotation != null && annotation.getValue(VALUE_HANDLING_VALUE).load(ValueHandling.class.getClassLoader()).resolve(ValueHandling.Sort.class) == sort;
1✔
543
        }
544
    }
545
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc