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

pkiraly / metadata-qa-marc / #1650

06 May 2026 09:15PM UTC coverage: 90.187% (-0.02%) from 90.202%
#1650

push

pkiraly
Reporting deprecated terms in MARC vocabularies #764: use in validation

20 of 22 new or added lines in 3 files covered. (90.91%)

6 existing lines in 1 file now uncovered.

36754 of 40753 relevant lines covered (90.19%)

0.9 hits per line

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

88.51
/src/main/java/de/gwdg/metadataqa/marc/analysis/validator/SubfieldValidator.java
1
package de.gwdg.metadataqa.marc.analysis.validator;
2

3
import de.gwdg.metadataqa.marc.MarcSubfield;
4
import de.gwdg.metadataqa.marc.definition.ValidatorResponse;
5
import de.gwdg.metadataqa.marc.definition.general.parser.ParserException;
6
import de.gwdg.metadataqa.marc.definition.general.parser.SubfieldContentParser;
7
import de.gwdg.metadataqa.marc.definition.structure.ControlfieldPositionDefinition;
8
import de.gwdg.metadataqa.marc.definition.structure.DataFieldDefinition;
9
import de.gwdg.metadataqa.marc.definition.structure.SubfieldDefinition;
10
import de.gwdg.metadataqa.marc.model.validation.ErrorsCollector;
11
import de.gwdg.metadataqa.marc.model.validation.ValidationErrorType;
12

13
import java.util.ArrayList;
14

15
import static de.gwdg.metadataqa.marc.model.validation.ValidationErrorType.SUBFIELD_NULL_CODE;
16
import static de.gwdg.metadataqa.marc.model.validation.ValidationErrorType.SUBFIELD_UNDEFINED;
17
import static de.gwdg.metadataqa.marc.model.validation.ValidationErrorType.SUBFIELD_UNPARSABLE_CONTENT;
18

19
public class SubfieldValidator extends AbstractValidator {
20

21
  private ErrorsCollector errors;
22
  private SubfieldDefinition definition;
23
  private DataFieldDefinition fieldDefinition;
24
  private MarcSubfield subfield;
25

26
  public SubfieldValidator() {
27
    super(new ValidatorConfiguration());
1✔
28
  }
1✔
29

30
  public SubfieldValidator(ValidatorConfiguration configuration) {
31
    super(configuration);
1✔
32
  }
1✔
33

34
  public boolean validate(MarcSubfield subfield) {
35
    this.subfield = subfield;
1✔
36

37
    errors = new ErrorsCollector();
1✔
38
    validationErrors = new ArrayList<>();
1✔
39
    definition = subfield.getDefinition();
1✔
40
    fieldDefinition = subfield.getField().getDefinition();
1✔
41

42
    if (definition == null) {
1✔
43
      addError(fieldDefinition.getExtendedTag(), SUBFIELD_UNDEFINED, subfield.getCode());
1✔
44
      validationErrors.addAll(errors.getErrors());
1✔
45
      return false;
1✔
46
    }
47

48
    if (subfield.getCode() == null) {
1✔
49
      addError(subfield.getField().getDefinition().getTag(), SUBFIELD_NULL_CODE, subfield.getCode());
×
50
      validationErrors.addAll(errors.getErrors());
×
51
      return false;
×
52
    }
53

54
    if (definition.isDisallowedIn(configuration.getMarcVersion())) {
1✔
55
      addError(subfield.getField().getDefinition().getTag(), SUBFIELD_UNDEFINED, subfield.getCode());
1✔
56
      validationErrors.addAll(errors.getErrors());
1✔
57
      return false;
1✔
58
    }
59

60
    if (definition.hasValidator()) {
1✔
61
      validateWithValidator();
1✔
62
    } else if (definition.hasContentParser()) {
1✔
63
      validateWithParser();
1✔
64
    } else if (definition.getCodes() != null
1✔
65
               && !definition.getCodes().isEmpty()
1✔
66
               && definition.getCode(subfield.getValue()) == null) {
1✔
67
      // If a subfield has a list of codes defined, and the value is not in the codelist, then it is invalid
68
      createCodeError(subfield, ValidationErrorType.SUBFIELD_INVALID_VALUE);
1✔
69
    } else if (definition.getCodeList() != null) {
1✔
70
      if (!definition.getCodeList().isValid(subfield.getValue())) {
1✔
71
        createCodeError(subfield, ValidationErrorType.SUBFIELD_INVALID_VALUE);
1✔
72
      }
73
      if (definition.getCodeList().isDeprecated(subfield.getValue())) {
1✔
NEW
74
        createCodeError(subfield, ValidationErrorType.SUBFIELD_DEPRECATED_VALUE);
×
75
      }
76
      // isValid = false;
77
      /**/
78
    } else if (definition.hasPositions()) {
1✔
79
      validatePositions();
1✔
80
    }
81

82
    validationErrors.addAll(errors.getErrors());
1✔
83
    return errors.isEmpty();
1✔
84
  }
85

86
  private void createCodeError(MarcSubfield subfield, ValidationErrorType errorType) {
87
    String message = subfield.getValue();
1✔
88
    String referencePath = subfield.getReferencePath();
1✔
89
    if (referencePath != null) {
1✔
NEW
90
      message += String.format(" (the field is embedded in %s)", referencePath);
×
91
    }
92
    String path = (referencePath == null
1✔
93
      ? definition.getPath()
1✔
94
      : referencePath + "->" + definition.getPath());
1✔
95
    addError(path, errorType, message);
1✔
96
  }
1✔
97

98
  private void validatePositions() {
99
    for (ControlfieldPositionDefinition positionDefinition : definition.getPositions()) {
1✔
100
      validatePosition(positionDefinition);
1✔
101
    }
1✔
102
  }
1✔
103

104
  private void validatePosition(ControlfieldPositionDefinition positionDefinition) {
105
    String subfieldValue = subfield.getValue();
1✔
106
    // If the subfield value is shorter than the position definition, then it is invalid
107
    if (subfieldValue.length() < positionDefinition.getPositionEnd()) {
1✔
108
      String path = String.format("%s/%s", definition.getPath(), positionDefinition.formatPositon());
×
109
      String errorMessage = String.format("invalid code for '%s': '%s' position %s out of range",
×
110
          positionDefinition.getLabel(), subfieldValue, positionDefinition.formatPositon());
×
111

112
      addError(path, ValidationErrorType.SUBFIELD_INVALID_VALUE, errorMessage);
×
113
      return;
×
114
    }
115
    String positionValue = subfield.getValue().substring(positionDefinition.getPositionStart(), positionDefinition.getPositionEnd());
1✔
116
    boolean isPositionDefinitionValid = positionDefinition.validate(positionValue);
1✔
117
    if (isPositionDefinitionValid) {
1✔
118
      return;
1✔
119
    }
120

121
    String path = String.format("%s/%s", definition.getPath(), positionDefinition.formatPositon());
1✔
122
    String errorMessage = String.format("invalid code for '%s': '%s' at position %s in '%s'",
1✔
123
        positionDefinition.getLabel(), positionValue, positionDefinition.formatPositon(), subfield.getValue());
1✔
124

125
    addError(path, ValidationErrorType.SUBFIELD_INVALID_VALUE, errorMessage);
1✔
126
  }
1✔
127

128
  /**
129
   * Validates a subfield by the given subfield validator which is assigned to the subfield definition in code.
130
   * @return True if the subfield is valid, false otherwise.
131
   */
132
  private boolean validateWithValidator() {
133
    de.gwdg.metadataqa.marc.definition.general.validator.SubfieldValidator validator = definition.getValidator();
1✔
134
    ValidatorResponse response = validator.isValid(subfield);
1✔
135
    if (!response.isValid()) {
1✔
136
      errors.addAll(response.getValidationErrors());
1✔
137
    }
138
    return response.isValid();
1✔
139
  }
140

141
  /**
142
   * Uses a similar approach as the subfield validator in the subfield definition trying to parse the subfield content.
143
   * @return True if the subfield is valid, false otherwise.
144
   */
145
  private boolean validateWithParser() {
146
    var isValid = true;
1✔
147
    SubfieldContentParser parser = definition.getContentParser();
1✔
148
    try {
149
      parser.parse(subfield.getValue());
1✔
150
    } catch (ParserException e) {
1✔
151
      addError(SUBFIELD_UNPARSABLE_CONTENT, e.getMessage());
1✔
152
      isValid = false;
1✔
153
    }
1✔
154
    return isValid;
1✔
155
  }
156

157
  private void addError(ValidationErrorType type, String message) {
158
    addError(definition.getPath(), type, message);
1✔
159
  }
1✔
160

161
  private void addError(String path, ValidationErrorType type, String message) {
162
    if (!isIgnorableType(type)) {
1✔
163
      String id = subfield.getMarcRecord() == null ? null : subfield.getMarcRecord().getId();
1✔
164
      String url = fieldDefinition.getDescriptionUrl();
1✔
165
      errors.add(id, path, type, message, url);
1✔
166
    }
167
  }
1✔
168
}
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