1 /*
2 * Copyright 2006-2016 The JGUIraffe Team.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.sf.jguiraffe.di.impl;
17
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.LinkedHashSet;
22 import java.util.Map;
23 import java.util.Set;
24
25 import net.sf.jguiraffe.di.BeanProvider;
26 import net.sf.jguiraffe.di.BeanStore;
27 import net.sf.jguiraffe.di.ConversionHelper;
28 import net.sf.jguiraffe.di.impl.providers.ConstantBeanProvider;
29
30 /**
31 * <p>
32 * A helper class that simplifies implementations of the {@code BeanStore}
33 * interface.
34 * </p>
35 * <p>
36 * The purpose of this class is to support {@code BeanStore}
37 * implementations based on classes that are not aware of {@link BeanProvider}s,
38 * but provide a map-like interface for accessing their data. This is achieved
39 * in the following ways:
40 * <ul>
41 * <li>Static data (in form of arbitrary objects) can directly be added to this
42 * class. Internally {@link ConstantBeanProvider} objects are created for the
43 * data objects to be managed. These providers are directly exposed through the
44 * {@code BeanStore} methods.</li>
45 * <li>If dynamic data is involved (i.e. objects that may change over time and
46 * cannot be kept in a map), components can implement the internal
47 * {@code BeanContributor} interface and register themselves at a
48 * {@code SimpleBeanStoreImpl} instance. The {@code BeanConstributor}
49 * interface is much easier to implement than the {@code BeanStore}
50 * interface. The implementations of the {@code BeanStore} methods provided
51 * by this class take the registered {@code BeanContributor}s into account.
52 * </li>
53 * <li>Methods of the {@code BeanStore} interface that do not directly deal
54 * with bean providers are already implemented by this class.</li>
55 * </ul>
56 * </p>
57 * <p>
58 * Note: The class per se is not thread-safe. When used inside the
59 * <em>dependency
60 * injection</em> framework, proper synchronization is automatically applied.
61 * But during initialization or for other use cases the developer has to ensure
62 * that there are no concurrent accesses.
63 * </p>
64 *
65 * @author Oliver Heger
66 * @version $Id: SimpleBeanStoreImpl.java 213 2012-07-14 19:40:51Z oheger $
67 */
68 public class SimpleBeanStoreImpl implements BeanStore
69 {
70 /** Stores a map with the managed beans. */
71 private final Map<String, Object> beans;
72
73 /** A list with the registered contributors. */
74 private final Collection<BeanContributor> contributors;
75
76 /** Stores the name of this bean store. */
77 private String name;
78
79 /** Stores the parent bean store. */
80 private BeanStore parent;
81
82 /** The conversion helper associated with this object. */
83 private ConversionHelper conversionHelper;
84
85 /**
86 * Creates a new instance of {@code SimpleBeanStoreImpl}.
87 */
88 public SimpleBeanStoreImpl()
89 {
90 beans = new HashMap<String, Object>();
91 contributors = new LinkedHashSet<BeanContributor>();
92 }
93
94 /**
95 * Creates a new instance of {@code SimpleBeanStoreImpl} and sets the
96 * name and the reference to the parent.
97 *
98 * @param name the name of this bean store
99 * @param parent the reference to the parent bean store
100 */
101 public SimpleBeanStoreImpl(String name, BeanStore parent)
102 {
103 this();
104 setName(name);
105 setParent(parent);
106 }
107
108 /**
109 * Adds a new {@code BeanContributor} to this object. This
110 * contributor will be triggered when this bean store is accessed.
111 *
112 * @param contr the contributor to be added (must not be <b>null</b>)
113 * @throws IllegalArgumentException if the contributor is <b>null</b>
114 */
115 public void addBeanContributor(BeanContributor contr)
116 {
117 if (contr == null)
118 {
119 throw new IllegalArgumentException(
120 "BeanContributor must not be null!");
121 }
122 contributors.add(contr);
123 }
124
125 /**
126 * Removes the specified bean contributor from this object.
127 *
128 * @param contr the contributor to remove
129 */
130 public void removeBeanContributor(BeanContributor contr)
131 {
132 contributors.remove(contr);
133 }
134
135 /**
136 * Adds the specified bean to this store. For the passed in bean a constant
137 * bean provider is created. It can then be queried through the
138 * {@code getBeanProvider()} method.
139 *
140 * @param name the name of the bean (must not be <b>null</b>)
141 * @param bean the bean (must not be <b>null</b>)
142 * @throws IllegalArgumentException if the name or the bean is <b>null</b>
143 */
144 public void addBean(String name, Object bean)
145 {
146 if (name == null)
147 {
148 throw new IllegalArgumentException("Bean name must not be null!");
149 }
150 if (bean == null)
151 {
152 throw new IllegalArgumentException("Bean must not be null!");
153 }
154 beans.put(name, bean);
155 }
156
157 /**
158 * Removes the bean with the given name.
159 *
160 * @param name the name of the bean to be removed
161 * @return the removed bean (<b>null</b> if the bean was unknown)
162 */
163 public Object removeBean(String name)
164 {
165 return beans.remove(name);
166 }
167
168 /**
169 * Returns a {@code BeanProvider} for the bean with the given name.
170 * This implementation checks whether such a bean was directly added using
171 * the {@code addBean()} method. If not, the registered bean
172 * contributors are consulted. If no such bean can be found, <b>null</b> is
173 * returned.
174 *
175 * @param name the name of the bean in question
176 * @return a {@code BeanProvider} for this bean
177 */
178 public BeanProvider getBeanProvider(String name)
179 {
180 Object bean = beans.get(name);
181
182 if (bean == null)
183 {
184 for (BeanContributor contr : contributors)
185 {
186 bean = contr.getBean(name);
187 if (bean != null)
188 {
189 break;
190 }
191 }
192 }
193
194 return (bean != null) ? providerFor(bean) : null;
195 }
196
197 /**
198 * Returns the name of this bean store.
199 *
200 * @return the name of this bean store
201 */
202 public String getName()
203 {
204 return name;
205 }
206
207 /**
208 * Sets the name of this bean store.
209 *
210 * @param name the new name
211 */
212 public void setName(String name)
213 {
214 this.name = name;
215 }
216
217 /**
218 * Returns the parent bean store.
219 *
220 * @return the parent store
221 */
222 public BeanStore getParent()
223 {
224 return parent;
225 }
226
227 /**
228 * Sets the parent bean store.
229 *
230 * @param parent the parent bean store
231 */
232 public void setParent(BeanStore parent)
233 {
234 this.parent = parent;
235 }
236
237 /**
238 * Returns the {@code ConversionHelper} associated with this instance.
239 *
240 * @return the {@code ConversionHelper}
241 */
242 public ConversionHelper getConversionHelper()
243 {
244 return conversionHelper;
245 }
246
247 /**
248 * Sets the {@code ConversionHelper} associated with this object. The object
249 * passed to this method will be returned by {@link #getConversionHelper()}.
250 *
251 * @param conversionHelper the {@code ConversionHelper}
252 */
253 public void setConversionHelper(ConversionHelper conversionHelper)
254 {
255 this.conversionHelper = conversionHelper;
256 }
257
258 /**
259 * Returns a set with the names of the available bean providers. This
260 * implementation will first obtain the names of all directly added beans.
261 * Then the registered bean contributors are invoked to add their bean names
262 * to the resulting list.
263 *
264 * @return a set with the names of the known bean providers
265 */
266 public Set<String> providerNames()
267 {
268 Set<String> beanNames = beans.keySet();
269
270 if (!contributors.isEmpty())
271 {
272 // copy set so it can be modified
273 beanNames = new HashSet<String>(beanNames);
274
275 for (BeanContributor contr : contributors)
276 {
277 contr.beanNames(beanNames);
278 }
279 }
280
281 return beanNames;
282 }
283
284 /**
285 * Returns a bean provider for the specified bean. This method is called
286 * whenever a bean has to be wrapped by a provider. This default
287 * implementation returns a {@code ConstantBeanProvider} for the
288 * passed in bean.
289 *
290 * @param bean the bean
291 * @return a provider for this bean
292 */
293 protected BeanProvider providerFor(Object bean)
294 {
295 return ConstantBeanProvider.getInstance(bean);
296 }
297
298 /**
299 * <p>
300 * Definition of an interface for objects that can contribute beans for a
301 * {@code SimpleBeanStoreImpl} object.
302 * </p>
303 * <p>
304 * The methods defined in this interface allow an implementation to deliver
305 * plain data objects (in contrast to {@code BeanProvider} objects.
306 * The implementations of the {@code BeanStore} methods delegate to
307 * these methods when the bean store is accessed.
308 * </p>
309 */
310 public interface BeanContributor
311 {
312 /**
313 * Obtains the names of the beans available by this contributor. This
314 * method is invoked by the {@code providerNames()} method.
315 *
316 * @param names a set, in which to store the names of the available
317 * beans
318 */
319 void beanNames(Set<String> names);
320
321 /**
322 * Returns the bean with the given name or <b>null</b> if the name is
323 * unknown. When queried for a bean the {@code BeanStore}
324 * implementation will iterate over all registered contributors and call
325 * this method. The first non <b>null</b> value is returned.
326 *
327 * @param name the name of the queried bean
328 * @return the bean with this name or <b>null</b>
329 */
330 Object getBean(String name);
331 }
332 }