001: /* ====================================================================
002: The Jicarilla Software License
003:
004: Copyright (c) 2003 Leo Simons.
005: All rights reserved.
006:
007: Permission is hereby granted, free of charge, to any person obtaining
008: a copy of this software and associated documentation files (the
009: "Software"), to deal in the Software without restriction, including
010: without limitation the rights to use, copy, modify, merge, publish,
011: distribute, sublicense, and/or sell copies of the Software, and to
012: permit persons to whom the Software is furnished to do so, subject to
013: the following conditions:
014:
015: The above copyright notice and this permission notice shall be
016: included in all copies or substantial portions of the Software.
017:
018: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
019: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
020: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
021: IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
022: CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
023: TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
024: SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
025: ==================================================================== */
026: package org.jicarilla.container.builder;
027:
028: import org.jicarilla.container.Adapter;
029: import org.jicarilla.container.Container;
030: import org.jicarilla.container.DefaultKeyRelayingContainer;
031: import org.jicarilla.container.KeyAwareAdapter;
032: import org.jicarilla.container.KeyRelayingContainer;
033: import org.jicarilla.container.Resolver;
034: import org.jicarilla.container.ResolverProvider;
035: import org.jicarilla.container.adapters.SingletonAdapter;
036: import org.jicarilla.container.factories.ManualFactory;
037: import org.jicarilla.container.factories.Type3Factory;
038: import org.jicarilla.container.selectors.InstanceofSelector;
039: import org.jicarilla.lang.Assert;
040: import org.jicarilla.lang.Selector;
041:
042: import java.util.ArrayList;
043: import java.util.Iterator;
044: import java.util.List;
045:
046: /**
047: * <p>A Builder implementation that's backed by a container itself. In essence,
048: * we're using a container to help build a container. The documentation for the
049: * builder API may be a bit complex, but the usage is actually quite easy.
050: * Just take a look at the following example:</p>
051: *
052: * <pre>
053: * Resolver parentContainerResolver = getParentResolver();
054: * Resolver resolver =
055: * DefaultBuilder.newInstance()
056: * .addComponent( HomerImpl.class )
057: * .addComponent( YoungHomer.class )
058: *
059: * .addComponent( new HomerImpl() )
060: * .addComponent( new BartImpl() )
061: * .addComponent(
062: * new ClassSelector( Marge.class ),
063: * new SingletonAdapter(
064: * new ManualFactory(
065: * new MargeImpl()
066: * )
067: * )
068: * )
069: * .addComponent( LisaImpl.class )
070: * .addComponent( parentContainerResolver )
071: * .create();
072: * </pre>
073: *
074: * <p>Note a Builder instance is single-use, and you should create a new
075: * instance (using {@link #newInstance()} every time you call
076: * {@link #create()}.</p>
077: *
078: * @author <a href="lsimons at jicarilla dot org">Leo Simons</a>
079: * @version $Id: DefaultBuilder.java,v 1.14 2004/03/23 15:59:52 lsimons Exp $
080: */
081: public class DefaultBuilder implements Builder {
082: // ----------------------------------------------------------------------
083: // Properties
084: // ----------------------------------------------------------------------
085: /**
086: * A container for the helpers we will use to populate {@link m_container}
087: * with the providers from {@link m_componentProviders}.
088: */
089: protected Container m_helpers;
090: /**
091: * The container we're populating.
092: */
093: protected KeyRelayingContainer m_container;
094: /**
095: * The providers to populate the {@link m_container} with.
096: */
097: protected List m_componentProviders = new ArrayList();
098:
099: // ----------------------------------------------------------------------
100: // Constructor and factory method
101: // ----------------------------------------------------------------------
102: /**
103: * Create a new builder that will be used to populate the provided
104: * container.
105: *
106: * @param container the container to populate.
107: */
108: protected DefaultBuilder(KeyRelayingContainer container) {
109: m_helpers = new DefaultKeyRelayingContainer();
110: m_container = container;
111: addHelpers();
112: }
113:
114: /**
115: * Create a new builder.
116: *
117: * @return a new builder instance to be used in building a single new
118: * container instance.
119: */
120: public static Builder newInstance() {
121: return new DefaultBuilder(new DefaultKeyRelayingContainer());
122: }
123:
124: // ----------------------------------------------------------------------
125: // Interface: Builder
126: // ----------------------------------------------------------------------
127:
128: /**
129: * Finish creation of the container and get access to it. After calling
130: * this method, you should discard this instance.
131: *
132: * @return the Resolver that's backed by the container we just created
133: * @throws Exception if a problem occurs creating the container for
134: * whatever reason
135: */
136: public Resolver create() throws Exception {
137: doPopulate();
138: return getResolver();
139: }
140:
141: public Resolver getResolver() {
142: return m_container.getResolver();
143: }
144:
145: /**
146: * Add a new <em>provider</em> to the container. A provider can be lots of
147: * things: another {@link Container container} or a generic
148: * {@link ResolverProvider resolver provider{, a {@link Resolver}, an
149: * {@link Adapter}, {@link KeyAwareAdapter}, {@link Entry entry}, a
150: * {@link Class}, an {@link Object instance}, and several other things.
151: * The builder will figure out what to do with the provider. Subclasses
152: * may add helpers for other kinds of providers.
153: *
154: * @param provider the provider to add to the container.
155: * @return the current instance.
156: */
157: public Builder addComponent(final Object provider) {
158: Assert.assertNotNull("provider argument may not be null",
159: provider);
160: m_componentProviders.add(provider);
161:
162: return this ;
163: }
164:
165: /**
166: * Add a new <em>provider</em> to the container. See
167: * {@link #addComponent(Object)} for a description of the supported
168: * providers. This method also allows you to specify the selection
169: * criterion that the container should apply when determining whether
170: * the provider can supply an instance. The selection criterion can be
171: * a {@link Selector}, {@link Resolver}, {@link Class} or
172: * an {@link Object}. Subclasses may add helpers that support other
173: * kinds of selection criteria.
174: *
175: * @param selectionCriterion the selection criterion the container should
176: * use when determining whether the provider can supply an instance.
177: * @param provider the provider to add to the container.
178: * @return the current instance.
179: */
180: public Builder addComponent(final Object selectionCriterion,
181: final Object provider) {
182: Assert.assertNotNull(
183: "selectionCriterion argument may not be null",
184: selectionCriterion);
185: Assert.assertNotNull("provider argument may not be null",
186: provider);
187: m_componentProviders
188: .add(new Entry(selectionCriterion, provider));
189:
190: return this ;
191: }
192:
193: // ----------------------------------------------------------------------
194: // Helper methods
195: // ----------------------------------------------------------------------
196:
197: protected void doPopulate() throws Exception {
198: final Iterator it = m_componentProviders.iterator();
199: while (it.hasNext()) {
200: final Object provider = it.next();
201: // checks against null in place... if( provider == null )
202: // continue;
203:
204: doAdd(provider);
205: }
206: }
207:
208: protected void doAdd(final Object provider) throws Exception {
209: final Object helper = m_helpers.getResolver().get(provider);
210:
211: // DefaultHelper will always work... if( helper == null )
212: // throw new NoUsableHelperException( provider );
213:
214: if (helper instanceof ContainerAwareBuilderHelper) {
215: final ContainerAwareBuilderHelper builder = (ContainerAwareBuilderHelper) helper;
216: final KeyAwareAdapter adapter = builder.getAdapter(
217: provider, m_container.getResolver());
218: final Selector selector = builder.getSelector(provider);
219:
220: m_container.registerAdapter(selector, adapter);
221: } else // we assume builder is correctly populated... if( helper instanceof BuilderHelper )
222: {
223: final BuilderHelper builder = (BuilderHelper) helper;
224: final KeyAwareAdapter adapter = builder
225: .getAdapter(provider);
226: final Selector selector = builder.getSelector(provider);
227:
228: m_container.registerAdapter(selector, adapter);
229: }
230: /*else
231: {
232: throw new JicarillaIllegalStateException(
233: "Found a helper for " + provider + " but the helper is " +
234: "not a ContainerAwareBuilderHelper or a BuilderHelper " +
235: "so we don't know what to do with it!" );
236: }*/
237: }
238:
239: protected void addHelpers() {
240: addHelperHelpers();
241:
242: // --------------------------------------------------------------------
243: // Jicarilla support
244: // --------------------------------------------------------------------
245: addHelper(Adapter.class, AdapterHelper.class);
246: addHelper(KeyAwareAdapter.class, KeyAwareAdapterHelper.class);
247: addHelper(Resolver.class, ResolverHelper.class);
248: addHelper(ResolverProvider.class, ResolverProviderHelper.class);
249: addHelper(Entry.class, EntryHelper.class);
250: addHelper(CustomComponent.class, CustomComponentHelper.class);
251:
252: // --------------------------------------------------------------------
253: // Basics
254: // --------------------------------------------------------------------
255:
256: addHelper(Class.class, SingletonType35Helper.class);
257: addHelper(Object.class, DefaultHelper.class);
258: }
259:
260: protected void addHelperHelpers() {
261: // helpers can use other helpers
262: Resolver r = m_helpers.getResolver();
263: m_helpers.registerAdapter(Resolver.class, new SingletonAdapter(
264: new ManualFactory(r)));
265:
266: // helpers can use the container we're building
267: m_helpers.registerAdapter(KeyRelayingContainer.class,
268: new SingletonAdapter(new ManualFactory(m_container)));
269: }
270:
271: protected void addHelper(final Class clazz, final Class helperClass) {
272: final Resolver r = m_helpers.getResolver();
273: m_helpers.registerAdapter(new InstanceofSelector(clazz),
274: new SingletonAdapter(new Type3Factory(r, helperClass
275: .getName())));
276: }
277:
278: // ----------------------------------------------------------------------
279: // Inner Class: Entry
280: // ----------------------------------------------------------------------
281:
282: /**
283: * You can add a provider using an entry if you want to use a custom
284: * selection criterion. Using
285: * <code>addComponent( new Entry( criterion, provider ) )</code> is roughly
286: * equivalent to using <code>addComponent( provider, criterion )</code>.
287: */
288: public static class Entry {
289: protected Object m_component;
290: protected Object m_selectionCriterion;
291:
292: public Entry(final Object selectionCriterion,
293: final Object component) {
294: m_component = component;
295: m_selectionCriterion = selectionCriterion;
296: }
297:
298: public Object getComponent() {
299: return m_component;
300: }
301:
302: public Object getSelectionCriterion() {
303: return m_selectionCriterion;
304: }
305: }
306: }
|