• 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

81.33
today-context/src/main/java/infra/validation/AbstractErrors.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.io.Serializable;
23
import java.util.ArrayDeque;
24
import java.util.ArrayList;
25
import java.util.Collections;
26
import java.util.Deque;
27
import java.util.List;
28
import java.util.NoSuchElementException;
29

30
import infra.util.StringUtils;
31

32
/**
33
 * Abstract implementation of the {@link Errors} interface.
34
 * Provides nested path handling but does not define concrete management
35
 * of {@link ObjectError ObjectErrors} and {@link FieldError FieldErrors}.
36
 *
37
 * @author Juergen Hoeller
38
 * @author Rossen Stoyanchev
39
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
40
 * @see AbstractBindingResult
41
 * @since 4.0
42
 */
43
@SuppressWarnings("serial")
44
public abstract class AbstractErrors implements Errors, Serializable {
2✔
45

46
  private String nestedPath = "";
3✔
47

48
  private final Deque<String> nestedPathStack = new ArrayDeque<>();
6✔
49

50
  @Override
51
  public void setNestedPath(@Nullable String nestedPath) {
52
    doSetNestedPath(nestedPath);
3✔
53
    this.nestedPathStack.clear();
3✔
54
  }
1✔
55

56
  @Override
57
  public String getNestedPath() {
58
    return this.nestedPath;
3✔
59
  }
60

61
  @Override
62
  public void pushNestedPath(String subPath) {
63
    this.nestedPathStack.push(getNestedPath());
5✔
64
    doSetNestedPath(getNestedPath() + subPath);
6✔
65
  }
1✔
66

67
  @Override
68
  public void popNestedPath() throws IllegalStateException {
69
    try {
70
      String formerNestedPath = this.nestedPathStack.pop();
5✔
71
      doSetNestedPath(formerNestedPath);
3✔
72
    }
73
    catch (NoSuchElementException ex) {
1✔
74
      throw new IllegalStateException("Cannot pop nested path: no nested path on stack");
5✔
75
    }
1✔
76
  }
1✔
77

78
  /**
79
   * Actually set the nested path.
80
   * Delegated to by setNestedPath and pushNestedPath.
81
   */
82
  protected void doSetNestedPath(@Nullable String nestedPath) {
83
    if (nestedPath == null) {
2!
84
      nestedPath = "";
×
85
    }
86
    nestedPath = canonicalFieldName(nestedPath);
4✔
87
    if (!nestedPath.isEmpty() && !nestedPath.endsWith(NESTED_PATH_SEPARATOR)) {
7✔
88
      nestedPath += NESTED_PATH_SEPARATOR;
3✔
89
    }
90
    this.nestedPath = nestedPath;
3✔
91
  }
1✔
92

93
  /**
94
   * Transform the given field into its full path,
95
   * regarding the nested path of this instance.
96
   */
97
  protected String fixedField(@Nullable String field) {
98
    if (StringUtils.isNotEmpty(field)) {
3✔
99
      return getNestedPath() + canonicalFieldName(field);
7✔
100
    }
101
    else {
102
      String path = getNestedPath();
3✔
103
      return path.endsWith(NESTED_PATH_SEPARATOR)
5✔
104
              ? path.substring(0, path.length() - NESTED_PATH_SEPARATOR.length())
9✔
105
              : path;
1✔
106
    }
107
  }
108

109
  /**
110
   * Determine the canonical field name for the given field.
111
   * <p>The default implementation simply returns the field name as-is.
112
   *
113
   * @param field the original field name
114
   * @return the canonical field name
115
   */
116
  protected String canonicalFieldName(String field) {
117
    return field;
2✔
118
  }
119

120
  @Override
121
  public List<FieldError> getFieldErrors(String field) {
122
    List<FieldError> fieldErrors = getFieldErrors();
×
123
    ArrayList<FieldError> result = new ArrayList<>();
×
124
    String fixedField = fixedField(field);
×
125
    for (FieldError fieldError : fieldErrors) {
×
126
      if (isMatchingFieldError(fixedField, fieldError)) {
×
127
        result.add(fieldError);
×
128
      }
129
    }
×
130
    return Collections.unmodifiableList(result);
×
131
  }
132

133
  /**
134
   * Check whether the given FieldError matches the given field.
135
   *
136
   * @param field the field that we are looking up FieldErrors for
137
   * @param fieldError the candidate FieldError
138
   * @return whether the FieldError matches the given field
139
   */
140
  protected boolean isMatchingFieldError(String field, FieldError fieldError) {
141
    if (field.equals(fieldError.getField())) {
5✔
142
      return true;
2✔
143
    }
144
    // Optimization: use charAt and regionMatches instead of endsWith and startsWith
145
    int endIndex = field.length() - 1;
5✔
146
    return (endIndex >= 0 && field.charAt(endIndex) == '*'
13✔
147
            && (endIndex == 0 || field.regionMatches(0, fieldError.getField(), 0, endIndex)));
8✔
148
  }
149

150
  @Override
151
  public String toString() {
152
    StringBuilder sb = new StringBuilder(getClass().getName());
7✔
153
    sb.append(": ").append(getErrorCount()).append(" errors");
9✔
154
    for (ObjectError error : getAllErrors()) {
11✔
155
      sb.append('\n').append(error);
6✔
156
    }
1✔
157
    return sb.toString();
3✔
158
  }
159

160
}
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