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

pkiraly / metadata-qa-api / #742

14 Jan 2026 12:45PM UTC coverage: 85.581% (-0.6%) from 86.157%
#742

push

pkiraly
Add parent check

32 of 96 new or added lines in 7 files covered. (33.33%)

2 existing lines in 2 files now uncovered.

5686 of 6644 relevant lines covered (85.58%)

0.86 hits per line

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

82.32
/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.BaseRuleChecker;
6
import de.gwdg.metadataqa.api.rule.logical.AndChecker;
7
import de.gwdg.metadataqa.api.rule.logical.LogicalChecker;
8
import de.gwdg.metadataqa.api.rule.logical.NotChecker;
9
import de.gwdg.metadataqa.api.rule.logical.OrChecker;
10
import de.gwdg.metadataqa.api.rule.pairchecker.DisjointChecker;
11
import de.gwdg.metadataqa.api.rule.pairchecker.LessThanPairChecker;
12
import de.gwdg.metadataqa.api.rule.singlefieldchecker.ContentTypeChecker;
13
import de.gwdg.metadataqa.api.rule.singlefieldchecker.DependencyChecker;
14
import de.gwdg.metadataqa.api.rule.singlefieldchecker.EnumerationChecker;
15
import de.gwdg.metadataqa.api.rule.pairchecker.EqualityChecker;
16
import de.gwdg.metadataqa.api.rule.singlefieldchecker.HasChildrenChecker;
17
import de.gwdg.metadataqa.api.rule.singlefieldchecker.HasValueChecker;
18
import de.gwdg.metadataqa.api.rule.singlefieldchecker.ImageDimensionChecker;
19
import de.gwdg.metadataqa.api.rule.singlefieldchecker.LanguageTagChecker;
20
import de.gwdg.metadataqa.api.rule.singlefieldchecker.LinkValidityChecker;
21
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MQAFPatternChecker;
22
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxCountChecker;
23
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxLengthChecker;
24
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxWordsChecker;
25
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinCountChecker;
26
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinLengthChecker;
27
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinWordsChecker;
28
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MultilingualChecker;
29
import de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker;
30
import de.gwdg.metadataqa.api.rule.singlefieldchecker.PatternChecker;
31
import de.gwdg.metadataqa.api.rule.RuleChecker;
32
import de.gwdg.metadataqa.api.rule.singlefieldchecker.UniquenessChecker;
33
import org.apache.commons.lang3.StringUtils;
34

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

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

47
public class SchemaUtils {
×
48

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

150
    if (rule.getDependencies() != null && !rule.getDependencies().isEmpty()) {
1✔
151
      DependencyChecker checker = new DependencyChecker(dataElement, rule.getDependencies());
1✔
152
      if (rule.getParentCheck() != null)
1✔
NEW
153
        checker.setParentCheck(rule.getParentCheck());
×
154
      ruleCheckers.add(checker);
1✔
155
    }
156

157
    if (rule.getUnique() != null && rule.getUnique().equals(Boolean.TRUE))
1✔
158
      ruleCheckers.add(new UniquenessChecker(dataElement));
×
159

160
    if (rule.getMultilingual() != null && rule.getMultilingual().equals(Boolean.TRUE))
1✔
161
      ruleCheckers.add(new MultilingualChecker(dataElement));
×
162

163
    if (rule.getHasLanguageTag() != null)
1✔
164
      ruleCheckers.add(new LanguageTagChecker(dataElement).withScope(rule.getHasLanguageTag()));
×
165

166
    if (rule.getLessThan() != null)
1✔
167
      pair(schema, ruleCheckers, dataElement, rule.getLessThan(), "LessThan");
×
168

169
    if (rule.getLessThanOrEquals() != null)
1✔
170
      pair(schema, ruleCheckers, dataElement, rule.getLessThan(), "lessThanOrEquals");
×
171

172
    if (rule.getLessThanOrEquals() != null)
1✔
173
      ruleCheckers.add(new DependencyChecker(dataElement, rule.getDependencies()));
×
174

175
    if (rule.getAnd() != null) {
1✔
176
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getAnd(), rule.getId());
1✔
177
      addLogicalChecker(rule, ruleCheckers, new AndChecker(dataElement, childRuleCheckers));
1✔
178
    }
179

180
    if (rule.getOr() != null) {
1✔
181
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getOr(), rule.getId());
1✔
182
      addLogicalChecker(rule, ruleCheckers, new OrChecker(dataElement, childRuleCheckers));
1✔
183
    }
184

185
    if (rule.getNot() != null) {
1✔
186
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getNot(), rule.getId());
1✔
187
      addLogicalChecker(rule, ruleCheckers, new NotChecker(dataElement, childRuleCheckers));
1✔
188
    }
189

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

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

230
    return ruleCheckers;
1✔
231
  }
232

233
  private static void addLogicalChecker(Rule rule, List<RuleChecker> ruleCheckers, LogicalChecker checker) {
234
    setAlwaysCheckDependencies(rule, checker);
1✔
235
    setPriorityOnFail(rule, checker);
1✔
236
    ruleCheckers.add(checker);
1✔
237
  }
1✔
238

239
  private static void setPriorityOnFail(Rule rule, LogicalChecker checker) {
240
    if (rule.getPriorityOnFail() != null && rule.getPriorityOnFail().equals(Boolean.TRUE))
1✔
NEW
241
      checker.setPriorityOnFail(true);
×
242
  }
1✔
243

244
  private static void setAlwaysCheckDependencies(Rule rule, LogicalChecker checker) {
245
    if (rule.getAlwaysCheckDependencies().equals(Boolean.TRUE))
1✔
NEW
246
      checker.setAlwaysCheckDependencies(true);
×
247
  }
1✔
248

249
  private static List<RuleChecker> getChildRuleCheckers(Schema schema, DataElement dataElement, List<Rule> rules, String id) {
250
    List<RuleChecker> childRuleCheckers = new ArrayList<>();
1✔
251
    for (Rule childRule : rules) {
1✔
252
      if (StringUtils.isBlank(childRule.getId()))
1✔
253
        childRule.setId(id);
1✔
254
      List<RuleChecker> localChildRuleCheckers = processRule(schema, dataElement, childRule);
1✔
255
      if (!localChildRuleCheckers.isEmpty())
1✔
256
        childRuleCheckers.addAll(localChildRuleCheckers);
1✔
257
    }
1✔
258
    return childRuleCheckers;
1✔
259
  }
260

261
  private static void pair(Schema schema,
262
                           List<RuleChecker> ruleCheckers,
263
                           DataElement dataElement,
264
                           String fieldReference,
265
                           String type) {
266
    DataElement field2 = schema.getPathByLabel(fieldReference);
1✔
267
    if (field2 != null) {
1✔
268
      RuleChecker ruleChecker = null;
1✔
269
      if ("equals".equals(type)) {
1✔
270
        ruleChecker = new EqualityChecker(dataElement, field2);
1✔
271
      } else if ("disjoint".equals(type)) {
1✔
272
        ruleChecker = new DisjointChecker(dataElement, field2);
1✔
273
      } else if ("lessThan".equals(type)) {
×
274
        ruleChecker = new LessThanPairChecker(dataElement, field2, LessThanPairChecker.TYPE.LESS_THAN);
×
275
      } else if ("lessThanOrEquals".equals(type)) {
×
276
        ruleChecker = new LessThanPairChecker(dataElement, field2, LessThanPairChecker.TYPE.LESS_THAN_OR_EQUALS);
×
277
      }
278

279
      if (ruleChecker != null)
1✔
280
        ruleCheckers.add(ruleChecker);
1✔
281
    } else
1✔
282
      LOGGER.warning(
×
283
        String.format("invalid field reference in %s: %s", type, fieldReference));
×
284
  }
1✔
285

286
  public static void setSchemaForFields(Schema schema) {
287
    for (DataElement dataElement : schema.getPaths())
1✔
288
      dataElement.setSchema(schema);
1✔
289
  }
1✔
290

291
  /**
292
   * Get a rule object by its identifier
293
   * @param schema The schema
294
   * @param id The identifier of the rule
295
   * @return the rule object
296
   */
297
  public static Rule getRuleById(Schema schema, String id) {
298
    for (DataElement dataElement : schema.getPaths()) {
1✔
299
      for (Rule rule : dataElement.getRules()) {
1✔
300
        if (rule.getId().equals(id))
1✔
301
          return rule;
1✔
302
      }
1✔
303
    }
1✔
304
    return null;
×
305
  }
306

307
  /**
308
   * Get the direct dependencies of a rule
309
   * @param rule A rule
310
   * @return List of the rule identifiers on that the current rule depends on
311
   */
312
  public static List<String> getDependencies(Rule rule) {
313
    List<String> dependencies = new ArrayList<>();
1✔
314
    if (rule.getDependencies() != null) {
1✔
315
      dependencies.addAll(rule.getDependencies());
1✔
316
    }
317
    if (rule.getAnd() != null && !rule.getAnd().isEmpty()) {
1✔
318
      for (Rule childRule : rule.getAnd()) {
1✔
319
        dependencies.addAll(getDependencies(childRule));
1✔
320
      }
1✔
321
    }
322
    if (rule.getOr() != null && !rule.getOr().isEmpty()) {
1✔
323
      for (Rule childRule : rule.getOr()) {
1✔
324
        dependencies.addAll(getDependencies(childRule));
1✔
325
      }
1✔
326
    }
327
    if (rule.getNot() != null && !rule.getNot().isEmpty()) {
1✔
328
      for (Rule childRule : rule.getNot()) {
×
329
        dependencies.addAll(getDependencies(childRule));
×
330
      }
×
331
    }
332
    return dependencies;
1✔
333
  }
334

335
  /**
336
   * Returns all dependencies
337
   * @param schema The schema
338
   * @param rule A rule in the schema
339
   * @return List of the rule identifiers on that the current rule directly or independently depends on
340
   */
341
  public static List<String> getAllDependencies(Schema schema, Rule rule) {
342
    Map<String, Boolean> dependenciesMap = new LinkedHashMap<>();
1✔
343
    getDependencies(rule).stream().forEach(e -> dependenciesMap.put(e, false));
1✔
344
    boolean runAgain = !dependenciesMap.isEmpty();
1✔
345
    while (runAgain) {
1✔
346
      runAgain = false;
1✔
347
      for (Map.Entry<String, Boolean> entry : dependenciesMap.entrySet()) {
1✔
348
        String ruleId = entry.getKey();
1✔
349
        boolean processed = entry.getValue();
1✔
350
        if (!processed) {
1✔
351
          Rule depRule = getRuleById(schema, ruleId);
1✔
352
          List<String> additionalOnes = getDependencies(depRule);
1✔
353
          int size = dependenciesMap.size();
1✔
354
          for (String additionalOne : additionalOnes)
1✔
355
            dependenciesMap.putIfAbsent(additionalOne, false);
1✔
356
          if (dependenciesMap.size() > size)
1✔
357
            runAgain = true;
1✔
358
          dependenciesMap.put(ruleId, true);
1✔
359
        }
360
      }
1✔
361
    }
362
    return dependenciesMap.keySet().stream().collect(Collectors.toList());
1✔
363
  }
364
}
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