public validate()

in language-service/src/parser/jsonParser.ts [164:306]


	public validate(schema: JSONSchema, validationResult: ValidationResult, matchingSchemas: ISchemaCollector): void {
		if (!matchingSchemas.include(this)) {
			return;
		}

		if (Array.isArray(schema.type)) {
			if ((<string[]>schema.type).indexOf(this.type) === -1) {
				//allow numbers to be validated as strings
				let isValid: boolean = false;
				if (this.type === 'number') {
					isValid = (<string[]>schema.type).indexOf('string') >= 0;
				}
				if (!isValid) {
					validationResult.addProblem({
						location: { start: this.start, end: this.end },
						severity: ProblemSeverity.Warning,
						getMessage: () => schema.errorMessage || localize('typeArrayMismatchWarning', 'Incorrect type. Expected one of {0}.', (<string[]>schema.type).join(', '))
					});
				}
			}
		}
		else if (schema.type) {
			if (this.type !== schema.type) {
				//count strings that look like numbers as strings
				if (this.type != "number" || schema.type != "string") {
					validationResult.addProblem({
						location: { start: this.start, end: this.end },
						severity: ProblemSeverity.Warning,
						getMessage: () => schema.errorMessage || localize('typeMismatchWarning', 'Incorrect type. Expected "{0}".', schema.type as string)
					});
				}
			}
		}
		if (Array.isArray(schema.allOf)) {
			schema.allOf.forEach((subSchema) => {
				this.validate(subSchema, validationResult, matchingSchemas);
			});
		}
		if (schema.not) {
			let subValidationResult = new ValidationResult();
			let subMatchingSchemas = matchingSchemas.newSub();
			this.validate(schema.not, subValidationResult, subMatchingSchemas);
			if (!subValidationResult.hasProblems()) {
				validationResult.addProblem({
					location: { start: this.start, end: this.end },
					severity: ProblemSeverity.Warning,
					getMessage: () => localize('notSchemaWarning', "Matches a schema that is not allowed.")
				});
			}
			subMatchingSchemas.schemas.forEach((ms) => {
				ms.inverted = !ms.inverted;
				matchingSchemas.add(ms);
			});
		}

		let testAlternatives = (alternatives: JSONSchema[], maxOneMatch: boolean) => {
			const matches: JSONSchema[] = [];

			const firstPropMatches: JSONSchema[] = this.getFirstPropertyMatches(alternatives);

			const possibleMatches: JSONSchema[] = (Array.isArray(firstPropMatches) && firstPropMatches.length > 0) ? firstPropMatches : alternatives;

			// remember the best match that is used for error messages
			let bestMatch: SchemaMatch = null;
			possibleMatches.forEach((subSchema) => {
				let subValidationResult = new ValidationResult();
				let subMatchingSchemas = matchingSchemas.newSub();

				this.validate(subSchema, subValidationResult, subMatchingSchemas);
				if (!subValidationResult.hasProblems()) {
					matches.push(subSchema);
				}
				if (!bestMatch) {
					bestMatch = { schema: subSchema, validationResult: subValidationResult, matchingSchemas: subMatchingSchemas };
				} else if(this.parserSettings.isKubernetes) {
					bestMatch = alternativeComparison(subValidationResult, bestMatch, subSchema, subMatchingSchemas);
				} else {
					bestMatch = genericComparison(maxOneMatch, subValidationResult, bestMatch, subSchema, subMatchingSchemas);
				}
			});

			if (matches.length > 1 && maxOneMatch && !this.parserSettings.isKubernetes) {
				validationResult.addProblem({
					location: { start: this.start, end: this.start + 1 },
					severity: ProblemSeverity.Warning,
					getMessage: () => localize('oneOfWarning', "Matches multiple schemas when only one must validate.")
				});
			}

			if (bestMatch !== null) {
				validationResult.mergeSubResult(bestMatch.validationResult);
				validationResult.propertiesMatches += bestMatch.validationResult.propertiesMatches;
				validationResult.propertiesValueMatches += bestMatch.validationResult.propertiesValueMatches;
				matchingSchemas.merge(bestMatch.matchingSchemas);
			}
			return matches.length;
		};
		if (Array.isArray(schema.anyOf)) {
			testAlternatives(schema.anyOf, false);
		}
		if (Array.isArray(schema.oneOf)) {
			testAlternatives(schema.oneOf, true);
		}

		if (Array.isArray(schema.enum)) {
			let val = this.getValue();
			//force number values to strings for the comparison
			if (typeof val === "number") {
				val = val.toString();
			}
			let enumValueMatch: boolean = false;
			if (val) {
				const ignoreCase: boolean = ASTNode.getIgnoreValueCase(schema);
				for (const e of schema.enum) {
					if (objects.equals(val, e) ||
					   (ignoreCase && typeof e === "string" && typeof val === "string" && e.toUpperCase() === val.toUpperCase())) {
						enumValueMatch = true;
						break;
					}
				}
			}
			validationResult.enumValues = schema.enum;
			validationResult.enumValueMatch = enumValueMatch;
			if (!enumValueMatch) {
				validationResult.addProblem({
					location: { start: this.start, end: this.end },
					severity: ProblemSeverity.Warning,
					code: ErrorCode.EnumValueMismatch,
					getMessage: () => schema.errorMessage || localize('enumWarning', 'Value is not accepted. Valid values: {0}.', schema.enum.map(v => JSON.stringify(v)).join(', '))
				});
			}
		}

		if (schema.deprecationMessage && this.parent) {
			validationResult.addProblem({
				location: { start: this.parent.start, end: this.parent.end },
				severity: ProblemSeverity.Hint,
				getMessage: () => schema.deprecationMessage
			});
		}

		matchingSchemas.add({ node: this, schema: schema });
	}