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 __());
21 if (typeof module === "object" && typeof module.exports === "object") {
22 var v = factory(require, exports);
23 if (v !== undefined) module.exports = v;
25 else if (typeof define === "function" && define.amd) {
26 define(["require", "exports", "jsonc-parser", "../utils/objects", "../utils/strings", "../jsonLanguageTypes", "vscode-nls"], factory);
28 })(function (require, exports) {
30 Object.defineProperty(exports, "__esModule", { value: true });
31 exports.parse = exports.JSONDocument = exports.contains = exports.getNodePath = exports.getNodeValue = exports.newJSONDocument = exports.ValidationResult = exports.EnumMatch = exports.asSchema = exports.ObjectASTNodeImpl = exports.PropertyASTNodeImpl = exports.StringASTNodeImpl = exports.NumberASTNodeImpl = exports.ArrayASTNodeImpl = exports.BooleanASTNodeImpl = exports.NullASTNodeImpl = exports.ASTNodeImpl = void 0;
32 var Json = require("jsonc-parser");
33 var objects_1 = require("../utils/objects");
34 var strings_1 = require("../utils/strings");
35 var jsonLanguageTypes_1 = require("../jsonLanguageTypes");
36 var nls = require("vscode-nls");
37 var localize = nls.loadMessageBundle();
39 '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})$/ },
40 '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 },
41 '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 },
42 '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 },
43 '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,}))$/ }
45 var ASTNodeImpl = /** @class */ (function () {
46 function ASTNodeImpl(parent, offset, length) {
47 if (length === void 0) { length = 0; }
52 Object.defineProperty(ASTNodeImpl.prototype, "children", {
59 ASTNodeImpl.prototype.toString = function () {
60 return 'type: ' + this.type + ' (' + this.offset + '/' + this.length + ')' + (this.parent ? ' parent: {' + this.parent.toString() + '}' : '');
64 exports.ASTNodeImpl = ASTNodeImpl;
65 var NullASTNodeImpl = /** @class */ (function (_super) {
66 __extends(NullASTNodeImpl, _super);
67 function NullASTNodeImpl(parent, offset) {
68 var _this = _super.call(this, parent, offset) || this;
73 return NullASTNodeImpl;
75 exports.NullASTNodeImpl = NullASTNodeImpl;
76 var BooleanASTNodeImpl = /** @class */ (function (_super) {
77 __extends(BooleanASTNodeImpl, _super);
78 function BooleanASTNodeImpl(parent, boolValue, offset) {
79 var _this = _super.call(this, parent, offset) || this;
80 _this.type = 'boolean';
81 _this.value = boolValue;
84 return BooleanASTNodeImpl;
86 exports.BooleanASTNodeImpl = BooleanASTNodeImpl;
87 var ArrayASTNodeImpl = /** @class */ (function (_super) {
88 __extends(ArrayASTNodeImpl, _super);
89 function ArrayASTNodeImpl(parent, offset) {
90 var _this = _super.call(this, parent, offset) || this;
95 Object.defineProperty(ArrayASTNodeImpl.prototype, "children", {
102 return ArrayASTNodeImpl;
104 exports.ArrayASTNodeImpl = ArrayASTNodeImpl;
105 var NumberASTNodeImpl = /** @class */ (function (_super) {
106 __extends(NumberASTNodeImpl, _super);
107 function NumberASTNodeImpl(parent, offset) {
108 var _this = _super.call(this, parent, offset) || this;
109 _this.type = 'number';
110 _this.isInteger = true;
111 _this.value = Number.NaN;
114 return NumberASTNodeImpl;
116 exports.NumberASTNodeImpl = NumberASTNodeImpl;
117 var StringASTNodeImpl = /** @class */ (function (_super) {
118 __extends(StringASTNodeImpl, _super);
119 function StringASTNodeImpl(parent, offset, length) {
120 var _this = _super.call(this, parent, offset, length) || this;
121 _this.type = 'string';
125 return StringASTNodeImpl;
127 exports.StringASTNodeImpl = StringASTNodeImpl;
128 var PropertyASTNodeImpl = /** @class */ (function (_super) {
129 __extends(PropertyASTNodeImpl, _super);
130 function PropertyASTNodeImpl(parent, offset, keyNode) {
131 var _this = _super.call(this, parent, offset) || this;
132 _this.type = 'property';
133 _this.colonOffset = -1;
134 _this.keyNode = keyNode;
137 Object.defineProperty(PropertyASTNodeImpl.prototype, "children", {
139 return this.valueNode ? [this.keyNode, this.valueNode] : [this.keyNode];
144 return PropertyASTNodeImpl;
146 exports.PropertyASTNodeImpl = PropertyASTNodeImpl;
147 var ObjectASTNodeImpl = /** @class */ (function (_super) {
148 __extends(ObjectASTNodeImpl, _super);
149 function ObjectASTNodeImpl(parent, offset) {
150 var _this = _super.call(this, parent, offset) || this;
151 _this.type = 'object';
152 _this.properties = [];
155 Object.defineProperty(ObjectASTNodeImpl.prototype, "children", {
157 return this.properties;
162 return ObjectASTNodeImpl;
164 exports.ObjectASTNodeImpl = ObjectASTNodeImpl;
165 function asSchema(schema) {
166 if (objects_1.isBoolean(schema)) {
167 return schema ? {} : { "not": {} };
171 exports.asSchema = asSchema;
173 (function (EnumMatch) {
174 EnumMatch[EnumMatch["Key"] = 0] = "Key";
175 EnumMatch[EnumMatch["Enum"] = 1] = "Enum";
176 })(EnumMatch = exports.EnumMatch || (exports.EnumMatch = {}));
177 var SchemaCollector = /** @class */ (function () {
178 function SchemaCollector(focusOffset, exclude) {
179 if (focusOffset === void 0) { focusOffset = -1; }
180 this.focusOffset = focusOffset;
181 this.exclude = exclude;
184 SchemaCollector.prototype.add = function (schema) {
185 this.schemas.push(schema);
187 SchemaCollector.prototype.merge = function (other) {
188 Array.prototype.push.apply(this.schemas, other.schemas);
190 SchemaCollector.prototype.include = function (node) {
191 return (this.focusOffset === -1 || contains(node, this.focusOffset)) && (node !== this.exclude);
193 SchemaCollector.prototype.newSub = function () {
194 return new SchemaCollector(-1, this.exclude);
196 return SchemaCollector;
198 var NoOpSchemaCollector = /** @class */ (function () {
199 function NoOpSchemaCollector() {
201 Object.defineProperty(NoOpSchemaCollector.prototype, "schemas", {
202 get: function () { return []; },
206 NoOpSchemaCollector.prototype.add = function (schema) { };
207 NoOpSchemaCollector.prototype.merge = function (other) { };
208 NoOpSchemaCollector.prototype.include = function (node) { return true; };
209 NoOpSchemaCollector.prototype.newSub = function () { return this; };
210 NoOpSchemaCollector.instance = new NoOpSchemaCollector();
211 return NoOpSchemaCollector;
213 var ValidationResult = /** @class */ (function () {
214 function ValidationResult() {
216 this.propertiesMatches = 0;
217 this.propertiesValueMatches = 0;
218 this.primaryValueMatches = 0;
219 this.enumValueMatch = false;
220 this.enumValues = undefined;
222 ValidationResult.prototype.hasProblems = function () {
223 return !!this.problems.length;
225 ValidationResult.prototype.mergeAll = function (validationResults) {
226 for (var _i = 0, validationResults_1 = validationResults; _i < validationResults_1.length; _i++) {
227 var validationResult = validationResults_1[_i];
228 this.merge(validationResult);
231 ValidationResult.prototype.merge = function (validationResult) {
232 this.problems = this.problems.concat(validationResult.problems);
234 ValidationResult.prototype.mergeEnumValues = function (validationResult) {
235 if (!this.enumValueMatch && !validationResult.enumValueMatch && this.enumValues && validationResult.enumValues) {
236 this.enumValues = this.enumValues.concat(validationResult.enumValues);
237 for (var _i = 0, _a = this.problems; _i < _a.length; _i++) {
239 if (error.code === jsonLanguageTypes_1.ErrorCode.EnumValueMismatch) {
240 error.message = localize('enumWarning', 'Value is not accepted. Valid values: {0}.', this.enumValues.map(function (v) { return JSON.stringify(v); }).join(', '));
245 ValidationResult.prototype.mergePropertyMatch = function (propertyValidationResult) {
246 this.merge(propertyValidationResult);
247 this.propertiesMatches++;
248 if (propertyValidationResult.enumValueMatch || !propertyValidationResult.hasProblems() && propertyValidationResult.propertiesMatches) {
249 this.propertiesValueMatches++;
251 if (propertyValidationResult.enumValueMatch && propertyValidationResult.enumValues && propertyValidationResult.enumValues.length === 1) {
252 this.primaryValueMatches++;
255 ValidationResult.prototype.compare = function (other) {
256 var hasProblems = this.hasProblems();
257 if (hasProblems !== other.hasProblems()) {
258 return hasProblems ? -1 : 1;
260 if (this.enumValueMatch !== other.enumValueMatch) {
261 return other.enumValueMatch ? -1 : 1;
263 if (this.primaryValueMatches !== other.primaryValueMatches) {
264 return this.primaryValueMatches - other.primaryValueMatches;
266 if (this.propertiesValueMatches !== other.propertiesValueMatches) {
267 return this.propertiesValueMatches - other.propertiesValueMatches;
269 return this.propertiesMatches - other.propertiesMatches;
271 return ValidationResult;
273 exports.ValidationResult = ValidationResult;
274 function newJSONDocument(root, diagnostics) {
275 if (diagnostics === void 0) { diagnostics = []; }
276 return new JSONDocument(root, diagnostics, []);
278 exports.newJSONDocument = newJSONDocument;
279 function getNodeValue(node) {
280 return Json.getNodeValue(node);
282 exports.getNodeValue = getNodeValue;
283 function getNodePath(node) {
284 return Json.getNodePath(node);
286 exports.getNodePath = getNodePath;
287 function contains(node, offset, includeRightBound) {
288 if (includeRightBound === void 0) { includeRightBound = false; }
289 return offset >= node.offset && offset < (node.offset + node.length) || includeRightBound && offset === (node.offset + node.length);
291 exports.contains = contains;
292 var JSONDocument = /** @class */ (function () {
293 function JSONDocument(root, syntaxErrors, comments) {
294 if (syntaxErrors === void 0) { syntaxErrors = []; }
295 if (comments === void 0) { comments = []; }
297 this.syntaxErrors = syntaxErrors;
298 this.comments = comments;
300 JSONDocument.prototype.getNodeFromOffset = function (offset, includeRightBound) {
301 if (includeRightBound === void 0) { includeRightBound = false; }
303 return Json.findNodeAtOffset(this.root, offset, includeRightBound);
307 JSONDocument.prototype.visit = function (visitor) {
309 var doVisit_1 = function (node) {
310 var ctn = visitor(node);
311 var children = node.children;
312 if (Array.isArray(children)) {
313 for (var i = 0; i < children.length && ctn; i++) {
314 ctn = doVisit_1(children[i]);
319 doVisit_1(this.root);
322 JSONDocument.prototype.validate = function (textDocument, schema, severity) {
323 if (severity === void 0) { severity = jsonLanguageTypes_1.DiagnosticSeverity.Warning; }
324 if (this.root && schema) {
325 var validationResult = new ValidationResult();
326 validate(this.root, schema, validationResult, NoOpSchemaCollector.instance);
327 return validationResult.problems.map(function (p) {
329 var range = jsonLanguageTypes_1.Range.create(textDocument.positionAt(p.location.offset), textDocument.positionAt(p.location.offset + p.location.length));
330 return jsonLanguageTypes_1.Diagnostic.create(range, p.message, (_a = p.severity) !== null && _a !== void 0 ? _a : severity, p.code);
335 JSONDocument.prototype.getMatchingSchemas = function (schema, focusOffset, exclude) {
336 if (focusOffset === void 0) { focusOffset = -1; }
337 var matchingSchemas = new SchemaCollector(focusOffset, exclude);
338 if (this.root && schema) {
339 validate(this.root, schema, new ValidationResult(), matchingSchemas);
341 return matchingSchemas.schemas;
345 exports.JSONDocument = JSONDocument;
346 function validate(n, schema, validationResult, matchingSchemas) {
347 if (!n || !matchingSchemas.include(n)) {
353 _validateObjectNode(node, schema, validationResult, matchingSchemas);
356 _validateArrayNode(node, schema, validationResult, matchingSchemas);
359 _validateStringNode(node, schema, validationResult, matchingSchemas);
362 _validateNumberNode(node, schema, validationResult, matchingSchemas);
365 return validate(node.valueNode, schema, validationResult, matchingSchemas);
368 matchingSchemas.add({ node: node, schema: schema });
369 function _validateNode() {
370 function matchesType(type) {
371 return node.type === type || (type === 'integer' && node.type === 'number' && node.isInteger);
373 if (Array.isArray(schema.type)) {
374 if (!schema.type.some(matchesType)) {
375 validationResult.problems.push({
376 location: { offset: node.offset, length: node.length },
377 message: schema.errorMessage || localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}.', schema.type.join(', '))
381 else if (schema.type) {
382 if (!matchesType(schema.type)) {
383 validationResult.problems.push({
384 location: { offset: node.offset, length: node.length },
385 message: schema.errorMessage || localize('typeMismatchWarning', 'Incorrect type. Expected "{0}".', schema.type)
389 if (Array.isArray(schema.allOf)) {
390 for (var _i = 0, _a = schema.allOf; _i < _a.length; _i++) {
391 var subSchemaRef = _a[_i];
392 validate(node, asSchema(subSchemaRef), validationResult, matchingSchemas);
395 var notSchema = asSchema(schema.not);
397 var subValidationResult = new ValidationResult();
398 var subMatchingSchemas = matchingSchemas.newSub();
399 validate(node, notSchema, subValidationResult, subMatchingSchemas);
400 if (!subValidationResult.hasProblems()) {
401 validationResult.problems.push({
402 location: { offset: node.offset, length: node.length },
403 message: localize('notSchemaWarning', "Matches a schema that is not allowed.")
406 for (var _b = 0, _c = subMatchingSchemas.schemas; _b < _c.length; _b++) {
408 ms.inverted = !ms.inverted;
409 matchingSchemas.add(ms);
412 var testAlternatives = function (alternatives, maxOneMatch) {
414 // remember the best match that is used for error messages
415 var bestMatch = undefined;
416 for (var _i = 0, alternatives_1 = alternatives; _i < alternatives_1.length; _i++) {
417 var subSchemaRef = alternatives_1[_i];
418 var subSchema = asSchema(subSchemaRef);
419 var subValidationResult = new ValidationResult();
420 var subMatchingSchemas = matchingSchemas.newSub();
421 validate(node, subSchema, subValidationResult, subMatchingSchemas);
422 if (!subValidationResult.hasProblems()) {
423 matches.push(subSchema);
426 bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
429 if (!maxOneMatch && !subValidationResult.hasProblems() && !bestMatch.validationResult.hasProblems()) {
430 // no errors, both are equally good matches
431 bestMatch.matchingSchemas.merge(subMatchingSchemas);
432 bestMatch.validationResult.propertiesMatches += subValidationResult.propertiesMatches;
433 bestMatch.validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
436 var compareResult = subValidationResult.compare(bestMatch.validationResult);
437 if (compareResult > 0) {
438 // our node is the best matching so far
439 bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
441 else if (compareResult === 0) {
442 // there's already a best matching but we are as good
443 bestMatch.matchingSchemas.merge(subMatchingSchemas);
444 bestMatch.validationResult.mergeEnumValues(subValidationResult);
449 if (matches.length > 1 && maxOneMatch) {
450 validationResult.problems.push({
451 location: { offset: node.offset, length: 1 },
452 message: localize('oneOfWarning', "Matches multiple schemas when only one must validate.")
456 validationResult.merge(bestMatch.validationResult);
457 validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches;
458 validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches;
459 matchingSchemas.merge(bestMatch.matchingSchemas);
461 return matches.length;
463 if (Array.isArray(schema.anyOf)) {
464 testAlternatives(schema.anyOf, false);
466 if (Array.isArray(schema.oneOf)) {
467 testAlternatives(schema.oneOf, true);
469 var testBranch = function (schema) {
470 var subValidationResult = new ValidationResult();
471 var subMatchingSchemas = matchingSchemas.newSub();
472 validate(node, asSchema(schema), subValidationResult, subMatchingSchemas);
473 validationResult.merge(subValidationResult);
474 validationResult.propertiesMatches += subValidationResult.propertiesMatches;
475 validationResult.propertiesValueMatches += subValidationResult.propertiesValueMatches;
476 matchingSchemas.merge(subMatchingSchemas);
478 var testCondition = function (ifSchema, thenSchema, elseSchema) {
479 var subSchema = asSchema(ifSchema);
480 var subValidationResult = new ValidationResult();
481 var subMatchingSchemas = matchingSchemas.newSub();
482 validate(node, subSchema, subValidationResult, subMatchingSchemas);
483 matchingSchemas.merge(subMatchingSchemas);
484 if (!subValidationResult.hasProblems()) {
486 testBranch(thenSchema);
489 else if (elseSchema) {
490 testBranch(elseSchema);
493 var ifSchema = asSchema(schema.if);
495 testCondition(ifSchema, asSchema(schema.then), asSchema(schema.else));
497 if (Array.isArray(schema.enum)) {
498 var val = getNodeValue(node);
499 var enumValueMatch = false;
500 for (var _d = 0, _e = schema.enum; _d < _e.length; _d++) {
502 if (objects_1.equals(val, e)) {
503 enumValueMatch = true;
507 validationResult.enumValues = schema.enum;
508 validationResult.enumValueMatch = enumValueMatch;
509 if (!enumValueMatch) {
510 validationResult.problems.push({
511 location: { offset: node.offset, length: node.length },
512 code: jsonLanguageTypes_1.ErrorCode.EnumValueMismatch,
513 message: schema.errorMessage || localize('enumWarning', 'Value is not accepted. Valid values: {0}.', schema.enum.map(function (v) { return JSON.stringify(v); }).join(', '))
517 if (objects_1.isDefined(schema.const)) {
518 var val = getNodeValue(node);
519 if (!objects_1.equals(val, schema.const)) {
520 validationResult.problems.push({
521 location: { offset: node.offset, length: node.length },
522 code: jsonLanguageTypes_1.ErrorCode.EnumValueMismatch,
523 message: schema.errorMessage || localize('constWarning', 'Value must be {0}.', JSON.stringify(schema.const))
525 validationResult.enumValueMatch = false;
528 validationResult.enumValueMatch = true;
530 validationResult.enumValues = [schema.const];
532 if (schema.deprecationMessage && node.parent) {
533 validationResult.problems.push({
534 location: { offset: node.parent.offset, length: node.parent.length },
535 severity: jsonLanguageTypes_1.DiagnosticSeverity.Warning,
536 message: schema.deprecationMessage,
537 code: jsonLanguageTypes_1.ErrorCode.Deprecated
541 function _validateNumberNode(node, schema, validationResult, matchingSchemas) {
542 var val = node.value;
543 function normalizeFloats(float) {
545 var parts = /^(-?\d+)(?:\.(\d+))?(?:e([-+]\d+))?$/.exec(float.toString());
547 value: Number(parts[1] + (parts[2] || '')),
548 multiplier: (((_a = parts[2]) === null || _a === void 0 ? void 0 : _a.length) || 0) - (parseInt(parts[3]) || 0)
552 if (objects_1.isNumber(schema.multipleOf)) {
554 if (Number.isInteger(schema.multipleOf)) {
555 remainder = val % schema.multipleOf;
558 var normMultipleOf = normalizeFloats(schema.multipleOf);
559 var normValue = normalizeFloats(val);
560 if (normMultipleOf && normValue) {
561 var multiplier = Math.pow(10, Math.abs(normValue.multiplier - normMultipleOf.multiplier));
562 if (normValue.multiplier < normMultipleOf.multiplier) {
563 normValue.value *= multiplier;
566 normMultipleOf.value *= multiplier;
568 remainder = normValue.value % normMultipleOf.value;
571 if (remainder !== 0) {
572 validationResult.problems.push({
573 location: { offset: node.offset, length: node.length },
574 message: localize('multipleOfWarning', 'Value is not divisible by {0}.', schema.multipleOf)
578 function getExclusiveLimit(limit, exclusive) {
579 if (objects_1.isNumber(exclusive)) {
582 if (objects_1.isBoolean(exclusive) && exclusive) {
587 function getLimit(limit, exclusive) {
588 if (!objects_1.isBoolean(exclusive) || !exclusive) {
593 var exclusiveMinimum = getExclusiveLimit(schema.minimum, schema.exclusiveMinimum);
594 if (objects_1.isNumber(exclusiveMinimum) && val <= exclusiveMinimum) {
595 validationResult.problems.push({
596 location: { offset: node.offset, length: node.length },
597 message: localize('exclusiveMinimumWarning', 'Value is below the exclusive minimum of {0}.', exclusiveMinimum)
600 var exclusiveMaximum = getExclusiveLimit(schema.maximum, schema.exclusiveMaximum);
601 if (objects_1.isNumber(exclusiveMaximum) && val >= exclusiveMaximum) {
602 validationResult.problems.push({
603 location: { offset: node.offset, length: node.length },
604 message: localize('exclusiveMaximumWarning', 'Value is above the exclusive maximum of {0}.', exclusiveMaximum)
607 var minimum = getLimit(schema.minimum, schema.exclusiveMinimum);
608 if (objects_1.isNumber(minimum) && val < minimum) {
609 validationResult.problems.push({
610 location: { offset: node.offset, length: node.length },
611 message: localize('minimumWarning', 'Value is below the minimum of {0}.', minimum)
614 var maximum = getLimit(schema.maximum, schema.exclusiveMaximum);
615 if (objects_1.isNumber(maximum) && val > maximum) {
616 validationResult.problems.push({
617 location: { offset: node.offset, length: node.length },
618 message: localize('maximumWarning', 'Value is above the maximum of {0}.', maximum)
622 function _validateStringNode(node, schema, validationResult, matchingSchemas) {
623 if (objects_1.isNumber(schema.minLength) && node.value.length < schema.minLength) {
624 validationResult.problems.push({
625 location: { offset: node.offset, length: node.length },
626 message: localize('minLengthWarning', 'String is shorter than the minimum length of {0}.', schema.minLength)
629 if (objects_1.isNumber(schema.maxLength) && node.value.length > schema.maxLength) {
630 validationResult.problems.push({
631 location: { offset: node.offset, length: node.length },
632 message: localize('maxLengthWarning', 'String is longer than the maximum length of {0}.', schema.maxLength)
635 if (objects_1.isString(schema.pattern)) {
636 var regex = strings_1.extendedRegExp(schema.pattern);
637 if (!regex.test(node.value)) {
638 validationResult.problems.push({
639 location: { offset: node.offset, length: node.length },
640 message: schema.patternErrorMessage || schema.errorMessage || localize('patternWarning', 'String does not match the pattern of "{0}".', schema.pattern)
645 switch (schema.format) {
647 case 'uri-reference':
649 var errorMessage = void 0;
651 errorMessage = localize('uriEmpty', 'URI expected.');
654 var match = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(node.value);
656 errorMessage = localize('uriMissing', 'URI is expected.');
658 else if (!match[2] && schema.format === 'uri') {
659 errorMessage = localize('uriSchemeMissing', 'URI with a scheme is expected.');
663 validationResult.problems.push({
664 location: { offset: node.offset, length: node.length },
665 message: schema.patternErrorMessage || schema.errorMessage || localize('uriFormatWarning', 'String is not a URI: {0}', errorMessage)
675 var format = formats[schema.format];
676 if (!node.value || !format.pattern.exec(node.value)) {
677 validationResult.problems.push({
678 location: { offset: node.offset, length: node.length },
679 message: schema.patternErrorMessage || schema.errorMessage || format.errorMessage
686 function _validateArrayNode(node, schema, validationResult, matchingSchemas) {
687 if (Array.isArray(schema.items)) {
688 var subSchemas = schema.items;
689 for (var index = 0; index < subSchemas.length; index++) {
690 var subSchemaRef = subSchemas[index];
691 var subSchema = asSchema(subSchemaRef);
692 var itemValidationResult = new ValidationResult();
693 var item = node.items[index];
695 validate(item, subSchema, itemValidationResult, matchingSchemas);
696 validationResult.mergePropertyMatch(itemValidationResult);
698 else if (node.items.length >= subSchemas.length) {
699 validationResult.propertiesValueMatches++;
702 if (node.items.length > subSchemas.length) {
703 if (typeof schema.additionalItems === 'object') {
704 for (var i = subSchemas.length; i < node.items.length; i++) {
705 var itemValidationResult = new ValidationResult();
706 validate(node.items[i], schema.additionalItems, itemValidationResult, matchingSchemas);
707 validationResult.mergePropertyMatch(itemValidationResult);
710 else if (schema.additionalItems === false) {
711 validationResult.problems.push({
712 location: { offset: node.offset, length: node.length },
713 message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.', subSchemas.length)
719 var itemSchema = asSchema(schema.items);
721 for (var _i = 0, _a = node.items; _i < _a.length; _i++) {
723 var itemValidationResult = new ValidationResult();
724 validate(item, itemSchema, itemValidationResult, matchingSchemas);
725 validationResult.mergePropertyMatch(itemValidationResult);
729 var containsSchema = asSchema(schema.contains);
730 if (containsSchema) {
731 var doesContain = node.items.some(function (item) {
732 var itemValidationResult = new ValidationResult();
733 validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance);
734 return !itemValidationResult.hasProblems();
737 validationResult.problems.push({
738 location: { offset: node.offset, length: node.length },
739 message: schema.errorMessage || localize('requiredItemMissingWarning', 'Array does not contain required item.')
743 if (objects_1.isNumber(schema.minItems) && node.items.length < schema.minItems) {
744 validationResult.problems.push({
745 location: { offset: node.offset, length: node.length },
746 message: localize('minItemsWarning', 'Array has too few items. Expected {0} or more.', schema.minItems)
749 if (objects_1.isNumber(schema.maxItems) && node.items.length > schema.maxItems) {
750 validationResult.problems.push({
751 location: { offset: node.offset, length: node.length },
752 message: localize('maxItemsWarning', 'Array has too many items. Expected {0} or fewer.', schema.maxItems)
755 if (schema.uniqueItems === true) {
756 var values_1 = getNodeValue(node);
757 var duplicates = values_1.some(function (value, index) {
758 return index !== values_1.lastIndexOf(value);
761 validationResult.problems.push({
762 location: { offset: node.offset, length: node.length },
763 message: localize('uniqueItemsWarning', 'Array has duplicate items.')
768 function _validateObjectNode(node, schema, validationResult, matchingSchemas) {
769 var seenKeys = Object.create(null);
770 var unprocessedProperties = [];
771 for (var _i = 0, _a = node.properties; _i < _a.length; _i++) {
772 var propertyNode = _a[_i];
773 var key = propertyNode.keyNode.value;
774 seenKeys[key] = propertyNode.valueNode;
775 unprocessedProperties.push(key);
777 if (Array.isArray(schema.required)) {
778 for (var _b = 0, _c = schema.required; _b < _c.length; _b++) {
779 var propertyName = _c[_b];
780 if (!seenKeys[propertyName]) {
781 var keyNode = node.parent && node.parent.type === 'property' && node.parent.keyNode;
782 var location = keyNode ? { offset: keyNode.offset, length: keyNode.length } : { offset: node.offset, length: 1 };
783 validationResult.problems.push({
785 message: localize('MissingRequiredPropWarning', 'Missing property "{0}".', propertyName)
790 var propertyProcessed = function (prop) {
791 var index = unprocessedProperties.indexOf(prop);
793 unprocessedProperties.splice(index, 1);
794 index = unprocessedProperties.indexOf(prop);
797 if (schema.properties) {
798 for (var _d = 0, _e = Object.keys(schema.properties); _d < _e.length; _d++) {
799 var propertyName = _e[_d];
800 propertyProcessed(propertyName);
801 var propertySchema = schema.properties[propertyName];
802 var child = seenKeys[propertyName];
804 if (objects_1.isBoolean(propertySchema)) {
805 if (!propertySchema) {
806 var propertyNode = child.parent;
807 validationResult.problems.push({
808 location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
809 message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
813 validationResult.propertiesMatches++;
814 validationResult.propertiesValueMatches++;
818 var propertyValidationResult = new ValidationResult();
819 validate(child, propertySchema, propertyValidationResult, matchingSchemas);
820 validationResult.mergePropertyMatch(propertyValidationResult);
825 if (schema.patternProperties) {
826 for (var _f = 0, _g = Object.keys(schema.patternProperties); _f < _g.length; _f++) {
827 var propertyPattern = _g[_f];
828 var regex = strings_1.extendedRegExp(propertyPattern);
829 for (var _h = 0, _j = unprocessedProperties.slice(0); _h < _j.length; _h++) {
830 var propertyName = _j[_h];
831 if (regex.test(propertyName)) {
832 propertyProcessed(propertyName);
833 var child = seenKeys[propertyName];
835 var propertySchema = schema.patternProperties[propertyPattern];
836 if (objects_1.isBoolean(propertySchema)) {
837 if (!propertySchema) {
838 var propertyNode = child.parent;
839 validationResult.problems.push({
840 location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
841 message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
845 validationResult.propertiesMatches++;
846 validationResult.propertiesValueMatches++;
850 var propertyValidationResult = new ValidationResult();
851 validate(child, propertySchema, propertyValidationResult, matchingSchemas);
852 validationResult.mergePropertyMatch(propertyValidationResult);
859 if (typeof schema.additionalProperties === 'object') {
860 for (var _k = 0, unprocessedProperties_1 = unprocessedProperties; _k < unprocessedProperties_1.length; _k++) {
861 var propertyName = unprocessedProperties_1[_k];
862 var child = seenKeys[propertyName];
864 var propertyValidationResult = new ValidationResult();
865 validate(child, schema.additionalProperties, propertyValidationResult, matchingSchemas);
866 validationResult.mergePropertyMatch(propertyValidationResult);
870 else if (schema.additionalProperties === false) {
871 if (unprocessedProperties.length > 0) {
872 for (var _l = 0, unprocessedProperties_2 = unprocessedProperties; _l < unprocessedProperties_2.length; _l++) {
873 var propertyName = unprocessedProperties_2[_l];
874 var child = seenKeys[propertyName];
876 var propertyNode = child.parent;
877 validationResult.problems.push({
878 location: { offset: propertyNode.keyNode.offset, length: propertyNode.keyNode.length },
879 message: schema.errorMessage || localize('DisallowedExtraPropWarning', 'Property {0} is not allowed.', propertyName)
885 if (objects_1.isNumber(schema.maxProperties)) {
886 if (node.properties.length > schema.maxProperties) {
887 validationResult.problems.push({
888 location: { offset: node.offset, length: node.length },
889 message: localize('MaxPropWarning', 'Object has more properties than limit of {0}.', schema.maxProperties)
893 if (objects_1.isNumber(schema.minProperties)) {
894 if (node.properties.length < schema.minProperties) {
895 validationResult.problems.push({
896 location: { offset: node.offset, length: node.length },
897 message: localize('MinPropWarning', 'Object has fewer properties than the required number of {0}', schema.minProperties)
901 if (schema.dependencies) {
902 for (var _m = 0, _o = Object.keys(schema.dependencies); _m < _o.length; _m++) {
904 var prop = seenKeys[key];
906 var propertyDep = schema.dependencies[key];
907 if (Array.isArray(propertyDep)) {
908 for (var _p = 0, propertyDep_1 = propertyDep; _p < propertyDep_1.length; _p++) {
909 var requiredProp = propertyDep_1[_p];
910 if (!seenKeys[requiredProp]) {
911 validationResult.problems.push({
912 location: { offset: node.offset, length: node.length },
913 message: localize('RequiredDependentPropWarning', 'Object is missing property {0} required by property {1}.', requiredProp, key)
917 validationResult.propertiesValueMatches++;
922 var propertySchema = asSchema(propertyDep);
923 if (propertySchema) {
924 var propertyValidationResult = new ValidationResult();
925 validate(node, propertySchema, propertyValidationResult, matchingSchemas);
926 validationResult.mergePropertyMatch(propertyValidationResult);
932 var propertyNames = asSchema(schema.propertyNames);
934 for (var _q = 0, _r = node.properties; _q < _r.length; _q++) {
938 validate(key, propertyNames, validationResult, NoOpSchemaCollector.instance);
944 function parse(textDocument, config) {
946 var lastProblemOffset = -1;
947 var text = textDocument.getText();
948 var scanner = Json.createScanner(text, false);
949 var commentRanges = config && config.collectComments ? [] : undefined;
950 function _scanNext() {
952 var token_1 = scanner.scan();
955 case 12 /* LineCommentTrivia */:
956 case 13 /* BlockCommentTrivia */:
957 if (Array.isArray(commentRanges)) {
958 commentRanges.push(jsonLanguageTypes_1.Range.create(textDocument.positionAt(scanner.getTokenOffset()), textDocument.positionAt(scanner.getTokenOffset() + scanner.getTokenLength())));
961 case 15 /* Trivia */:
962 case 14 /* LineBreakTrivia */:
969 function _accept(token) {
970 if (scanner.getToken() === token) {
976 function _errorAtRange(message, code, startOffset, endOffset, severity) {
977 if (severity === void 0) { severity = jsonLanguageTypes_1.DiagnosticSeverity.Error; }
978 if (problems.length === 0 || startOffset !== lastProblemOffset) {
979 var range = jsonLanguageTypes_1.Range.create(textDocument.positionAt(startOffset), textDocument.positionAt(endOffset));
980 problems.push(jsonLanguageTypes_1.Diagnostic.create(range, message, severity, code, textDocument.languageId));
981 lastProblemOffset = startOffset;
984 function _error(message, code, node, skipUntilAfter, skipUntil) {
985 if (node === void 0) { node = undefined; }
986 if (skipUntilAfter === void 0) { skipUntilAfter = []; }
987 if (skipUntil === void 0) { skipUntil = []; }
988 var start = scanner.getTokenOffset();
989 var end = scanner.getTokenOffset() + scanner.getTokenLength();
990 if (start === end && start > 0) {
992 while (start > 0 && /\s/.test(text.charAt(start))) {
997 _errorAtRange(message, code, start, end);
999 _finalize(node, false);
1001 if (skipUntilAfter.length + skipUntil.length > 0) {
1002 var token_2 = scanner.getToken();
1003 while (token_2 !== 17 /* EOF */) {
1004 if (skipUntilAfter.indexOf(token_2) !== -1) {
1008 else if (skipUntil.indexOf(token_2) !== -1) {
1011 token_2 = _scanNext();
1016 function _checkScanError() {
1017 switch (scanner.getTokenError()) {
1018 case 4 /* InvalidUnicode */:
1019 _error(localize('InvalidUnicode', 'Invalid unicode sequence in string.'), jsonLanguageTypes_1.ErrorCode.InvalidUnicode);
1021 case 5 /* InvalidEscapeCharacter */:
1022 _error(localize('InvalidEscapeCharacter', 'Invalid escape character in string.'), jsonLanguageTypes_1.ErrorCode.InvalidEscapeCharacter);
1024 case 3 /* UnexpectedEndOfNumber */:
1025 _error(localize('UnexpectedEndOfNumber', 'Unexpected end of number.'), jsonLanguageTypes_1.ErrorCode.UnexpectedEndOfNumber);
1027 case 1 /* UnexpectedEndOfComment */:
1028 _error(localize('UnexpectedEndOfComment', 'Unexpected end of comment.'), jsonLanguageTypes_1.ErrorCode.UnexpectedEndOfComment);
1030 case 2 /* UnexpectedEndOfString */:
1031 _error(localize('UnexpectedEndOfString', 'Unexpected end of string.'), jsonLanguageTypes_1.ErrorCode.UnexpectedEndOfString);
1033 case 6 /* InvalidCharacter */:
1034 _error(localize('InvalidCharacter', 'Invalid characters in string. Control characters must be escaped.'), jsonLanguageTypes_1.ErrorCode.InvalidCharacter);
1039 function _finalize(node, scanNext) {
1040 node.length = scanner.getTokenOffset() + scanner.getTokenLength() - node.offset;
1046 function _parseArray(parent) {
1047 if (scanner.getToken() !== 3 /* OpenBracketToken */) {
1050 var node = new ArrayASTNodeImpl(parent, scanner.getTokenOffset());
1051 _scanNext(); // consume OpenBracketToken
1053 var needsComma = false;
1054 while (scanner.getToken() !== 4 /* CloseBracketToken */ && scanner.getToken() !== 17 /* EOF */) {
1055 if (scanner.getToken() === 5 /* CommaToken */) {
1057 _error(localize('ValueExpected', 'Value expected'), jsonLanguageTypes_1.ErrorCode.ValueExpected);
1059 var commaOffset = scanner.getTokenOffset();
1060 _scanNext(); // consume comma
1061 if (scanner.getToken() === 4 /* CloseBracketToken */) {
1063 _errorAtRange(localize('TrailingComma', 'Trailing comma'), jsonLanguageTypes_1.ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
1068 else if (needsComma) {
1069 _error(localize('ExpectedComma', 'Expected comma'), jsonLanguageTypes_1.ErrorCode.CommaExpected);
1071 var item = _parseValue(node);
1073 _error(localize('PropertyExpected', 'Value expected'), jsonLanguageTypes_1.ErrorCode.ValueExpected, undefined, [], [4 /* CloseBracketToken */, 5 /* CommaToken */]);
1076 node.items.push(item);
1080 if (scanner.getToken() !== 4 /* CloseBracketToken */) {
1081 return _error(localize('ExpectedCloseBracket', 'Expected comma or closing bracket'), jsonLanguageTypes_1.ErrorCode.CommaOrCloseBacketExpected, node);
1083 return _finalize(node, true);
1085 var keyPlaceholder = new StringASTNodeImpl(undefined, 0, 0);
1086 function _parseProperty(parent, keysSeen) {
1087 var node = new PropertyASTNodeImpl(parent, scanner.getTokenOffset(), keyPlaceholder);
1088 var key = _parseString(node);
1090 if (scanner.getToken() === 16 /* Unknown */) {
1091 // give a more helpful error message
1092 _error(localize('DoubleQuotesExpected', 'Property keys must be doublequoted'), jsonLanguageTypes_1.ErrorCode.Undefined);
1093 var keyNode = new StringASTNodeImpl(node, scanner.getTokenOffset(), scanner.getTokenLength());
1094 keyNode.value = scanner.getTokenValue();
1096 _scanNext(); // consume Unknown
1103 var seen = keysSeen[key.value];
1105 _errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), jsonLanguageTypes_1.ErrorCode.DuplicateKey, node.keyNode.offset, node.keyNode.offset + node.keyNode.length, jsonLanguageTypes_1.DiagnosticSeverity.Warning);
1106 if (typeof seen === 'object') {
1107 _errorAtRange(localize('DuplicateKeyWarning', "Duplicate object key"), jsonLanguageTypes_1.ErrorCode.DuplicateKey, seen.keyNode.offset, seen.keyNode.offset + seen.keyNode.length, jsonLanguageTypes_1.DiagnosticSeverity.Warning);
1109 keysSeen[key.value] = true; // if the same key is duplicate again, avoid duplicate error reporting
1112 keysSeen[key.value] = node;
1114 if (scanner.getToken() === 6 /* ColonToken */) {
1115 node.colonOffset = scanner.getTokenOffset();
1116 _scanNext(); // consume ColonToken
1119 _error(localize('ColonExpected', 'Colon expected'), jsonLanguageTypes_1.ErrorCode.ColonExpected);
1120 if (scanner.getToken() === 10 /* StringLiteral */ && textDocument.positionAt(key.offset + key.length).line < textDocument.positionAt(scanner.getTokenOffset()).line) {
1121 node.length = key.length;
1125 var value = _parseValue(node);
1127 return _error(localize('ValueExpected', 'Value expected'), jsonLanguageTypes_1.ErrorCode.ValueExpected, node, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
1129 node.valueNode = value;
1130 node.length = value.offset + value.length - node.offset;
1133 function _parseObject(parent) {
1134 if (scanner.getToken() !== 1 /* OpenBraceToken */) {
1137 var node = new ObjectASTNodeImpl(parent, scanner.getTokenOffset());
1138 var keysSeen = Object.create(null);
1139 _scanNext(); // consume OpenBraceToken
1140 var needsComma = false;
1141 while (scanner.getToken() !== 2 /* CloseBraceToken */ && scanner.getToken() !== 17 /* EOF */) {
1142 if (scanner.getToken() === 5 /* CommaToken */) {
1144 _error(localize('PropertyExpected', 'Property expected'), jsonLanguageTypes_1.ErrorCode.PropertyExpected);
1146 var commaOffset = scanner.getTokenOffset();
1147 _scanNext(); // consume comma
1148 if (scanner.getToken() === 2 /* CloseBraceToken */) {
1150 _errorAtRange(localize('TrailingComma', 'Trailing comma'), jsonLanguageTypes_1.ErrorCode.TrailingComma, commaOffset, commaOffset + 1);
1155 else if (needsComma) {
1156 _error(localize('ExpectedComma', 'Expected comma'), jsonLanguageTypes_1.ErrorCode.CommaExpected);
1158 var property = _parseProperty(node, keysSeen);
1160 _error(localize('PropertyExpected', 'Property expected'), jsonLanguageTypes_1.ErrorCode.PropertyExpected, undefined, [], [2 /* CloseBraceToken */, 5 /* CommaToken */]);
1163 node.properties.push(property);
1167 if (scanner.getToken() !== 2 /* CloseBraceToken */) {
1168 return _error(localize('ExpectedCloseBrace', 'Expected comma or closing brace'), jsonLanguageTypes_1.ErrorCode.CommaOrCloseBraceExpected, node);
1170 return _finalize(node, true);
1172 function _parseString(parent) {
1173 if (scanner.getToken() !== 10 /* StringLiteral */) {
1176 var node = new StringASTNodeImpl(parent, scanner.getTokenOffset());
1177 node.value = scanner.getTokenValue();
1178 return _finalize(node, true);
1180 function _parseNumber(parent) {
1181 if (scanner.getToken() !== 11 /* NumericLiteral */) {
1184 var node = new NumberASTNodeImpl(parent, scanner.getTokenOffset());
1185 if (scanner.getTokenError() === 0 /* None */) {
1186 var tokenValue = scanner.getTokenValue();
1188 var numberValue = JSON.parse(tokenValue);
1189 if (!objects_1.isNumber(numberValue)) {
1190 return _error(localize('InvalidNumberFormat', 'Invalid number format.'), jsonLanguageTypes_1.ErrorCode.Undefined, node);
1192 node.value = numberValue;
1195 return _error(localize('InvalidNumberFormat', 'Invalid number format.'), jsonLanguageTypes_1.ErrorCode.Undefined, node);
1197 node.isInteger = tokenValue.indexOf('.') === -1;
1199 return _finalize(node, true);
1201 function _parseLiteral(parent) {
1203 switch (scanner.getToken()) {
1204 case 7 /* NullKeyword */:
1205 return _finalize(new NullASTNodeImpl(parent, scanner.getTokenOffset()), true);
1206 case 8 /* TrueKeyword */:
1207 return _finalize(new BooleanASTNodeImpl(parent, true, scanner.getTokenOffset()), true);
1208 case 9 /* FalseKeyword */:
1209 return _finalize(new BooleanASTNodeImpl(parent, false, scanner.getTokenOffset()), true);
1214 function _parseValue(parent) {
1215 return _parseArray(parent) || _parseObject(parent) || _parseString(parent) || _parseNumber(parent) || _parseLiteral(parent);
1217 var _root = undefined;
1218 var token = _scanNext();
1219 if (token !== 17 /* EOF */) {
1220 _root = _parseValue(_root);
1222 _error(localize('Invalid symbol', 'Expected a JSON object, array or literal.'), jsonLanguageTypes_1.ErrorCode.Undefined);
1224 else if (scanner.getToken() !== 17 /* EOF */) {
1225 _error(localize('End of file expected', 'End of file expected.'), jsonLanguageTypes_1.ErrorCode.Undefined);
1228 return new JSONDocument(_root, problems, commentRanges);
1230 exports.parse = parse;