diff --git a/ext/js/data/json-schema.js b/ext/js/data/json-schema.js index b5fac452..470348d5 100644 --- a/ext/js/data/json-schema.js +++ b/ext/js/data/json-schema.js @@ -25,8 +25,8 @@ class JsonSchema { this._startSchema = schema; this._rootSchema = typeof rootSchema !== 'undefined' ? rootSchema : schema; this._regexCache = null; - this._valuePath = []; - this._schemaPath = []; + this._valueStack = []; + this._schemaStack = []; this._schemaPush(null, null); this._valuePush(null, null); @@ -58,8 +58,8 @@ class JsonSchema { } validate(value) { - this._schemaPush(null, this._startSchema); - this._valuePush(null, value); + this._schemaPush(this._startSchema, null); + this._valuePush(value, null); try { this._validate(value); } finally { @@ -69,24 +69,24 @@ class JsonSchema { } getValidValueOrDefault(value) { - return this._getValidValueOrDefault(null, value, [{path: null, schema: this._startSchema}]); + return this._getValidValueOrDefault(null, value, {schema: this._startSchema, path: null}); } getObjectPropertySchema(property) { - this._schemaPush(null, this._startSchema); + this._schemaPush(this._startSchema, null); try { - const schemaPath = this._getObjectPropertySchemaPath(property); - return schemaPath !== null ? new JsonSchema(schemaPath[schemaPath.length - 1].schema, this._rootSchema) : null; + const schemaInfo = this._getObjectPropertySchemaPath(property); + return schemaInfo !== null ? new JsonSchema(schemaInfo.schema, this._rootSchema) : null; } finally { this._schemaPop(); } } getArrayItemSchema(index) { - this._schemaPush(null, this._startSchema); + this._schemaPush(this._startSchema, null); try { - const schemaPath = this._getArrayItemSchemaPath(index); - return schemaPath !== null ? new JsonSchema(schemaPath[schemaPath.length - 1].schema, this._rootSchema) : null; + const schemaInfo = this._getArrayItemSchemaPath(index); + return schemaInfo !== null ? new JsonSchema(schemaInfo.schema, this._rootSchema) : null; } finally { this._schemaPop(); } @@ -99,44 +99,44 @@ class JsonSchema { // Stack - _valuePush(path, value) { - this._valuePath.push({path, value}); + _valuePush(value, path) { + this._valueStack.push({value, path}); } _valuePop() { - this._valuePath.pop(); + this._valueStack.pop(); } - _schemaPush(path, schema) { - this._schemaPath.push({path, schema}); + _schemaPush(schema, path) { + this._schemaStack.push({schema, path}); this._schema = schema; } _schemaPop() { - this._schemaPath.pop(); - this._schema = this._schemaPath[this._schemaPath.length - 1].schema; + this._schemaStack.pop(); + this._schema = this._schemaStack[this._schemaStack.length - 1].schema; } // Private _createError(message) { - const valuePath = []; - for (let i = 1, ii = this._valuePath.length; i < ii; ++i) { - const {path, value} = this._valuePath[i]; - valuePath.push({path, value}); + const valueStack = []; + for (let i = 1, ii = this._valueStack.length; i < ii; ++i) { + const {value, path} = this._valueStack[i]; + valueStack.push({value, path}); } - const schemaPath = []; - for (let i = 1, ii = this._schemaPath.length; i < ii; ++i) { - const {path, schema} = this._schemaPath[i]; - schemaPath.push({path, schema}); + const schemaStack = []; + for (let i = 1, ii = this._schemaStack.length; i < ii; ++i) { + const {schema, path} = this._schemaStack[i]; + schemaStack.push({schema, path}); } const error = new Error(message); - error.value = valuePath[valuePath.length - 1].value; + error.value = valueStack[valueStack.length - 1].value; error.schema = this._schema; - error.valuePath = valuePath; - error.schemaPath = schemaPath; + error.valueStack = valueStack; + error.schemaStack = schemaStack; return error; } @@ -167,10 +167,7 @@ class JsonSchema { if (this._isObject(properties)) { const propertySchema = properties[property]; if (this._isObject(propertySchema)) { - return [ - {path: 'properties', schema: properties}, - {path: property, schema: propertySchema} - ]; + return {schema: propertySchema, path: ['properties', property]}; } } @@ -178,26 +175,23 @@ class JsonSchema { if (additionalProperties === false) { return null; } else if (this._isObject(additionalProperties)) { - return [{path: 'additionalProperties', schema: additionalProperties}]; + return {schema: additionalProperties, path: 'additionalProperties'}; } else { const result = this._getUnconstrainedSchema(); - return [{path: null, schema: result}]; + return {schema: result, path: null}; } } _getArrayItemSchemaPath(index) { const {items} = this._schema; if (this._isObject(items)) { - return [{path: 'items', schema: items}]; + return {schema: items, path: 'items'}; } if (Array.isArray(items)) { if (index >= 0 && index < items.length) { const propertySchema = items[index]; if (this._isObject(propertySchema)) { - return [ - {path: 'items', schema: items}, - {path: index, schema: propertySchema} - ]; + return {schema: propertySchema, path: ['items', index]}; } } } @@ -206,10 +200,10 @@ class JsonSchema { if (additionalItems === false) { return null; } else if (this._isObject(additionalItems)) { - return [{path: 'additionalItems', schema: additionalItems}]; + return {schema: additionalItems, path: 'additionalItems'}; } else { const result = this._getUnconstrainedSchema(); - return [{path: null, schema: result}]; + return {schema: result, path: null}; } } @@ -298,7 +292,7 @@ class JsonSchema { if (!this._isObject(ifSchema)) { return; } let okay = true; - this._schemaPush('if', ifSchema); + this._schemaPush(ifSchema, 'if'); try { this._validate(value); } catch (e) { @@ -310,7 +304,7 @@ class JsonSchema { const nextSchema = okay ? this._schema.then : this._schema.else; if (this._isObject(nextSchema)) { return; } - this._schemaPush(okay ? 'then' : 'else', nextSchema); + this._schemaPush(nextSchema, okay ? 'then' : 'else'); try { this._validate(value); } finally { @@ -322,13 +316,13 @@ class JsonSchema { const subSchemas = this._schema.allOf; if (!Array.isArray(subSchemas)) { return; } - this._schemaPush('allOf', subSchemas); + this._schemaPush(subSchemas, 'allOf'); try { for (let i = 0, ii = subSchemas.length; i < ii; ++i) { const subSchema = subSchemas[i]; if (!this._isObject(subSchema)) { continue; } - this._schemaPush(i, subSchema); + this._schemaPush(subSchema, i); try { this._validate(value); } finally { @@ -344,13 +338,13 @@ class JsonSchema { const subSchemas = this._schema.anyOf; if (!Array.isArray(subSchemas)) { return; } - this._schemaPush('anyOf', subSchemas); + this._schemaPush(subSchemas, 'anyOf'); try { for (let i = 0, ii = subSchemas.length; i < ii; ++i) { const subSchema = subSchemas[i]; if (!this._isObject(subSchema)) { continue; } - this._schemaPush(i, subSchema); + this._schemaPush(subSchema, i); try { this._validate(value); return; @@ -371,14 +365,14 @@ class JsonSchema { const subSchemas = this._schema.oneOf; if (!Array.isArray(subSchemas)) { return; } - this._schemaPush('oneOf', subSchemas); + this._schemaPush(subSchemas, 'oneOf'); try { let count = 0; for (let i = 0, ii = subSchemas.length; i < ii; ++i) { const subSchema = subSchemas[i]; if (!this._isObject(subSchema)) { continue; } - this._schemaPush(i, subSchema); + this._schemaPush(subSchema, i); try { this._validate(value); ++count; @@ -401,13 +395,13 @@ class JsonSchema { const subSchemas = this._schema.not; if (!Array.isArray(subSchemas)) { return; } - this._schemaPush('not', subSchemas); + this._schemaPush(subSchemas, 'not'); try { for (let i = 0, ii = subSchemas.length; i < ii; ++i) { const subSchema = subSchemas[i]; if (!this._isObject(subSchema)) { continue; } - this._schemaPush(i, subSchema); + this._schemaPush(subSchema, i); try { this._validate(value); } catch (e) { @@ -527,20 +521,20 @@ class JsonSchema { this._validateArrayContains(value); for (let i = 0; i < length; ++i) { - const schemaPath = this._getArrayItemSchemaPath(i); - if (schemaPath === null) { + const schemaInfo = this._getArrayItemSchemaPath(i); + if (schemaInfo === null) { throw this._createError(`No schema found for array[${i}]`); } const propertyValue = value[i]; - for (const {path, schema} of schemaPath) { this._schemaPush(path, schema); } - this._valuePush(i, propertyValue); + this._schemaPush(schemaInfo.schema, schemaInfo.path); + this._valuePush(propertyValue, i); try { this._validate(propertyValue); } finally { this._valuePop(); - for (let j = 0, jj = schemaPath.length; j < jj; ++j) { this._schemaPop(); } + this._schemaPop(); } } } @@ -549,11 +543,11 @@ class JsonSchema { const containsSchema = this._schema.contains; if (!this._isObject(containsSchema)) { return; } - this._schemaPush('contains', containsSchema); + this._schemaPush(containsSchema, 'contains'); try { for (let i = 0, ii = value.length; i < ii; ++i) { const propertyValue = value[i]; - this._valuePush(i, propertyValue); + this._valuePush(propertyValue, i); try { this._validate(propertyValue); return; @@ -592,20 +586,20 @@ class JsonSchema { } for (const property of properties) { - const schemaPath = this._getObjectPropertySchemaPath(property); - if (schemaPath === null) { + const schemaInfo = this._getObjectPropertySchemaPath(property); + if (schemaInfo === null) { throw this._createError(`No schema found for ${property}`); } const propertyValue = value[property]; - for (const {path, schema} of schemaPath) { this._schemaPush(path, schema); } - this._valuePush(property, propertyValue); + this._schemaPush(schemaInfo.schema, schemaInfo.path); + this._valuePush(propertyValue, property); try { this._validate(propertyValue); } finally { this._valuePop(); - for (let j = 0, jj = schemaPath.length; j < jj; ++j) { this._schemaPop(); } + this._schemaPop(); } } } @@ -643,14 +637,14 @@ class JsonSchema { ); } - _getValidValueOrDefault(path, value, schemaPath) { - this._valuePush(path, value); - for (const {path: path2, schema} of schemaPath) { this._schemaPush(path2, schema); } + _getValidValueOrDefault(path, value, schemaInfo) { + this._schemaPush(schemaInfo.schema, schemaInfo.path); + this._valuePush(value, path); try { return this._getValidValueOrDefaultInner(value); } finally { - for (let i = 0, ii = schemaPath.length; i < ii; ++i) { this._schemaPop(); } this._valuePop(); + this._schemaPop(); } } @@ -688,19 +682,19 @@ class JsonSchema { if (Array.isArray(required)) { for (const property of required) { properties.delete(property); - const schemaPath = this._getObjectPropertySchemaPath(property); - if (schemaPath === null) { continue; } + const schemaInfo = this._getObjectPropertySchemaPath(property); + if (schemaInfo === null) { continue; } const propertyValue = Object.prototype.hasOwnProperty.call(value, property) ? value[property] : void 0; - value[property] = this._getValidValueOrDefault(property, propertyValue, schemaPath); + value[property] = this._getValidValueOrDefault(property, propertyValue, schemaInfo); } } for (const property of properties) { - const schemaPath = this._getObjectPropertySchemaPath(property); - if (schemaPath === null) { + const schemaInfo = this._getObjectPropertySchemaPath(property); + if (schemaInfo === null) { Reflect.deleteProperty(value, property); } else { - value[property] = this._getValidValueOrDefault(property, value[property], schemaPath); + value[property] = this._getValidValueOrDefault(property, value[property], schemaInfo); } } @@ -709,18 +703,18 @@ class JsonSchema { _populateArrayDefaults(value) { for (let i = 0, ii = value.length; i < ii; ++i) { - const schemaPath = this._getArrayItemSchemaPath(i); - if (schemaPath === null) { continue; } + const schemaInfo = this._getArrayItemSchemaPath(i); + if (schemaInfo === null) { continue; } const propertyValue = value[i]; - value[i] = this._getValidValueOrDefault(i, propertyValue, schemaPath); + value[i] = this._getValidValueOrDefault(i, propertyValue, schemaInfo); } const {minItems, maxItems} = this._schema; if (typeof minItems === 'number' && value.length < minItems) { for (let i = value.length; i < minItems; ++i) { - const schemaPath = this._getArrayItemSchemaPath(i); - if (schemaPath === null) { break; } - const item = this._getValidValueOrDefault(i, void 0, schemaPath); + const schemaInfo = this._getArrayItemSchemaPath(i); + if (schemaInfo === null) { break; } + const item = this._getValidValueOrDefault(i, void 0, schemaInfo); value.push(item); } } diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js index 9365683b..a0806a3a 100644 --- a/ext/js/language/dictionary-importer.js +++ b/ext/js/language/dictionary-importer.js @@ -253,8 +253,8 @@ class DictionaryImporter { } _formatSchemaError(e, fileName) { - const valuePathString = this._getSchemaErrorPathString(e.valuePath, 'dictionary'); - const schemaPathString = this._getSchemaErrorPathString(e.schemaPath, 'schema'); + const valuePathString = this._getSchemaErrorPathString(e.valueStack, 'dictionary'); + const schemaPathString = this._getSchemaErrorPathString(e.schemaStack, 'schema'); const e2 = new Error(`Dictionary has invalid data in '${fileName}' for value '${valuePathString}', validated against '${schemaPathString}': ${e.message}`); e2.data = e; @@ -265,16 +265,23 @@ class DictionaryImporter { _getSchemaErrorPathString(infoList, base='') { let result = base; for (const {path} of infoList) { - switch (typeof path) { - case 'string': - if (result.length > 0) { - result += '.'; + const pathArray = Array.isArray(path) ? path : [path]; + for (const pathPart of pathArray) { + if (pathPart === null) { + result = base; + } else { + switch (typeof pathPart) { + case 'string': + if (result.length > 0) { + result += '.'; + } + result += pathPart; + break; + case 'number': + result += `[${pathPart}]`; + break; } - result += path; - break; - case 'number': - result += `[${path}]`; - break; + } } } return result;