Rename folders
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import {TemplateSchema} from ".";
|
||||
import {DEFINITION, BOOLEAN} from "../template-constants";
|
||||
import {MappingToken, LiteralToken} from "../tokens";
|
||||
import {TokenType} from "../tokens/types";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {ScalarDefinition} from "./scalar-definition";
|
||||
|
||||
export class BooleanDefinition extends ScalarDefinition {
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case BOOLEAN: {
|
||||
const mapping = definitionPair.value.assertMapping(`${DEFINITION} ${BOOLEAN}`);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${DEFINITION} ${BOOLEAN} key`);
|
||||
switch (mappingKey.value) {
|
||||
default:
|
||||
// throws
|
||||
mappingKey.assertUnexpectedValue(`${DEFINITION} ${BOOLEAN} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.Boolean;
|
||||
}
|
||||
|
||||
public override isMatch(literal: LiteralToken): boolean {
|
||||
return literal.templateTokenType === TokenType.Boolean;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import {TemplateSchema} from ".";
|
||||
import {Definition} from "./definition";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {ScalarDefinition} from "./scalar-definition";
|
||||
|
||||
export class DefinitionInfo {
|
||||
private readonly _schema: TemplateSchema;
|
||||
public readonly isDefinitionInfo = true;
|
||||
public readonly definition: Definition;
|
||||
public readonly allowedContext: string[];
|
||||
|
||||
public constructor(schema: TemplateSchema, name: string);
|
||||
public constructor(parent: DefinitionInfo, name: string);
|
||||
public constructor(parent: DefinitionInfo, definition: Definition);
|
||||
public constructor(schemaOrParent: TemplateSchema | DefinitionInfo, nameOrDefinition: string | Definition) {
|
||||
const parent: DefinitionInfo | undefined =
|
||||
(schemaOrParent as DefinitionInfo | undefined)?.isDefinitionInfo === true
|
||||
? (schemaOrParent as DefinitionInfo)
|
||||
: undefined;
|
||||
this._schema = parent === undefined ? (schemaOrParent as TemplateSchema) : parent._schema;
|
||||
|
||||
// Lookup the definition if a key was passed in
|
||||
this.definition =
|
||||
typeof nameOrDefinition === "string" ? this._schema.getDefinition(nameOrDefinition) : nameOrDefinition;
|
||||
|
||||
// Record allowed context
|
||||
if (this.definition.readerContext.length > 0) {
|
||||
this.allowedContext = [];
|
||||
|
||||
// Copy parent allowed context
|
||||
const upperSeen: {[upper: string]: boolean} = {};
|
||||
for (const context of parent?.allowedContext ?? []) {
|
||||
this.allowedContext.push(context);
|
||||
upperSeen[context.toUpperCase()] = true;
|
||||
}
|
||||
|
||||
// Append context if unseen
|
||||
for (const context of this.definition.readerContext) {
|
||||
const upper = context.toUpperCase();
|
||||
if (!upperSeen[upper]) {
|
||||
this.allowedContext.push(context);
|
||||
upperSeen[upper] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.allowedContext = parent?.allowedContext ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
public getScalarDefinitions(): ScalarDefinition[] {
|
||||
return this._schema.getScalarDefinitions(this.definition);
|
||||
}
|
||||
|
||||
public getDefinitionsOfType(type: DefinitionType): Definition[] {
|
||||
return this._schema.getDefinitionsOfType(this.definition, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export enum DefinitionType {
|
||||
Null,
|
||||
Boolean,
|
||||
Number,
|
||||
String,
|
||||
Sequence,
|
||||
Mapping,
|
||||
OneOf,
|
||||
AllowedValues
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import {CONTEXT, DEFINITION, DESCRIPTION} from "../template-constants";
|
||||
import {MappingToken} from "../tokens";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
|
||||
/**
|
||||
* Defines the allowable schema for a user defined type
|
||||
*/
|
||||
export abstract class Definition {
|
||||
/**
|
||||
* Used by the template reader to determine allowed expression values and functions.
|
||||
* Also used by the template reader to validate function min/max parameters.
|
||||
*/
|
||||
public readonly readerContext: string[] = [];
|
||||
|
||||
/**
|
||||
* Used by the template evaluator to determine allowed expression values and functions.
|
||||
* The min/max parameter info is omitted.
|
||||
*/
|
||||
public readonly evaluatorContext: string[] = [];
|
||||
|
||||
// A key to uniquely identify a definition
|
||||
public readonly key: string;
|
||||
|
||||
public readonly description: string | undefined;
|
||||
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
this.key = key;
|
||||
if (definition) {
|
||||
for (let i = 0; i < definition.count; ) {
|
||||
const definitionKey = definition.get(i).key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case CONTEXT: {
|
||||
const context = definition.get(i).value.assertSequence(`${DEFINITION} ${CONTEXT}`);
|
||||
definition.remove(i);
|
||||
const seenReaderContext: {[key: string]: boolean} = {};
|
||||
const seenEvaluatorContext: {[key: string]: boolean} = {};
|
||||
for (const item of context) {
|
||||
const itemStr = item.assertString(`${CONTEXT} item`).value;
|
||||
const upperItemStr = itemStr.toUpperCase();
|
||||
if (seenReaderContext[upperItemStr]) {
|
||||
throw new Error(`Duplicate context item '${itemStr}'`);
|
||||
}
|
||||
seenReaderContext[upperItemStr] = true;
|
||||
this.readerContext.push(itemStr);
|
||||
|
||||
// Remove min/max parameter info
|
||||
const paramIndex = itemStr.indexOf("(");
|
||||
const modifiedItemStr = paramIndex > 0 ? itemStr.substr(0, paramIndex + 1) + ")" : itemStr;
|
||||
const upperModifiedItemStr = modifiedItemStr.toUpperCase();
|
||||
if (seenEvaluatorContext[upperModifiedItemStr]) {
|
||||
throw new Error(`Duplicate context item '${modifiedItemStr}'`);
|
||||
}
|
||||
seenEvaluatorContext[upperModifiedItemStr] = true;
|
||||
this.evaluatorContext.push(modifiedItemStr);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case DESCRIPTION: {
|
||||
const value = definition.get(i).value;
|
||||
this.description = value.assertString(DESCRIPTION).value;
|
||||
definition.remove(i);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract get definitionType(): DefinitionType;
|
||||
|
||||
public abstract validate(schema: TemplateSchema, name: string): void;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export {TemplateSchema} from "./template-schema";
|
||||
@@ -0,0 +1,90 @@
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
import {DEFINITION, MAPPING, PROPERTIES, LOOSE_KEY_TYPE, LOOSE_VALUE_TYPE} from "../template-constants";
|
||||
import {MappingToken} from "../tokens";
|
||||
import {Definition} from "./definition";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {PropertyDefinition} from "./property-definition";
|
||||
|
||||
export class MappingDefinition extends Definition {
|
||||
public readonly properties: {[key: string]: PropertyDefinition} = {};
|
||||
public looseKeyType = "";
|
||||
public looseValueType = "";
|
||||
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case MAPPING: {
|
||||
const mapping = definitionPair.value.assertMapping(`${DEFINITION} ${MAPPING}`);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${DEFINITION} ${MAPPING} key`);
|
||||
switch (mappingKey.value) {
|
||||
case PROPERTIES: {
|
||||
const properties = mappingPair.value.assertMapping(`${DEFINITION} ${MAPPING} ${PROPERTIES}`);
|
||||
for (const propertiesPair of properties) {
|
||||
const propertyName = propertiesPair.key.assertString(`${DEFINITION} ${MAPPING} ${PROPERTIES} key`);
|
||||
this.properties[propertyName.value] = new PropertyDefinition(propertiesPair.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LOOSE_KEY_TYPE: {
|
||||
const looseKeyType = mappingPair.value.assertString(`${DEFINITION} ${MAPPING} ${LOOSE_KEY_TYPE}`);
|
||||
this.looseKeyType = looseKeyType.value;
|
||||
break;
|
||||
}
|
||||
case LOOSE_VALUE_TYPE: {
|
||||
const looseValueType = mappingPair.value.assertString(`${DEFINITION} ${MAPPING} ${LOOSE_VALUE_TYPE}`);
|
||||
this.looseValueType = looseValueType.value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// throws
|
||||
mappingKey.assertUnexpectedValue(`${DEFINITION} ${MAPPING} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.Mapping;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {
|
||||
// Lookup loose key type
|
||||
if (this.looseKeyType) {
|
||||
schema.getDefinition(this.looseKeyType);
|
||||
|
||||
// Lookup loose value type
|
||||
if (this.looseValueType) {
|
||||
schema.getDefinition(this.looseValueType);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Property '${LOOSE_KEY_TYPE}' is defined but '${LOOSE_VALUE_TYPE}' is not defined on '${name}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
// Otherwise validate loose value type not be defined
|
||||
else if (this.looseValueType) {
|
||||
throw new Error(`Property '${LOOSE_VALUE_TYPE}' is defined but '${LOOSE_KEY_TYPE}' is not defined on '${name}'`);
|
||||
}
|
||||
|
||||
// Lookup each property
|
||||
for (const propertyName of Object.keys(this.properties)) {
|
||||
const propertyDef = this.properties[propertyName];
|
||||
if (!propertyDef.type) {
|
||||
throw new Error(`Type not specified for the property '${propertyName}' on '${name}'`);
|
||||
}
|
||||
|
||||
schema.getDefinition(propertyDef.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
import {DEFINITION, NULL} from "../template-constants";
|
||||
import {MappingToken, LiteralToken} from "../tokens";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {ScalarDefinition} from "./scalar-definition";
|
||||
import {TokenType} from "../tokens/types";
|
||||
|
||||
export class NullDefinition extends ScalarDefinition {
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case NULL: {
|
||||
const mapping = definitionPair.value.assertMapping(`${DEFINITION} ${NULL}`);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${DEFINITION} ${NULL} key`);
|
||||
switch (mappingKey.value) {
|
||||
default:
|
||||
// throws
|
||||
mappingKey.assertUnexpectedValue(`${DEFINITION} ${NULL} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.Null;
|
||||
}
|
||||
|
||||
public override isMatch(literal: LiteralToken): boolean {
|
||||
return literal.templateTokenType === TokenType.Null;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
import {DEFINITION, NUMBER} from "../template-constants";
|
||||
import {MappingToken, LiteralToken} from "../tokens";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {ScalarDefinition} from "./scalar-definition";
|
||||
import {TokenType} from "../tokens/types";
|
||||
|
||||
export class NumberDefinition extends ScalarDefinition {
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case NUMBER: {
|
||||
const mapping = definitionPair.value.assertMapping(`${DEFINITION} ${NUMBER}`);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${DEFINITION} ${NUMBER} key`);
|
||||
switch (mappingKey.value) {
|
||||
default:
|
||||
// throws
|
||||
mappingKey.assertUnexpectedValue(`${DEFINITION} ${NUMBER} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.Number;
|
||||
}
|
||||
|
||||
public override isMatch(literal: LiteralToken): boolean {
|
||||
return literal.templateTokenType === TokenType.Number;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
import {
|
||||
DEFINITION,
|
||||
ONE_OF,
|
||||
SEQUENCE,
|
||||
NULL,
|
||||
BOOLEAN,
|
||||
NUMBER,
|
||||
SCALAR,
|
||||
CONSTANT,
|
||||
LOOSE_KEY_TYPE,
|
||||
ALLOWED_VALUES
|
||||
} from "../template-constants";
|
||||
import {MappingToken} from "../tokens";
|
||||
import {BooleanDefinition} from "./boolean-definition";
|
||||
import {Definition} from "./definition";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {MappingDefinition} from "./mapping-definition";
|
||||
import {NullDefinition} from "./null-definition";
|
||||
import {NumberDefinition} from "./number-definition";
|
||||
import {SequenceDefinition} from "./sequence-definition";
|
||||
import {StringDefinition} from "./string-definition";
|
||||
import {PropertyDefinition} from "./property-definition";
|
||||
|
||||
/**
|
||||
* Must resolve to exactly one of the referenced definitions
|
||||
*/
|
||||
export class OneOfDefinition extends Definition {
|
||||
public readonly oneOf: string[] = [];
|
||||
public readonly oneOfPrefix: string[] = [];
|
||||
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case ONE_OF: {
|
||||
const oneOf = definitionPair.value.assertSequence(`${DEFINITION} ${ONE_OF}`);
|
||||
for (const item of oneOf) {
|
||||
const oneOfItem = item.assertString(`${DEFINITION} ${ONE_OF} item`);
|
||||
this.oneOf.push(oneOfItem.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ALLOWED_VALUES: {
|
||||
const oneOf = definitionPair.value.assertSequence(`${DEFINITION} ${ALLOWED_VALUES}`);
|
||||
for (const item of oneOf) {
|
||||
const oneOfItem = item.assertString(`${DEFINITION} ${ONE_OF} item`);
|
||||
this.oneOf.push(this.key + "-" + oneOfItem.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// throws
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.OneOf;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {
|
||||
if (this.oneOf.length === 0) {
|
||||
throw new Error(`'${name}' does not contain any references`);
|
||||
}
|
||||
|
||||
let foundLooseKeyType = false;
|
||||
const mappingDefinitions: MappingDefinition[] = [];
|
||||
let allowedValuesDefinition: OneOfDefinition | undefined;
|
||||
let sequenceDefinition: SequenceDefinition | undefined;
|
||||
let nullDefinition: NullDefinition | undefined;
|
||||
let booleanDefinition: BooleanDefinition | undefined;
|
||||
let numberDefinition: NumberDefinition | undefined;
|
||||
const stringDefinitions: StringDefinition[] = [];
|
||||
const seenNestedTypes: {[key: string]: boolean} = {};
|
||||
|
||||
for (const nestedType of this.oneOf) {
|
||||
if (seenNestedTypes[nestedType]) {
|
||||
throw new Error(`'${name}' contains duplicate nested type '${nestedType}'`);
|
||||
}
|
||||
seenNestedTypes[nestedType] = true;
|
||||
|
||||
const nestedDefinition = schema.getDefinition(nestedType);
|
||||
if (nestedDefinition.readerContext.length > 0) {
|
||||
throw new Error(
|
||||
`'${name}' is a one-of definition and references another definition that defines context. This is currently not supported.`
|
||||
);
|
||||
}
|
||||
|
||||
switch (nestedDefinition.definitionType) {
|
||||
case DefinitionType.Mapping: {
|
||||
const mappingDefinition = nestedDefinition as MappingDefinition;
|
||||
mappingDefinitions.push(mappingDefinition);
|
||||
if (mappingDefinition.looseKeyType) {
|
||||
foundLooseKeyType = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DefinitionType.Sequence: {
|
||||
// Multiple sequence definitions not allowed
|
||||
if (sequenceDefinition) {
|
||||
throw new Error(`'${name}' refers to more than one definition of type '${SEQUENCE}'`);
|
||||
}
|
||||
sequenceDefinition = nestedDefinition as SequenceDefinition;
|
||||
break;
|
||||
}
|
||||
case DefinitionType.Null: {
|
||||
// Multiple null definitions not allowed
|
||||
if (nullDefinition) {
|
||||
throw new Error(`'${name}' refers to more than one definition of type '${NULL}'`);
|
||||
}
|
||||
nullDefinition = nestedDefinition as NullDefinition;
|
||||
break;
|
||||
}
|
||||
case DefinitionType.Boolean: {
|
||||
// Multiple boolean definitions not allowed
|
||||
if (booleanDefinition) {
|
||||
throw new Error(`'${name}' refers to more than one definition of type '${BOOLEAN}'`);
|
||||
}
|
||||
booleanDefinition = nestedDefinition as BooleanDefinition;
|
||||
break;
|
||||
}
|
||||
case DefinitionType.Number: {
|
||||
// Multiple number definitions not allowed
|
||||
if (numberDefinition) {
|
||||
throw new Error(`'${name}' refers to more than one definition of type '${NUMBER}'`);
|
||||
}
|
||||
numberDefinition = nestedDefinition as NumberDefinition;
|
||||
break;
|
||||
}
|
||||
case DefinitionType.String: {
|
||||
const stringDefinition = nestedDefinition as StringDefinition;
|
||||
|
||||
// Multiple string definitions
|
||||
if (stringDefinitions.length > 0 && (!stringDefinitions[0].constant || !stringDefinition.constant)) {
|
||||
throw new Error(`'${name}' refers to more than one '${SCALAR}', but some do not set '${CONSTANT}'`);
|
||||
}
|
||||
|
||||
stringDefinitions.push(stringDefinition);
|
||||
break;
|
||||
}
|
||||
case DefinitionType.OneOf: {
|
||||
// Multiple allowed-values definitions not allowed
|
||||
if (allowedValuesDefinition) {
|
||||
throw new Error(`'${name}' contains multiple allowed-values definitions`);
|
||||
}
|
||||
allowedValuesDefinition = nestedDefinition as OneOfDefinition;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`'${name}' refers to a definition with type '${nestedDefinition.definitionType}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (mappingDefinitions.length > 1) {
|
||||
if (foundLooseKeyType) {
|
||||
throw new Error(
|
||||
`'${name}' refers to two mappings and at least one sets '${LOOSE_KEY_TYPE}'. This is not currently supported.`
|
||||
);
|
||||
}
|
||||
|
||||
const seenProperties: {[key: string]: PropertyDefinition} = {};
|
||||
for (const mappingDefinition of mappingDefinitions) {
|
||||
for (const propertyName of Object.keys(mappingDefinition.properties)) {
|
||||
const newPropertyDef = mappingDefinition.properties[propertyName];
|
||||
|
||||
// Already seen
|
||||
const existingPropertyDef: PropertyDefinition | undefined = seenProperties[propertyName];
|
||||
if (existingPropertyDef) {
|
||||
// Types match
|
||||
if (existingPropertyDef.type === newPropertyDef.type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Collision
|
||||
throw new Error(
|
||||
`'${name}' contains two mappings with the same property, but each refers to a different type. All matching properties must refer to the same type.`
|
||||
);
|
||||
}
|
||||
// New
|
||||
else {
|
||||
seenProperties[propertyName] = newPropertyDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import {MAPPING_PROPERTY_VALUE, TYPE, REQUIRED, DESCRIPTION} from "../template-constants";
|
||||
import {TemplateToken, StringToken} from "../tokens";
|
||||
import {TokenType} from "../tokens/types";
|
||||
|
||||
export class PropertyDefinition {
|
||||
public readonly type: string = "";
|
||||
public readonly required: boolean = false;
|
||||
public readonly description: string | undefined;
|
||||
|
||||
public constructor(token: TemplateToken) {
|
||||
if (token.templateTokenType === TokenType.String) {
|
||||
this.type = (token as StringToken).value;
|
||||
} else {
|
||||
const mapping = token.assertMapping(MAPPING_PROPERTY_VALUE);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${MAPPING_PROPERTY_VALUE} key`);
|
||||
switch (mappingKey.value) {
|
||||
case TYPE:
|
||||
this.type = mappingPair.value.assertString(`${MAPPING_PROPERTY_VALUE} ${TYPE}`).value;
|
||||
break;
|
||||
case REQUIRED:
|
||||
this.required = mappingPair.value.assertBoolean(`${MAPPING_PROPERTY_VALUE} ${REQUIRED}`).value;
|
||||
break;
|
||||
case DESCRIPTION:
|
||||
this.description = mappingPair.value.assertString(`${MAPPING_PROPERTY_VALUE} ${DESCRIPTION}`).value;
|
||||
break;
|
||||
default:
|
||||
mappingKey.assertUnexpectedValue(`${MAPPING_PROPERTY_VALUE} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {LiteralToken, MappingToken} from "../tokens";
|
||||
import {Definition} from "./definition";
|
||||
|
||||
export abstract class ScalarDefinition extends Definition {
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
}
|
||||
|
||||
public abstract isMatch(literal: LiteralToken): boolean;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import {DEFINITION, SEQUENCE, ITEM_TYPE} from "../template-constants";
|
||||
import {MappingToken} from "../tokens";
|
||||
import {Definition} from "./definition";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
|
||||
export class SequenceDefinition extends Definition {
|
||||
public itemType = "";
|
||||
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case SEQUENCE: {
|
||||
const mapping = definitionPair.value.assertMapping(`${DEFINITION} ${SEQUENCE}`);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${DEFINITION} ${SEQUENCE} key`);
|
||||
switch (mappingKey.value) {
|
||||
case ITEM_TYPE: {
|
||||
const itemType = mappingPair.value.assertString(`${DEFINITION} ${SEQUENCE} ${ITEM_TYPE}`);
|
||||
this.itemType = itemType.value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// throws
|
||||
mappingKey.assertUnexpectedValue(`${DEFINITION} ${SEQUENCE} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.Sequence;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {
|
||||
if (!this.itemType) {
|
||||
throw new Error(`'${name}' does not defined '${ITEM_TYPE}'`);
|
||||
}
|
||||
|
||||
// Lookup item type
|
||||
schema.getDefinition(this.itemType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import {CONSTANT, DEFINITION, IGNORE_CASE, IS_EXPRESSION, REQUIRE_NON_EMPTY, STRING} from "../template-constants";
|
||||
import {LiteralToken, MappingToken, StringToken} from "../tokens";
|
||||
import {TokenType} from "../tokens/types";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {ScalarDefinition} from "./scalar-definition";
|
||||
import {TemplateSchema} from "./template-schema";
|
||||
|
||||
export class StringDefinition extends ScalarDefinition {
|
||||
public constant = "";
|
||||
public ignoreCase = false;
|
||||
public requireNonEmpty = false;
|
||||
public isExpression = false;
|
||||
|
||||
public constructor(key: string, definition?: MappingToken) {
|
||||
super(key, definition);
|
||||
if (definition) {
|
||||
for (const definitionPair of definition) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
switch (definitionKey.value) {
|
||||
case STRING: {
|
||||
const mapping = definitionPair.value.assertMapping(`${DEFINITION} ${STRING}`);
|
||||
for (const mappingPair of mapping) {
|
||||
const mappingKey = mappingPair.key.assertString(`${DEFINITION} ${STRING} key`);
|
||||
switch (mappingKey.value) {
|
||||
case CONSTANT: {
|
||||
const constantStringToken = mappingPair.value.assertString(`${DEFINITION} ${STRING} ${CONSTANT}`);
|
||||
this.constant = constantStringToken.value;
|
||||
break;
|
||||
}
|
||||
case IGNORE_CASE: {
|
||||
const ignoreCaseBooleanToken = mappingPair.value.assertBoolean(
|
||||
`${DEFINITION} ${STRING} ${IGNORE_CASE}`
|
||||
);
|
||||
this.ignoreCase = ignoreCaseBooleanToken.value;
|
||||
break;
|
||||
}
|
||||
case REQUIRE_NON_EMPTY: {
|
||||
const requireNonEmptyBooleanToken = mappingPair.value.assertBoolean(
|
||||
`${DEFINITION} ${STRING} ${REQUIRE_NON_EMPTY}`
|
||||
);
|
||||
this.requireNonEmpty = requireNonEmptyBooleanToken.value;
|
||||
break;
|
||||
}
|
||||
case IS_EXPRESSION: {
|
||||
const isExpressionToken = mappingPair.value.assertBoolean(`${DEFINITION} ${STRING} ${IS_EXPRESSION}`);
|
||||
this.isExpression = isExpressionToken.value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// throws
|
||||
mappingKey.assertUnexpectedValue(`${DEFINITION} ${STRING} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} key`); // throws
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override get definitionType(): DefinitionType {
|
||||
return DefinitionType.String;
|
||||
}
|
||||
|
||||
public override isMatch(literal: LiteralToken): boolean {
|
||||
if (literal.templateTokenType === TokenType.String) {
|
||||
const value = (literal as StringToken).value;
|
||||
if (this.constant) {
|
||||
return this.ignoreCase ? this.constant.toUpperCase() === value.toUpperCase() : this.constant === value;
|
||||
} else if (this.requireNonEmpty) {
|
||||
return !!value;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override validate(schema: TemplateSchema, name: string): void {
|
||||
if (this.constant && this.requireNonEmpty) {
|
||||
throw new Error(`Properties '${CONSTANT}' and '${REQUIRE_NON_EMPTY}' cannot both be set`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,551 @@
|
||||
import {TokenType} from "../../templates/tokens/types";
|
||||
import {ObjectReader} from "../object-reader";
|
||||
import {
|
||||
ALLOWED_VALUES,
|
||||
ANY,
|
||||
BOOLEAN,
|
||||
BOOLEAN_DEFINITION,
|
||||
BOOLEAN_DEFINITION_PROPERTIES,
|
||||
CONSTANT,
|
||||
CONTEXT,
|
||||
DEFINITION,
|
||||
DEFINITIONS,
|
||||
DESCRIPTION,
|
||||
IGNORE_CASE,
|
||||
IS_EXPRESSION,
|
||||
ITEM_TYPE,
|
||||
LOOSE_KEY_TYPE,
|
||||
LOOSE_VALUE_TYPE,
|
||||
MAPPING,
|
||||
MAPPING_DEFINITION,
|
||||
MAPPING_DEFINITION_PROPERTIES,
|
||||
MAPPING_PROPERTY_VALUE,
|
||||
NON_EMPTY_STRING,
|
||||
NULL,
|
||||
NULL_DEFINITION,
|
||||
NULL_DEFINITION_PROPERTIES,
|
||||
NUMBER,
|
||||
NUMBER_DEFINITION,
|
||||
NUMBER_DEFINITION_PROPERTIES,
|
||||
ONE_OF,
|
||||
ONE_OF_DEFINITION,
|
||||
PROPERTIES,
|
||||
PROPERTY_VALUE,
|
||||
REQUIRED,
|
||||
REQUIRE_NON_EMPTY,
|
||||
SEQUENCE,
|
||||
SEQUENCE_DEFINITION,
|
||||
SEQUENCE_DEFINITION_PROPERTIES,
|
||||
SEQUENCE_OF_NON_EMPTY_STRING,
|
||||
STRING,
|
||||
STRING_DEFINITION,
|
||||
STRING_DEFINITION_PROPERTIES,
|
||||
TEMPLATE_SCHEMA,
|
||||
TYPE,
|
||||
VERSION
|
||||
} from "../template-constants";
|
||||
import {TemplateContext, TemplateValidationErrors} from "../template-context";
|
||||
import {readTemplate} from "../template-reader";
|
||||
import {MappingToken, SequenceToken, StringToken} from "../tokens";
|
||||
import {NoOperationTraceWriter} from "../trace-writer";
|
||||
import {BooleanDefinition} from "./boolean-definition";
|
||||
import {Definition} from "./definition";
|
||||
import {DefinitionType} from "./definition-type";
|
||||
import {MappingDefinition} from "./mapping-definition";
|
||||
import {NullDefinition} from "./null-definition";
|
||||
import {NumberDefinition} from "./number-definition";
|
||||
import {OneOfDefinition} from "./one-of-definition";
|
||||
import {PropertyDefinition} from "./property-definition";
|
||||
import {ScalarDefinition} from "./scalar-definition";
|
||||
import {SequenceDefinition} from "./sequence-definition";
|
||||
import {StringDefinition} from "./string-definition";
|
||||
|
||||
/**
|
||||
* This models the root schema object and contains definitions
|
||||
*/
|
||||
export class TemplateSchema {
|
||||
private static readonly _definitionNamePattern = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
|
||||
private static _internalSchema: TemplateSchema | undefined;
|
||||
public readonly definitions: {[key: string]: Definition} = {};
|
||||
public readonly version: string = "";
|
||||
|
||||
public constructor(mapping?: MappingToken) {
|
||||
// Add built-in type: null
|
||||
this.definitions[NULL] = new NullDefinition(NULL);
|
||||
|
||||
// Add built-in type: boolean
|
||||
this.definitions[BOOLEAN] = new BooleanDefinition(BOOLEAN);
|
||||
|
||||
// Add built-in type: number
|
||||
this.definitions[NUMBER] = new NumberDefinition(NUMBER);
|
||||
|
||||
// Add built-in type: string
|
||||
this.definitions[STRING] = new StringDefinition(STRING);
|
||||
|
||||
// Add built-in type: sequence
|
||||
const sequenceDefinition = new SequenceDefinition(SEQUENCE);
|
||||
sequenceDefinition.itemType = ANY;
|
||||
this.definitions[sequenceDefinition.key] = sequenceDefinition;
|
||||
|
||||
// Add built-in type: mapping
|
||||
const mappingDefinition = new MappingDefinition(MAPPING);
|
||||
mappingDefinition.looseKeyType = STRING;
|
||||
mappingDefinition.looseValueType = ANY;
|
||||
this.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// Add built-in type: any
|
||||
const anyDefinition = new OneOfDefinition(ANY);
|
||||
anyDefinition.oneOf.push(NULL);
|
||||
anyDefinition.oneOf.push(BOOLEAN);
|
||||
anyDefinition.oneOf.push(NUMBER);
|
||||
anyDefinition.oneOf.push(STRING);
|
||||
anyDefinition.oneOf.push(SEQUENCE);
|
||||
anyDefinition.oneOf.push(MAPPING);
|
||||
this.definitions[anyDefinition.key] = anyDefinition;
|
||||
|
||||
if (mapping) {
|
||||
for (const pair of mapping) {
|
||||
const key = pair.key.assertString(`${TEMPLATE_SCHEMA} key`);
|
||||
switch (key.value) {
|
||||
case VERSION: {
|
||||
this.version = pair.value.assertString(`${TEMPLATE_SCHEMA} ${VERSION}`).value;
|
||||
break;
|
||||
}
|
||||
case DEFINITIONS: {
|
||||
const definitions = pair.value.assertMapping(`${TEMPLATE_SCHEMA} ${DEFINITIONS}`);
|
||||
for (const definitionsPair of definitions) {
|
||||
const definitionsKey = definitionsPair.key.assertString(`${TEMPLATE_SCHEMA} ${DEFINITIONS} key`);
|
||||
const definitionsValue = definitionsPair.value.assertMapping(`${TEMPLATE_SCHEMA} ${DEFINITIONS} value`);
|
||||
let definition: Definition | undefined;
|
||||
for (const definitionPair of definitionsValue) {
|
||||
const definitionKey = definitionPair.key.assertString(`${DEFINITION} key`);
|
||||
const mappingToken = definitionsPair.value as MappingToken;
|
||||
switch (definitionKey.value) {
|
||||
case NULL:
|
||||
definition = new NullDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case BOOLEAN:
|
||||
definition = new BooleanDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case NUMBER:
|
||||
definition = new NumberDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case STRING:
|
||||
definition = new StringDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case SEQUENCE:
|
||||
definition = new SequenceDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case MAPPING:
|
||||
definition = new MappingDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case ONE_OF:
|
||||
definition = new OneOfDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case ALLOWED_VALUES:
|
||||
// Change the allowed-values definition into a one-of definition and its corresponding string definitions
|
||||
for (const item of mappingToken) {
|
||||
if (item.value.templateTokenType === TokenType.Sequence) {
|
||||
// Create a new string definition for each StringToken in the sequence
|
||||
const sequenceToken = item.value as SequenceToken;
|
||||
for (const activity of sequenceToken) {
|
||||
if (activity.templateTokenType === TokenType.String) {
|
||||
const stringToken = activity as StringToken;
|
||||
const allowedValuesKey = definitionsKey.value + "-" + stringToken.value;
|
||||
const allowedValuesDef = new StringDefinition(allowedValuesKey);
|
||||
allowedValuesDef.constant = stringToken.toDisplayString();
|
||||
this.definitions[allowedValuesKey] = allowedValuesDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
definition = new OneOfDefinition(definitionsKey.value, definitionsValue);
|
||||
break;
|
||||
case CONTEXT:
|
||||
case DESCRIPTION:
|
||||
continue;
|
||||
default:
|
||||
// throws
|
||||
definitionKey.assertUnexpectedValue(`${DEFINITION} mapping key`);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!definition) {
|
||||
throw new Error(`Not enough information to construct definition '${definitionsKey.value}'`);
|
||||
}
|
||||
|
||||
this.definitions[definitionsKey.value] = definition;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// throws
|
||||
key.assertUnexpectedValue(`${TEMPLATE_SCHEMA} key`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a definition by name
|
||||
*/
|
||||
public getDefinition(name: string): Definition {
|
||||
const result = this.definitions[name];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
throw new Error(`Schema definition '${name}' not found`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands one-of definitions and returns all scalar definitions
|
||||
*/
|
||||
public getScalarDefinitions(definition: Definition): ScalarDefinition[] {
|
||||
const result: ScalarDefinition[] = [];
|
||||
switch (definition.definitionType) {
|
||||
case DefinitionType.Null:
|
||||
case DefinitionType.Boolean:
|
||||
case DefinitionType.Number:
|
||||
case DefinitionType.String:
|
||||
result.push(definition as ScalarDefinition);
|
||||
break;
|
||||
case DefinitionType.OneOf: {
|
||||
const oneOf = definition as OneOfDefinition;
|
||||
|
||||
// Expand nested one-of definitions
|
||||
for (const nestedName of oneOf.oneOf) {
|
||||
const nestedDefinition = this.getDefinition(nestedName);
|
||||
result.push(...this.getScalarDefinitions(nestedDefinition));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands one-of definitions and returns all matching definitions by type
|
||||
*/
|
||||
public getDefinitionsOfType(definition: Definition, type: DefinitionType): Definition[] {
|
||||
const result: Definition[] = [];
|
||||
if (definition.definitionType === type) {
|
||||
result.push(definition);
|
||||
} else if (definition.definitionType === DefinitionType.OneOf) {
|
||||
const oneOf = definition as OneOfDefinition;
|
||||
for (const nestedName of oneOf.oneOf) {
|
||||
const nestedDefinition = this.getDefinition(nestedName);
|
||||
if (nestedDefinition.definitionType === type) {
|
||||
result.push(nestedDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts match the property name to a property defined by any of the specified definitions.
|
||||
* If matched, any unmatching definitions are filtered from the definitions array.
|
||||
* Returns the type information for the matched property.
|
||||
*/
|
||||
public matchPropertyAndFilter(
|
||||
definitions: MappingDefinition[],
|
||||
propertyName: string
|
||||
): PropertyDefinition | undefined {
|
||||
let result: PropertyDefinition | undefined;
|
||||
|
||||
// Check for a matching well-known property
|
||||
let notFoundInSome = false;
|
||||
for (const definition of definitions) {
|
||||
const propertyDef = definition.properties[propertyName];
|
||||
if (propertyDef) {
|
||||
result = propertyDef;
|
||||
} else {
|
||||
notFoundInSome = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the matched definitions if needed
|
||||
if (result && notFoundInSome) {
|
||||
for (let i = 0; i < definitions.length; ) {
|
||||
if (definitions[i].properties[propertyName]) {
|
||||
i++;
|
||||
} else {
|
||||
definitions.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private validate(): void {
|
||||
const oneOfDefinitions: {[key: string]: OneOfDefinition} = {};
|
||||
|
||||
for (const name of Object.keys(this.definitions)) {
|
||||
if (!name.match(TemplateSchema._definitionNamePattern)) {
|
||||
throw new Error(`Invalid definition name '${name}'`);
|
||||
}
|
||||
|
||||
const definition = this.definitions[name];
|
||||
|
||||
// Delay validation for 'one-of' definitions
|
||||
if (definition.definitionType === DefinitionType.OneOf) {
|
||||
oneOfDefinitions[name] = definition as OneOfDefinition;
|
||||
}
|
||||
// Otherwise validate now
|
||||
else {
|
||||
definition.validate(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate 'one-of' definitions
|
||||
for (const name of Object.keys(oneOfDefinitions)) {
|
||||
const oneOf = oneOfDefinitions[name];
|
||||
oneOf.validate(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a user-defined schema file
|
||||
*/
|
||||
public static load(objectReader: ObjectReader): TemplateSchema {
|
||||
const context = new TemplateContext(
|
||||
new TemplateValidationErrors(10, 500),
|
||||
TemplateSchema.getInternalSchema(),
|
||||
new NoOperationTraceWriter()
|
||||
);
|
||||
const template = readTemplate(context, TEMPLATE_SCHEMA, objectReader, undefined);
|
||||
context.errors.check();
|
||||
|
||||
const mapping = template!.assertMapping(TEMPLATE_SCHEMA);
|
||||
const schema = new TemplateSchema(mapping);
|
||||
schema.validate();
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal schema used for reading user-defined schema files
|
||||
*/
|
||||
private static getInternalSchema(): TemplateSchema {
|
||||
if (TemplateSchema._internalSchema === undefined) {
|
||||
const schema = new TemplateSchema();
|
||||
|
||||
// template-schema
|
||||
let mappingDefinition = new MappingDefinition(TEMPLATE_SCHEMA);
|
||||
mappingDefinition.properties[VERSION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[DEFINITIONS] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, DEFINITIONS, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// definitions
|
||||
mappingDefinition = new MappingDefinition(DEFINITIONS);
|
||||
mappingDefinition.looseKeyType = NON_EMPTY_STRING;
|
||||
mappingDefinition.looseValueType = DEFINITION;
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// definition
|
||||
let oneOfDefinition = new OneOfDefinition(DEFINITION);
|
||||
oneOfDefinition.oneOf.push(NULL_DEFINITION);
|
||||
oneOfDefinition.oneOf.push(BOOLEAN_DEFINITION);
|
||||
oneOfDefinition.oneOf.push(NUMBER_DEFINITION);
|
||||
oneOfDefinition.oneOf.push(STRING_DEFINITION);
|
||||
oneOfDefinition.oneOf.push(SEQUENCE_DEFINITION);
|
||||
oneOfDefinition.oneOf.push(MAPPING_DEFINITION);
|
||||
oneOfDefinition.oneOf.push(ONE_OF_DEFINITION);
|
||||
schema.definitions[oneOfDefinition.key] = oneOfDefinition;
|
||||
|
||||
// null-definition
|
||||
mappingDefinition = new MappingDefinition(NULL_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[NULL] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NULL_DEFINITION_PROPERTIES, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// null-definition-properties
|
||||
mappingDefinition = new MappingDefinition(NULL_DEFINITION_PROPERTIES);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// boolean-definition
|
||||
mappingDefinition = new MappingDefinition(BOOLEAN_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[BOOLEAN] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, BOOLEAN_DEFINITION_PROPERTIES, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// boolean-definition-properties
|
||||
mappingDefinition = new MappingDefinition(BOOLEAN_DEFINITION_PROPERTIES);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// number-definition
|
||||
mappingDefinition = new MappingDefinition(NUMBER_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[NUMBER] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NUMBER_DEFINITION_PROPERTIES, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// number-definition-properties
|
||||
mappingDefinition = new MappingDefinition(NUMBER_DEFINITION_PROPERTIES);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// string-definition
|
||||
mappingDefinition = new MappingDefinition(STRING_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[STRING] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING_DEFINITION_PROPERTIES, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// string-definition-properties
|
||||
mappingDefinition = new MappingDefinition(STRING_DEFINITION_PROPERTIES);
|
||||
mappingDefinition.properties[CONSTANT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[IGNORE_CASE] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, BOOLEAN, undefined)
|
||||
);
|
||||
mappingDefinition.properties[REQUIRE_NON_EMPTY] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, BOOLEAN, undefined)
|
||||
);
|
||||
mappingDefinition.properties[IS_EXPRESSION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, BOOLEAN, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// sequence-definition
|
||||
mappingDefinition = new MappingDefinition(SEQUENCE_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[SEQUENCE] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_DEFINITION_PROPERTIES, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// sequence-definition-properties
|
||||
mappingDefinition = new MappingDefinition(SEQUENCE_DEFINITION_PROPERTIES);
|
||||
mappingDefinition.properties[ITEM_TYPE] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// mapping-definition
|
||||
mappingDefinition = new MappingDefinition(MAPPING_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[MAPPING] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, MAPPING_DEFINITION_PROPERTIES, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// mapping-definition-properties
|
||||
mappingDefinition = new MappingDefinition(MAPPING_DEFINITION_PROPERTIES);
|
||||
mappingDefinition.properties[PROPERTIES] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, PROPERTIES, undefined)
|
||||
);
|
||||
mappingDefinition.properties[LOOSE_KEY_TYPE] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[LOOSE_VALUE_TYPE] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// properties
|
||||
mappingDefinition = new MappingDefinition(PROPERTIES);
|
||||
mappingDefinition.looseKeyType = NON_EMPTY_STRING;
|
||||
mappingDefinition.looseValueType = PROPERTY_VALUE;
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// property-value
|
||||
oneOfDefinition = new OneOfDefinition(PROPERTY_VALUE);
|
||||
oneOfDefinition.oneOf.push(NON_EMPTY_STRING);
|
||||
oneOfDefinition.oneOf.push(MAPPING_PROPERTY_VALUE);
|
||||
schema.definitions[oneOfDefinition.key] = oneOfDefinition;
|
||||
|
||||
// mapping-property-value
|
||||
mappingDefinition = new MappingDefinition(MAPPING_PROPERTY_VALUE);
|
||||
mappingDefinition.properties[TYPE] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[REQUIRED] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, BOOLEAN, undefined)
|
||||
);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// one-of-definition
|
||||
mappingDefinition = new MappingDefinition(ONE_OF_DEFINITION);
|
||||
mappingDefinition.properties[DESCRIPTION] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[CONTEXT] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[ONE_OF] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
mappingDefinition.properties[ALLOWED_VALUES] = new PropertyDefinition(
|
||||
new StringToken(undefined, undefined, SEQUENCE_OF_NON_EMPTY_STRING, undefined)
|
||||
);
|
||||
schema.definitions[mappingDefinition.key] = mappingDefinition;
|
||||
|
||||
// non-empty-string
|
||||
const stringDefinition = new StringDefinition(NON_EMPTY_STRING);
|
||||
stringDefinition.requireNonEmpty = true;
|
||||
schema.definitions[stringDefinition.key] = stringDefinition;
|
||||
|
||||
// sequence-of-non-empty-string
|
||||
const sequenceDefinition = new SequenceDefinition(SEQUENCE_OF_NON_EMPTY_STRING);
|
||||
sequenceDefinition.itemType = NON_EMPTY_STRING;
|
||||
schema.definitions[sequenceDefinition.key] = sequenceDefinition;
|
||||
|
||||
schema.validate();
|
||||
|
||||
TemplateSchema._internalSchema = schema;
|
||||
}
|
||||
|
||||
return TemplateSchema._internalSchema;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user