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

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

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

90.8
today-context/src/main/java/infra/validation/SimpleErrors.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.validation;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.beans.PropertyDescriptor;
23
import java.io.Serializable;
24
import java.lang.reflect.Field;
25
import java.util.ArrayList;
26
import java.util.List;
27

28
import infra.beans.BeanUtils;
29
import infra.lang.Assert;
30
import infra.util.ObjectUtils;
31
import infra.util.ReflectionUtils;
32
import infra.util.StringUtils;
33

34
/**
35
 * A simple implementation of the {@link Errors} interface, managing global
36
 * errors and field errors for a top-level target object. Flexibly retrieves
37
 * field values through bean property getter methods, and automatically
38
 * falls back to raw field access if necessary.
39
 *
40
 * <p>Note that this {@link Errors} implementation comes without support for
41
 * nested paths. It is exclusively designed for the validation of individual
42
 * top-level objects, not aggregating errors from multiple sources.
43
 * If this is insufficient for your purposes, use a binding-capable
44
 * {@link Errors} implementation such as {@link BeanPropertyBindingResult}.
45
 *
46
 * @author Juergen Hoeller
47
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
48
 * @see Validator#validateObject(Object)
49
 * @see BeanPropertyBindingResult
50
 * @see DirectFieldBindingResult
51
 * @since 4.0 2023/8/6 21:00
52
 */
53
@SuppressWarnings("serial")
54
public class SimpleErrors implements Errors, Serializable {
55

56
  private final Object target;
57

58
  private final String objectName;
59

60
  private final ArrayList<FieldError> fieldErrors = new ArrayList<>();
10✔
61

62
  private final ArrayList<ObjectError> globalErrors = new ArrayList<>();
10✔
63

64
  /**
65
   * Create a new {@link SimpleErrors} holder for the given target,
66
   * using the simple name of the target class as the object name.
67
   *
68
   * @param target the target to wrap
69
   */
70
  public SimpleErrors(Object target) {
2✔
71
    Assert.notNull(target, "Target is required");
3✔
72
    this.target = target;
3✔
73
    this.objectName = this.target.getClass().getSimpleName();
6✔
74
  }
1✔
75

76
  /**
77
   * Create a new {@link SimpleErrors} holder for the given target.
78
   *
79
   * @param target the target to wrap
80
   * @param objectName the name of the target object for error reporting
81
   */
82
  public SimpleErrors(Object target, String objectName) {
2✔
83
    Assert.notNull(target, "Target is required");
3✔
84
    this.target = target;
3✔
85
    this.objectName = objectName;
3✔
86
  }
1✔
87

88
  @Override
89
  public String getObjectName() {
90
    return this.objectName;
3✔
91
  }
92

93
  @Override
94
  public void reject(String errorCode, Object @Nullable [] errorArgs, @Nullable String defaultMessage) {
95
    this.globalErrors.add(new ObjectError(getObjectName(), new String[] { errorCode }, errorArgs, defaultMessage));
17✔
96
  }
1✔
97

98
  @Override
99
  public void rejectValue(@Nullable String field, String errorCode,
100
          Object @Nullable [] errorArgs, @Nullable String defaultMessage) {
101

102
    if (StringUtils.isEmpty(field)) {
3✔
103
      reject(errorCode, errorArgs, defaultMessage);
5✔
104
      return;
1✔
105
    }
106

107
    Object newVal = getFieldValue(field);
4✔
108
    this.fieldErrors.add(new FieldError(getObjectName(), field, newVal, false,
20✔
109
            new String[] { errorCode }, errorArgs, defaultMessage));
110
  }
1✔
111

112
  @Override
113
  public void addAllErrors(Errors errors) {
114
    this.globalErrors.addAll(errors.getGlobalErrors());
6✔
115
    this.fieldErrors.addAll(errors.getFieldErrors());
6✔
116
  }
1✔
117

118
  @Override
119
  public List<ObjectError> getGlobalErrors() {
120
    return this.globalErrors;
3✔
121
  }
122

123
  @Override
124
  public List<FieldError> getFieldErrors() {
125
    return this.fieldErrors;
3✔
126
  }
127

128
  @Override
129
  @Nullable
130
  public Object getFieldValue(String field) {
131
    FieldError fieldError = getFieldError(field);
4✔
132
    if (fieldError != null) {
2✔
133
      return fieldError.getRejectedValue();
3✔
134
    }
135

136
    PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(this.target.getClass(), field);
6✔
137
    if (pd != null && pd.getReadMethod() != null) {
5!
138
      ReflectionUtils.makeAccessible(pd.getReadMethod());
4✔
139
      return ReflectionUtils.invokeMethod(pd.getReadMethod(), this.target);
6✔
140
    }
141

142
    Field rawField = ReflectionUtils.findField(this.target.getClass(), field);
6✔
143
    if (rawField != null) {
2!
144
      ReflectionUtils.makeAccessible(rawField);
×
145
      return ReflectionUtils.getField(rawField, this.target);
×
146
    }
147

148
    throw new IllegalArgumentException("Cannot retrieve value for field '%s' - neither a getter method nor a raw field found".formatted(field));
12✔
149
  }
150

151
  @Nullable
152
  @Override
153
  public Class<?> getFieldType(String field) {
154
    PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(this.target.getClass(), field);
6✔
155
    if (pd != null) {
2✔
156
      return pd.getPropertyType();
3✔
157
    }
158
    Field rawField = ReflectionUtils.findField(this.target.getClass(), field);
6✔
159
    if (rawField != null) {
2!
160
      return rawField.getType();
×
161
    }
162
    return null;
2✔
163
  }
164

165
  @Override
166
  public boolean equals(@Nullable Object other) {
167
    return this == other || (
7✔
168
            other instanceof SimpleErrors that
7✔
169
                    && ObjectUtils.nullSafeEquals(this.target, that.target)
6✔
170
                    && this.globalErrors.equals(that.globalErrors)
6!
171
                    && this.fieldErrors.equals(that.fieldErrors));
5!
172
  }
173

174
  @Override
175
  public int hashCode() {
176
    return this.target.hashCode();
4✔
177
  }
178

179
  @Override
180
  public String toString() {
181
    StringBuilder sb = new StringBuilder();
4✔
182
    for (ObjectError error : this.globalErrors) {
11✔
183
      sb.append('\n').append(error);
6✔
184
    }
1✔
185
    for (ObjectError error : this.fieldErrors) {
11✔
186
      sb.append('\n').append(error);
6✔
187
    }
1✔
188
    return sb.toString();
3✔
189
  }
190

191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc