1 /*
2  * %injeqt copyright begin%
3  * Copyright 2014 Rafał Malinowski (rafal.przemyslaw.malinowski@gmail.com)
4  * %injeqt copyright end%
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #pragma once
22 
23 #include <injeqt/injeqt.h>
24 #include <injeqt/type.h>
25 
26 #include <memory>
27 #include <vector>
28 #include <QtCore/QObject>
29 
30 /**
31  * @file
32  * @brief Contains classes and functions for creating injectors.
33  */
34 
35 namespace injeqt { namespace internal {
36 	class injector_impl;
37 }}
38 
39 namespace injeqt { namespace v1 {
40 
41 class module;
42 
43 /**
44  * @brief Injector is created from set of modules that contains injectable types.
45  *
46  * Injector is constructed from set of modules that contains groups of related types.
47  * Then it can be used to get instances of configured types that have their dependencies
48  * resolved and set - there is no need to manually wire services and factories.
49  *
50  * If two or more modules have the same type configured an exception is thrown during construction.
51  * Otherwise, from all types in modules a set of available types is computed. Each type directly
52  * added to modules is added to it. Then all unique super types are added to this set. This means
53  * that two types with common ancestor can be used in injection. Note that Qt meta object system
54  * only supports single inheritance.
55  *
56  * Injector takes ownership of modules passed to it. Also all objects created inside injector
57  * (using default constructor - configured with module::add_type<T>(), or factory - configured
58  * with module::add_factory<T, F>()) are under its ownership. Lifetime of objects added as ready
59  * objects (configured with module::add_ready_object<T>(QObject *) is not managed by injector.
60  * For clarity ready objects can be stored in module instances as unique pointers. Injector will own
61  * then as it own modules.
62  */
63 class INJEQT_API injector final
64 {
65 
66 public:
67 	/**
68 	 * @brief Create empty injector with no types configured.
69 	 *
70 	 * Empty injector will not be able to produce any object of any type.
71 	 */
72 	injector();
73 
74 	/**
75 	 * @brief Create new injector from provided modules.
76 	 * @param modules list of modules
77 	 * @throw ambiguous_types if one or more types in @p modules is ambiguous
78 	 * @throw unresolvable_dependencies if a type with unresolvable dependency is found in @p modules
79 	 * @throw dependency_on_self when type depends on self
80 	 * @throw dependency_on_subtype when type depends on own supertype
81 	 * @throw dependency_on_subtype when type depends on own subtype
82 	 * @throw invalid_setter if any tagged setter has parameter that is not a QObject-derived pointer
83 	 * @throw invalid_setter if any tagged setter has parameter that is a QObject pointer
84 	 * @throw invalid_setter if any tagged setter has other number of parameters than one
85 	 *
86 	 * Creates injector with all types from modules configured. If combined configuration
87 	 * of all modules is invalid an exception is thrown. Configuration is invalid when:
88 	 * * any type is configured in more than one module
89 	 * * a dependency exists with type that is non configured in any module
90 	 * * a cycle of factories is found (currently does not throw an exception)
91 	 */
92 	explicit injector(std::vector<std::unique_ptr<module>> modules);
93 
94 	/**
95 	 * @brief Create new injector from provided modules with set of parent injectors.
96 	 * @param super_injectors list of injectors providing types for this one to use
97 	 * @param modules list of modules
98 	 * @throw ambiguous_types if one or more types in @p modules is ambiguous
99 	 * @throw unresolvable_dependencies if a type with unresolvable dependency is found in @p modules
100 	 * @throw dependency_on_self when type depends on self
101 	 * @throw dependency_on_subtype when type depends on own supertype
102 	 * @throw dependency_on_subtype when type depends on own subtype
103 	 * @throw invalid_setter if any tagged setter has parameter that is not a QObject-derived pointer
104 	 * @throw invalid_setter if any tagged setter has parameter that is a QObject pointer
105 	 * @throw invalid_setter if any tagged setter has other number of parameters than one
106 	 *
107 	 * Creates injector with all types from modules configured. Also a virtual module consisting of all
108 	 * provided types from @p super_injectors is added to module set. If combined configuration
109 	 * of all modules is invalid an exception is thrown. Configuration is invalid when:
110 	 * * any type is configured in more than one module
111 	 * * a dependency exists with type that is non configured in any module
112 	 * * a cycle of factories is found (currently does not throw an exception)
113 	 */
114 	explicit injector(std::vector<injector *> super_injectors, std::vector<std::unique_ptr<module>> modules);
115 
116 	injector(injector &&x);
117 	~injector();
118 
119 	injector & operator = (injector &&x);
120 
121 	/**
122 	 * @brief Instantiates object of given type @tparam T
123 	 * @tparam T type of object to instantiate
124 	 * @throw unknown_type if @@tparam T was not configured in injector
125 	 * @throw instantiation_failed if instantiation of one of required types failed
126 	 *
127 	 * When object of given type is instantiated by instantiate<T>() method, injector first check if T is in set of
128 	 * available types. If not, an exception is thrown. Next an unique configured type U that implements T
129 	 * is found. If object of type U was already created, method returns. If not, it is created with all
130 	 * dependencies it requires. Depending on configuration of U it can be created directly by U default
131 	 * constructor or it can be created by factory that is assigned to that type (note: injector will
132 	 * create itself all required factories with the same alghoritm). After U with all its dependencies
133 	 * is created all dependency setters are called with proper arguments. Then U object is added to cache.
134 	 */
135 	template<typename T>
instantiate()136 	void instantiate()
137 	{
138 		instantiate(make_type<T>());
139 	}
140 
141 	/**
142 	 * @brief Returns pointer to object of given type T.
143 	 * @tparam T type of object to return
144 	 * @throw qobject_type if T is QObject
145 	 * @throw unknown_type if @p interface_type was not configured in injector
146 	 * @throw instantiation_failed if instantiation of one of required types failed
147 	 *
148 	 * When object of given type is requested by get&lt;T&gt;() method, injector first check if T is in set of
149 	 * available types. If not, an exception is thrown. Next an unique configured type U that implements T
150 	 * is found. If object of type U was already created, it is returned. If not, it is created with all
151 	 * dependencies it requires. Depending on configuration of U it can be created directly by U default
152 	 * constructor or it can be created by factory that is assigned to that type (note: injector will
153 	 * create itself all required factories with the same alghoritm). After U with all its dependencies
154 	 * is created all dependency setters are called with proper arguments. Then U object is added to cache
155 	 * and is itself returned.
156 	 */
157 	template<typename T>
get()158 	T * get()
159 	{
160 		return qobject_cast<T *>(get(make_type<T>()));
161 	}
162 
163 	/**
164 	 * @brief Returns all objects with given @p type_role.
165 	 * @throw instantiation_failed if instantiation of one of found types failed
166 	 */
167 	std::vector<QObject *> get_all_with_type_role(const std::string &type_role);
168 
169 	/**
170 	 * @brief Instantiates object of given type @p interface_type
171 	 * @param interface_type type of object to return
172 	 * @throw empty_type if interface_type is empty
173 	 * @throw qobject_type if interface_type represents QObject
174 	 * @throw unknown_type if @p interface_type was not configured in injector
175 	 * @throw instantiation_failed if instantiation of one of required types failed
176 	 */
177 	void instantiate(const type &interface_type);
178 
179 	/**
180 	 * @brief Instantiate all objects with given @p type_role.
181 	 * @throw instantiation_failed if instantiation of one of found types failed
182 	 */
183 	void instantiate_all_with_type_role(const std::string &type_role);
184 
185 	/**
186 	 * @brief Returns pointer to object of given type interface_type.
187 	 * @param interface_type type of object to return
188 	 * @throw empty_type if interface_type is empty
189 	 * @throw qobject_type if interface_type represents QObject
190 	 * @throw unknown_type if @p interface_type was not configured in injector
191 	 * @throw instantiation_failed if instantiation of one of required types failed
192 	 *
193 	 * @see T * get<T>()
194 	 */
195 	QObject * get(const type &interface_type);
196 
197 	/**
198 	 * @brief Inject dependencies into @p object.
199 	 * @param object object to inject dependencies into.
200 	 * @throw invalid_setter if any tagged setter has parameter that is not a QObject-derived pointer
201 	 * @throw invalid_setter if any tagged setter has parameter that is a QObject pointer
202 	 * @throw invalid_setter if any tagged setter has parameter that is a QObject-derived pointer of not configured type
203 	 * @throw invalid_setter if any tagged setter has other number of parameters than one
204 	 * @pre object != nullptr
205 	 *
206 	 * This method looks for invokable setters tagged with INJEQT_SET in @p object. If any of setter is not valid
207 	 * dependency injector setter or its parameter is of type not configured in injector an exception is thrown.
208 	 * If all setters are valid, they are called with proper objects (which may be already available in injector
209 	 * or created on demand).
210 	 */
211 	void inject_into(QObject *object);
212 
213 private:
214 	std::unique_ptr<injeqt::internal::injector_impl> _pimpl;
215 
216 };
217 
218 }}
219