1 /*---------------------------------------------------------------------------------------------
2 * Copyright (c) Microsoft Corporation. All rights reserved.
3 * Licensed under the MIT License. See License.txt in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5 var __extends = (this && this.__extends) || (function () {
6 var extendStatics = function (d, b) {
7 extendStatics = Object.setPrototypeOf ||
8 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
9 function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
10 return extendStatics(d, b);
12 return function (d, b) {
13 if (typeof b !== "function" && b !== null)
14 throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
16 function __() { this.constructor = d; }
17 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
20 import * as Json from 'jsonc-parser';
21 import { isNumber, equals, isBoolean, isString, isDefined } from '../utils/objects';
22 import { extendedRegExp } from '../utils/strings';
23 import { ErrorCode, Diagnostic, DiagnosticSeverity, Range } from '../jsonLanguageTypes';
24 import * as nls from 'vscode-nls';
25 var localize = nls.loadMessageBundle();
27 'color-hex': { errorMessage: localize('colorHexFormatWarning', 'Invalid color format. Use #RGB, #RGBA, #RRGGBB or #RRGGBBAA.'), pattern: /^#([0-9A-Fa-f]{3,4}|([0-9A-Fa-f]{2}){3,4})$/ },
28 'date-time': { errorMessage: localize('dateTimeFormatWarning', 'String is not a RFC3339 date-time.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
29 'date': { errorMessage: localize('dateFormatWarning', 'String is not a RFC3339 date.'), pattern: /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/i },
30 'time': { errorMessage: localize('timeFormatWarning', 'String is not a RFC3339 time.'), pattern: /^([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)([01][0-9]|2[0-3]):([0-5][0-9]))$/i },
31 'email': { errorMessage: localize('emailFormatWarning', 'String is not an e-mail address.'), pattern: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ }
33 var ASTNodeImpl = /** @class */ (function () {
34 function ASTNodeImpl(parent, offset, length) {
35 if (length === void 0) { length = 0; }
40 Object.defineProperty(ASTNodeImpl.prototype, "children", {
47 ASTNodeImpl.prototype.toString = function () {
48 return 'type: ' + this.type + ' (' + this.offset + '/' + this.length + ')' + (this.parent ? ' parent: {' + this.parent.toString() + '}' : '');
52 export { ASTNodeImpl };
53 var NullASTNodeImpl = /** @class */ (function (_super) {
54 __extends(NullASTNodeImpl, _super);
55 function NullASTNodeImpl(parent, offset) {
56 var _this = _super.call(this, parent, offset) || this;
61 return NullASTNodeImpl;
63 export { NullASTNodeImpl };
64 var BooleanASTNodeImpl = /** @class */ (function (_super) {
65 __extends(BooleanASTNodeImpl, _super);
66 function BooleanASTNodeImpl(parent, boolValue, offset) {
67 var _this = _super.call(this, parent, offset) || this;
68 _this.type = 'boolean';
69 _this.value = boolValue;
72 return BooleanASTNodeImpl;
74 export { BooleanASTNodeImpl };
75 var ArrayASTNodeImpl = /** @class */ (function (_super) {
76 __extends(ArrayASTNodeImpl, _super);
77 function ArrayASTNodeImpl(parent, offset) {
78 var _this = _super.call(this, parent, offset) || this;
83 Object.defineProperty(ArrayASTNodeImpl.prototype, "children", {
90 return ArrayASTNodeImpl;
92 export { ArrayASTNodeImpl };
93 var NumberASTNodeImpl = /** @class */ (function (_super) {
94 __extends(NumberASTNodeImpl, _super);
95 function NumberASTNodeImpl(parent, offset) {
96 var _this = _super.call(this, parent, offset) || this;
97 _this.type = 'number';
98 _this.isInteger = true;
99 _this.value = Number.NaN;
102 return NumberASTNodeImpl;
104 export { NumberASTNodeImpl };
105 var StringASTNodeImpl = /** @class */ (function (_super) {
106 __extends(StringASTNodeImpl, _super);
107 function StringASTNodeImpl(parent, offset, length) {
108 var _this = _super.call(this, parent, offset, length) || this;
109 _this.type = 'string';
113 return StringASTNodeImpl;
115 export { StringASTNodeImpl };
116 var PropertyASTNodeImpl = /** @class */ (function (_super) {
117 __extends(PropertyASTNodeImpl, _super);
118 function PropertyASTNodeImpl(parent, offset, keyNode) {
119 var _this = _super.call(this, parent, offset) || this;
120 _this.type = 'property';
121 _this.colonOffset = -1;
122 _this.keyNode = keyNode;
125 Object.defineProperty(PropertyASTNodeImpl.prototype, "children", {
127 return this.valueNode ? [this.keyNode, this.valueNode] : [this.keyNode];
132 return PropertyASTNodeImpl;
134 export { PropertyASTNodeImpl };
135 var ObjectASTNodeImpl = /** @class */ (function (_super) {
136 __extends(ObjectASTNodeImpl, _super);
137 function ObjectASTNodeImpl(parent, offset) {
138 var _this = _super.call(this, parent, offset) || this;
139 _this.type = 'object';
140 _this.properties = [];
143 Object.defineProperty(ObjectASTNodeImpl.prototype, "children", {
145 return this.properties;
150 return ObjectASTNodeImpl;
152 export { ObjectASTNodeImpl };
153 export function asSchema(schema) {
154 if (isBoolean(schema)) {
155 return schema ? {} : { "not": {} };
159 export var EnumMatch;
160 (function (EnumMatch) {
161 EnumMatch[EnumMatch["Key"] = 0] = "Key";
162 EnumMatch[EnumMatch["Enum"] = 1] = "Enum";
163 })(EnumMatch || (EnumMatch = {}));
164 var SchemaCollector = /** @class */ (function () {
165 function SchemaCollector(focusOffset, exclude) {
166 if (focusOffset === void 0) { focusOffset = -1; }
167 this.focusOffset = focusOffset;
168 this.exclude = exclude;
171 SchemaCollector.prototype.add = function (schema) {
172 this.schemas.push(schema);
174 SchemaCollector.prototype.merge = function (other) {
175 Array.prototype.push.apply(this.schemas, other.schemas);
177 SchemaCollector.prototype.include = function (node) {
178 return (this.focusOffset === -1 || contains(node, this.focusOffset)) && (node !== this.exclude);
180 SchemaCollector.prototype.newSub = function () {
181 return new SchemaCollector(-1, this.exclude);
183 return SchemaCollector;
185 var NoOpSchemaCollector = /** @class */ (function () {
186 function NoOpSchemaCollector() {
188 Object.defineProperty(NoOpSchemaCollector.prototype, "schemas", {
189 get: function () { return []; },
193 NoOpSchemaCollector.prototype.add = function (schema) { };
194 NoOpSchemaCollector.prototype.merge = function (other) { };
195 NoOpSchemaCollector.prototype.include = function (node) { return true; };
196 NoOpSchemaCollector.prototype.newSub = function () { return this; };
197 NoOpSchemaCollector.instance = new NoOpSchemaCollector();
198 return NoOpSchemaCollector;
200 var ValidationResult = /** @class */ (function () {
201 function ValidationResult() {
203 this.propertiesMatches = 0;
204 this.propertiesValueMatches = 0;
205 this.primaryValueMatches = 0;
206 this.enumValueMatch = false;
207 this.enumValues = undefined;
209 ValidationResult.prototype.hasProblems = function () {
210 return !!this.problems.length;
212 ValidationResult.prototype.mergeAll = function (validationResults) {
213 for (var _i = 0, validationResults_1 = validationResults; _i < validationResults_1.length; _i++) {
214 var validationResult = validationResults_1[_i];
215 this.merge(validationResult);
218 ValidationResult.prototype.merge = function (validationResult) {
219 this.problems = this.problems.concat(validationResult.problems);
221 ValidationResult.prototype.mergeEnumValues = function (validationResult) {
222 if (!this.enumValueMatch && !validationResult.enumValueMatch && this.enumValues && validationResult.enumValues) {
223 this.enumValues = this.enumValues.concat(validationResult.enumValues);
224 for (var _i = 0, _a = this.problems; _i < _a.length; _i++) {
226 if (error.code === ErrorCode.EnumValueMismatch) {
227 error.message = localize('enumWarning', 'Value is not accepted. Valid values: {0}.', this.enumValues.map(function (v) { return JSON.stringify(v); }).join(', '));
232 ValidationResult.prototype.mergePropertyMatch = function (propertyValidationResult) {
233 this.merge(propertyValidationResult);
234 this.propertiesMatches++;
235 if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasProblems() && propertyValidationResult.propertiesMatches) {
236 this.propertiesValueMatches++;
238 if (propertyValidationResult.enumValueMatch && propertyValidationResult.enumValues && propertyValidationResult.enumValues.length === 1) {
239 this.primaryValueMatches++;
242 ValidationResult.prototype.compare = function (other) {
243 var hasProblems = this.hasProblems();
244 if (hasProblems !== other.hasProblems()) {
245 return hasProblems ? -1 : 1;
247 if (this.enumValueMatch !== other.enumValueMatch) {
248 return other.enumValueMatch ? -1 : 1;
250 if (this.primaryValueMatches !== other.primaryValueMatches) {
251 return this.primaryValueMatches - other.primaryValueMatches;
253 if (this.propertiesValueMatches !== other.propertiesValueMatches) {
254 return this.propertiesValueMatches - other.propertiesValueMatches;
256 return this.propertiesMatches - other.propertiesMatches;
258 return ValidationResult;
260 export { ValidationResult };
261 export function newJSONDocument(root, diagnostics) {
262 if (diagnostics === void 0) { diagnostics = []; }
263 return new JSONDocument(root, diagnostics, []);
265 export function getNodeValue(node) {
266 return Json.getNodeValue(node);
268 export function getNodePath(node) {
269 return Json.getNodePath(node);
271 export function contains(node, offset, includeRightBound) {
272 if (includeRightBound === void 0) { includeRightBound = false; }
273 return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length);
275 var JSONDocument = /** @class */ (function () {
276 function JSONDocument(root, syntaxErrors, comments) {
277 if (syntaxErrors === void 0) { syntaxErrors = []; }
278 if (comments === void 0) { comments = []; }
280 this.syntaxErrors = syntaxErrors;
281 this.comments = comments;
283 JSONDocument.prototype.getNodeFromOffset = function (offset, includeRightBound) {
284 if (includeRightBound === void 0) { includeRightBound = false; }
286 return Json.findNodeAtOffset(this.root, offset, includeRightBound);
290 JSONDocument.prototype.visit = function (visitor) {
292 var doVisit_1 = function (node) {
293 var ctn = visitor(node);
294 var children = node.children;
295 if (Array.isArray(children)) {
296 for (var i = 0; i < children.length && ctn; i++) {
297 ctn = doVisit_1(children[i]);
302 doVisit_1(this.root);
305 JSONDocument.prototype.validate = function (textDocument, schema, severity) {
306 if (severity === void 0) { severity = DiagnosticSeverity.Warning; }
307 if (this.root && schema) {
308 var validationResult = new ValidationResult();
309 validate(this.root, schema, validationResult, NoOpSchemaCollector.instance);
310 return validationResult.problems.map(function (p) {
312 var range = Range.create(textDocument.positionAt(p.location.offset), textDocument.positionAt(p.location.offset + p.location.length));
313 return Diagnostic.create(range, p.message, (_a = p.severity) !== null && _a !== void 0 ? _a : severity, p.code);
318 JSONDocument.prototype.getMatchingSchemas = function (schema, focusOffset, exclude) {
319 if (focusOffset === void 0) { focusOffset = -1; }
320 var matchingSchemas = new SchemaCollector(focusOffset, exclude);
321 if (this.root && schema) {
322 validate(this.root, schema, new ValidationResult(), matchingSchemas);
324 return matchingSchemas.schemas;
328 export { JSONDocument };
329 function validate(n, schema, validationResult, matchingSchemas) {
330 if (!n || !matchingSchemas.include(n)) {
336 _validateObjectNode(node, schema, validationResult, matchingSchemas);
339 _validateArrayNode(node, schema, validationResult, matchingSchemas);
342 _validateStringNode(node, schema, validationResult, matchingSchemas);
345 _validateNumberNode(node, schema, validationResult, matchingSchemas);
348 return validate(node.valueNode, schema, validationResult, matchingSchemas);
351 matchingSchemas.add({ node: node, schema: schema });
352 function _validateNode() {
353 function matchesType(type) {
354 return node.type === type || (type === 'integer' && node.type === 'number' && node.isInteger);
356 if (Array.isArray(schema.type)) {
357 if (!schema.type.some(matchesType)) {
358 validationResult.problems.push({
359 location: { offset: node.offset, length: node.length },
360 message: schema.errorMessage || localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}.', schema.type.join(', '))
364 else if (schema.type) {
365 if (!matchesType(schema.type)) {
366 validationResult.problems.push({
367 location: { offset: node.offset, length: node.length },
368 message: schema.errorMessage || localize('typeMismatchWarning', 'Incorrect type. Expected "{0}".', schema.type)
372 if (Array.isArray(schema.allOf)) {
373 for (var _i = 0, _a = schema.allOf; _i < _a.length; _i++) {
374 var subSchemaRef = _a[_i];
375 validate(node, asSchema(subSchemaRef), validationResult, matchingSchemas);
378 var notSchema = asSchema(schema.not);
380 var subValidationResult = new ValidationResult();
381 var subMatchingSchemas = matchingSchemas.newSub();
382 validate(node, notSchema, subValidationResult, subMatchingSchemas);
383 if (!subValidationResult.hasProblems()) {
384 validationResult.problems.push({
385 location: { offset: node.offset, length: node.length },
386 message: localize('notSchemaWarning', "Matches a schema that is not allowed.")
389 for (var _b = 0, _c = subMatchingSchemas.schemas; _b < _c.length; _b++) {
391 ms.inverted = !ms.inverted;
392 matchingSchemas.add(ms);
395 var testAlternatives = function (alternatives, maxOneMatch) {
397 // remember the best match that is used for error messages
398 var bestMatch = undefined;
399 for (var _i = 0, alternatives_1 = alternatives; _i < alternatives_1.length; _i++) {
400 var subSchemaRef = alternatives_1[_i];
401 var subSchema = asSchema(subSchemaRef);
402 var subValidationResult = new ValidationResult();
403 var subMatchingSchemas = matchingSchemas.newSub();
404 validate(node, subSchema, subValidationResult, subMatchingSchemas);
405 if (!subValidationResult.hasProblems()) {
406 matches.push(subSchema);
409 bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
412 if (!maxOneMatch && !subValidationResult.hasProblems() && !bestMatch.validationResult.hasProblems()) {
413 // no errors, both are equally good matches
414 bestMatch.matchingSchemas.merge(subMatchingSchemas);
415 bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches;
416 bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
419 var compareResult = subValidationResult.compare(bestMatch.validationResult);
420 if (compareResult > 0) {
421 // our node is the best matching so far
422 bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
424 else if (compareResult === 0) {
425 // there's already a best matching but we are as good
426 bestMatch.matchingSchemas.merge(subMatchingSchemas);
427 bestMatch.validationResult.mergeEnumValues(subValidationResult);
432 if (matches.length > 1 && maxOneMatch) {
433 validationResult.problems.push({
434 location: { offset: node.offset, length: 1 },
435 message: localize('oneOfWarning', "Matches multiple schemas when only one must validate.")
439 validationResult.merge(bestMatch.validationResult);
440 validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches;
441 validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches;
442 matchingSchemas.merge(bestMatch.matchingSchemas);
444 return matches.length;
446 if (Array.isArray(schema.anyOf)) {
447 testAlternatives(schema.anyOf, false);
449 if (Array.isArray(schema.oneOf)) {
450 testAlternatives(schema.oneOf, true);
452 var testBranch = function (schema) {
453 var subValidationResult = new ValidationResult();
454 var subMatchingSchemas = matchingSchemas.newSub();
455 validate(node, asSchema(schema), subValidationResult, subMatchingSchemas);
456 validationResult.merge(subValidationResult);
457 validationResult.propertiesMatches += subValidationResult.propertiesMatches;
458 validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
459 matchingSchemas.merge(subMatchingSchemas);
461 var testCondition = function (ifSchema, thenSchema, elseSchema) {
462 var subSchema = asSchema(ifSchema);
463 var subValidationResult = new ValidationResult();
464 var subMatchingSchemas = matchingSchemas.newSub();
465 validate(node, subSchema, subValidationResult, subMatchingSchemas);
466 matchingSchemas.merge(subMatchingSchemas);
467 if (!subValidationResult.hasProblems()) {
469 testBranch(thenSchema);
472 else if (elseSchema) {
473 testBranch(elseSchema);
476 var ifSchema = asSchema(schema.if);
478 testCondition(ifSchema, asSchema(schema.then), asSchema(schema.else));
480 if (Array.isArray(schema.enum)) {
481 var val = getNodeValue(node);
482 var enumValueMatch = false;
483 for (var _d = 0, _e = schema.enum; _d < _e.length; _d++) {
485 if (equals(val, e)) {
486 enumValueMatch = true;
490 validationResult.enumValues = schema.enum;
491 validationResult.enumValueMatch = enumValueMatch;
492 if (!enumValueMatch) {
493 validationResult.problems.push({
494 location: { offset: node.offset, length: node.length },
495 code: ErrorCode.EnumValueMismatch,
496 message: schema.errorMessage || localize('enumWarning', 'Value is not accepted. Valid values: {0}.', schema.enum.map(function (v) { return JSON.stringify(v); }).join(', '))
500 if (isDefined(schema.const)) {
501 var val = getNodeValue(node);
502 if (!equals(val, schema.const)) {
503 validationResult.problems.push({
504 location: { offset: node.offset, length: node.length },
505 code: ErrorCode.EnumValueMismatch,
506 message: schema.errorMessage || localize('constWarning', 'Value must be {0}.', JSON.stringify(schema.const))
508 validationResult.enumValueMatch = false;
511 validationResult.enumValueMatch = true;
513 validationResult.enumValues = [schema.const];
515 if (schema.deprecationMessage && node.parent) {
516 validationResult.problems.push({
517 location: { offset: node.parent.offset, length: node.parent.length },
518 severity: DiagnosticSeverity.Warning,
519 message: schema.deprecationMessage,
520 code: ErrorCode.Deprecated
524 function _validateNumberNode(node, schema, validationResult, matchingSchemas) {
525 var val = node.value;
526 function normalizeFloats(float) {
528 var parts = /^(-?\d+)(?:\.(\d+))?(?:e([-+]\d+))?$/.exec(float.toString());
530 value: Number(parts[1] + (parts[2] || '')),
531 multiplier: (((_a = parts[2]) === null || _a === void 0 ? void 0 : _a.length) || 0) - (parseInt(parts[3]) || 0)
535 if (isNumber(schema.multipleOf)) {
537 if (Number.isInteger(schema.multipleOf)) {
538 remainder = val % schema.multipleOf;
541 var normMultipleOf = normalizeFloats(schema.multipleOf);
542 var normValue = normalizeFloats(val);
543 if (normMultipleOf && normValue) {
544 var multiplier = Math.pow(10, Math.abs(normValue.multiplier - normMultipleOf.multiplier));
545 if (normValue.multiplier < normMultipleOf.multiplier) {
546 normValue.value *= multiplier;
549 normMultipleOf.value *= multiplier;
551 remainder = normValue.value % normMultipleOf.value;
554 if (remainder !== 0) {
555 validationResult.problems.push({
556 location: { offset: node.offset, length: node.length },
557 message: localize('multipleOfWarning', 'Value is not divisible by {0}.', schema.multipleOf)
561 function getExclusiveLimit(limit, exclusive) {
562 if (isNumber(exclusive)) {
565 if (isBoolean(exclusive) && exclusive) {
570 function getLimit(limit, exclusive) {
571 if (!isBoolean(exclusive) || !exclusive) {
576 var exclusiveMinimum = getExclusiveLimit(schema.minimum, schema.exclusiveMinimum);
577 if (isNumber(exclusiveMinimum) && val <= exclusiveMinimum) {
578 validationResult.problems.push({
579 location: { offset: node.offset, length: node.length },
580 message: localize('exclusiveMinimumWarning', 'Value is below the exclusive minimum of {0}.', exclusiveMinimum)
583 var exclusiveMaximum = getExclusiveLimit(schema.maximum, schema.exclusiveMaximum);
584 if (isNumber(exclusiveMaximum) && val >= exclusiveMaximum) {
585 validationResult.problems.push({
586 location: { offset: node.offset, length: node.length },
587 message: localize('exclusiveMaximumWarning', 'Value is above the exclusive maximum of {0}.', exclusiveMaximum)
590 var minimum = getLimit(schema.minimum, schema.exclusiveMinimum);
591 if (isNumber(minimum) && val < minimum) {
592 validationResult.problems.push({
593 location: { offset: node.offset, length: node.length },
594 message: localize('minimumWarning', 'Value is below the minimum of {0}.', minimum)
597 var maximum = getLimit(schema.maximum, schema.exclusiveMaximum);
598 if (isNumber(maximum) && val > maximum) {
599 validationResult.problems.push({
600 location: { offset: node.offset, length: node.length },
601 message: localize('maximumWarning', 'Value is above the maximum of {0}.', maximum)
605 function _validateStringNode(node, schema, validationResult, matchingSchemas) {
606 if (isNumber(schema.minLength) && node.value.length < schema.minLength) {
607 validationResult.problems.push({
608 location: { offset: node.offset, length: node.length },
609 message: localize('minLengthWarning', 'String is shorter than the minimum length of {0}.', schema.minLength)
612 if (isNumber(schema.maxLength) && node.value.length > schema.maxLength) {
613 validationResult.problems.push({
614 location: { offset: node.offset, length: node.length },
615 message: localize('maxLengthWarning', 'String is longer than the maximum length of {0}.', schema.maxLength)
618 if (isString(schema.pattern)) {
619 var regex = extendedRegExp(schema.pattern);
620 if (!regex.test(node.value)) {
621 validationResult.problems.push({
622 location: { offset: node.offset, length: node.length },
623 message: schema.patternErrorMessage || schema.errorMessage || localize('patternWarning', 'String does not match the pattern of "{0}".', schema.pattern)
628 switch (schema.format) {
630 case 'uri-reference':
632 var errorMessage = void 0;
634 errorMessage = localize('uriEmpty', 'URI expected.');
637 var match = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(node.value);
639 errorMessage = localize('uriMissing', 'URI is expected.');
641 else if (!match[2] && schema.format === 'uri') {
642 errorMessage = localize('uriSchemeMissing', 'URI with a scheme is expected.');
646 validationResult.problems.push({
647 location: { offset: node.offset, length: node.length },
648 message: schema.patternErrorMessage || schema.errorMessage || localize('uriFormatWarning', 'String is not a URI: {0}', errorMessage)
658 var format = formats[schema.format];
659 if (!node.value || !format.pattern.exec(node.value)) {
660 validationResult.problems.push({
661 location: { offset: node.offset, length: node.length },
662 message: schema.patternErrorMessage || schema.errorMessage || format.errorMessage
669 function _validateArrayNode(node, schema, validationResult, matchingSchemas) {
670 if (Array.isArray(schema.items)) {
671 var subSchemas = schema.items;
672 for (var index = 0; index < subSchemas.length; index++) {
673 var subSchemaRef = subSchemas[index];
674 var subSchema = asSchema(subSchemaRef);
675 var itemValidationResult = new ValidationResult();
676 var item = node.items[index];
678 validate(item, subSchema, itemValidationResult, matchingSchemas);
679 validationResult.mergePropertyMatch(itemValidationResult);
681 else if (node.items.length >= subSchemas.length) {
682 validationResult.propertiesValueMatches++;
685 if (node.items.length > subSchemas.length) {
686 if (typeof schema.additionalItems === 'object') {
687 for (var i = subSchemas.length; i < node.items.length; i++) {
688 var itemValidationResult = new ValidationResult();
689 validate(node.items[i], schema.additionalItems, itemValidationResult, matchingSchemas);
690 validationResult.mergePropertyMatch(itemValidationResult);
693 else if (schema.additionalItems === false) {
694 validationResult.problems.push({
695 location: { offset: node.offset, length: node.length },
696 message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.', subSchemas.length)
702 var itemSchema = asSchema(schema.items);
704 for (var _i = 0, _a = node.items; _i < _a.length; _i++) {
706 var itemValidationResult = new ValidationResult();
707 validate(item, itemSchema, itemValidationResult, matchingSchemas);
708 validationResult.mergePropertyMatch(itemValidationResult);
712 var containsSchema = asSchema(schema.contains);
713 if (containsSchema) {
714 var doesContain = node.items.some(function (item) {
715 var itemValidationResult = new ValidationResult();
716 validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance);
717 return !itemValidationResult.hasProblems();
720 validationResult.problems.push({
721 location: { offset: node.offset, length: node.length },
722 message: schema.errorMessage || localize('requiredItemMissingWarning', 'Array does not contain required item.')
726 if (isNumber(schema.minItems) && node.items.length < schema.minItems) {
727 validationResult.problems.push({
728 location: { offset: node.offset, length: node.length },
729 message: localize('minItemsWarning', 'Array has too few items. Expected {0} or more.', schema.minItems)
732 if (isNumber(schema.maxItems) && node.items.length > schema.maxItems) {
733 validationResult.problems.push({
734 location: { offset: node.offset, length: node.length },
735 message: localize('maxItemsWarning', 'Array has too many items. Expected {0} or fewer.', schema.maxItems)
738 if (schema.uniqueItems === true) {
739 var values_1 = getNodeValue(node);
740 var duplicates = values_1.some(function (value, index) {
741 return index !== values_1.lastIndexOf(value);
744 validationResult.problems.push({
745 location: { offset: node.offset, length: node.length },
746 message: localize('uniqueItemsWarning', 'Array has duplicate items.')
751 function _validateObjectNode(node, schema, validationResult, matchingSchemas) {
752 var seenKeys = Object.create(null);
753 var unprocessedProperties = [];
754 for (var _i = 0, _a = node.properties; _i < _a.length; _i++) {
755 var propertyNode = _a[_i];
756 var key = propertyNode.keyNode.value;
757 seenKeys[key] = propertyNode.valueNode;
758 unprocessedProperties.push(key);
760 if (Array.isArray(schema.required)) {
761 for (var _b = 0, _c = schema.required; _b < _c.length; _b++) {
762 var propertyName = _c[_b];
763 if (!seenKeys[propertyName]) {
764 var keyNode = node.parent && node.parent.type === 'property' && node.parent.keyNode;
765 var location = keyNode ? { offset: keyNode.offset, length: keyNode.length } : { offset: node.offset, length: 1 };
766 validationResult.problems.push({
768 message: localize('MissingRequiredPropWarning', 'Missing property "{0}".', propertyName)
773 var propertyProcessed = function (prop) {
774 var index = unprocessedProperties.indexOf(prop);
776 unprocessedProperties.splice(index, 1);
777 index = unprocessedProperties.indexOf(prop);
780 if (schema.properties) {
781 for (var _d = 0, _e = Object.keys(schema.properties); _d < _e.length; _d++) {
782 var propertyName = _e[_d];
783 propertyProcessed(propertyName);
784 var propertySchema = schema.properties[propertyName];
785 var child = seenKeys[propertyName];
787 if (isBoolean(propertySchema)) {
788 if (!propertySchema) {
789 var propertyNode = child.parent;
790 validationResult.problems.push({
791 location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
792 message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
796 validationResult.propertiesMatches++;
797 validationResult.propertiesValueMatches++;
801 var propertyValidationResult = new ValidationResult();
802 validate(child, propertySchema, propertyValidationResult, matchingSchemas);
803 validationResult.mergePropertyMatch(propertyValidationResult);
808 if (schema.patternProperties) {
809 for (var _f = 0, _g = Object.keys(schema.patternProperties); _f < _g.length; _f++) {
810 var propertyPattern = _g[_f];
811 var regex = extendedRegExp(propertyPattern);
812 for (var _h = 0, _j = unprocessedProperties.slice(0); _h < _j.length; _h++) {
813 var propertyName = _j[_h];
814 if (regex.test(propertyName)) {
815 propertyProcessed(propertyName);
816 var child = seenKeys[propertyName];
818 var propertySchema = schema.patternProperties[propertyPattern];
819 if (isBoolean(propertySchema)) {
820 if (!propertySchema) {
821 var propertyNode = child.parent;
822 validationResult.problems.push({
823 location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
824 message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
828 validationResult.propertiesMatches++;
829 validationResult.propertiesValueMatches++;
833 var propertyValidationResult = new ValidationResult();
834 validate(child, propertySchema, propertyValidationResult, matchingSchemas);
835 validationResult.mergePropertyMatch(propertyValidationResult);
842 if (typeof schema.additionalProperties === 'object') {
843 for (var _k = 0, unprocessedProperties_1 = unprocessedProperties; _k < unprocessedProperties_1.length; _k++) {
844 var propertyName = unprocessedProperties_1[_k];
845 var child = seenKeys[propertyName];
847 var propertyValidationResult = new ValidationResult();
848 validate(child, schema.additionalProperties, propertyValidationResult, matchingSchemas);
849 validationResult.mergePropertyMatch(propertyValidationResult);
853 else if (schema.additionalProperties === false) {
854 if (unprocessedProperties.length > 0) {
855 for (var _l = 0, unprocessedProperties_2 = unprocessedProperties; _l < unprocessedProperties_2.length; _l++) {
856 var propertyName = unprocessedProperties_2[_l];
857 var child = seenKeys[propertyName];
859 var propertyNode = child.parent;
860 validationResult.problems.push({
861 location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
862 message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
868 if (isNumber(schema.maxProperties)) {
869 if (node.properties.length > schema.maxProperties) {
870 validationResult.problems.push({
871 location: { offset: node.offset, length: node.length },
872 message: localize('MaxPropWarning', 'Object has more properties than limit of {0}.', schema.maxProperties)
876 if (isNumber(schema.minProperties)) {
877 if (node.properties.length < schema.minProperties) {
878 validationResult.problems.push({
879 location: { offset: node.offset, length: node.length },
880 message: localize('MinPropWarning', 'Object has fewer properties than the required number of {0}', schema.minProperties)
884 if (schema.dependencies) {
885 for (var _m = 0, _o = Object.keys(schema.dependencies); _m < _o.length; _m++) {
887 var prop = seenKeys[key];
889 var propertyDep = schema.dependencies[key];
890 if (Array.isArray(propertyDep)) {
891 for (var _p = 0, propertyDep_1 = propertyDep; _p < propertyDep_1.length; _p++) {
892 var requiredProp = propertyDep_1[_p];
893 if (!seenKeys[requiredProp]) {
894 validationResult.problems.push({
895 location: { offset: node.offset, length: node.length },
896 message: localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}.', requiredProp, key)
900 validationResult.propertiesValueMatches++;
905 var propertySchema = asSchema(propertyDep);
906 if (propertySchema) {
907 var propertyValidationResult = new ValidationResult();
908 validate(node, propertySchema, propertyValidationResult, matchingSchemas);
909 validationResult.mergePropertyMatch(propertyValidationResult);
915 var propertyNames = asSchema(schema.propertyNames);
917 for (var _q = 0, _r = node.properties; _q < _r.length; _q++) {
921 validate(key, propertyNames, validationResult, NoOpSchemaCollector.instance);
927 export function parse(textDocument, config) {
929 var lastProblemOffset = -1;
930 var text = textDocument.getText();
931 var scanner = Json.createScanner(text, false);
932 var commentRanges = config && config.collectComments ? [] : undefined;
933 function _scanNext() {
935 var token_1 = scanner.scan();
938 case 12 /* LineCommentTrivia */:
939 case 13 /* BlockCommentTrivia */:
940 if (Array.isArray(commentRanges)) {
941 commentRanges.push(Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength())));
944 case 15 /* Trivia */:
945 case 14 /* LineBreakTrivia */:
952 function _accept(token) {
953 if (scanner.getToken() === token) {
959 function _errorAtRange(message, code, startOffset, endOffset, severity) {
960 if (severity === void 0) { severity = DiagnosticSeverity.Error; }
961 if (problems.length === 0 || startOffset !== lastProblemOffset) {
962 var range = Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset));
963 problems.push(Diagnostic.create(range, message, severity, code, textDocument.languageId));
964 lastProblemOffset = startOffset;
967 function _error(message, code, node, skipUntilAfter, skipUntil) {
968 if (node === void 0) { node = undefined; }
969 if (skipUntilAfter === void 0) { skipUntilAfter = []; }
970 if (skipUntil === void 0) { skipUntil = []; }
971 var start = scanner.getTokenOffset();
972 var end = scanner.getTokenOffset() + scanner.getTokenLength();
973 if (start === end && start > 0) {
975 while (start > 0 && /\s/.test(text.charAt(start))) {
980 _errorAtRange(message, code, start, end);
982 _finalize(node, false);
984 if (skipUntilAfter.length + skipUntil.length > 0) {
985 var token_2 = scanner.getToken();
986 while (token_2 !== 17 /* EOF */) {
987 if (skipUntilAfter.indexOf(token_2) !== -1) {
991 else if (skipUntil.indexOf(token_2) !== -1) {
994 token_2 = _scanNext();
999 function _checkScanError() {
1000 switch (scanner.getTokenError()) {
1001 case 4 /* InvalidUnicode */:
1002 _error(localize('InvalidUnicode', 'Invalid unicode sequence in string.'), ErrorCode.InvalidUnicode);
1004 case 5 /* InvalidEscapeCharacter */:
1005 _error(localize('InvalidEscapeCharacter', 'Invalid escape character in string.'), ErrorCode.InvalidEscapeCharacter);
1007 case 3 /* UnexpectedEndOfNumber */:
1008 _error(localize('UnexpectedEndOfNumber', 'Unexpected end of number.'), ErrorCode.UnexpectedEndOfNumber);
1010 case 1 /* UnexpectedEndOfComment */:
1011 _error(localize('UnexpectedEndOfComment', 'Unexpected end of comment.'), ErrorCode.UnexpectedEndOfComment);
1013 case 2 /* UnexpectedEndOfString */:
1014 _error(localize('UnexpectedEndOfString', 'Unexpected end of string.'), ErrorCode.UnexpectedEndOfString);
1016 case 6 /* InvalidCharacter */:
1017 _error(localize('InvalidCharacter', 'Invalid characters in string. Control characters must be escaped.'), ErrorCode.InvalidCharacter);
1022 function _finalize(node, scanNext) {
1023 node.length = scanner.getTokenOffset() + scanner.getTokenLength() - node.offset;
1029 function _parseArray(parent) {
1030 if (scanner.getToken() !== 3 /* OpenBracketToken */) {
1033 var node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset());
1034 _scanNext(); // consume OpenBracketToken
1036 var needsComma = false;
1037 while (scanner.getToken() !== 4 /* CloseBracketToken */ && scanner.getToken() !== 17 /* EOF */) {
1038 if (scanner.getToken() === 5 /* CommaToken */) {
1040 _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected);
1042 var commaOffset = scanner.getTokenOffset();
1043 _scanNext(); // consume comma
1044 if (scanner.getToken() === 4 /* CloseBracketToken */) {
1046 _errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
1051 else if (needsComma) {
1052 _error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
1054 var item = _parseValue(node);
1056 _error(localize('PropertyExpected', 'Value expected'), ErrorCode.ValueExpected, undefined, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
1059 node.items.push(item);
1063 if (scanner.getToken() !== 4 /* CloseBracketToken */) {
1064 return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), ErrorCode.CommaOrCloseBacketExpected, node);
1066 return _finalize(node, true);
1068 var keyPlaceholder = new StringASTNodeImpl(undefined, 0, 0);
1069 function _parseProperty(parent, keysSeen) {
1070 var node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset(), keyPlaceholder);
1071 var key = _parseString(node);
1073 if (scanner.getToken() === 16 /* Unknown */) {
1074 // give a more helpful error message
1075 _error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'), ErrorCode.Undefined);
1076 var keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength());
1077 keyNode.value = scanner.getTokenValue();
1079 _scanNext(); // consume Unknown
1086 var seen = keysSeen[key.value];
1088 _errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, DiagnosticSeverity.Warning);
1089 if (typeof seen === 'object') {
1090 _errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, DiagnosticSeverity.Warning);
1092 keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting
1095 keysSeen[key.value] = node;
1097 if (scanner.getToken() === 6 /* ColonToken */) {
1098 node.colonOffset = scanner.getTokenOffset();
1099 _scanNext(); // consume ColonToken
1102 _error(localize('ColonExpected', 'Colon expected'), ErrorCode.ColonExpected);
1103 if (scanner.getToken() === 10 /* StringLiteral */ && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) {
1104 node.length = key.length;
1108 var value = _parseValue(node);
1110 return _error(localize('ValueExpected', 'Value expected'), ErrorCode.ValueExpected, node, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
1112 node.valueNode = value;
1113 node.length = value.offset + value.length - node.offset;
1116 function _parseObject(parent) {
1117 if (scanner.getToken() !== 1 /* OpenBraceToken */) {
1120 var node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset());
1121 var keysSeen = Object.create(null);
1122 _scanNext(); // consume OpenBraceToken
1123 var needsComma = false;
1124 while (scanner.getToken() !== 2 /* CloseBraceToken */ && scanner.getToken() !== 17 /* EOF */) {
1125 if (scanner.getToken() === 5 /* CommaToken */) {
1127 _error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected);
1129 var commaOffset = scanner.getTokenOffset();
1130 _scanNext(); // consume comma
1131 if (scanner.getToken() === 2 /* CloseBraceToken */) {
1133 _errorAtRange(localize('TrailingComma', 'Trailing comma'), ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
1138 else if (needsComma) {
1139 _error(localize('ExpectedComma', 'Expected comma'), ErrorCode.CommaExpected);
1141 var property = _parseProperty(node, keysSeen);
1143 _error(localize('PropertyExpected', 'Property expected'), ErrorCode.PropertyExpected, undefined, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
1146 node.properties.push(property);
1150 if (scanner.getToken() !== 2 /* CloseBraceToken */) {
1151 return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), ErrorCode.CommaOrCloseBraceExpected, node);
1153 return _finalize(node, true);
1155 function _parseString(parent) {
1156 if (scanner.getToken() !== 10 /* StringLiteral */) {
1159 var node = new StringASTNodeImpl(parent, scanner.getTokenOffset());
1160 node.value = scanner.getTokenValue();
1161 return _finalize(node, true);
1163 function _parseNumber(parent) {
1164 if (scanner.getToken() !== 11 /* NumericLiteral */) {
1167 var node = new NumberASTNodeImpl(parent, scanner.getTokenOffset());
1168 if (scanner.getTokenError() === 0 /* None */) {
1169 var tokenValue = scanner.getTokenValue();
1171 var numberValue = JSON.parse(tokenValue);
1172 if (!isNumber(numberValue)) {
1173 return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
1175 node.value = numberValue;
1178 return _error(localize('InvalidNumberFormat', 'Invalid number format.'), ErrorCode.Undefined, node);
1180 node.isInteger = tokenValue.indexOf('.') === -1;
1182 return _finalize(node, true);
1184 function _parseLiteral(parent) {
1186 switch (scanner.getToken()) {
1187 case 7 /* NullKeyword */:
1188 return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true);
1189 case 8 /* TrueKeyword */:
1190 return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true);
1191 case 9 /* FalseKeyword */:
1192 return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true);
1197 function _parseValue(parent) {
1198 return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent);
1200 var _root = undefined;
1201 var token = _scanNext();
1202 if (token !== 17 /* EOF */) {
1203 _root = _parseValue(_root);
1205 _error(localize('Invalid symbol', 'Expected a JSON object, array or literal.'), ErrorCode.Undefined);
1207 else if (scanner.getToken() !== 17 /* EOF */) {
1208 _error(localize('End of file expected', 'End of file expected.'), ErrorCode.Undefined);
1211 return new JSONDocument(_root, problems, commentRanges);