1 /**
2 Contains factories and primitives used for building proxy objects.
3 
4 License:
5 	Boost Software License - Version 1.0 - August 17th, 2003
6 
7 	Permission is hereby granted, free of charge, to any person or organization
8 	obtaining a copy of the software and accompanying documentation covered by
9 	this license (the "Software") to use, reproduce, display, distribute,
10 	execute, and transmit the Software, and to prepare derivative works of the
11 	Software, and to permit third-parties to whom the Software is furnished to
12 	do so, all subject to the following:
13 
14 	The copyright notices in the Software and this entire statement, including
15 	the above license grant, this restriction and the following disclaimer,
16 	must be included in all copies of the Software, in whole or in part, and
17 	all derivative works of the Software, unless such copies or derivative
18 	works are solely in the form of machine-executable object code generated by
19 	a source language processor.
20 
21 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 	DEALINGS IN THE SOFTWARE.
28 
29 Authors:
30 	aermicioi
31 **/
32 module aermicioi.aedi.factory.proxy_factory;
33 
34 import aermicioi.aedi.factory.factory;
35 import aermicioi.aedi.factory.generic_factory;
36 import aermicioi.aedi.storage.decorator;
37 import aermicioi.aedi.storage.locator;
38 import aermicioi.aedi.exception.di_exception;
39 import aermicioi.aedi.storage.locator_aware;
40 
41 import aermicioi.aedi.util.traits;
42 
43 import std.algorithm;
44 import std.meta;
45 import std.range;
46 import std.traits;
47 import std.typecons : AutoImplement;
48 
49 /**
50 Creates a proxy to an object or interface of type T,
51 that is located in source locator by some identity.
52 **/
53 @safe class ProxyFactory(T) : Factory!T
54     if (
55         (
56             is(T == class) &&
57             !isFinalClass!T &&
58             !isAbstractClass!T
59         ) ||
60         (is(T == interface))
61     ) {
62 
63     import aermicioi.aedi.storage.allocator_aware : AllocatorAwareMixin, make, dispose, theAllocator;
64     mixin AllocatorAwareMixin!(typeof(this));
65 
66     private {
67         string identity_;
68         Locator!() source_;
69     }
70 
71     public {
72         /**
73             Constructor for ProxyFactory!(T)
74 
75             Params:
76                 identity = identity of original object stored in a locator
77                 original = locator that contains original object that a proxy should proxy.
78         **/
79         this(string identity, Locator!() original) {
80             this.identity = identity;
81             this.source = original;
82             this.allocator = theAllocator;
83         }
84 
85         /**
86 		Instantiates component of type T.
87 
88 		Returns:
89 			T instantiated component of type T.
90 		**/
91         Proxy!T factory() @trusted {
92             auto proxy = this.allocator.make!(Proxy!T);
93             proxy.__id__ = this.identity;
94             proxy.__locator__ = this.source;
95 
96             return proxy;
97         }
98 
99         /**
100         Destructs a component of type T.
101 
102         Params:
103             component = component that is to ve destroyed.
104 
105         Returns:
106 
107         **/
108         void destruct(ref T component) @trusted {
109             Proxy!T proxy = cast(Proxy!T) component;
110 
111             if (proxy !is null) {
112 
113                 this.allocator.dispose(proxy);
114                 return;
115             }
116 
117             import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException;
118             throw new InvalidCastException(
119                 "Cannot destruct component ${identity} because it is not a proxied object. Expected ${expected} while got ${actual}",
120                 null,
121                 typeid(Proxy!T),
122                 typeid(T)
123             );
124         }
125 
126         @property {
127             /**
128             Set the identity of proxied object
129 
130             Params:
131             	identity = the identity of proxied object
132 
133             Returns:
134             	this
135             **/
136             ProxyFactory!T identity(string identity) @safe nothrow {
137             	this.identity_ = identity;
138 
139             	return this;
140             }
141 
142             /**
143             Get the identity of proxied object.
144 
145             Returns:
146             	string identity
147             **/
148             string identity() @safe nothrow {
149             	return this.identity_;
150             }
151 
152             /**
153             Set the source of proxied object.
154 
155             Params:
156             	source = source locator where proxied object resides.
157 
158             Returns:
159             	this
160             **/
161             ProxyFactory!T source(Locator!() source) @safe nothrow {
162             	this.source_ = source;
163 
164             	return this;
165             }
166 
167             /**
168             Get the source of proxied object.
169 
170             Returns:
171             	Locator!() source
172             **/
173             Locator!() source() @safe nothrow {
174             	return this.source_;
175             }
176 
177             /**
178             Set locator
179 
180             Params:
181                 locator = locator or source of original component proxied by proxy
182             Returns:
183                 typeof(this)
184             **/
185             LocatorAware!(Object, string) locator(Locator!(Object, string) locator) @safe nothrow {
186                 this.source = locator;
187 
188                 return this;
189             }
190 
191             /**
192             Get locator
193 
194             Returns:
195                 Locator!()
196             **/
197             Locator!() locator() @safe nothrow {
198                 return this.source;
199             }
200 
201             /**
202     		Get the type info of T that is created.
203 
204     		Returns:
205     			TypeInfo object of created component.
206     		**/
207             TypeInfo type() @safe nothrow const {
208                 return typeid(Proxy!T);
209             }
210 
211         }
212     }
213 }
214 
215 /**
216 Auto implements a proxy for object or interface of type T which
217 is not a final or abstract class.
218 
219 Warning:
220     Current implmentation uses AutoImplement from phobos which
221     has some unfixed bugs.
222 **/
223 template ProxyImpl(T)
224     if (
225         (
226             is(T == class) &&
227             !isFinalClass!T &&
228             !isAbstractClass!T
229         ) ||
230         is(T == interface)
231     ) {
232 
233     class ProxyImpl : T {
234         private {
235 
236             Locator!() __locator_;
237             string __id_;
238         }
239 
240         public {
241 
242             this() {
243                 super();
244             }
245         }
246 
247         @property public {
248             ProxyImpl!T __locator__(Locator!() locator) @safe nothrow @nogc {
249             	this.__locator_ = locator;
250 
251             	return this;
252             }
253 
254             Locator!() __locator__() @safe nothrow @nogc {
255             	return this.__locator_;
256             }
257 
258             ProxyImpl!T __id__(string id) @safe nothrow @nogc {
259             	this.__id_ = id;
260 
261             	return this;
262             }
263 
264             string __id__() @safe nothrow @nogc {
265             	return this.__id_;
266             }
267         }
268     }
269 }
270 
271 template how(T) {
272     static string how(C, alias fun)() {
273         static if (identifier!fun == "__ctor") {
274             pragma(msg, "here");
275             return q{super(args)};
276         } static if (identifier!fun == "__dtor") {
277             return q{};
278         } else {
279 
280             string stmt = q{
281                 import aermicioi.aedi.storage.locator;
282                 import aermicioi.aedi.exception.di_exception;
283                 import aermicioi.aedi.factory.proxy_factory;
284                 } ~ fullyQualifiedName!T ~ " original;
285                 try {
286                     original = this.__locator__.locate!(" ~ fullyQualifiedName!T ~ ")(this.__id__);
287                 } catch (Exception e) {
288                     assert(false, \"Failed to fetch \" ~ __id__ ~ \" in proxy object.\");
289                 }
290             ";
291 
292             static if (!is(ReturnType!fun == void)) {
293                 stmt ~= "
294                     return original." ~ __traits(identifier, fun) ~ "(args);
295                 ";
296             } else {
297                 stmt ~= "
298                     original." ~ __traits(identifier, fun) ~ "(args);
299                 ";
300             }
301 
302             return stmt;
303         }
304 
305     }
306 }
307 
308 alias Proxy(T) = AutoImplement!(ProxyImpl!T, how!T, templateAnd!(
309         templateNot!isFinalFunction
310 ));
311 
312 /**
313 A ProxyObjectFactory instantiates a proxy to some type of object located in source locator.
314 **/
315 @safe interface ProxyObjectFactory : ObjectFactory {
316 
317     @property {
318 
319         /**
320         Get the identity of original object that proxy factory will intantiate proxy object.
321 
322         Returns:
323         	string the original object identity
324         **/
325         string identity() @safe nothrow;
326 
327         /**
328         Get the original locator that is used by proxy to fetch the proxied object.
329 
330         Returns:
331         	Locator!() original locator containing the proxied object.
332         **/
333         Locator!() source() @safe nothrow;
334     }
335 }
336 
337 /**
338 Proxy factory decorator, that conforms to requirements of a container, exposing as well the ability
339 to set proxied object's identity and locator.
340 **/
341 @safe class ProxyObjectWrappingFactory(T) : ProxyObjectFactory, MutableDecorator!(ProxyFactory!T)
342     if (is(T : Object) && !isFinalClass!T) {
343 
344     import aermicioi.aedi.storage.allocator_aware : AllocatorAwareMixin;
345     mixin AllocatorAwareMixin!(typeof(this));
346 
347     private {
348         ProxyFactory!T decorated_;
349 
350     }
351 
352     public {
353 
354         /**
355         Constructor for ProxyObjectWrappingFactory!T
356 
357         Params:
358             factory = proxy factory that is decorated
359         **/
360         this(ProxyFactory!T factory) {
361             this.decorated = factory;
362         }
363 
364         @property {
365             /**
366             Get the identity of original object that proxy factory will intantiate proxy object.
367 
368             Returns:
369                 string the original object identity
370             **/
371             ProxyObjectWrappingFactory!T identity(string identity) @safe nothrow {
372             	this.decorated.identity = identity;
373 
374             	return this;
375             }
376 
377             /**
378             Get identity
379 
380             Returns:
381                 string
382             **/
383             string identity() @safe nothrow {
384             	return this.decorated.identity;
385             }
386 
387             /**
388             Get the original locator that is used by proxy to fetch the proxied object.
389 
390             Returns:
391                 Locator!() original locator containing the proxied object.
392             **/
393             ProxyObjectWrappingFactory!T source(Locator!() source) @safe nothrow {
394             	this.decorated.source = source;
395 
396             	return this;
397             }
398 
399             /**
400             Get source
401 
402             Returns:
403                 Locator!()
404             **/
405             Locator!() source() @safe nothrow {
406             	return this.decorated.source;
407             }
408 
409             mixin MutableDecoratorMixin!(ProxyFactory!T);
410 
411             /**
412             Set a locator to object.
413 
414             Params:
415                 locator = the locator that is set to oject.
416 
417             Returns:
418                 LocatorAware.
419             **/
420             ProxyObjectWrappingFactory!T locator(Locator!() locator) @safe nothrow {
421             	this.decorated.locator = locator;
422 
423             	return this;
424             }
425 
426             /**
427             Get locator
428 
429             Returns:
430                 Locator!()
431             **/
432             Locator!() locator() @safe nothrow {
433             	return this.decorated.locator;
434             }
435 
436             /**
437     		Get the type info of T that is created.
438 
439     		Returns:
440     			TypeInfo object of created component.
441     		**/
442             TypeInfo type() @safe nothrow const {
443                 return this.decorated.type;
444             }
445         }
446         /**
447 		Instantiates component of type Object.
448 
449 		Returns:
450 			Object instantiated component.
451 		**/
452         Object factory() @safe {
453             return this.decorated.factory();
454         }
455 
456         /**
457         Destructs a component of type T.
458 
459         Params:
460             component = component that is to ve destroyed.
461 
462         Returns:
463 
464         **/
465         void destruct(ref Object component) @safe
466         in (component !is null, "Cannot destroy a null component, expected component of type " ~ typeid(T).toString) {
467             T proxy = cast(T) component;
468 
469             if (proxy !is null) {
470 
471                 this.decorated.destruct(proxy);
472                 return;
473             }
474 
475             import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException;
476             throw new InvalidCastException(
477                 "Cannot destruct component ${identity} because it is not managed by this proxy factory. Expected ${expected} while got ${actual}",
478                 null,
479                 typeid(Proxy!T),
480                 component.classinfo
481             );
482         }
483     }
484 }