1 /**
2 
3 License:
4 	Boost Software License - Version 1.0 - August 17th, 2003
5 
6     Permission is hereby granted, free of charge, to any person or organization
7     obtaining a copy of the software and accompanying documentation covered by
8     this license (the "Software") to use, reproduce, display, distribute,
9     execute, and transmit the Software, and to prepare derivative works of the
10     Software, and to permit third-parties to whom the Software is furnished to
11     do so, all subject to the following:
12 
13     The copyright notices in the Software and this entire statement, including
14     the above license grant, this restriction and the following disclaimer,
15     must be included in all copies of the Software, in whole or in part, and
16     all derivative works of the Software, unless such copies or derivative
17     works are solely in the form of machine-executable object code generated by
18     a source language processor.
19 
20     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26     DEALINGS IN THE SOFTWARE.
27 
28 Authors:
29 	Alexandru Ermicioi
30 **/
31 module aermicioi.aedi.container.prototype_container;
32 
33 import aermicioi.aedi.container.container;
34 import aermicioi.aedi.storage.object_storage;
35 import aermicioi.aedi.util.typecons : Pair, pair;
36 import aermicioi.aedi.factory.factory;
37 import aermicioi.aedi.exception;
38 import aermicioi.aedi.container.factory;
39 
40 import std.range.interfaces;
41 
42 /**
43  Prototype container.
44 
45  Instantiates a new object using passed ObjectFactory implementation, on each request of it by some part of an application.
46 **/
47 @safe class PrototypeContainer : ConfigurableContainer {
48 
49     private {
50 
51         ObjectStorage!(ObjectFactory, string) factories;
52         ObjectStorage!(Object[], string) proxies;
53     }
54 
55     public {
56 
57         /**
58          * Default constructor for PrototypeContainer
59         **/
60         this() {
61             this.factories = new ObjectStorage!(ObjectFactory, string);
62             this.proxies = new ObjectStorage!(Object[], string);
63         }
64 
65         /**
66          * Set object factory
67          *
68          * Params:
69          * 	object = factory for a object that is to be managed by prototype container.
70          *  key = identity of factory
71          * Returns:
72          * 	typeof(this)
73         **/
74         PrototypeContainer set(ObjectFactory object, string key) {
75             this.factories.set(new ExceptionChainingObjectFactory(new InProcessObjectFactoryDecorator(object), key), key);
76 
77             return this;
78         }
79 
80         /**
81          * Remove an object factory from container.
82          *
83          * Params:
84          * 	key = identity of factory to be removed
85          * Returns:
86          * 	typeof(this)
87         **/
88         PrototypeContainer remove(string key) {
89             if (this.proxies.has(key)) {
90                 import std.algorithm : each;
91 
92                 this.proxies.get(key).each!((proxy) => this.factories.get(key).destruct(proxy));
93             }
94 
95             this.factories.remove(key);
96             this.proxies.remove(key);
97 
98             return this;
99         }
100 
101         /**
102          * Get object created by a factory identified by key
103          *
104          * Params:
105          *  key = identity of factory
106          * Returns:
107          * 	Object
108         **/
109         Object get(string key) {
110             if (this.factories.has(key)) {
111 
112                 Object obj = this.factories.get(key).factory();
113 
114                 Object[] proxies;
115 
116                 if (this.proxies.has(key)) {
117                     proxies = this.proxies.get(key);
118                 }
119 
120                 this.proxies.set(proxies ~ obj, key);
121 
122                 return obj;
123             }
124 
125             throw new NotFoundException("Component ${identity} not found", key);
126         }
127 
128         /**
129          * Check if an object factory for it exists in container.
130          *
131          * Params:
132          * 	key = identity of factory
133          * Returns:
134          * 	bool
135         **/
136         bool has(in string key) inout {
137             return this.factories.has(key);
138         }
139 
140         /**
141         Sets up the internal state of container.
142 
143         Sets up the internal state of container (Ex, for singleton container it will spawn all objects that locator contains).
144         **/
145         PrototypeContainer instantiate() {
146 
147             return this;
148         }
149 
150         /**
151         Destruct all managed components.
152 
153         Destruct all managed components. The method denotes the end of container lifetime, and therefore destruction of all managed components
154         by it.
155         **/
156         PrototypeContainer terminate() {
157             import std.algorithm : each;
158 
159             foreach (pair; this.proxies.contents.byKeyValue) {
160                 pair.value.each!((proxy) => this.factories.get(pair.key).destruct(proxy));
161             }
162 
163             (() @trusted scope => this.proxies.contents.clear)();
164 
165             return this;
166         }
167         /**
168         Alias a key to an alias_.
169 
170         Params:
171         	key = the originial identity which is to be aliased.
172         	alias_ = the alias of identity.
173 
174 		Returns:
175 			this
176         **/
177         PrototypeContainer link(string key, string alias_) {
178             this.factories.link(key, alias_);
179 
180             return this;
181         }
182 
183         /**
184         Removes alias.
185 
186         Params:
187         	alias_ = alias to remove.
188 
189         Returns:
190             this
191 
192         **/
193         PrototypeContainer unlink(string alias_) {
194             this.factories.unlink(alias_);
195 
196             return this;
197         }
198 
199         /**
200         Resolve an alias to original identity, if possible.
201 
202         Params:
203         	key = alias of original identity
204 
205         Returns:
206         	Type the last identity in alias chain.
207 
208         **/
209         const(string) resolve(in string key) const {
210             return this.factories.resolve(key);
211         }
212 
213         /**
214         Get factory for constructed component identified by identity.
215 
216         Get factory for constructed component identified by identity.
217         Params:
218         	identity = the identity of component that factory constructs.
219 
220         Throws:
221         	NotFoundException when factory for it is not found.
222 
223         Returns:
224         	ObjectFactory the factory for constructed component.
225         **/
226         ObjectFactory getFactory(string identity) {
227             return this.factories.get(identity);
228         }
229 
230         /**
231         Get all factories available in container.
232 
233         Get all factories available in container.
234 
235         Returns:
236         	InputRange!(Pair!(ObjectFactory, string)) a pair of factory => identity.
237         **/
238         InputRange!(Pair!(ObjectFactory, string)) getFactories() {
239             import std.algorithm : map;
240 
241             return this.factories.contents.byKeyValue.map!(
242                 a => Pair!(ObjectFactory, string)(a.value, a.key)
243             ).inputRangeObject;
244         }
245     }
246 }