Skip to content

Commit

Permalink
refactor(state): Simplify resolution; Rename descend utils #41
Browse files Browse the repository at this point in the history
  • Loading branch information
korzio committed Sep 19, 2017
1 parent 4fa6497 commit c55f3af
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 109 deletions.
4 changes: 2 additions & 2 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function normalize(uri) {
return decodeURIComponent(uri.replace(/~1/g, '/').replace(/~0/g, '~'));
}

function descent(uri, parentSchema) {
function descend(uri, parentSchema) {
let uriFragment = fragment(uri);
if (!uriFragment && isFullUri(uri)) {
return parentSchema;
Expand Down Expand Up @@ -211,5 +211,5 @@ module.exports = {
isFullUri,
head,
fragment,
descent,
descend,
};
165 changes: 59 additions & 106 deletions lib/utils/state.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { list: validators } = require('../validators');
const { body, restore, template } = require('./template');
const {
descent,
descend,
makePath,
head,
isFullUri,
Expand All @@ -13,15 +13,25 @@ const {

function State(schema = {}, env) {
Object.assign(this, {
resolution: [0],
context: [],
entries: new Map(),
env,
});

this.push(schema);
}

/**
* @name generate
* @type {function}
* @description
* The main schema process function.
* Available and used both in external and internal generation.
* Saves the state for internal recursive calls.
* @param {object} env - djv environment
* @param {object} schema - to process
* @param {State} state - saved state
* @param {Environment} options
* @returns {function} restoredFunction
*/
function generate(env, schema, state = new State(schema, env), options) {
const tpl = template(state, options);
tpl.visit(schema);
Expand All @@ -31,56 +41,50 @@ function generate(env, schema, state = new State(schema, env), options) {
}

State.prototype = Object.assign(Object.create(Array.prototype), {
// template related methods
/**
* @name addEntry
* @type {function}
* @description
* Generates an internal function.
* Usually necessary for `allOf` types of validators.
* Caches generated functions by schema object key.
* Checks for an existing schema in a context stack to avoid double parsing and generation.
* @param {string} url
* @param {object} schema
* @returns {number/boolean} index
*/
addEntry(url, schema) {
if (!isSchema(schema)) {
return schema;
let entry = this.entries.get(schema);
if (entry === false) {
// has already been added to process queue
// will be revealed as an entry
return this.context.push(schema);
}

const options = { inner: true };
const entry = this.entries.get(schema) || generate(this.env, schema, this, options);
this.entries.set(schema, entry);
this.revealReference(url);
if (typeof entry === 'undefined') {
// start to process schema
this.entries.set(schema, false);
entry = generate(this.env, schema, this, { inner: true });
this.entries.set(schema, entry);
this.revealReference(schema);
}

return this.context.push(entry);
},
/**
* @name addReference
* @name revealReference
* @type {function}
* @description
* Checks for an existing schema in a context stack to avoid double parsing and generation.
* @param {string} url
* If a schema was added during the add entry phase
* Then it should be revealed in this step
* @param {object} schema
* @returns {number/boolean} index
* @returns {void}
*/
addReference(url, schema) {
const schemaIndex = this.indexOf(schema);
// reference to itself
if (schemaIndex === 0) {
return this.context.push(0);
}
if (schemaIndex !== -1 && schemaIndex !== this.length - 1) {
// has already been added to process queue
return this.context.push(url);
}
return false;
},
revealReference(url) {
revealReference(schema) {
for (
let doubled = this.context.indexOf(url);
let doubled = this.context.indexOf(schema);
doubled !== -1;
doubled = this.context.indexOf(url)
doubled = this.context.indexOf(schema)
) {
this.context[doubled] = this.context.length;
}
Expand All @@ -89,53 +93,15 @@ State.prototype = Object.assign(Object.create(Array.prototype), {
* @name link
* @type {function}
* @description
* Resolves schema by given url and current registered context stack.
* Returns an entry's index of in a context stack.
* Returns an entry's index in a context stack.
* @param {string} url
* @returns {string} entry
*/
link(url) {
const schema = this.resolve(url);
const entry = this.addReference(url, schema) || this.addEntry(url, schema);

const entry = this.addEntry(url, schema);
return entry;
},
// pure scope resolution methods
push(schema) {
// if schema id is partial
// it should be resolved against the closest parent(s)
// so only valid URIs added to resolution
if (schema && schema.id && this.length && isFullUri(schema.id)) {
this.resolution.push(this.length);
}

Array.prototype.push.call(this, schema);
},
getResolvedSchema(path) {
if (hasProperty(this.env.resolved, path)) {
return this.env.resolved[path].schema;
}

const cleanPath = cleanId(path);
if (hasProperty(this.env.resolved, cleanPath)) {
return this.env.resolved[cleanPath].schema;
}

if (cleanPath) {
const foundResolvedIndex = this.resolution
.reverse()
.find(index => this[index].id && (
this[index].id === cleanPath ||
this[index].id === path
));

if (typeof foundResolvedIndex !== 'undefined') {
return this[foundResolvedIndex];
}
}

return null;
},
/**
* @name ascend
* @type {function}
Expand All @@ -146,47 +112,40 @@ State.prototype = Object.assign(Object.create(Array.prototype), {
* @returns {object} parentSchema
*/
ascend(reference) {
const indexOfParent = Math.max(this.resolution[this.resolution.length - 1], 1);

let parentSchemaPath = head(reference);
let parentSchema = this[indexOfParent];

if (parentSchemaPath && this.length > 1 && !isFullUri(reference)) {
parentSchemaPath = makePath(
this.slice(indexOfParent)
.map(({ id }) => id)
.concat(parentSchemaPath)
);
const path = head(reference);
// already saved by resolved id
if (hasProperty(this.env.resolved, path)) {
const resolvedWithEnvironment = this.env.resolved[path];
return resolvedWithEnvironment.schema;
}

const resolvedSchema = this.getResolvedSchema(parentSchemaPath);
if (!parentSchema || resolvedSchema) {
parentSchema = resolvedSchema;
}
// search in a backward order for the last "resolution" schema
// TODO refactor reverse, slice on every call
const parentSchema = this
.slice()
.reverse()
.find(({ id }) => id)
|| this[0];

// try to resolve it against environment
if (hasProperty(parentSchema, '$ref')) {
const ref = head(parentSchema.$ref);
if (ref !== '#' && this.getResolvedSchema(ref)) {
parentSchema = this.getResolvedSchema(ref);
}
}

this.push(parentSchema);
return parentSchema;
},
/**
* @name resolve
* @type {function}
* @description
* Resolves schema by given reference and current registered context stack.
* @param {string} url
* @returns {object} schema
*/
resolve(reference) {
if (reference === '#') {
return 0;
}
if (typeof reference !== 'string') {
return reference;
}

const parent = this.ascend(reference);
const resolved = descent(reference, parent);
const parentSchema = this.ascend(reference);
const subSchema = descend(reference, parentSchema);

return resolved;
return subSchema;
},
/**
* @name visit
Expand All @@ -199,18 +158,12 @@ State.prototype = Object.assign(Object.create(Array.prototype), {
* @returns {void}
*/
visit(pseudoSchema, tpl) {
const initialStateLength = this.length;
const initialResolutionLength = this.resolution.length;

const schema = transformSchema(pseudoSchema);
this.push(schema);

validators.forEach((validator) => {
validator(schema, tpl);
});

this.length = initialStateLength;
this.resolution.length = initialResolutionLength;
},
});

Expand Down
2 changes: 1 addition & 1 deletion lib/utils/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ function body(tpl, state, { inner, errorHandler } = {}) {
state.context
.forEach((value, i) => {
if (typeof value === 'number') {
references.push(`${i + 1}=f${value}`);
references.push(`${i + 1}=f${value + 1}`);
return;
}
functions.push(`${i + 1}=${value}`);
Expand Down

0 comments on commit c55f3af

Please sign in to comment.