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.container.aggregate_container;
31 
32 import aermicioi.aedi.container.container;
33 import aermicioi.aedi.storage.storage;
34 import aermicioi.aedi.util.typecons : Pair, pair;
35 import aermicioi.aedi.storage.locator;
36 import aermicioi.aedi.storage.object_storage;
37 import aermicioi.aedi.exception.not_found_exception;
38 import aermicioi.aedi.util.range;
39 import std.range.interfaces;
40 import std.range : chain;
41 import std.algorithm : filter, map;
42 
43 /**
44 Aggregate container, that delegates the task of locating to containers
45 managed by it.
46 
47 **/
48 @safe class AggregateContainer : Container, Storage!(Container, string), AggregateLocator!(Object, string) {
49 
50     private {
51         alias Entry = Pair!(Container, string);
52 
53         Entry[] containers;
54     }
55 
56     public {
57 
58         /**
59         Set a container into aggregate container
60 
61         Set a container into aggregate container
62         Description
63 
64         Params:
65         	container = container that is added to aggregate container
66         	identity = identity by which container is identified in aggregate container
67 
68         Returns:
69         	AggregateContainer
70         **/
71         AggregateContainer set(Container container, string identity) {
72         	this.containers ~= Entry(container, identity);
73 
74         	return this;
75         }
76 
77         /**
78         Remove a container from aggregate container.
79 
80         Remove a container from aggregate container.
81 
82         Params:
83         	identity = identity of container to be removed
84 
85         Returns:
86         	AggregateContainer
87         **/
88         AggregateContainer remove(string identity) {
89             import std.array : array;
90         	this.containers = this.containers.filter!(entry => entry.key != identity).array;
91 
92         	return this;
93         }
94 
95         /**
96         Get a container, or an object that is contained by managed containers.
97 
98         Get a container, or an object that is contained by managed containers.
99 
100         Params:
101         	identity = identity of object that is to be supplied.
102 
103         Throws:
104         	NotFoundException when no requested object by identity is present in container
105 
106         Returns:
107         	Object the object contained in one of containers or a container itself.
108         **/
109         Object get(string identity) {
110             foreach (entry; this.containers.filter!(entry => entry.key == identity)) {
111                 Object container = (() scope @trusted => cast(Object) entry.value)();
112 
113                 if (container !is null) {
114                     return container;
115                 }
116             }
117 
118             foreach (container; this.containers.map!(entry => entry.value)) {
119                 foreach (type; typeid(container).inheritance.chain(
120                     typeid((() @trusted => cast(Object) container)()).inheritance)
121                 ) {
122                     if (type.name == identity) {
123                         return (() scope @trusted => cast(Object) container)();
124                     }
125                 }
126             }
127 
128         	foreach (container; this.containers.map!(entry => entry.value)) {
129         	    if (container.has(identity)) {
130         	        return container.get(identity);
131         	    }
132         	}
133 
134         	throw new NotFoundException("Component ${identity} not found.", identity);
135         }
136 
137         /**
138         Check if an object is present in one of containers, or it is a container itself.
139 
140         Check if an object is present in one of containers, or it is a container itself.
141 
142         Params:
143         	identity = identity of object to be checked
144 
145         Returns:
146         	bool true if exists, false otherwise
147         **/
148         bool has(in string identity) inout {
149             foreach (entry; this.containers) {
150                 if (entry.key == identity) {
151                     return true;
152                 }
153             }
154 
155             foreach (entry; this.containers) {
156                 foreach (type; typeid(entry.value).inheritance.chain(
157                     typeid((() @trusted => cast(Object) entry.value)()).inheritance)
158                 ) {
159                     if (type.name == identity) {
160                         return true;
161                     }
162                 }
163             }
164 
165             foreach (entry; this.containers) {
166                 if (entry.value.has(identity)) {
167                     return true;
168                 }
169             }
170 
171             return false;
172         }
173 
174         /**
175         Finalize all unfinished initialization work in containers.
176 
177         Finalize all unfinished initialization work in containers.
178 
179         Returns:
180         	AggregateContainer
181         **/
182         AggregateContainer instantiate() {
183 
184             foreach (container; this.containers.map!(entry => entry.value)) {
185                 container.instantiate;
186             }
187 
188             return this;
189         }
190 
191         /**
192         Destruct all managed components.
193 
194         Destruct all managed components. The method denotes the end of container lifetime, and therefore destruction of all managed components
195         by it.
196         **/
197         AggregateContainer terminate() {
198             foreach (container; this.containers.map!(entry => entry.value)) {
199                 container.terminate;
200             }
201 
202             return this;
203         }
204 
205         /**
206         Get a specific container.
207 
208         Params:
209             identity = the container identity.
210         **/
211         Locator!(Object, string) getLocator(string identity) {
212 
213             return this.containers.filter!(entry => entry.key == identity).front.value;
214         }
215 
216         /**
217         Get all containers in aggregate container
218 
219         Returns:
220         	InputRange!(Tuple!(Locator!(Object, string), string)) a range of container => identity
221         **/
222         InputRange!(Pair!(Locator!(), string)) getLocators() {
223             import std.algorithm : map;
224 
225             return this.containers.map!(entry => Pair!(Locator!(), string)(entry.value, entry.key)).inputRangeObject;
226         }
227 
228         /**
229         Check if aggregate container contains a specific container.
230 
231         Params:
232         	key = the identity of container in aggregate container
233         **/
234         bool hasLocator(string identity) inout {
235 
236             foreach (entry; this.containers) {
237                 if (entry.key == identity) {
238                     return true;
239                 }
240             }
241 
242             return false;
243         }
244     }
245 }