[483] | 1 | define(["dojo/_base/kernel", "dojox", "dojo/_base/array"], function(dojo, dojox){ |
---|
| 2 | |
---|
| 3 | dojo.getObject("json.schema", true, dojox); |
---|
| 4 | |
---|
| 5 | |
---|
| 6 | dojox.json.schema.validate = function(/*Any*/instance,/*Object*/schema){ |
---|
| 7 | // summary: |
---|
| 8 | // To use the validator call this with an instance object and an optional schema object. |
---|
| 9 | // If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating), |
---|
| 10 | // that schema will be used to validate and the schema parameter is not necessary (if both exist, |
---|
| 11 | // both validations will occur). |
---|
| 12 | // instance: |
---|
| 13 | // The instance value/object to validate |
---|
| 14 | // schema: |
---|
| 15 | // The schema to use to validate |
---|
| 16 | // description: |
---|
| 17 | // The validate method will return an object with two properties: |
---|
| 18 | // |
---|
| 19 | // - valid: A boolean indicating if the instance is valid by the schema |
---|
| 20 | // - errors: An array of validation errors. If there are no errors, then an |
---|
| 21 | // empty list will be returned. A validation error will have two properties: |
---|
| 22 | // - property: which indicates which property had the error |
---|
| 23 | // - message: which indicates what the error was |
---|
| 24 | |
---|
| 25 | return this._validate(instance,schema,false); |
---|
| 26 | }; |
---|
| 27 | dojox.json.schema.checkPropertyChange = function(/*Any*/value,/*Object*/schema, /*String*/ property){ |
---|
| 28 | // summary: |
---|
| 29 | // The checkPropertyChange method will check to see if an value can legally be in property with the given schema |
---|
| 30 | // This is slightly different than the validate method in that it will fail if the schema is readonly and it will |
---|
| 31 | // not check for self-validation, it is assumed that the passed in value is already internally valid. |
---|
| 32 | // The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for |
---|
| 33 | // information. |
---|
| 34 | // value: |
---|
| 35 | // The new instance value/object to check |
---|
| 36 | // schema: |
---|
| 37 | // The schema to use to validate |
---|
| 38 | // returns: |
---|
| 39 | // see dojox.validate.jsonSchema.validate |
---|
| 40 | |
---|
| 41 | return this._validate(value,schema, property || "property"); |
---|
| 42 | }; |
---|
| 43 | dojox.json.schema.mustBeValid = function(result){ |
---|
| 44 | // summary: |
---|
| 45 | // This checks to ensure that the result is valid and will throw an appropriate error message if it is not |
---|
| 46 | // result: the result returned from checkPropertyChange or validate |
---|
| 47 | if(!result.valid){ |
---|
| 48 | throw new TypeError(dojo.map(result.errors,function(error){return "for property " + error.property + ': ' + error.message;}).join(", ")); |
---|
| 49 | } |
---|
| 50 | }; |
---|
| 51 | dojox.json.schema._validate = function(/*Any*/instance,/*Object*/schema,/*Boolean*/ _changing){ |
---|
| 52 | |
---|
| 53 | var errors = []; |
---|
| 54 | // validate a value against a property definition |
---|
| 55 | function checkProp(value, schema, path,i){ |
---|
| 56 | var l; |
---|
| 57 | path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i; |
---|
| 58 | function addError(message){ |
---|
| 59 | errors.push({property:path,message:message}); |
---|
| 60 | } |
---|
| 61 | |
---|
| 62 | if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function')){ |
---|
| 63 | if(typeof schema == 'function'){ |
---|
| 64 | if(!(Object(value) instanceof schema)){ |
---|
| 65 | addError("is not an instance of the class/constructor " + schema.name); |
---|
| 66 | } |
---|
| 67 | }else if(schema){ |
---|
| 68 | addError("Invalid schema/property definition " + schema); |
---|
| 69 | } |
---|
| 70 | return null; |
---|
| 71 | } |
---|
| 72 | if(_changing && schema.readonly){ |
---|
| 73 | addError("is a readonly field, it can not be changed"); |
---|
| 74 | } |
---|
| 75 | if(schema['extends']){ // if it extends another schema, it must pass that schema as well |
---|
| 76 | checkProp(value,schema['extends'],path,i); |
---|
| 77 | } |
---|
| 78 | // validate a value against a type definition |
---|
| 79 | function checkType(type,value){ |
---|
| 80 | if(type){ |
---|
| 81 | if(typeof type == 'string' && type != 'any' && |
---|
| 82 | (type == 'null' ? value !== null : typeof value != type) && |
---|
| 83 | !(value instanceof Array && type == 'array') && |
---|
| 84 | !(type == 'integer' && value%1===0)){ |
---|
| 85 | return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}]; |
---|
| 86 | } |
---|
| 87 | if(type instanceof Array){ |
---|
| 88 | var unionErrors=[]; |
---|
| 89 | for(var j = 0; j < type.length; j++){ // a union type |
---|
| 90 | if(!(unionErrors=checkType(type[j],value)).length){ |
---|
| 91 | break; |
---|
| 92 | } |
---|
| 93 | } |
---|
| 94 | if(unionErrors.length){ |
---|
| 95 | return unionErrors; |
---|
| 96 | } |
---|
| 97 | }else if(typeof type == 'object'){ |
---|
| 98 | var priorErrors = errors; |
---|
| 99 | errors = []; |
---|
| 100 | checkProp(value,type,path); |
---|
| 101 | var theseErrors = errors; |
---|
| 102 | errors = priorErrors; |
---|
| 103 | return theseErrors; |
---|
| 104 | } |
---|
| 105 | } |
---|
| 106 | return []; |
---|
| 107 | } |
---|
| 108 | if(value === undefined){ |
---|
| 109 | if(!schema.optional){ |
---|
| 110 | addError("is missing and it is not optional"); |
---|
| 111 | } |
---|
| 112 | }else{ |
---|
| 113 | errors = errors.concat(checkType(schema.type,value)); |
---|
| 114 | if(schema.disallow && !checkType(schema.disallow,value).length){ |
---|
| 115 | addError(" disallowed value was matched"); |
---|
| 116 | } |
---|
| 117 | if(value !== null){ |
---|
| 118 | if(value instanceof Array){ |
---|
| 119 | if(schema.items){ |
---|
| 120 | if(schema.items instanceof Array){ |
---|
| 121 | for(i=0,l=value.length; i<l; i++){ |
---|
| 122 | errors.concat(checkProp(value[i],schema.items[i],path,i)); |
---|
| 123 | } |
---|
| 124 | }else{ |
---|
| 125 | for(i=0,l=value.length; i<l; i++){ |
---|
| 126 | errors.concat(checkProp(value[i],schema.items,path,i)); |
---|
| 127 | } |
---|
| 128 | } |
---|
| 129 | } |
---|
| 130 | if(schema.minItems && value.length < schema.minItems){ |
---|
| 131 | addError("There must be a minimum of " + schema.minItems + " in the array"); |
---|
| 132 | } |
---|
| 133 | if(schema.maxItems && value.length > schema.maxItems){ |
---|
| 134 | addError("There must be a maximum of " + schema.maxItems + " in the array"); |
---|
| 135 | } |
---|
| 136 | }else if(schema.properties){ |
---|
| 137 | errors.concat(checkObj(value,schema.properties,path,schema.additionalProperties)); |
---|
| 138 | } |
---|
| 139 | if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){ |
---|
| 140 | addError("does not match the regex pattern " + schema.pattern); |
---|
| 141 | } |
---|
| 142 | if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){ |
---|
| 143 | addError("may only be " + schema.maxLength + " characters long"); |
---|
| 144 | } |
---|
| 145 | if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){ |
---|
| 146 | addError("must be at least " + schema.minLength + " characters long"); |
---|
| 147 | } |
---|
| 148 | if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum && |
---|
| 149 | schema.minimum > value){ |
---|
| 150 | addError("must have a minimum value of " + schema.minimum); |
---|
| 151 | } |
---|
| 152 | if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum && |
---|
| 153 | schema.maximum < value){ |
---|
| 154 | addError("must have a maximum value of " + schema.maximum); |
---|
| 155 | } |
---|
| 156 | if(schema['enum']){ |
---|
| 157 | var enumer = schema['enum']; |
---|
| 158 | l = enumer.length; |
---|
| 159 | var found; |
---|
| 160 | for(var j = 0; j < l; j++){ |
---|
| 161 | if(enumer[j]===value){ |
---|
| 162 | found=1; |
---|
| 163 | break; |
---|
| 164 | } |
---|
| 165 | } |
---|
| 166 | if(!found){ |
---|
| 167 | addError("does not have a value in the enumeration " + enumer.join(", ")); |
---|
| 168 | } |
---|
| 169 | } |
---|
| 170 | if(typeof schema.maxDecimal == 'number' && |
---|
| 171 | (value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){ |
---|
| 172 | addError("may only have " + schema.maxDecimal + " digits of decimal places"); |
---|
| 173 | } |
---|
| 174 | } |
---|
| 175 | } |
---|
| 176 | return null; |
---|
| 177 | } |
---|
| 178 | // validate an object against a schema |
---|
| 179 | function checkObj(instance,objTypeDef,path,additionalProp){ |
---|
| 180 | |
---|
| 181 | if(typeof objTypeDef =='object'){ |
---|
| 182 | if(typeof instance != 'object' || instance instanceof Array){ |
---|
| 183 | errors.push({property:path,message:"an object is required"}); |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | for(var i in objTypeDef){ |
---|
| 187 | if(objTypeDef.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_')){ |
---|
| 188 | var value = instance[i]; |
---|
| 189 | var propDef = objTypeDef[i]; |
---|
| 190 | checkProp(value,propDef,path,i); |
---|
| 191 | } |
---|
| 192 | } |
---|
| 193 | } |
---|
| 194 | for(i in instance){ |
---|
| 195 | if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){ |
---|
| 196 | errors.push({property:path,message:(typeof value) + "The property " + i + |
---|
| 197 | " is not defined in the schema and the schema does not allow additional properties"}); |
---|
| 198 | } |
---|
| 199 | var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires; |
---|
| 200 | if(requires && !(requires in instance)){ |
---|
| 201 | errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"}); |
---|
| 202 | } |
---|
| 203 | value = instance[i]; |
---|
| 204 | if(objTypeDef && typeof objTypeDef == 'object' && !(i in objTypeDef)){ |
---|
| 205 | checkProp(value,additionalProp,path,i); |
---|
| 206 | } |
---|
| 207 | if(!_changing && value && value.$schema){ |
---|
| 208 | errors = errors.concat(checkProp(value,value.$schema,path,i)); |
---|
| 209 | } |
---|
| 210 | } |
---|
| 211 | return errors; |
---|
| 212 | } |
---|
| 213 | if(schema){ |
---|
| 214 | checkProp(instance,schema,'',_changing || ''); |
---|
| 215 | } |
---|
| 216 | if(!_changing && instance && instance.$schema){ |
---|
| 217 | checkProp(instance,instance.$schema,'',''); |
---|
| 218 | } |
---|
| 219 | return {valid:!errors.length,errors:errors}; |
---|
| 220 | }; |
---|
| 221 | |
---|
| 222 | return dojox.json.schema; |
---|
| 223 | }); |
---|