Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F101104137
StatsFactory.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Wed, Feb 5, 19:08
Size
7 KB
Mime Type
text/x-c++
Expires
Fri, Feb 7, 19:08 (2 d)
Engine
blob
Format
Raw Data
Handle
24092670
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
StatsFactory.js
View Options
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { HookMap, SyncBailHook, SyncWaterfallHook } = require("tapable");
const { concatComparators, keepOriginalOrder } = require("../util/comparators");
const smartGrouping = require("../util/smartGrouping");
/** @typedef {import("../util/smartGrouping").GroupConfig<any, object>} GroupConfig */
class StatsFactory {
constructor() {
this.hooks = Object.freeze({
/** @type {HookMap<SyncBailHook<[Object, any, Object]>>} */
extract: new HookMap(
() => new SyncBailHook(["object", "data", "context"])
),
/** @type {HookMap<SyncBailHook<[any, Object, number, number]>>} */
filter: new HookMap(
() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
),
/** @type {HookMap<SyncBailHook<[(function(any, any): number)[], Object]>>} */
sort: new HookMap(() => new SyncBailHook(["comparators", "context"])),
/** @type {HookMap<SyncBailHook<[any, Object, number, number]>>} */
filterSorted: new HookMap(
() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
),
/** @type {HookMap<SyncBailHook<[GroupConfig[], Object]>>} */
groupResults: new HookMap(
() => new SyncBailHook(["groupConfigs", "context"])
),
/** @type {HookMap<SyncBailHook<[(function(any, any): number)[], Object]>>} */
sortResults: new HookMap(
() => new SyncBailHook(["comparators", "context"])
),
filterResults: new HookMap(
() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
),
/** @type {HookMap<SyncBailHook<[any[], Object]>>} */
merge: new HookMap(() => new SyncBailHook(["items", "context"])),
/** @type {HookMap<SyncBailHook<[any[], Object]>>} */
result: new HookMap(() => new SyncWaterfallHook(["result", "context"])),
/** @type {HookMap<SyncBailHook<[any, Object]>>} */
getItemName: new HookMap(() => new SyncBailHook(["item", "context"])),
/** @type {HookMap<SyncBailHook<[any, Object]>>} */
getItemFactory: new HookMap(() => new SyncBailHook(["item", "context"]))
});
const hooks = this.hooks;
this._caches = /** @type {Record<keyof typeof hooks, Map<string, SyncBailHook<[any[], Object]>[]>>} */ ({});
for (const key of Object.keys(hooks)) {
this._caches[key] = new Map();
}
this._inCreate = false;
}
_getAllLevelHooks(hookMap, cache, type) {
const cacheEntry = cache.get(type);
if (cacheEntry !== undefined) {
return cacheEntry;
}
const hooks = [];
const typeParts = type.split(".");
for (let i = 0; i < typeParts.length; i++) {
const hook = hookMap.get(typeParts.slice(i).join("."));
if (hook) {
hooks.push(hook);
}
}
cache.set(type, hooks);
return hooks;
}
_forEachLevel(hookMap, cache, type, fn) {
for (const hook of this._getAllLevelHooks(hookMap, cache, type)) {
const result = fn(hook);
if (result !== undefined) return result;
}
}
_forEachLevelWaterfall(hookMap, cache, type, data, fn) {
for (const hook of this._getAllLevelHooks(hookMap, cache, type)) {
data = fn(hook, data);
}
return data;
}
_forEachLevelFilter(hookMap, cache, type, items, fn, forceClone) {
const hooks = this._getAllLevelHooks(hookMap, cache, type);
if (hooks.length === 0) return forceClone ? items.slice() : items;
let i = 0;
return items.filter((item, idx) => {
for (const hook of hooks) {
const r = fn(hook, item, idx, i);
if (r !== undefined) {
if (r) i++;
return r;
}
}
i++;
return true;
});
}
create(type, data, baseContext) {
if (this._inCreate) {
return this._create(type, data, baseContext);
} else {
try {
this._inCreate = true;
return this._create(type, data, baseContext);
} finally {
for (const key of Object.keys(this._caches)) this._caches[key].clear();
this._inCreate = false;
}
}
}
_create(type, data, baseContext) {
const context = {
...baseContext,
type,
[type]: data
};
if (Array.isArray(data)) {
// run filter on unsorted items
const items = this._forEachLevelFilter(
this.hooks.filter,
this._caches.filter,
type,
data,
(h, r, idx, i) => h.call(r, context, idx, i),
true
);
// sort items
const comparators = [];
this._forEachLevel(this.hooks.sort, this._caches.sort, type, h =>
h.call(comparators, context)
);
if (comparators.length > 0) {
items.sort(
// @ts-expect-error number of arguments is correct
concatComparators(...comparators, keepOriginalOrder(items))
);
}
// run filter on sorted items
const items2 = this._forEachLevelFilter(
this.hooks.filterSorted,
this._caches.filterSorted,
type,
items,
(h, r, idx, i) => h.call(r, context, idx, i),
false
);
// for each item
let resultItems = items2.map((item, i) => {
const itemContext = {
...context,
_index: i
};
// run getItemName
const itemName = this._forEachLevel(
this.hooks.getItemName,
this._caches.getItemName,
`${type}[]`,
h => h.call(item, itemContext)
);
if (itemName) itemContext[itemName] = item;
const innerType = itemName ? `${type}[].${itemName}` : `${type}[]`;
// run getItemFactory
const itemFactory =
this._forEachLevel(
this.hooks.getItemFactory,
this._caches.getItemFactory,
innerType,
h => h.call(item, itemContext)
) || this;
// run item factory
return itemFactory.create(innerType, item, itemContext);
});
// sort result items
const comparators2 = [];
this._forEachLevel(
this.hooks.sortResults,
this._caches.sortResults,
type,
h => h.call(comparators2, context)
);
if (comparators2.length > 0) {
resultItems.sort(
// @ts-expect-error number of arguments is correct
concatComparators(...comparators2, keepOriginalOrder(resultItems))
);
}
// group result items
const groupConfigs = [];
this._forEachLevel(
this.hooks.groupResults,
this._caches.groupResults,
type,
h => h.call(groupConfigs, context)
);
if (groupConfigs.length > 0) {
resultItems = smartGrouping(resultItems, groupConfigs);
}
// run filter on sorted result items
const finalResultItems = this._forEachLevelFilter(
this.hooks.filterResults,
this._caches.filterResults,
type,
resultItems,
(h, r, idx, i) => h.call(r, context, idx, i),
false
);
// run merge on mapped items
let result = this._forEachLevel(
this.hooks.merge,
this._caches.merge,
type,
h => h.call(finalResultItems, context)
);
if (result === undefined) result = finalResultItems;
// run result on merged items
return this._forEachLevelWaterfall(
this.hooks.result,
this._caches.result,
type,
result,
(h, r) => h.call(r, context)
);
} else {
const object = {};
// run extract on value
this._forEachLevel(this.hooks.extract, this._caches.extract, type, h =>
h.call(object, data, context)
);
// run result on extracted object
return this._forEachLevelWaterfall(
this.hooks.result,
this._caches.result,
type,
object,
(h, r) => h.call(r, context)
);
}
}
}
module.exports = StatsFactory;
Event Timeline
Log In to Comment