forked from paulhiggs/dvb-i-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathErrorList.js
240 lines (223 loc) · 7.76 KB
/
ErrorList.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/**
* ErrorList.js
*
* Manages errors and warnings for the application
*
*/
import { datatypeIs } from "./phlib/phlib.js";
export const ERROR = "(E)",
WARNING = "(W)",
INFORMATION = "(I)",
APPLICATION = "(A)";
const MAX_FRAGMENT_LINES = 6; // the maximum number of lines in an element to display when that element has an error
var nthIndexOf = (str, pat, n) => {
let i = -1;
while (n-- && i++ < str.length) {
i = str.indexOf(pat, i);
if (i < 0) break;
}
return i;
};
export default class ErrorList {
#numCountsE; // keep these counters as arrays constructed by
#numCountsW; // direct insertion do not maintain them
#numCountsI;
constructor() {
this.countsErr = [];
this.#numCountsE = 0;
this.countsWarn = [];
this.#numCountsW = 0;
this.countsInfo = [];
this.#numCountsI = 0;
this.errors = [];
this.warnings = [];
this.informationals = [];
this.markupXML = [];
this.errorDescriptions = [];
}
/**
* loads the text that can be marked up with any validation errors/warnings etc
* @param {string} doc The document received for validation
*/
loadDocument(doc) {
this.markupXML = doc.split("\n").map((str, index) => ({ value: str, ix: index + 1 }));
}
/**
* attach an error message to a particular line in the received text
* @param {*} type the type of error message, e.g. APPLICATION, ERROR, WARNING...
* @param {*} code the short code of the error
* @param {*} message the verbose error message
* @param {*} lineNo the line number in the received text to attach the error to
*/
/* private method */ #setError(type, code, message, lineNo) {
let found = this.markupXML.find((line) => line.ix == lineNo);
if (found) {
if (!found.validationErrors) found.validationErrors = [];
found.validationErrors.push(`${type} ${code}: ${message}`);
}
}
/* private method*/ #increment(key) {
if (this.countsErr[key] === undefined) this.set(key);
else this.countsErr[key]++;
}
set(key, value = 1) {
this.countsErr[key] = value;
this.#numCountsE++;
}
/* private method*/ #incrementW(key) {
if (this.countsWarn[key] === undefined) this.setW(key);
else this.countsWarn[key]++;
}
setW(key, value = 1) {
this.countsWarn[key] = value;
this.#numCountsW++;
}
/* private method*/ #incrementI(key) {
if (this.countsInfo[key] === undefined) this.setI(key);
else this.countsInfo[key]++;
}
setI(key, value = 1) {
this.countsInfo[key] = value;
this.#numCountsI++;
}
/* private method */ #prettyPrint(node) {
// clean up and redo formatting
let tmp = node.toString({ declaration: false, format: true });
let maxLen = nthIndexOf(tmp, "\n", MAX_FRAGMENT_LINES);
return maxLen == -1 ? tmp : `${tmp.slice(0, maxLen)}\n....\n`;
}
/* private method */ #insertErrorData(type, key, err) {
switch (type) {
case ERROR:
this.errors.push(err);
if (key) this.#increment(key);
break;
case APPLICATION:
this.errors.push(err);
this.#increment("application process error");
break;
case WARNING:
this.warnings.push(err);
if (key) this.#incrementW(key);
break;
case INFORMATION:
this.informationals.push(err);
if (key) this.#incrementI(key);
break;
}
}
/**
* log an error from the service list or program metadata analysis
*
* @param {integer} e.type (optional) ERROR(default) or WARNING
* @param {string} e.code Error code
* @param {string} e.message The error message
* @param {string} e.key (optional)The category of the message
* @param {string or libxmljs2:Node} e.fragment (optional) The XML fragment (or node in the XML document) triggering the error
* @param {integer} e.line (optional) the line number of the element in the XML document that triggered the error
*/
addError(e) {
let _INVALID_CALL = "invalid addError call",
argsOK = true;
if (!Object.prototype.hasOwnProperty.call(e, "type")) e.type = ERROR;
if (!Object.prototype.hasOwnProperty.call(e, "reportInTable")) e.reportInTable = true;
if (![ERROR, WARNING, INFORMATION, APPLICATION].includes(e.type)) {
this.errors.push({ code: "ERR000", message: `addError() called with invalid type property (${e.type})` });
this.#increment(_INVALID_CALL);
argsOK = false;
}
if (!e.code) {
this.errors.push({ code: "ERR001", message: "addError() called without code property" });
this.#increment(_INVALID_CALL);
e.code = "ERR001";
argsOK = false;
}
if (!e.message) {
this.errors.push({ code: "ERR002", message: "addError() called without message property" });
this.#increment(_INVALID_CALL);
e.message = "no error message";
argsOK = false;
}
if (!argsOK) return;
if (e.multiElementError) {
/**
* this type of error involves multiple elements, for example when the cardinality exceeds a specified limit.
* each element of multiElementError is an element that is marked up, but the error message is
* only reported once in the error list
*/
this.#insertErrorData(e.type, e.key, { code: e.code, message: e.message });
e.multiElementError.forEach((fragment) => {
if (fragment && !datatypeIs(fragment, "string")) this.#setError(e.type, e.code, e.message, fragment.line());
});
} else if (e.fragments) {
// note that the line of the error is derived from the fragment -- e.line is only used with the fragment is already a string
e.fragments.forEach((fragment) => {
let newError = { code: e.code, message: e.message };
if (fragment) {
newError.element = datatypeIs(fragment, "string") ? fragment : this.#prettyPrint(fragment);
if (datatypeIs(fragment, "string")) {
if (Object.prototype.hasOwnProperty.call(e, "line")) {
this.#setError(e.type, e.code, e.message, e.line);
newError.line = e.line - 2;
}
} else {
this.#setError(e.type, e.code, e.message, fragment.line());
newError.line = fragment.line() - 2;
}
if (e.reportInTable) this.#insertErrorData(e.type, e.key, newError);
}
});
} else if (e.fragment) {
// note that the line of the error is derived from the fragment -- e.line is only used with the fragment is already a string
let newError = { code: e.code, message: e.message, element: datatypeIs(e.fragment, "string") ? e.fragment : this.#prettyPrint(e.fragment) };
if (datatypeIs(e.fragment, "string")) {
if (Object.prototype.hasOwnProperty.call(e, "line")) {
this.#setError(e.type, e.code, e.message, e.line);
newError.line = e.line - 2;
}
} else {
this.#setError(e.type, e.code, e.message, e.fragment.line());
newError.line = e.fragment.line() - 2;
}
if (e.reportInTable) this.#insertErrorData(e.type, e.key, newError);
} else {
let newError = { code: e.code, message: e.message, element: null };
if (e.line) {
this.#setError(e.type, e.code, e.message, e.line);
newError.line = e.line - 2;
}
if (e.reportInTable) this.#insertErrorData(e.type, e.key, newError);
}
}
numErrors() {
return this.errors.length;
}
numWarnings() {
return this.warnings.length;
}
numInformationals() {
return this.informationals.length;
}
numCountsErr() {
return this.#numCountsE;
}
numCountsWarn() {
return this.#numCountsW;
}
numCountsInfo() {
return this.#numCountsI;
}
/**
* built up descriptive information on the errors found in the analysis
*
* @param {string} e.code Error code, should be the same as @e.code passed to addError
* @param {string} e.description A long form description of the stated error code
*/
errorDescription(e) {
if (!e.code || !e.description) return;
let found = this.errorDescriptions.find((element) => e.code == element.code);
if (found) {
if (found.description.indexOf(e.description) == -1) found.description += `\n${e.description}`;
} else this.errorDescriptions.push(e);
}
}