• 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

94.24
today-context/src/main/java/infra/context/condition/ConditionEvaluationReportMessage.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.context.condition;
19

20
import java.util.ArrayList;
21
import java.util.Collections;
22
import java.util.LinkedHashMap;
23
import java.util.List;
24
import java.util.Map;
25
import java.util.Map.Entry;
26
import java.util.Set;
27

28
import infra.lang.Assert;
29
import infra.util.ClassUtils;
30
import infra.util.LinkedMultiValueMap;
31
import infra.util.MultiValueMap;
32
import infra.util.StringUtils;
33

34
/**
35
 * A condition evaluation report message that can logged or printed.
36
 *
37
 * @author Phillip Webb
38
 * @author Andy Wilkinson
39
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
40
 * @since 4.0
41
 */
42
public class ConditionEvaluationReportMessage {
43

44
  private final StringBuilder message;
45

46
  public ConditionEvaluationReportMessage(ConditionEvaluationReport report) {
47
    this(report, "CONDITIONS EVALUATION REPORT");
4✔
48
  }
1✔
49

50
  public ConditionEvaluationReportMessage(ConditionEvaluationReport report, String title) {
2✔
51
    this.message = getLogMessage(report, title);
6✔
52
  }
1✔
53

54
  private StringBuilder getLogMessage(ConditionEvaluationReport report, String title) {
55
    StringBuilder message = new StringBuilder();
4✔
56
    message.append(String.format("%n%n%n"));
7✔
57
    StringBuilder separator = new StringBuilder();
4✔
58
    separator.append("=".repeat(title.length()));
7✔
59
    message.append(String.format("%s%n", separator));
11✔
60
    message.append(String.format("%s%n", title));
11✔
61
    message.append(String.format("%s%n%n%n", separator));
11✔
62
    Map<String, ConditionEvaluationReport.ConditionAndOutcomes> shortOutcomes = orderByName(report.getConditionAndOutcomesBySource());
5✔
63
    logPositiveMatches(message, shortOutcomes);
4✔
64
    logNegativeMatches(message, shortOutcomes);
4✔
65
    logExclusions(report, message);
4✔
66
    logUnconditionalClasses(report, message);
4✔
67
    message.append(String.format("%n%n"));
7✔
68
    return message;
2✔
69
  }
70

71
  private void logPositiveMatches(StringBuilder message, Map<String, ConditionEvaluationReport.ConditionAndOutcomes> shortOutcomes) {
72
    message.append(String.format("Positive matches:%n"));
7✔
73
    message.append(String.format("-----------------%n"));
7✔
74
    List<Entry<String, ConditionEvaluationReport.ConditionAndOutcomes>> matched = shortOutcomes.entrySet().stream()
4✔
75
            .filter((entry) -> entry.getValue().isFullMatch()).toList();
8✔
76
    if (matched.isEmpty()) {
3✔
77
      message.append(String.format("%n    None%n"));
8✔
78
    }
79
    else {
80
      matched.forEach((entry) -> addMatchLogMessage(message, entry.getKey(), entry.getValue()));
15✔
81
    }
82
    message.append(String.format("%n%n"));
7✔
83
  }
1✔
84

85
  private void logNegativeMatches(StringBuilder message, Map<String, ConditionEvaluationReport.ConditionAndOutcomes> shortOutcomes) {
86
    message.append(String.format("Negative matches:%n"));
7✔
87
    message.append(String.format("-----------------%n"));
7✔
88
    List<Entry<String, ConditionEvaluationReport.ConditionAndOutcomes>> nonMatched = shortOutcomes.entrySet().stream()
4✔
89
            .filter((entry) -> !entry.getValue().isFullMatch()).toList();
12✔
90
    if (nonMatched.isEmpty()) {
3✔
91
      message.append(String.format("%n    None%n"));
8✔
92
    }
93
    else {
94
      nonMatched.forEach((entry) -> addNonMatchLogMessage(message, entry.getKey(), entry.getValue()));
15✔
95
    }
96
    message.append(String.format("%n%n"));
7✔
97
  }
1✔
98

99
  private void logExclusions(ConditionEvaluationReport report, StringBuilder message) {
100
    message.append(String.format("Exclusions:%n"));
7✔
101
    message.append(String.format("-----------%n"));
7✔
102
    if (report.getExclusions().isEmpty()) {
4✔
103
      message.append(String.format("%n    None%n"));
8✔
104
    }
105
    else {
106
      for (String exclusion : report.getExclusions()) {
11✔
107
        message.append(String.format("%n    %s%n", exclusion));
11✔
108
      }
1✔
109
    }
110
    message.append(String.format("%n%n"));
7✔
111
  }
1✔
112

113
  private void logUnconditionalClasses(ConditionEvaluationReport report, StringBuilder message) {
114
    message.append(String.format("Unconditional classes:%n"));
7✔
115
    message.append(String.format("----------------------%n"));
7✔
116
    if (report.getUnconditionalClasses().isEmpty()) {
4!
117
      message.append(String.format("%n    None%n"));
8✔
118
    }
119
    else {
120
      for (String unconditionalClass : report.getUnconditionalClasses()) {
×
121
        message.append(String.format("%n    %s%n", unconditionalClass));
×
122
      }
×
123
    }
124
  }
1✔
125

126
  private Map<String, ConditionEvaluationReport.ConditionAndOutcomes> orderByName(Map<String, ConditionEvaluationReport.ConditionAndOutcomes> outcomes) {
127
    MultiValueMap<String, String> map = mapToFullyQualifiedNames(outcomes.keySet());
5✔
128
    List<String> shortNames = new ArrayList<>(map.keySet());
6✔
129
    Collections.sort(shortNames);
2✔
130
    Map<String, ConditionEvaluationReport.ConditionAndOutcomes> result = new LinkedHashMap<>();
4✔
131
    for (String shortName : shortNames) {
10✔
132
      List<String> fullyQualifiedNames = map.get(shortName);
5✔
133
      Assert.state(fullyQualifiedNames != null, "'fullyQualifiedNames' is required");
6!
134
      if (fullyQualifiedNames.size() > 1) {
4✔
135
        fullyQualifiedNames.forEach(
6✔
136
                (fullyQualifiedName) -> result.put(fullyQualifiedName, outcomes.get(fullyQualifiedName)));
9✔
137
      }
138
      else {
139
        result.put(shortName, outcomes.get(fullyQualifiedNames.get(0)));
10✔
140
      }
141
    }
1✔
142
    return result;
2✔
143
  }
144

145
  private MultiValueMap<String, String> mapToFullyQualifiedNames(Set<String> keySet) {
146
    LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
4✔
147
    keySet.forEach(fullyQualifiedName -> map.add(ClassUtils.getShortName(fullyQualifiedName), fullyQualifiedName));
10✔
148
    return map;
2✔
149
  }
150

151
  private void addMatchLogMessage(StringBuilder message, String source, ConditionEvaluationReport.ConditionAndOutcomes matches) {
152
    message.append(String.format("%n   %s matched:%n", source));
11✔
153
    for (ConditionEvaluationReport.ConditionAndOutcome match : matches) {
10✔
154
      logConditionAndOutcome(message, "      ", match);
5✔
155
    }
1✔
156
  }
1✔
157

158
  private void addNonMatchLogMessage(StringBuilder message, String source, ConditionEvaluationReport.ConditionAndOutcomes conditionAndOutcomes) {
159
    message.append(String.format("%n   %s:%n", source));
11✔
160
    List<ConditionEvaluationReport.ConditionAndOutcome> matches = new ArrayList<>();
4✔
161
    List<ConditionEvaluationReport.ConditionAndOutcome> nonMatches = new ArrayList<>();
4✔
162
    for (ConditionEvaluationReport.ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
10✔
163
      if (conditionAndOutcome.outcome.isMatch()) {
4✔
164
        matches.add(conditionAndOutcome);
5✔
165
      }
166
      else {
167
        nonMatches.add(conditionAndOutcome);
4✔
168
      }
169
    }
1✔
170
    message.append(String.format("      Did not match:%n"));
7✔
171
    for (ConditionEvaluationReport.ConditionAndOutcome nonMatch : nonMatches) {
10✔
172
      logConditionAndOutcome(message, "         ", nonMatch);
5✔
173
    }
1✔
174
    if (!matches.isEmpty()) {
3✔
175
      message.append(String.format("      Matched:%n"));
7✔
176
      for (ConditionEvaluationReport.ConditionAndOutcome match : matches) {
10✔
177
        logConditionAndOutcome(message, "         ", match);
5✔
178
      }
1✔
179
    }
180
  }
1✔
181

182
  private void logConditionAndOutcome(StringBuilder message, String indent, ConditionEvaluationReport.ConditionAndOutcome conditionAndOutcome) {
183
    message.append(String.format("%s- ", indent));
11✔
184
    String outcomeMessage = conditionAndOutcome.outcome.getMessage();
4✔
185
    if (StringUtils.isNotEmpty(outcomeMessage)) {
3✔
186
      message.append(outcomeMessage);
5✔
187
    }
188
    else {
189
      message.append(conditionAndOutcome.outcome.isMatch() ? "matched" : "did not match");
9!
190
    }
191
    message.append(" (");
4✔
192
    message.append(ClassUtils.getShortName(conditionAndOutcome.condition.getClass()));
7✔
193
    message.append(String.format(")%n"));
7✔
194
  }
1✔
195

196
  @Override
197
  public String toString() {
198
    return this.message.toString();
4✔
199
  }
200

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