blob: 772b5a1b51894bc2ec29d9c91e292df19ff48849 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* @author Jorge Bay Gondra
*/
'use strict';
const utils = require('../utils');
const itemDone = Object.freeze({ value: null, done: true });
const asyncIteratorSymbol = Symbol.asyncIterator || Symbol('@@asyncIterator');
class Traversal {
constructor(graph, traversalStrategies, bytecode) {
this.graph = graph;
this.traversalStrategies = traversalStrategies;
this.bytecode = bytecode;
/** @type {Array<Traverser>} */
this.traversers = null;
this.sideEffects = null;
this._traversalStrategiesPromise = null;
this._traversersIteratorIndex = 0;
}
/**
* Async iterable method implementation.
*/
[asyncIteratorSymbol]() {
return this;
}
/** @returns {Bytecode} */
getBytecode() {
return this.bytecode;
}
/**
* Returns an Array containing the traverser objects.
* @returns {Promise.<Array>}
*/
toList() {
return this._applyStrategies().then(() => {
const result = [];
let it;
while ((it = this._getNext()) && !it.done) {
result.push(it.value);
}
return result;
});
};
/**
* Determines if there are any more items to iterate from the traversal.
* @returns {Promise.<boolean>}
*/
hasNext() {
return this._applyStrategies().then(() => {
return this.traversers && this.traversers.length > 0 &&
this._traversersIteratorIndex < this.traversers.length &&
this.traversers[this._traversersIteratorIndex].bulk > 0;
});
}
/**
* Iterates all Traverser instances in the traversal.
* @returns {Promise}
*/
iterate() {
this.bytecode.addStep('none');
return this._applyStrategies().then(() => {
let it;
while ((it = this._getNext()) && !it.done) {
}
});
}
/**
* Async iterator method implementation.
* Returns a promise containing an iterator item.
* @returns {Promise.<{value, done}>}
*/
next() {
return this._applyStrategies().then(() => this._getNext());
}
/**
* Synchronous iterator of traversers including
* @private
*/
_getNext() {
while (this.traversers && this._traversersIteratorIndex < this.traversers.length) {
let traverser = this.traversers[this._traversersIteratorIndex];
if (traverser.bulk > 0) {
traverser.bulk--;
return { value: traverser.object, done: false };
}
this._traversersIteratorIndex++;
}
return itemDone;
}
_applyStrategies() {
if (this._traversalStrategiesPromise) {
// Apply strategies only once
return this._traversalStrategiesPromise;
}
return this._traversalStrategiesPromise = this.traversalStrategies.applyStrategies(this);
}
/**
* Returns the Bytecode JSON representation of the traversal
* @returns {String}
*/
toString() {
return this.bytecode.toString();
};
}
class P {
/**
* Represents an operation.
* @constructor
*/
constructor(operator, value, other) {
this.operator = operator;
this.value = value;
this.other = other;
}
/**
* Returns the string representation of the instance.
* @returns {string}
*/
toString() {
if (this.other === undefined) {
return this.operator + '(' + this.value + ')';
}
return this.operator + '(' + this.value + ', ' + this.other + ')';
}
and(arg) {
return new P('and', this, arg);
}
or(arg) {
return new P('or', this, arg);
}
static within(...args) {
if (args.length === 1 && Array.isArray(args[0])) {
return new P("within", args[0], null);
} else {
return new P("within", args, null);
}
}
static without(...args) {
if (args.length === 1 && Array.isArray(args[0])) {
return new P("without", args[0], null);
} else {
return new P("without", args, null);
}
}
<% pmethods.findAll{!(it in ["within","without"])}.each{ method -> %>
/** @param {...Object} args */
static <%= toJs.call(method) %>(...args) {
return createP('<%= method %>', args);
}
<% } %>
}
function createP(operator, args) {
args.unshift(null, operator);
return new (Function.prototype.bind.apply(P, args));
}
class Traverser {
constructor(object, bulk) {
this.object = object;
this.bulk = bulk || 1;
}
}
class TraversalSideEffects {
}
function toEnum(typeName, keys) {
const result = {};
keys.split(' ').forEach(k => {
let jsKey = k;
if (jsKey === jsKey.toUpperCase()) {
jsKey = jsKey.toLowerCase();
}
result[jsKey] = new EnumValue(typeName, k);
});
return result;
}
class EnumValue {
constructor(typeName, elementName) {
this.typeName = typeName;
this.elementName = elementName;
}
toString() {
return this.elementName;
}
}
module.exports = {
EnumValue,
P,
Traversal,
TraversalSideEffects,
Traverser<%
enums.each{ enumClass ->
out.print ",\n " + decapitalize.call(enumClass.simpleName) + ": toEnum('" + enumClass.simpleName + "', '" +
enumClass.getEnumConstants().sort { a, b -> a.name() <=> b.name() }.collect { toJs.call(it.name()) }.join(' ') + "')"
}
%>
};