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.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 }