146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ----------------------------------------------------------------------------- 646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// NOTE: If you change this file you need to touch 746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// extension_renderer_resources.grd to have your change take effect. 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ----------------------------------------------------------------------------- 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//============================================================================== 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file contains a class that implements a subset of JSON Schema. 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// See: http://www.json.com/json-schema-proposal/ for more details. 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The following features of JSON Schema are not implemented: 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - requires 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - unique 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - disallow 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - union types (but replaced with 'choices') 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The following properties are not applicable to the interface exposed by 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// this class: 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - options 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - readonly 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - title 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - description 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - format 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - default 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - transient 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - hidden 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// There are also these departures from the JSON Schema proposal: 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - function and undefined types are supported 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - null counts as 'unspecified' for optional values 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - added the 'choices' property, to allow specifying a list of possible types 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// for a value 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - by default an "object" typed schema does not allow additional properties. 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// if present, "additionalProperties" is to be a schema against which all 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// additional properties will be validated. 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//============================================================================== 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var loadTypeSchema = require('utils').loadTypeSchema; 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)var CHECK = requireNative('logging').CHECK; 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function isInstanceOfClass(instance, className) { 45a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) while ((instance = instance.__proto__)) { 46a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) if (instance.constructor.name == className) 47a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return true; 48a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) } 49a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles) return false; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function isOptionalValue(value) { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return typeof(value) === 'undefined' || value === null; 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)function enumToString(enumValue) { 570f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) if (enumValue.name === undefined) 580f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return enumValue; 590f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) 600f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) return enumValue.name; 611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates an instance against a schema and accumulates errors. Usage: 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 66868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) * var validator = new JSONSchemaValidator(); 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * validator.validate(inst, schema); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * if (validator.errors.length == 0) 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * console.log("Valid!"); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * else 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * console.log(validator.errors); 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The errors property contains a list of objects. Each object has two 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * properties: "path" and "message". The "path" property contains the path to 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the key that had the problem, and the "message" property contains a sentence 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * describing the error. 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 78868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)function JSONSchemaValidator() { 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.errors = []; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.types = []; 81868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)} 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 83868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.messages = { 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) invalidEnum: "Value must be one of: [*].", 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) propertyRequired: "Property is required.", 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unexpectedProperty: "Unexpected property.", 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arrayMinItems: "Array must have at least * items.", 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) arrayMaxItems: "Array must not have more than * items.", 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) itemRequired: "Item is required.", 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stringMinLength: "String must be at least * characters long.", 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stringMaxLength: "String must not be more than * characters long.", 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) stringPattern: "String must match the pattern: *.", 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) numberFiniteNotNan: "Value must not be *.", 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) numberMinValue: "Value must not be less than *.", 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) numberMaxValue: "Value must not be greater than *.", 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) numberIntValue: "Value must fit in a 32-bit signed integer.", 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) numberMaxDecimal: "Value must not have more than * decimal places.", 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) invalidType: "Expected '*' but got '*'.", 992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) invalidTypeIntegerNumber: 1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) "Expected 'integer' but got 'number', consider using Math.round().", 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) invalidChoice: "Value does not match any valid type choices.", 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) invalidPropertyType: "Missing property type.", 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) schemaRequired: "Schema value required.", 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unknownSchemaReference: "Unknown schema reference: *.", 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) notInstance: "Object must be an instance of *." 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Builds an error message. Key is the property in the |errors| object, and 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |opt_replacements| is an array of values to replace "*" characters with. 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 112868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.formatError = function(key, opt_replacements) { 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var message = this.messages[key]; 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (opt_replacements) { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < opt_replacements.length; i++) { 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) message = message.replace("*", opt_replacements[i]); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return message; 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Classifies a value as one of the JSON schema primitive types. Note that we 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * don't explicitly disallow 'function', because we want to allow functions in 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the input values. 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.getType = function(value) { 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var s = typeof value; 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (s == "object") { 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (value === null) { 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "null"; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (Object.prototype.toString.call(value) == "[object Array]") { 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "array"; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (typeof(ArrayBuffer) != "undefined" && 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) value.constructor == ArrayBuffer) { 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "binary"; 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (s == "number") { 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (value % 1 == 0) { 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return "integer"; 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return s; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Add types that may be referenced by validated schemas that reference them 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * with "$ref": <typeId>. Each type must be a valid schema and define an 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * "id" property. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.addTypes = function(typeOrTypeList) { 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function addType(validator, type) { 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!type.id) 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) throw new Error("Attempt to addType with missing 'id' property"); 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) validator.types[type.id] = type; 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeOrTypeList instanceof Array) { 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < typeOrTypeList.length; i++) { 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) addType(this, typeOrTypeList[i]); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) addType(this, typeOrTypeList); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns a list of strings of the types that this schema accepts. 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 172868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.getAllTypesForSchema = function(schema) { 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var schemaTypes = []; 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.type) 175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch $Array.push(schemaTypes, schema.type); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.choices) { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < schema.choices.length; i++) { 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var choiceTypes = this.getAllTypesForSchema(schema.choices[i]); 179eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch schemaTypes = $Array.concat(schemaTypes, choiceTypes); 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var ref = schema['$ref']; 1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (ref) { 1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var type = this.getOrAddType(ref); 1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CHECK(type, 'Could not find type ' + ref); 186eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch schemaTypes = $Array.concat(schemaTypes, this.getAllTypesForSchema(type)); 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return schemaTypes; 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 191868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.getOrAddType = function(typeName) { 1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!this.types[typeName]) 1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.types[typeName] = loadTypeSchema(typeName); 1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return this.types[typeName]; 1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}; 1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns true if |schema| would accept an argument of type |type|. 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 200868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.isValidSchemaType = function(type, schema) { 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (type == 'any') 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(kalman): I don't understand this code. How can type be "null"? 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.optional && (type == "null" || type == "undefined")) 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var schemaTypes = this.getAllTypesForSchema(schema); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < schemaTypes.length; i++) { 2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (schemaTypes[i] == "any" || type == schemaTypes[i] || 2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) (type == "integer" && schemaTypes[i] == "number")) 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Returns true if there is a non-null argument that both |schema1| and 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |schema2| would accept. 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 222868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.checkSchemaOverlap = function(schema1, schema2) { 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var schema1Types = this.getAllTypesForSchema(schema1); 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < schema1Types.length; i++) { 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.isValidSchemaType(schema1Types[i], schema2)) 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates an instance against a schema. The instance can be any JavaScript 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * value and will be validated recursively. When this method returns, the 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |errors| property will contain a list of errors, if any. 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 236868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validate = function(instance, schema, opt_path) { 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var path = opt_path || ""; 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!schema) { 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "schemaRequired"); 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If this schema defines itself as reference type, save it in this.types. 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.id) 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.types[schema.id] = schema; 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the schema has an extends property, the instance must validate against 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // that schema too. 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.extends) 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance, schema.extends, path); 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the schema has a $ref property, the instance must validate against 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // that schema too. It must be present in this.types to be referenced. 2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) var ref = schema["$ref"]; 2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (ref) { 2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!this.getOrAddType(ref)) 2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.addError(path, "unknownSchemaReference", [ ref ]); 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.validate(instance, this.getOrAddType(ref), path) 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the schema has a choices property, the instance must validate against at 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // least one of the items in that array. 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.choices) { 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validateChoices(instance, schema, path); 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the schema has an enum property, the instance must be one of those 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // values. 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.enum) { 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.validateEnum(instance, schema, path)) 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.type && schema.type != "any") { 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!this.validateType(instance, schema, path)) 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Type-specific validation. 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (schema.type) { 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case "object": 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validateObject(instance, schema, path); 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case "array": 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validateArray(instance, schema, path); 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case "string": 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validateString(instance, schema, path); 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case "number": 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case "integer": 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validateNumber(instance, schema, path); 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates an instance against a choices schema. The instance must match at 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * least one of the provided choices. 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 304868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateChoices = 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function(instance, schema, path) { 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var originalErrors = this.errors; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < schema.choices.length; i++) { 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.errors = []; 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance, schema.choices[i], path); 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (this.errors.length == 0) { 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.errors = originalErrors; 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.errors = originalErrors; 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "invalidChoice"); 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates an instance against a schema with an enum type. Populates the 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |errors| property, and returns a boolean indicating whether the instance 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * validates. 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 326868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateEnum = function(instance, schema, path) { 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < schema.enum.length; i++) { 3281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (instance === enumToString(schema.enum[i])) 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) this.addError(path, "invalidEnum", 3331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) [schema.enum.map(enumToString).join(", ")]); 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates an instance against an object schema and populates the errors 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * property. 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 341868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateObject = 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function(instance, schema, path) { 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.properties) { 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var prop in schema.properties) { 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It is common in JavaScript to add properties to Object.prototype. This 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // check prevents such additions from being interpreted as required 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // schema properties. 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // TODO(aa): If it ever turns out that we actually want this to work, 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // there are other checks we could put here, like requiring that schema 3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // properties be objects that have a 'type' property. 351eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!$Object.hasOwnProperty(schema.properties, prop)) 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var propPath = path ? path + "." + prop : prop; 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.properties[prop] == undefined) { 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(propPath, "invalidPropertyType"); 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (prop in instance && !isOptionalValue(instance[prop])) { 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance[prop], schema.properties[prop], propPath); 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (!schema.properties[prop].optional) { 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(propPath, "propertyRequired"); 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If "instanceof" property is set, check that this object inherits from 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the specified constructor (function). 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.isInstanceOf) { 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!isInstanceOfClass(instance, schema.isInstanceOf)) 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(propPath, "notInstance", [schema.isInstanceOf]); 3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Exit early from additional property check if "type":"any" is defined. 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.additionalProperties && 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) schema.additionalProperties.type && 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) schema.additionalProperties.type == "any") { 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // By default, additional properties are not allowed on instance objects. This 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // can be overridden by setting the additionalProperties property to a schema 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // which any additional properties must validate against. 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var prop in instance) { 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.properties && prop in schema.properties) 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Any properties inherited through the prototype are ignored. 387eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (!$Object.hasOwnProperty(instance, prop)) 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) continue; 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var propPath = path ? path + "." + prop : prop; 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.additionalProperties) 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance[prop], schema.additionalProperties, propPath); 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(propPath, "unexpectedProperty"); 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates an instance against an array schema and populates the errors 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * property. 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 402868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateArray = function(instance, schema, path) { 403868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) var typeOfItems = JSONSchemaValidator.getType(schema.items); 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeOfItems == 'object') { 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.minItems && instance.length < schema.minItems) { 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "arrayMinItems", [schema.minItems]); 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (typeof schema.maxItems != "undefined" && 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) instance.length > schema.maxItems) { 4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "arrayMaxItems", [schema.maxItems]); 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the items property is a single schema, each item in the array must 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // have that schema. 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < instance.length; i++) { 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance[i], schema.items, path + "." + i); 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (typeOfItems == 'array') { 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the items property is an array of schemas, each item in the array must 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // validate against the corresponding schema. 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = 0; i < schema.items.length; i++) { 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var itemPath = path ? path + "." + i : String(i); 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (i in instance && !isOptionalValue(instance[i])) { 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance[i], schema.items[i], itemPath); 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (!schema.items[i].optional) { 4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(itemPath, "itemRequired"); 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.additionalProperties) { 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (var i = schema.items.length; i < instance.length; i++) { 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) var itemPath = path ? path + "." + i : String(i); 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.validate(instance[i], schema.additionalProperties, itemPath); 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (instance.length > schema.items.length) { 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "arrayMaxItems", [schema.items.length]); 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates a string and populates the errors property. 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 448868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateString = 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function(instance, schema, path) { 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.minLength && instance.length < schema.minLength) 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "stringMinLength", [schema.minLength]); 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.maxLength && instance.length > schema.maxLength) 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "stringMaxLength", [schema.maxLength]); 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.pattern && !schema.pattern.test(instance)) 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "stringPattern", [schema.pattern]); 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates a number and populates the errors property. The instance is 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * assumed to be a number. 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 464868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateNumber = 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function(instance, schema, path) { 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Forbid NaN, +Infinity, and -Infinity. Our APIs don't use them, and 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // JSON serialization encodes them as 'null'. Re-evaluate supporting 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // them if we add an API that could reasonably take them as a parameter. 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (isNaN(instance) || 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) instance == Number.POSITIVE_INFINITY || 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) instance == Number.NEGATIVE_INFINITY ) 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "numberFiniteNotNan", [instance]); 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.minimum !== undefined && instance < schema.minimum) 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "numberMinValue", [schema.minimum]); 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.maximum !== undefined && instance > schema.maximum) 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "numberMaxValue", [schema.maximum]); 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check for integer values outside of -2^31..2^31-1. 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.type === "integer" && (instance | 0) !== instance) 4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "numberIntValue", []); 4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (schema.maxDecimal && instance * Math.pow(10, schema.maxDecimal) % 1) 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "numberMaxDecimal", [schema.maxDecimal]); 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Validates the primitive type of an instance and populates the errors 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * property. Returns true if the instance validates, false otherwise. 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 492868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.validateType = function(instance, schema, path) { 493868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) var actualType = JSONSchemaValidator.getType(instance); 4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (schema.type == actualType || 4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) (schema.type == "number" && actualType == "integer")) { 4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 4972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else if (schema.type == "integer" && actualType == "number") { 4982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) this.addError(path, "invalidTypeIntegerNumber"); 4992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 5002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else { 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.addError(path, "invalidType", [schema.type, actualType]); 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Adds an error message. |key| is an index into the |messages| object. 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * |replacements| is an array of values to replace '*' characters in the 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * message. 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 511868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.addError = function(path, key, replacements) { 512eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch $Array.push(this.errors, { 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) path: path, 514868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) message: JSONSchemaValidator.formatError(key, replacements) 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) }); 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** 5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Resets errors to an empty list so you can call 'validate' again. 5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 521868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)JSONSchemaValidator.prototype.resetErrors = function() { 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) this.errors = []; 5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 524868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 525868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)exports.JSONSchemaValidator = JSONSchemaValidator; 526