Skip to content

Commit 02dd0e6

Browse files
committed
Decouple instantiation logic from Loader class
1 parent c8b2e33 commit 02dd0e6

16 files changed

+660
-637
lines changed

index.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
export * from './lib/factory/UnnamedComponentFactory';
2-
export * from './lib/factory/UnmappedNamedComponentFactory';
3-
export * from './lib/factory/MappedNamedComponentFactory';
4-
export * from './lib/factory/IComponentFactory';
51
export * from './lib/factory/ComponentFactory';
2+
export * from './lib/factory/ComponentFactoryOptions';
3+
export * from './lib/factory/IComponentFactory';
4+
export * from './lib/factory/MappedNamedComponentFactory';
5+
export * from './lib/factory/UnmappedNamedComponentFactory';
6+
export * from './lib/factory/UnnamedComponentFactory';
67
export * from './lib/rdf/PrefetchedDocumentLoader';
78
export * from './lib/rdf/RdfParser';
89
export * from './lib/rdf/RdfStreamIncluder';
910
export * from './lib/CompileUtil';
11+
export * from './lib/IInstancePool';
12+
export * from './lib/InstancePool';
1013
export * from './lib/Loader';
1114
export * from './lib/LogLevel';
1215
export * from './lib/ModuleStateBuilder';

lib/CompileUtil.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ export async function compileConfig(
3838

3939
// Serialize the config
4040
const moduleLines: string[] = [];
41+
await loader.registerConfigStream(configStream);
4142
const serializationVariableName = await loader
42-
.instantiateFromStream(configResourceUri, configStream, { serializations: moduleLines, asFunction });
43+
.getComponentInstance(configResourceUri, { serializations: moduleLines, asFunction });
4344
let document: string = moduleLines.join('\n');
4445

4546
// Override main variable name if needed

lib/IInstancePool.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import type { Resource } from 'rdf-object';
2+
import type { ICreationSettings, IComponentFactory } from './factory/IComponentFactory';
3+
4+
/**
5+
* Manages and creates instances of components.
6+
*/
7+
export interface IInstancePool {
8+
9+
/**
10+
* Get a component config constructor based on a Resource.
11+
* @param configResource A config resource.
12+
* @returns The component factory.
13+
*/
14+
getConfigConstructor: (configResource: Resource) => IComponentFactory;
15+
16+
/**
17+
* Instantiate a component based on a Resource.
18+
* @param configResource A config resource.
19+
* @param settings The settings for creating the instance.
20+
* @returns {any} The run instance.
21+
*/
22+
instantiate: (configResource: Resource, settings: ICreationSettings) => Promise<any>;
23+
24+
}

lib/InstancePool.ts

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import type { Resource, RdfObjectLoader } from 'rdf-object';
2+
import { ComponentFactory } from './factory/ComponentFactory';
3+
import type { ComponentFactoryOptions } from './factory/ComponentFactoryOptions';
4+
import type { ICreationSettings, ICreationSettingsInner, IComponentFactory } from './factory/IComponentFactory';
5+
import type { IInstancePool } from './IInstancePool';
6+
import type { IModuleState } from './ModuleStateBuilder';
7+
import Util = require('./Util');
8+
import { resourceIdToString, resourceToString } from './Util';
9+
10+
/**
11+
* Manages and creates instances of components.
12+
*/
13+
export class InstancePool implements IInstancePool {
14+
private readonly objectLoader: RdfObjectLoader;
15+
private readonly componentResources: Record<string, Resource>;
16+
private readonly moduleState: IModuleState;
17+
private readonly overrideRequireNames: Record<string, string>;
18+
19+
private readonly runTypeConfigs: Record<string, Resource[]> = {};
20+
private readonly instances: Record<string, any> = {};
21+
22+
public constructor(options: IInstancePoolOptions) {
23+
this.objectLoader = options.objectLoader;
24+
this.componentResources = options.componentResources;
25+
this.moduleState = options.moduleState;
26+
this.overrideRequireNames = options.overrideRequireNames;
27+
}
28+
29+
/**
30+
* Let then given config inherit parameter values from referenced passed configs.
31+
* @param configResource The config
32+
* @param componentResource The component
33+
*/
34+
public inheritParameterValues(configResource: Resource, componentResource: Resource): void {
35+
// Inherit parameter values from passed instances of the given types
36+
if (componentResource.property.parameters) {
37+
for (const parameter of componentResource.properties.parameters) {
38+
// Collect all owl:Restriction's
39+
const restrictions: Resource[] = parameter.properties.inheritValues
40+
.reduce((acc: Resource[], clazz: Resource) => {
41+
if (clazz.properties.types.reduce((subAcc: boolean, type: Resource) => subAcc ||
42+
type.value === `${Util.PREFIXES.owl}Restriction`, false)) {
43+
acc.push(clazz);
44+
}
45+
return acc;
46+
}, []);
47+
48+
for (const restriction of restrictions) {
49+
if (restriction.property.from) {
50+
if (!restriction.property.onParameter) {
51+
throw new Error(`Parameters that inherit values must refer to a property: ${resourceToString(parameter)}`);
52+
}
53+
54+
for (const componentType of restriction.properties.from) {
55+
if (componentType.type !== 'NamedNode') {
56+
throw new Error(`Parameter inheritance values must refer to component type identifiers, not literals: ${resourceToString(componentType)}`);
57+
}
58+
59+
const typeInstances: Resource[] = this.runTypeConfigs[componentType.value];
60+
if (typeInstances) {
61+
for (const instance of typeInstances) {
62+
for (const parentParameter of restriction.properties.onParameter) {
63+
// TODO: this might be a bug in the JSON-LD parser
64+
// if (parentParameter.termType !== 'NamedNode') {
65+
// throw new Error('Parameters that inherit values must refer to sub properties as URI\'s: '
66+
// + JSON.stringify(parentParameter));
67+
// }
68+
if (instance.property[parentParameter.value]) {
69+
// Copy the parameters
70+
for (const value of instance.properties[parentParameter.value]) {
71+
configResource.properties[parentParameter.value].push(value);
72+
}
73+
74+
// Also add the parameter to the parameter type list
75+
if (!componentResource.properties.parameters.includes(parentParameter)) {
76+
componentResource.properties.parameters.push(parentParameter);
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}
83+
}
84+
}
85+
}
86+
}
87+
}
88+
89+
/**
90+
* Get a component config constructor based on a Resource.
91+
* @param configResource A config resource.
92+
* @returns The component factory.
93+
*/
94+
public getConfigConstructor(configResource: Resource): IComponentFactory {
95+
const allTypes: string[] = [];
96+
const componentTypes: Resource[] = configResource.properties.types
97+
.reduce((types: Resource[], typeUri: Resource) => {
98+
const componentResource: Resource = this.componentResources[typeUri.value];
99+
allTypes.push(typeUri.value);
100+
if (componentResource) {
101+
types.push(componentResource);
102+
if (!this.runTypeConfigs[componentResource.value]) {
103+
this.runTypeConfigs[componentResource.value] = [];
104+
}
105+
this.runTypeConfigs[componentResource.value].push(configResource);
106+
}
107+
return types;
108+
}, []);
109+
if (componentTypes.length !== 1 &&
110+
!configResource.property.requireName &&
111+
!configResource.property.requireElement) {
112+
throw new Error(`Could not run config ${resourceIdToString(configResource, this.objectLoader)} because exactly one valid component type ` +
113+
`was expected, while ${componentTypes.length} were found in the defined types [${allTypes}]. ` +
114+
`Alternatively, the requireName and requireElement must be provided.\nFound: ${
115+
resourceToString(configResource)}\nAll available usable types: [\n${
116+
Object.keys(this.componentResources).join(',\n')}\n]`);
117+
}
118+
119+
let options: ComponentFactoryOptions = {
120+
objectLoader: this.objectLoader,
121+
config: configResource,
122+
overrideRequireNames: this.overrideRequireNames,
123+
instancePool: this,
124+
constructable: !configResource.isA(Util.DF.namedNode(`${Util.PREFIXES.oo}ComponentInstance`)),
125+
};
126+
127+
if (componentTypes.length > 0) {
128+
const componentResource = componentTypes[0];
129+
const moduleResource = componentResource.property.module;
130+
if (!moduleResource) {
131+
throw new Error(`No module was found for the component ${resourceIdToString(componentResource, this.objectLoader)}`);
132+
}
133+
134+
this.inheritParameterValues(configResource, componentResource);
135+
136+
options = {
137+
...options,
138+
moduleDefinition: moduleResource,
139+
componentDefinition: componentResource,
140+
};
141+
}
142+
143+
return new ComponentFactory(options);
144+
}
145+
146+
/**
147+
* Instantiate a component based on a Resource.
148+
* @param configResource A config resource.
149+
* @param settings The settings for creating the instance.
150+
* @returns {any} The run instance.
151+
*/
152+
public async instantiate(configResource: Resource, settings: ICreationSettings): Promise<any> {
153+
const settingsInner: ICreationSettingsInner = { ...settings, moduleState: this.moduleState };
154+
// Check if this resource is required as argument in its own chain,
155+
// if so, return a dummy value, to avoid infinite recursion.
156+
const resourceBlacklist = settingsInner.resourceBlacklist || {};
157+
if (resourceBlacklist[configResource.value]) {
158+
return {};
159+
}
160+
161+
// Before instantiating, first check if the resource is a variable
162+
if (configResource.isA(Util.IRI_VARIABLE)) {
163+
if (settingsInner.serializations) {
164+
if (settingsInner.asFunction) {
165+
return `getVariableValue('${configResource.value}')`;
166+
}
167+
throw new Error(`Detected a variable during config compilation: ${resourceIdToString(configResource, this.objectLoader)}. Variables are not supported, but require the -f flag to expose the compiled config as function.`);
168+
} else {
169+
const value = settingsInner.variables ? settingsInner.variables[configResource.value] : undefined;
170+
if (value === undefined) {
171+
throw new Error(`Undefined variable: ${resourceIdToString(configResource, this.objectLoader)}`);
172+
}
173+
return value;
174+
}
175+
}
176+
177+
if (!this.instances[configResource.value]) {
178+
const subBlackList: Record<string, boolean> = { ...resourceBlacklist };
179+
subBlackList[configResource.value] = true;
180+
this.instances[configResource.value] = await this.getConfigConstructor(configResource).create(
181+
{ resourceBlacklist: subBlackList, ...settingsInner },
182+
);
183+
}
184+
return this.instances[configResource.value];
185+
}
186+
}
187+
188+
export interface IInstancePoolOptions {
189+
objectLoader: RdfObjectLoader;
190+
componentResources: Record<string, Resource>;
191+
moduleState: IModuleState;
192+
overrideRequireNames: Record<string, string>;
193+
}

0 commit comments

Comments
 (0)