View Javadoc

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 }