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

pkiraly / metadata-qa-api / #730

23 Oct 2025 09:26AM UTC coverage: 86.354% (-0.06%) from 86.412%
#730

push

pkiraly
Fix OR, AND and ContentType checkers

15 of 34 new or added lines in 5 files covered. (44.12%)

1 existing line in 1 file now uncovered.

5670 of 6566 relevant lines covered (86.35%)

0.86 hits per line

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

81.87
/src/main/java/de/gwdg/metadataqa/api/schema/SchemaUtils.java
1
package de.gwdg.metadataqa.api.schema;
2

3
import de.gwdg.metadataqa.api.configuration.schema.Rule;
4
import de.gwdg.metadataqa.api.json.DataElement;
5
import de.gwdg.metadataqa.api.rule.logical.AndChecker;
6
import de.gwdg.metadataqa.api.rule.logical.LogicalChecker;
7
import de.gwdg.metadataqa.api.rule.logical.NotChecker;
8
import de.gwdg.metadataqa.api.rule.logical.OrChecker;
9
import de.gwdg.metadataqa.api.rule.pairchecker.DisjointChecker;
10
import de.gwdg.metadataqa.api.rule.pairchecker.LessThanPairChecker;
11
import de.gwdg.metadataqa.api.rule.singlefieldchecker.ContentTypeChecker;
12
import de.gwdg.metadataqa.api.rule.singlefieldchecker.DependencyChecker;
13
import de.gwdg.metadataqa.api.rule.singlefieldchecker.EnumerationChecker;
14
import de.gwdg.metadataqa.api.rule.pairchecker.EqualityChecker;
15
import de.gwdg.metadataqa.api.rule.singlefieldchecker.HasChildrenChecker;
16
import de.gwdg.metadataqa.api.rule.singlefieldchecker.HasValueChecker;
17
import de.gwdg.metadataqa.api.rule.singlefieldchecker.ImageDimensionChecker;
18
import de.gwdg.metadataqa.api.rule.singlefieldchecker.LanguageTagChecker;
19
import de.gwdg.metadataqa.api.rule.singlefieldchecker.LinkValidityChecker;
20
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MQAFPatternChecker;
21
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxCountChecker;
22
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxLengthChecker;
23
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxWordsChecker;
24
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinCountChecker;
25
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinLengthChecker;
26
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinWordsChecker;
27
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MultilingualChecker;
28
import de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker;
29
import de.gwdg.metadataqa.api.rule.singlefieldchecker.PatternChecker;
30
import de.gwdg.metadataqa.api.rule.RuleChecker;
31
import de.gwdg.metadataqa.api.rule.singlefieldchecker.UniquenessChecker;
32
import org.apache.commons.lang3.StringUtils;
33

34
import java.util.ArrayList;
35
import java.util.LinkedHashMap;
36
import java.util.List;
37
import java.util.Map;
38
import java.util.logging.Logger;
39
import java.util.stream.Collectors;
40

41
import static de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker.TYPE.MAX_EXCLUSIVE;
42
import static de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker.TYPE.MAX_INCLUSIVE;
43
import static de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker.TYPE.MIN_EXCLUSIVE;
44
import static de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker.TYPE.MIN_INCLUSIVE;
45

46
public class SchemaUtils {
×
47

48
  private static final Logger LOGGER = Logger.getLogger(SchemaUtils.class.getCanonicalName());
1✔
49
  static int id = 0;
1✔
50

51
  /**
52
   * Create rule checkers based on the schema
53
   *
54
   * @param schema
55
   * @return The list of rule checkers
56
   */
57
  public static List<RuleChecker> getRuleCheckers(Schema schema) {
58
    setSchemaForFields(schema);
1✔
59
    id = 0;
1✔
60
    List<RuleChecker> allRuleCheckers = new ArrayList<>();
1✔
61
    for (DataElement dataElement : schema.getPaths()) {
1✔
62
      if (dataElement.getRules() != null) {
1✔
63
        List<Rule> rules = dataElement.getRules();
1✔
64
        for (Rule rule : rules) {
1✔
65
          List<RuleChecker> ruleCheckers = processRule(schema, dataElement, rule);
1✔
66
          if (!ruleCheckers.isEmpty())
1✔
67
            allRuleCheckers.addAll(ruleCheckers);
1✔
68
        }
1✔
69
      }
70
    }
1✔
71
    return allRuleCheckers;
1✔
72
  }
73

74
  private static List<RuleChecker> processRule(Schema schema, DataElement dataElement, Rule rule) {
75
    List<RuleChecker> ruleCheckers = new ArrayList<>();
1✔
76
    if (rule.getSkip().equals(Boolean.TRUE))
1✔
77
      return ruleCheckers;
×
78

79
    if (StringUtils.isNotBlank(rule.getPattern()))
1✔
80
      ruleCheckers.add(new PatternChecker(dataElement, rule.getPattern()));
1✔
81

82
    if (rule.getMqafPattern() != null)
1✔
83
      ruleCheckers.add(new MQAFPatternChecker(dataElement, rule.getMqafPattern()));
×
84

85
    if (StringUtils.isNotBlank(rule.getEquals()))
1✔
86
      pair(schema, ruleCheckers, dataElement, rule.getEquals(), "equals");
1✔
87

88
    if (StringUtils.isNotBlank(rule.getDisjoint()))
1✔
89
      pair(schema, ruleCheckers, dataElement, rule.getDisjoint(), "disjoint");
1✔
90

91
    if (rule.getIn() != null && !rule.getIn().isEmpty())
1✔
92
      ruleCheckers.add(new EnumerationChecker(dataElement, rule.getIn()));
1✔
93

94
    if (rule.getMinCount() != null)
1✔
95
      ruleCheckers.add(new MinCountChecker(dataElement, rule.getMinCount(), rule.getAllowEmptyInstances()));
1✔
96

97
    if (rule.getMaxCount() != null)
1✔
98
      ruleCheckers.add(new MaxCountChecker(dataElement, rule.getMaxCount(), rule.getAllowEmptyInstances()));
1✔
99

100
    if (rule.getMinLength() != null)
1✔
101
      ruleCheckers.add(new MinLengthChecker(dataElement, rule.getMinLength()));
1✔
102

103
    if (rule.getMaxLength() != null)
1✔
104
      ruleCheckers.add(new MaxLengthChecker(dataElement, rule.getMaxLength()));
1✔
105

106
    if (rule.getMaxWords() != null)
1✔
107
      ruleCheckers.add(new MaxWordsChecker(dataElement, rule.getMaxWords()));
×
108

109
    if (rule.getMinWords() != null)
1✔
110
      ruleCheckers.add(new MinWordsChecker(dataElement, rule.getMinWords()));
×
111

112
    if (StringUtils.isNotBlank(rule.getHasValue()))
1✔
113
      ruleCheckers.add(new HasValueChecker(dataElement, rule.getHasValue()));
1✔
114

115
    if (rule.getHasChildren() != null && !rule.getHasChildren().isEmpty())
1✔
116
      ruleCheckers.add(new HasChildrenChecker(dataElement, rule.getHasChildren()));
1✔
117

118
    if (rule.getMinInclusive() != null)
1✔
119
      ruleCheckers.add(new NumericValueChecker(dataElement, rule.getMinInclusive(), MIN_INCLUSIVE));
×
120

121
    if (rule.getMaxInclusive() != null)
1✔
122
      ruleCheckers.add(new NumericValueChecker(dataElement, rule.getMinInclusive(), MAX_INCLUSIVE));
×
123

124
    if (rule.getMinExclusive() != null)
1✔
125
      ruleCheckers.add(new NumericValueChecker(dataElement, rule.getMinInclusive(), MIN_EXCLUSIVE));
×
126

127
    if (rule.getMaxExclusive() != null)
1✔
128
      ruleCheckers.add(new NumericValueChecker(dataElement, rule.getMinInclusive(), MAX_EXCLUSIVE));
×
129

130
    if (rule.getContentType() != null && !rule.getContentType().isEmpty()) {
1✔
131
      ContentTypeChecker contentTypeChecker = rule.getTimeout() == null
1✔
132
        ? new ContentTypeChecker(dataElement, rule.getContentType())
1✔
133
        : new ContentTypeChecker(dataElement, dataElement.getLabel(), rule.getContentType(), rule.getTimeout());
1✔
134
      if (StringUtils.isNotBlank(rule.getSkippableUrl()))
1✔
135
        contentTypeChecker.setSkippableUrl(rule.getSkippableUrl());
×
136
      ruleCheckers.add(contentTypeChecker);
1✔
137
    }
138

139
    if (rule.getValidLink() != null) {
1✔
140
      LinkValidityChecker checker = rule.getTimeout() == null
×
141
        ? new LinkValidityChecker(dataElement, rule.getValidLink())
×
142
        : new LinkValidityChecker(dataElement, dataElement.getLabel(), rule.getValidLink(), rule.getTimeout());
×
143
      ruleCheckers.add(checker);
×
144
    }
145

146
    if (rule.getDimension() != null)
1✔
147
      ruleCheckers.add(new ImageDimensionChecker(dataElement, rule.getDimension()));
1✔
148

149
    if (rule.getDependencies() != null && !rule.getDependencies().isEmpty())
1✔
150
      ruleCheckers.add(new DependencyChecker(dataElement, rule.getDependencies()));
1✔
151

152
    if (rule.getUnique() != null && rule.getUnique().equals(Boolean.TRUE))
1✔
153
      ruleCheckers.add(new UniquenessChecker(dataElement));
×
154

155
    if (rule.getMultilingual() != null && rule.getMultilingual().equals(Boolean.TRUE))
1✔
156
      ruleCheckers.add(new MultilingualChecker(dataElement));
×
157

158
    if (rule.getHasLanguageTag() != null)
1✔
159
      ruleCheckers.add(new LanguageTagChecker(dataElement).withScope(rule.getHasLanguageTag()));
×
160

161
    if (rule.getLessThan() != null)
1✔
162
      pair(schema, ruleCheckers, dataElement, rule.getLessThan(), "LessThan");
×
163

164
    if (rule.getLessThanOrEquals() != null)
1✔
165
      pair(schema, ruleCheckers, dataElement, rule.getLessThan(), "lessThanOrEquals");
×
166

167
    if (rule.getLessThanOrEquals() != null)
1✔
168
      ruleCheckers.add(new DependencyChecker(dataElement, rule.getDependencies()));
×
169

170
    if (rule.getAnd() != null) {
1✔
171
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getAnd(), rule.getId());
1✔
172
      AndChecker checker = new AndChecker(dataElement, childRuleCheckers);
1✔
173
      if (rule.getAlwaysCheckDependencies().equals(Boolean.TRUE))
1✔
174
        checker.setAlwaysCheckDependencies(true);
×
175
      if (rule.getPriorityOnFail() != null && rule.getPriorityOnFail().equals(Boolean.TRUE))
1✔
176
        checker.setPriorityOnFail(true);
×
177
      ruleCheckers.add(checker);
1✔
178
    }
179

180
    if (rule.getOr() != null) {
1✔
181
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getOr(), rule.getId());
1✔
182
      OrChecker checker = new OrChecker(dataElement, childRuleCheckers);
1✔
183
      if (rule.getAlwaysCheckDependencies().equals(Boolean.TRUE))
1✔
184
        checker.setAlwaysCheckDependencies(true);
×
185
      if (rule.getPriorityOnFail() != null && rule.getPriorityOnFail().equals(Boolean.TRUE))
1✔
NEW
186
        checker.setPriorityOnFail(true);
×
187
      ruleCheckers.add(checker);
1✔
188
    }
189

190
    if (rule.getNot() != null) {
1✔
191
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getNot(), rule.getId());
1✔
192
      ruleCheckers.add(new NotChecker(dataElement, childRuleCheckers));
1✔
193
    }
194

195
    // General properties
196
    // List<String> debuggables = new ArrayList<>();
197
    if (!ruleCheckers.isEmpty()) {
1✔
198
      for (RuleChecker ruleChecker : ruleCheckers) {
1✔
199
        ruleChecker.setFailureScore(rule.getFailureScore());
1✔
200
        ruleChecker.setSuccessScore(rule.getSuccessScore());
1✔
201
        ruleChecker.setNaScore(rule.getNaScore());
1✔
202
        String idValue = StringUtils.isNotBlank(rule.getId()) ? rule.getId() : String.valueOf(++id);
1✔
203
        ruleChecker.setId(idValue);
1✔
204
        ruleChecker.setScope(rule.getScope());
1✔
205
        if (rule.getHidden().equals(Boolean.TRUE))
1✔
206
          ruleChecker.setHidden();
1✔
207
        if (rule.getMandatory().equals(Boolean.TRUE))
1✔
208
          ruleChecker.setMandatory();
×
209
        if (rule.getDebug().equals(Boolean.TRUE)) {
1✔
210
          ruleChecker.setDebug();
1✔
211
          if (ruleChecker instanceof LogicalChecker) {
1✔
212
            for (RuleChecker child : ((LogicalChecker) ruleChecker).getCheckers()) {
1✔
213
              child.setDebug();
1✔
214
            }
1✔
215
          }
216
          // if (ruleChecker instanceof DependencyChecker)
217
          //   debuggables.addAll(((DependencyChecker) ruleChecker).getDependencies());
218
        }
219
        if (StringUtils.isNotBlank(rule.getValuePath()))
1✔
220
          ruleChecker.setValuePath(rule.getValuePath());
1✔
221
      }
1✔
222
    }
223

224
    /*
225
    if (!debuggables.isEmpty()) {
226
      for (RuleChecker ruleChecker : ruleCheckers) {
227
        if (debuggables.contains(ruleChecker.getId()))
228
          ruleChecker.setDebug();
229
      }
230
    }
231
     */
232

233
    return ruleCheckers;
1✔
234
  }
235

236
  private static List<RuleChecker> getChildRuleCheckers(Schema schema, DataElement dataElement, List<Rule> rules, String id) {
237
    List<RuleChecker> childRuleCheckers = new ArrayList<>();
1✔
238
    for (Rule childRule : rules) {
1✔
239
      if (StringUtils.isBlank(childRule.getId()))
1✔
240
        childRule.setId(id);
1✔
241
      List<RuleChecker> localChildRuleCheckers = processRule(schema, dataElement, childRule);
1✔
242
      if (!localChildRuleCheckers.isEmpty())
1✔
243
        childRuleCheckers.addAll(localChildRuleCheckers);
1✔
244
    }
1✔
245
    return childRuleCheckers;
1✔
246
  }
247

248
  private static void pair(Schema schema,
249
                           List<RuleChecker> ruleCheckers,
250
                           DataElement dataElement,
251
                           String fieldReference,
252
                           String type) {
253
    DataElement field2 = schema.getPathByLabel(fieldReference);
1✔
254
    if (field2 != null) {
1✔
255
      RuleChecker ruleChecker = null;
1✔
256
      if ("equals".equals(type)) {
1✔
257
        ruleChecker = new EqualityChecker(dataElement, field2);
1✔
258
      } else if ("disjoint".equals(type)) {
1✔
259
        ruleChecker = new DisjointChecker(dataElement, field2);
1✔
260
      } else if ("lessThan".equals(type)) {
×
261
        ruleChecker = new LessThanPairChecker(dataElement, field2, LessThanPairChecker.TYPE.LESS_THAN);
×
262
      } else if ("lessThanOrEquals".equals(type)) {
×
263
        ruleChecker = new LessThanPairChecker(dataElement, field2, LessThanPairChecker.TYPE.LESS_THAN_OR_EQUALS);
×
264
      }
265

266
      if (ruleChecker != null)
1✔
267
        ruleCheckers.add(ruleChecker);
1✔
268
    } else
1✔
269
      LOGGER.warning(
×
270
        String.format("invalid field reference in %s: %s", type, fieldReference));
×
271
  }
1✔
272

273
  public static void setSchemaForFields(Schema schema) {
274
    for (DataElement dataElement : schema.getPaths())
1✔
275
      dataElement.setSchema(schema);
1✔
276
  }
1✔
277

278
  /**
279
   * Get a rule object by its identifier
280
   * @param schema The schema
281
   * @param id The identifier of the rule
282
   * @return the rule object
283
   */
284
  public static Rule getRuleById(Schema schema, String id) {
285
    for (DataElement dataElement : schema.getPaths()) {
1✔
286
      for (Rule rule : dataElement.getRules()) {
1✔
287
        if (rule.getId().equals(id))
1✔
288
          return rule;
1✔
289
      }
1✔
290
    }
1✔
291
    return null;
×
292
  }
293

294
  /**
295
   * Get the direct dependencies of a rule
296
   * @param rule A rule
297
   * @return List of the rule identifiers on that the current rule depends on
298
   */
299
  public static List<String> getDependencies(Rule rule) {
300
    List<String> dependencies = new ArrayList<>();
1✔
301
    if (rule.getDependencies() != null) {
1✔
302
      dependencies.addAll(rule.getDependencies());
1✔
303
    }
304
    if (rule.getAnd() != null && !rule.getAnd().isEmpty()) {
1✔
305
      for (Rule childRule : rule.getAnd()) {
1✔
306
        dependencies.addAll(getDependencies(childRule));
1✔
307
      }
1✔
308
    }
309
    if (rule.getOr() != null && !rule.getOr().isEmpty()) {
1✔
310
      for (Rule childRule : rule.getOr()) {
1✔
311
        dependencies.addAll(getDependencies(childRule));
1✔
312
      }
1✔
313
    }
314
    if (rule.getNot() != null && !rule.getNot().isEmpty()) {
1✔
315
      for (Rule childRule : rule.getNot()) {
×
316
        dependencies.addAll(getDependencies(childRule));
×
317
      }
×
318
    }
319
    return dependencies;
1✔
320
  }
321

322
  /**
323
   * Returns all dependencies
324
   * @param schema The schema
325
   * @param rule A rule in the schema
326
   * @return List of the rule identifiers on that the current rule directly or independently depends on
327
   */
328
  public static List<String> getAllDependencies(Schema schema, Rule rule) {
329
    Map<String, Boolean> dependenciesMap = new LinkedHashMap<>();
1✔
330
    getDependencies(rule).stream().forEach(e -> dependenciesMap.put(e, false));
1✔
331
    boolean runAgain = !dependenciesMap.isEmpty();
1✔
332
    while (runAgain) {
1✔
333
      runAgain = false;
1✔
334
      for (Map.Entry<String, Boolean> entry : dependenciesMap.entrySet()) {
1✔
335
        String ruleId = entry.getKey();
1✔
336
        boolean processed = entry.getValue();
1✔
337
        if (!processed) {
1✔
338
          Rule depRule = getRuleById(schema, ruleId);
1✔
339
          List<String> additionalOnes = getDependencies(depRule);
1✔
340
          int size = dependenciesMap.size();
1✔
341
          for (String additionalOne : additionalOnes)
1✔
342
            dependenciesMap.putIfAbsent(additionalOne, false);
1✔
343
          if (dependenciesMap.size() > size)
1✔
344
            runAgain = true;
1✔
345
          dependenciesMap.put(ruleId, true);
1✔
346
        }
347
      }
1✔
348
    }
349
    return dependenciesMap.keySet().stream().collect(Collectors.toList());
1✔
350
  }
351
}
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