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.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
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.MutableBeanStore;
29
30 import org.apache.commons.lang.ObjectUtils;
31
32 /**
33 * <p>
34 * A simple yet fully functional default implementation of the
35 * <code>BeanStore</code> interface.
36 * </p>
37 * <p>
38 * This implementation is based on a <code>HashMap</code>.
39 * <code>BeanProvider</code> objects can be added to this map using the
40 * <code>addBeanProvider()</code> method. They can then be queried through the
41 * <code>getBeanProvider</code> method.
42 * </p>
43 * <p>
44 * Note: This implementation is not thread-safe. The underlying map is not
45 * synchronized. This does not cause any problems when used read-only by the
46 * dependency injection framework. But if bean providers should be concurrently
47 * added or other properties are to be manipulated, manual synchronization is
48 * required. The intended use case is that an instance is created and populated
49 * with {@link BeanProvider} objects in an initialization phase. Then it should
50 * only be accessed in a read-only fashion by the dependency injection
51 * framework.
52 * </p>
53 *
54 * @author Oliver Heger
55 * @version $Id: DefaultBeanStore.java 205 2012-01-29 18:29:57Z oheger $
56 */
57 public class DefaultBeanStore implements MutableBeanStore
58 {
59 /** Constant for the prefix used for anonymous bean providers. */
60 private static final String PREFIX_ANONYMOUS = "_jguiraffe.anonymousBean_";
61
62 /** A map with the contained bean providers. */
63 private final Map<String, BeanProvider> providers;
64
65 /** Stores the parent store. */
66 private BeanStore parent;
67
68 /** The conversion helper associated with this bean store. */
69 private ConversionHelper conversionHelper;
70
71 /** Stores the name of this bean store. */
72 private String name;
73
74 /**
75 * Creates a new instance of <code>DefaultBeanStore</code>.
76 */
77 public DefaultBeanStore()
78 {
79 providers = new HashMap<String, BeanProvider>();
80 }
81
82 /**
83 * Creates a new instance of <code>DefaultBeanStore</code> and sets the name
84 * and the reference to the parent.
85 *
86 * @param name the name of this bean store
87 * @param parent the reference to the parent bean store
88 */
89 public DefaultBeanStore(String name, BeanStore parent)
90 {
91 this();
92 setName(name);
93 setParent(parent);
94 }
95
96 /**
97 * Returns the {@code ConversionHelper} associated with this object.
98 *
99 * @return the {@code ConversionHelper}
100 */
101 public ConversionHelper getConversionHelper()
102 {
103 return conversionHelper;
104 }
105
106 /**
107 * Sets the {@code ConversionHelper} object to be associated with this
108 * instance. The object passed to this method is returned by
109 * {@link #getConversionHelper()}.
110 *
111 * @param conversionHelper the {@code ConversionHelper} object
112 */
113 public void setConversionHelper(ConversionHelper conversionHelper)
114 {
115 this.conversionHelper = conversionHelper;
116 }
117
118 /**
119 * A convenience method for retrieving a {@code ConversionHelper} object
120 * from a hierarchy of bean stores. This method queries the specified
121 * {@code BeanStore} for its {@code ConversionHelper}. If it has one, the
122 * helper object is returned. Otherwise, the parent {@code BeanStore} is
123 * queried. This continues until a {@code ConversionHelper} object is found
124 * or the top of the hierarchy is reached. If no {@code ConversionHelper}
125 * can be found and the {@code createIfNecessary} flag is <b>true</b>, a new
126 * default helper object is created. Otherwise, the method returns
127 * <b>null</b> if no helper can be found.
128 *
129 * @param store the {@code BeanStore} where to start the search
130 * @param createIfNecessary a flag whether a default helper instance should
131 * be created if none can be found
132 * @return the found {@code ConversionHelper} or <b>null</b>
133 */
134 public static ConversionHelper fetchConversionHelper(BeanStore store,
135 boolean createIfNecessary)
136 {
137 BeanStore currentStore = store;
138
139 while (currentStore != null)
140 {
141 ConversionHelper ch = currentStore.getConversionHelper();
142 if (ch != null)
143 {
144 return ch;
145 }
146 currentStore = currentStore.getParent();
147 }
148
149 return createIfNecessary ? new ConversionHelper() : null;
150 }
151
152 /**
153 * Adds the specified <code>BeanProvider</code> to this bean store under the
154 * given name.
155 *
156 * @param name the name of the bean provider (must not be <b>null</b>)
157 * @param provider the <code>BeanProvider</code> to be registered
158 * @throws IllegalArgumentException if the name or the provider is
159 * <b>null</b>
160 */
161 public void addBeanProvider(String name, BeanProvider provider)
162 {
163 if (name == null)
164 {
165 throw new IllegalArgumentException(
166 "Name of bean provider must not be null!");
167 }
168 if (provider == null)
169 {
170 throw new IllegalArgumentException(
171 "Bean providern must not be null!");
172 }
173
174 providers.put(name, provider);
175 }
176
177 /**
178 * Adds an anonymous <code>BeanProvider</code>. This method will generate a
179 * special name (which is mainly used internally) for the bean provider to
180 * add and store it under this name. The name is returned.
181 *
182 * @param index the index of the bean provider
183 * @param provider the <code>BeanProvider</code> to be added (must not be
184 * <b>null</b>)
185 * @return the name used for this <code>BeanProvider</code>
186 * @throws IllegalArgumentException if the provider is <b>null</b>
187 */
188 public String addAnonymousBeanProvider(int index, BeanProvider provider)
189 {
190 String name = PREFIX_ANONYMOUS + index;
191 addBeanProvider(name, provider);
192 return name;
193 }
194
195 /**
196 * Removes the <code>BeanProvider</code> with the specified name from this
197 * bean store. If this provider cannot be found, this operation has no
198 * effect.
199 *
200 * @param name the name of the provider to remove
201 * @return a reference to the removed provider or <b>null</b> if it could
202 * not be found
203 */
204 public BeanProvider removeBeanProvider(String name)
205 {
206 return providers.remove(name);
207 }
208
209 /**
210 * Removes all <code>BeanProvider</code>s from this bean store.
211 */
212 public void clear()
213 {
214 providers.clear();
215 }
216
217 /**
218 * Returns the <code>BeanProvider</code> with the specified name. If no such
219 * element exists, <b>null</b> is returned.
220 *
221 * @param name the name of the desired provider
222 * @return the <code>BeanProvider</code> with this name
223 */
224 public BeanProvider getBeanProvider(String name)
225 {
226 return providers.get(name);
227 }
228
229 /**
230 * Returns the name of this bean store.
231 *
232 * @return the name of this bean store
233 */
234 public String getName()
235 {
236 return name;
237 }
238
239 /**
240 * Sets the name of this bean store.
241 *
242 * @param n the new name
243 */
244 public void setName(String n)
245 {
246 name = n;
247 }
248
249 /**
250 * Returns the parent of this bean store or <b>null</b> if this is a top
251 * level store.
252 *
253 * @return the parent of this bean store
254 */
255 public BeanStore getParent()
256 {
257 return parent;
258 }
259
260 /**
261 * Sets the parent for this bean store.
262 *
263 * @param p the parent
264 */
265 public void setParent(BeanStore p)
266 {
267 parent = p;
268 }
269
270 /**
271 * Returns a set with the names of all contained bean providers. This
272 * implementation ensures that the names of anonymous bean providers do not
273 * appear in the set returned.
274 *
275 * @return the names of the registered bean providers
276 */
277 public Set<String> providerNames()
278 {
279 Set<String> names = new HashSet<String>(providers.keySet());
280
281 // remove names of anonymous providers
282 for (Iterator<String> it = names.iterator(); it.hasNext();)
283 {
284 if (it.next().startsWith(PREFIX_ANONYMOUS))
285 {
286 it.remove();
287 }
288 }
289
290 return Collections.unmodifiableSet(names);
291 }
292
293 /**
294 * Returns a string representation of this object. This implementation
295 * returns a string listing all bean providers that belong to this store.
296 *
297 * @return a string for this object
298 */
299 @Override
300 public String toString()
301 {
302 StringBuilder buf =
303 new StringBuilder(ObjectUtils.identityToString(this));
304 buf.append("[ name = ").append(getName());
305 buf.append(" providers = { ");
306 boolean first = true;
307 for (Map.Entry<String, BeanProvider> e : providers.entrySet())
308 {
309 if (first)
310 {
311 first = false;
312 }
313 else
314 {
315 buf.append(", ");
316 }
317 buf.append(e.getKey()).append(" = ").append(e.getValue());
318 }
319 buf.append(" } ]");
320 return buf.toString();
321 }
322 }