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

pkiraly / metadata-qa-api / #692

04 Jun 2025 11:56AM UTC coverage: 86.93% (-0.3%) from 87.231%
#692

push

pkiraly
Merge branch 'main' of github.com:pkiraly/metadata-qa-api

5547 of 6381 relevant lines covered (86.93%)

0.87 hits per line

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

84.36
/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.MQAFPatternChecker;
20
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxCountChecker;
21
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxLengthChecker;
22
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MaxWordsChecker;
23
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinCountChecker;
24
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinLengthChecker;
25
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MinWordsChecker;
26
import de.gwdg.metadataqa.api.rule.singlefieldchecker.MultilingualChecker;
27
import de.gwdg.metadataqa.api.rule.singlefieldchecker.NumericValueChecker;
28
import de.gwdg.metadataqa.api.rule.singlefieldchecker.PatternChecker;
29
import de.gwdg.metadataqa.api.rule.RuleChecker;
30
import de.gwdg.metadataqa.api.rule.singlefieldchecker.UniquenessChecker;
31
import org.apache.commons.lang3.StringUtils;
32

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

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

45
public class SchemaUtils {
×
46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

129
    if (rule.getContentType() != null && !rule.getContentType().isEmpty())
1✔
130
      ruleCheckers.add(new ContentTypeChecker(dataElement, rule.getContentType()));
1✔
131

132
    if (rule.getDimension() != null)
1✔
133
      ruleCheckers.add(new ImageDimensionChecker(dataElement, rule.getDimension()));
1✔
134

135
    if (rule.getDependencies() != null && !rule.getDependencies().isEmpty())
1✔
136
      ruleCheckers.add(new DependencyChecker(dataElement, rule.getDependencies()));
1✔
137

138
    if (rule.getUnique() != null && rule.getUnique().equals(Boolean.TRUE))
1✔
139
      ruleCheckers.add(new UniquenessChecker(dataElement));
×
140

141
    if (rule.getMultilingual() != null && rule.getMultilingual().equals(Boolean.TRUE))
1✔
142
      ruleCheckers.add(new MultilingualChecker(dataElement));
×
143

144
    if (rule.getHasLanguageTag() != null)
1✔
145
      ruleCheckers.add(new LanguageTagChecker(dataElement).withScope(rule.getHasLanguageTag()));
×
146

147
    if (rule.getLessThan() != null)
1✔
148
      pair(schema, ruleCheckers, dataElement, rule.getLessThan(), "LessThan");
×
149

150
    if (rule.getLessThanOrEquals() != null)
1✔
151
      pair(schema, ruleCheckers, dataElement, rule.getLessThan(), "lessThanOrEquals");
×
152

153
    if (rule.getLessThanOrEquals() != null)
1✔
154
      ruleCheckers.add(new DependencyChecker(dataElement, rule.getDependencies()));
×
155

156
    if (rule.getAnd() != null) {
1✔
157
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getAnd(), rule.getId());
1✔
158
      AndChecker checker = new AndChecker(dataElement, childRuleCheckers);
1✔
159
      if (rule.getAlwaysCheckDependencies().equals(Boolean.TRUE))
1✔
160
        checker.setAlwaysCheckDependencies(true);
×
161
      ruleCheckers.add(checker);
1✔
162
    }
163

164
    if (rule.getOr() != null) {
1✔
165
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getOr(), rule.getId());
1✔
166
      OrChecker checker = new OrChecker(dataElement, childRuleCheckers);
1✔
167
      if (rule.getAlwaysCheckDependencies().equals(Boolean.TRUE))
1✔
168
        checker.setAlwaysCheckDependencies(true);
×
169
      ruleCheckers.add(checker);
1✔
170
    }
171

172
    if (rule.getNot() != null) {
1✔
173
      List<RuleChecker> childRuleCheckers = getChildRuleCheckers(schema, dataElement, rule.getNot(), rule.getId());
1✔
174
      ruleCheckers.add(new NotChecker(dataElement, childRuleCheckers));
1✔
175
    }
176

177
    // General properties
178
    // List<String> debuggables = new ArrayList<>();
179
    if (!ruleCheckers.isEmpty()) {
1✔
180
      for (RuleChecker ruleChecker : ruleCheckers) {
1✔
181
        ruleChecker.setFailureScore(rule.getFailureScore());
1✔
182
        ruleChecker.setSuccessScore(rule.getSuccessScore());
1✔
183
        ruleChecker.setNaScore(rule.getNaScore());
1✔
184
        String idValue = StringUtils.isNotBlank(rule.getId()) ? rule.getId() : String.valueOf(++id);
1✔
185
        ruleChecker.setId(idValue);
1✔
186
        ruleChecker.setScope(rule.getScope());
1✔
187
        if (rule.getHidden().equals(Boolean.TRUE))
1✔
188
          ruleChecker.setHidden();
1✔
189
        if (rule.getMandatory().equals(Boolean.TRUE))
1✔
190
          ruleChecker.setMandatory();
×
191
        if (rule.getDebug().equals(Boolean.TRUE)) {
1✔
192
          ruleChecker.setDebug();
1✔
193
          if (ruleChecker instanceof LogicalChecker) {
1✔
194
            for (RuleChecker child : ((LogicalChecker) ruleChecker).getCheckers()) {
1✔
195
              child.setDebug();
1✔
196
            }
1✔
197
          }
198
          // if (ruleChecker instanceof DependencyChecker)
199
          //   debuggables.addAll(((DependencyChecker) ruleChecker).getDependencies());
200
        }
201
        if (StringUtils.isNotBlank(rule.getValuePath()))
1✔
202
          ruleChecker.setValuePath(rule.getValuePath());
1✔
203
      }
1✔
204
    }
205

206
    /*
207
    if (!debuggables.isEmpty()) {
208
      for (RuleChecker ruleChecker : ruleCheckers) {
209
        if (debuggables.contains(ruleChecker.getId()))
210
          ruleChecker.setDebug();
211
      }
212
    }
213
     */
214

215
    return ruleCheckers;
1✔
216
  }
217

218
  private static List<RuleChecker> getChildRuleCheckers(Schema schema, DataElement dataElement, List<Rule> rules, String id) {
219
    List<RuleChecker> childRuleCheckers = new ArrayList<>();
1✔
220
    for (Rule childRule : rules) {
1✔
221
      if (StringUtils.isBlank(childRule.getId()))
1✔
222
        childRule.setId(id);
1✔
223
      List<RuleChecker> localChildRuleCheckers = processRule(schema, dataElement, childRule);
1✔
224
      if (!localChildRuleCheckers.isEmpty())
1✔
225
        childRuleCheckers.addAll(localChildRuleCheckers);
1✔
226
    }
1✔
227
    return childRuleCheckers;
1✔
228
  }
229

230
  private static void pair(Schema schema,
231
                           List<RuleChecker> ruleCheckers,
232
                           DataElement dataElement,
233
                           String fieldReference,
234
                           String type) {
235
    DataElement field2 = schema.getPathByLabel(fieldReference);
1✔
236
    if (field2 != null) {
1✔
237
      RuleChecker ruleChecker = null;
1✔
238
      if ("equals".equals(type)) {
1✔
239
        ruleChecker = new EqualityChecker(dataElement, field2);
1✔
240
      } else if ("disjoint".equals(type)) {
1✔
241
        ruleChecker = new DisjointChecker(dataElement, field2);
1✔
242
      } else if ("lessThan".equals(type)) {
×
243
        ruleChecker = new LessThanPairChecker(dataElement, field2, LessThanPairChecker.TYPE.LESS_THAN);
×
244
      } else if ("lessThanOrEquals".equals(type)) {
×
245
        ruleChecker = new LessThanPairChecker(dataElement, field2, LessThanPairChecker.TYPE.LESS_THAN_OR_EQUALS);
×
246
      }
247

248
      if (ruleChecker != null)
1✔
249
        ruleCheckers.add(ruleChecker);
1✔
250
    } else
1✔
251
      LOGGER.warning(
×
252
        String.format("invalid field reference in %s: %s", type, fieldReference));
×
253
  }
1✔
254

255
  public static void setSchemaForFields(Schema schema) {
256
    for (DataElement dataElement : schema.getPaths())
1✔
257
      dataElement.setSchema(schema);
1✔
258
  }
1✔
259

260
  /**
261
   * Get a rule object by its identifier
262
   * @param schema The schema
263
   * @param id The identifier of the rule
264
   * @return the rule object
265
   */
266
  public static Rule getRuleById(Schema schema, String id) {
267
    for (DataElement dataElement : schema.getPaths()) {
1✔
268
      for (Rule rule : dataElement.getRules()) {
1✔
269
        if (rule.getId().equals(id))
1✔
270
          return rule;
1✔
271
      }
1✔
272
    }
1✔
273
    return null;
×
274
  }
275

276
  /**
277
   * Get the direct dependencies of a rule
278
   * @param rule A rule
279
   * @return List of the rule identifiers on that the current rule depends on
280
   */
281
  public static List<String> getDependencies(Rule rule) {
282
    List<String> dependencies = new ArrayList<>();
1✔
283
    if (rule.getDependencies() != null) {
1✔
284
      dependencies.addAll(rule.getDependencies());
1✔
285
    }
286
    if (rule.getAnd() != null && !rule.getAnd().isEmpty()) {
1✔
287
      for (Rule childRule : rule.getAnd()) {
1✔
288
        dependencies.addAll(getDependencies(childRule));
1✔
289
      }
1✔
290
    }
291
    if (rule.getOr() != null && !rule.getOr().isEmpty()) {
1✔
292
      for (Rule childRule : rule.getOr()) {
1✔
293
        dependencies.addAll(getDependencies(childRule));
1✔
294
      }
1✔
295
    }
296
    if (rule.getNot() != null && !rule.getNot().isEmpty()) {
1✔
297
      for (Rule childRule : rule.getNot()) {
×
298
        dependencies.addAll(getDependencies(childRule));
×
299
      }
×
300
    }
301
    return dependencies;
1✔
302
  }
303

304
  /**
305
   * Returns all dependencies
306
   * @param schema The schema
307
   * @param rule A rule in the schema
308
   * @return List of the rule identifiers on that the current rule directly or independently depends on
309
   */
310
  public static List<String> getAllDependencies(Schema schema, Rule rule) {
311
    Map<String, Boolean> dependenciesMap = new LinkedHashMap<>();
1✔
312
    getDependencies(rule).stream().forEach(e -> dependenciesMap.put(e, false));
1✔
313
    boolean runAgain = !dependenciesMap.isEmpty();
1✔
314
    while (runAgain) {
1✔
315
      runAgain = false;
1✔
316
      for (Map.Entry<String, Boolean> entry : dependenciesMap.entrySet()) {
1✔
317
        String ruleId = entry.getKey();
1✔
318
        boolean processed = entry.getValue();
1✔
319
        if (!processed) {
1✔
320
          Rule depRule = getRuleById(schema, ruleId);
1✔
321
          List<String> additionalOnes = getDependencies(depRule);
1✔
322
          int size = dependenciesMap.size();
1✔
323
          for (String additionalOne : additionalOnes)
1✔
324
            dependenciesMap.putIfAbsent(additionalOne, false);
1✔
325
          if (dependenciesMap.size() > size)
1✔
326
            runAgain = true;
1✔
327
          dependenciesMap.put(ruleId, true);
1✔
328
        }
329
      }
1✔
330
    }
331
    return dependenciesMap.keySet().stream().collect(Collectors.toList());
1✔
332
  }
333
}
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