1 /**
2 License:
3 	Boost Software License - Version 1.0 - August 17th, 2003
4 
5 	Permission is hereby granted, free of charge, to any person or organization
6 	obtaining a copy of the software and accompanying documentation covered by
7 	this license (the "Software") to use, reproduce, display, distribute,
8 	execute, and transmit the Software, and to prepare derivative works of the
9 	Software, and to permit third-parties to whom the Software is furnished to
10 	do so, all subject to the following:
11 
12 	The copyright notices in the Software and this entire statement, including
13 	the above license grant, this restriction and the following disclaimer,
14 	must be included in all copies of the Software, in whole or in part, and
15 	all derivative works of the Software, unless such copies or derivative
16 	works are solely in the form of machine-executable object code generated by
17 	a source language processor.
18 
19 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 	DEALINGS IN THE SOFTWARE.
26 
27 Authors:
28 	aermicioi
29 **/
30 module aermicioi.aedi.configurer.register.context;
31 
32 import aermicioi.aedi.configurer.register.configuration_context_factory;
33 import aermicioi.aedi.factory.factory;
34 import aermicioi.aedi.factory.generic_factory;
35 import aermicioi.aedi.factory.wrapping_factory;
36 import aermicioi.aedi.storage.locator;
37 import aermicioi.aedi.storage.storage;
38 import std.experimental.allocator : RCIAllocator, theAllocator;
39 import std.traits;
40 import std.meta : AliasSeq;
41 
42 @safe:
43 
44 struct RegistrationContext(Policies...)
45     if (Policies.length > 1) {
46     /**
47     Storage into which to store components;
48     **/
49     Storage!(ObjectFactory, string) storage;
50 
51     /**
52     Locator used for fetching components dependencies;
53     **/
54     Locator!(Object, string) locator;
55 
56     /**
57     Allocator used for registered components.
58     **/
59     RCIAllocator allocator;
60 
61     alias ConfigurableFactoryType(T) = ConfigurableFactory!(T, FactoryPolicyExtractor!Policies);
62 
63     ref typeof(this) initialize() {
64         static foreach (Policy; Policies) {
65             Policy.initialize(storage, locator, allocator);
66         }
67 
68         return this;
69     }
70 
71     /**
72     Register a component of type T by identity, type, or interface it implements.
73 
74     Register a component of type T by identity, type, or interface it implements.
75 
76     Params:
77         Interface = interface of registered component that it implements
78         T = type of registered component
79         identity = identity by which component is stored in storage
80 
81     Returns:
82         GenericFactory!T factory for component for further configuration
83     **/
84     ConfigurableFactoryType!T register(T, string file = __FILE__, size_t line = __LINE__)(string identity) {
85         ConfigurableFactoryType!T factory = new ConfigurableFactoryType!T();
86         static if (is(typeof(factory) : Class!(ComponentType, FactoryPolicies), alias Class, ComponentType, FactoryPolicies...)) {
87             static foreach (FactoryPolicy; FactoryPolicies) {
88                 static if (__traits(isSame, FactoryPolicy, StoragePolicy)) {
89                     factory.storage = storage;
90                     factory.identity = identity;
91                 }
92 
93                 static if (__traits(isSame, FactoryPolicy, DecoratingFactoryPolicy)) {
94                     factory.locator = locator;
95                     factory.allocator = allocator;
96                 }
97 
98                 static if (__traits(isSame, FactoryPolicy, RegistrationStorePolicy)) {
99                     factory.file = file;
100                     factory.line = line;
101                 }
102             }
103         }
104 
105         static foreach (Policy; Policies) {
106             Policy.apply(factory);
107         }
108 
109         return factory;
110     }
111 
112     /**
113     ditto
114     **/
115     ConfigurableFactoryType!T register(T, string file = __FILE__, size_t line = __LINE__)() {
116         return register!(T, file, line)(fullyQualifiedName!T);
117     }
118 
119     /**
120     ditto
121     **/
122     ConfigurableFactoryType!T register(Interface, T : Interface, string file = __FILE__, size_t line = __LINE__)()
123         if (!is(T == Interface)) {
124         return register!(T, file, line)(fullyQualifiedName!Interface);
125     }
126 
127     /**
128     Register a component of type T by identity, type, or interface it implements with a default value.
129 
130     Register a component of type T by identity, type, or interface it implements with a default value.
131 
132     Params:
133         Interface = interface of registered component that it implements
134         T = type of registered component
135         identity = identity by which component is stored in storage
136         value = initial value of component;
137 
138     Returns:
139         GenericFactory!T factory for component for further configuration
140     **/
141     ConfigurableFactoryType!T register(T, string file = __FILE__, size_t line = __LINE__)(auto ref T value, string identity) {
142         import aermicioi.aedi.configurer.register.factory_configurer : val = value;
143 
144         ConfigurableFactoryType!T factory = register!(T, file, line)(identity);
145 
146         factory.val(value);
147 
148         return factory;
149     }
150 
151     /**
152     ditto
153     **/
154     ConfigurableFactoryType!T register(T, string file = __FILE__, size_t line = __LINE__)(auto ref T value)
155         if (!is(T == string)) {
156 
157         return register!(T, file, line)(value, fullyQualifiedName!T);
158     }
159 
160     /**
161     ditto
162     **/
163     ConfigurableFactoryType!T register(Interface, T : Interface, string file = __FILE__, size_t line = __LINE__)(auto ref T value)
164         if (!is(T == Interface)) {
165 
166         return register!(T, file, line)(value, fullyQualifiedName!Interface);
167     }
168 }
169 
170 /**
171 Policy responsible for creation of generic factory that will create component T.
172 **/
173 struct GenericFactoryPolicy {
174 
175     alias FactoryPolicy = DecoratingFactoryPolicy;
176 
177     static void initialize(Storage!(ObjectFactory, string) storage, Locator!(Object, string) locator, RCIAllocator allocator) {
178 
179     }
180 
181     static void apply(Z : ConfigurableFactory!(T, Policies), T, Policies...)(Z factory) {
182         factory.decorated = new GenericFactoryImpl!T(factory.locator);
183         factory.decorated.allocator = factory.allocator;
184     }
185 }
186 
187 /**
188 Policy responsible for creation of factory wrapper suitable for storing into storage.
189 **/
190 struct WrappingFactoryPolicy {
191 
192     alias FactoryPolicy = WrapperStorePolicy;
193 
194     static void initialize(Storage!(ObjectFactory, string) storage, Locator!(Object, string) locator, RCIAllocator allocator) {
195 
196     }
197 
198     static void apply(Z : ConfigurableFactory!(T, Policies), T, Policies...)(Z factory) {
199         factory.wrapper = new WrappingFactory!(Factory!T)(factory);
200     }
201 }
202 
203 /**
204 Policy responsible for persisting wrapper into storage by some identity.
205 **/
206 struct PersistingFactoryPolicy {
207 
208     alias FactoryPolicy = StoragePolicy;
209 
210     static void initialize(Storage!(ObjectFactory, string) storage, Locator!(Object, string) locator, RCIAllocator allocator) {
211 
212     }
213 
214     static void apply(Z : ConfigurableFactory!(T, Policies), T, Policies...)(Z factory) {
215         factory.storage.set(factory.wrapper, factory.identity);
216     }
217 }
218 
219 /**
220 Start registering components using a storage and a locator.
221 
222 Start registering components using a storage and a locator.
223 
224 Params:
225 	storage = store registered components into it.
226 	locator = locator of dependencies for registered components
227     allocator = default allocation strategy for registered components
228 
229 Returns:
230 	RegistrationContext context with registration interface used to register components.
231 **/
232 RegistrationContext!Policies configure(Policies...)
233     (Storage!(ObjectFactory, string) storage, Locator!(Object, string) locator, RCIAllocator allocator = theAllocator)
234     if (Policies.length > 0) {
235     return RegistrationContext!(Policies)(storage, locator, allocator).initialize();
236 }
237 
238 /**
239 ditto
240 **/
241 RegistrationContext!Policies configure(Policies...)
242     (Locator!(Object, string) locator, Storage!(ObjectFactory, string) storage, RCIAllocator allocator = theAllocator)
243     if (Policies.length > 0) {
244     return RegistrationContext!(Policies)(storage, locator, allocator).initialize();
245 }
246 
247 /**
248 ditto
249 **/
250 RegistrationContext!(
251     GenericFactoryPolicy,
252     WrappingFactoryPolicy,
253     DeferredFactoryPolicy,
254     RegistrationAwarePolicy,
255     PersistingFactoryPolicy
256 ) configure(
257         Storage!(ObjectFactory, string) storage,
258         Locator!(Object, string) locator,
259         RCIAllocator allocator = theAllocator)
260 {
261     return storage.configure!(
262         GenericFactoryPolicy,
263         WrappingFactoryPolicy,
264         DeferredFactoryPolicy,
265         RegistrationAwarePolicy,
266         PersistingFactoryPolicy)(locator, allocator);
267 }
268 
269 /**
270 ditto
271 **/
272 RegistrationContext!(
273     GenericFactoryPolicy,
274     WrappingFactoryPolicy,
275     DeferredFactoryPolicy,
276     RegistrationAwarePolicy,
277     PersistingFactoryPolicy
278 ) configure(
279         Locator!(Object, string) locator,
280         Storage!(ObjectFactory, string) storage,
281         RCIAllocator allocator = theAllocator)
282 {
283     return storage.configure!(
284         GenericFactoryPolicy,
285         WrappingFactoryPolicy,
286         DeferredFactoryPolicy,
287         RegistrationAwarePolicy,
288         PersistingFactoryPolicy)(locator, allocator);
289 }
290 
291 /**
292 Start registering components using a container.
293 
294 Start registering components using a container.
295 
296 Params:
297 	container = storage and locator of components.
298     allocator = default allocation strategy for registered components
299 
300 Returns:
301 	RegistrationContext context with registration interface used to register components.
302 **/
303 auto configure(T)(T container, RCIAllocator allocator = theAllocator)
304     if (is(T : Storage!(ObjectFactory, string)) && is(T : Locator!(Object, string))) {
305 
306     return configure(cast(Storage!(ObjectFactory, string)) container, container, allocator);
307 }
308 
309 /**
310 Start registering components using a storage and a locator.
311 
312 Start registering components using a storage and a locator.
313 
314 Params:
315 	storage = identity of a storage located in locator used by registration context to store components.
316 	locator = locator of dependencies for registered components
317     allocator = default allocation strategy for registered components
318 
319 Returns:
320 	RegistrationContext!Policies context with registration interface used to register components.
321 **/
322 RegistrationContext!Policies configure(Policies...)(Locator!(Object, string) locator, string storage, RCIAllocator allocator = theAllocator)
323     if (Policies.length > 1) {
324     return configure!Policies(locator, locator.locate!(Storage!(ObjectFactory, string))(storage), allocator);
325 }
326 
327 auto configure(Locator!(Object, string) locator, string storage, RCIAllocator allocator = theAllocator) {
328     return configure(locator, locator.locate!(Storage!(ObjectFactory, string))(storage), allocator);
329 }
330 
331 /**
332 Use locator/storage/allocator as basis for registering components.
333 
334 Use locator/storage/allocator as basis for registering components.
335 
336 Params:
337     context = context for which to set new configured storage, or used locator
338 	storage = store registered components into it.
339 	locator = locator of dependencies for registered components
340     allocator = allocator used as default allocation strategy for components.
341 
342 Returns:
343 	RegistrationContext!Policies context with registration interface used to register components.
344 **/
345 RegistrationContext!Policies along(Policies...)(RegistrationContext!Policies context, Storage!(ObjectFactory, string) storage) {
346     context.storage = storage;
347 
348     return context.initialize;
349 }
350 
351 /**
352 ditto
353 **/
354 RegistrationContext!Policies along(Policies...)(RegistrationContext!Policies context, Locator!(Object, string) locator) {
355     context.locator = locator;
356 
357     return context.initialize;
358 }
359 
360 /**
361 ditto
362 **/
363 RegistrationContext!Policies along(Policies...)(RegistrationContext!Policies context, RCIAllocator allocator) {
364     context.allocator = allocator;
365 
366     return context.initialize;
367 }
368 
369 /**
370 Use storage as basis for registering components.
371 
372 Use storage as basis for registering components.
373 
374 Params:
375     context = context for which to set new configured storage, or used locator
376 	storage = identity of a storage located in locator that should be used by registrationContext to store components.
377 
378 Returns:
379 	RegistrationContext context with registration interface used to register components.
380 **/
381 RegistrationContext!Policies along(Policies...)(RegistrationContext!Policies context, string storage) {
382     import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException;
383 
384     context.storage = context.locator.locate!(Storage!(ObjectFactory, string))(storage);
385 
386     return context.initialize;
387 }
388 
389 /**
390 Apply a policy after existing policies in a registration context, or at a position if specified.
391 
392 Params:
393     context = context to which add a new policy
394     at = position at whicht to apply policy
395     Policy = new policy to inject into registration context
396 
397 Returns:
398     RegistrationContext!(Policies, Policy)
399 **/
400 RegistrationContext!(Policies, Policy) applying(Policy, Policies...)(RegistrationContext!Policies context) {
401     return RegistrationContext!(Policies, Policy)(context.storage, context.locator, context.allocator).initialize;
402 }
403 
404 /**
405 ditto
406 **/
407 RegistrationContext!(Policies[0 .. at], Policy, Policies[at .. $]) applying(size_t at, Policy, Policies...)(RegistrationContext!Policies context) {
408     return RegistrationContext!(Policies[0 .. at], Policy, Policies[at .. $])(context.storage, context.locator, context.allocator).initialize;
409 }
410 
411 /**
412 A registration interface for components already created.
413 
414 Value registration context, provides a nice registration
415 api over Object containers, to store already instantiated
416 components into container.
417 **/
418 struct ValueRegistrationContext {
419 
420     public {
421         /**
422         Storage for already instantiated components.
423         **/
424         Storage!(Object, string) storage;
425 
426         /**
427         Locator used for configuration purposes of features outside value container, yet related to the managed components.
428         **/
429         Locator!() locator;
430 
431         /**
432         Register a component into value container by identity, type or interface.
433 
434         Register a component into value container by identity, type or interface.
435 
436         Params:
437         	value = component to be registered in container
438         	identity = identity of component in container
439         	T = type of component
440         	Interface = interface that T component implements
441 
442         Returns:
443         	ValueRegistrationContext
444         **/
445         ValueContext register(T)(auto ref T value, string identity) {
446             static if (is(T : Object)) {
447                 storage.set(value, identity);
448             } else {
449                 import aermicioi.aedi.storage.wrapper : WrapperImpl;
450 
451                 storage.set(new WrapperImpl!T(value), identity);
452             }
453 
454             return ValueContext(identity, storage, locator);
455         }
456 
457         /**
458         ditto
459         **/
460         ValueContext register(T)(auto ref T value) {
461             return register!T(value, fullyQualifiedName!T);
462         }
463 
464         /**
465         ditto
466         **/
467         ValueContext register(Interface, T : Interface)(auto ref T value) {
468             return register!T(value, fullyQualifiedName!Interface);
469         }
470     }
471 
472     /**
473     Value context useable for further configuration of container
474     **/
475     static struct ValueContext {
476 
477         /**
478         Identity of managed component
479         **/
480         string identity;
481 
482         /**
483         Storage of component
484         **/
485         Storage!(Object, string) storage;
486 
487         /**
488         Locator of components. Used for configuration of features not directly related to value container.
489         **/
490         Locator!() locator;
491     }
492 }
493 
494 /**
495 Start registering instantiated components into a value container.
496 
497 Start registering instantiated components into a value container.
498 Description
499 
500 Params:
501 	storage = value container used to store instantiated components
502 
503 Returns:
504 	ValueRegistrationContext context that provides register api, using storage to store registered components.
505 **/
506 ValueRegistrationContext configure(Storage!(Object, string) storage) {
507     return ValueRegistrationContext(storage);
508 }
509 
510 /**
511 Start registering instantiated components into a value container.
512 
513 Start registering instantiated components into a value container.
514 Description
515 
516 Params:
517     locator = container that has the storage
518 	storage = identity of storage to use
519 
520 Returns:
521 	ValueRegistrationContext context that provides register api, using storage to store registered components.
522 **/
523 ValueRegistrationContext configureValueContainer(Locator!() locator, string storage) {
524     return ValueRegistrationContext(locator.locate!(Storage!(Object, string))(storage), locator);
525 }
526 
527 /**
528 Adds registration location information in component's factory for easier debugging.
529 
530 Params:
531     context = original preconfigured registration context to use as basis.
532 **/
533 struct RegistrationAwarePolicy {
534 
535     alias FactoryPolicy = RegistrationStorePolicy;
536 
537     static void initialize(Storage!(ObjectFactory, string) storage, Locator!(Object, string) locator, RCIAllocator allocator) {
538 
539     }
540 
541     static void apply(Z : ConfigurableFactory!(T, Policies), T, Policies...)(Z factory) {
542         import aermicioi.aedi.factory.decorating_factory : RegistrationAwareDecoratingFactory;
543         RegistrationAwareDecoratingFactory!Object wrapper = new RegistrationAwareDecoratingFactory!Object();
544         wrapper.file = factory.file;
545         wrapper.line = factory.line;
546 
547         wrapper.decorated = factory.wrapper;
548         factory.wrapper = wrapper;
549     }
550 }
551 
552 struct DeferredFactoryPolicy {
553     import aermicioi.aedi.factory.deferring_factory : DeferralContext;
554 
555     alias FactoryPolicy = AliasSeq!();
556 
557     static void initialize(Storage!(ObjectFactory, string) storage, Locator!(Object, string) locator, RCIAllocator allocator)
558     in (storage !is null, "Storage is required for initialization of deferred policy.")
559     in (locator !is null, "Locator is required for initialization of deferred policy.")
560     in (!allocator.isNull, "Allocator is required for initialization of deferred policy.") {
561 
562     }
563 
564     static void apply(Z : ConfigurableFactory!(T, Policies), T, Policies...)(Z factory) {
565         import aermicioi.aedi.exception.not_found_exception : NotFoundException;
566         import aermicioi.aedi.factory.deferring_factory;
567         import aermicioi.aedi.util.typecons : optional;
568         import aermicioi.aedi.factory.reference : lref;
569 
570         if (factory.locator.has(fullyQualifiedName!DeferralContext)) {
571             DeferringFactory!T deferring = new DeferringFactory!T(factory.decorated, factory.locator.locate!DeferralContext);
572             factory.decorated = deferring;
573         }
574     }
575 }
576 
577 private template FactoryPolicyExtractor(Policies...) {
578 
579     static if (Policies.length > 1) {
580         alias FactoryPolicyExtractor = AliasSeq!(Policies[0].FactoryPolicy, FactoryPolicyExtractor!(Policies[1 .. $]));
581     } else {
582         alias FactoryPolicyExtractor = Policies[0].FactoryPolicy;
583     }
584 }