001// Licensed under the Apache License, Version 2.0 (the "License");
002// you may not use this file except in compliance with the License.
003// You may obtain a copy of the License at
004//
005// http://www.apache.org/licenses/LICENSE-2.0
006//
007// Unless required by applicable law or agreed to in writing, software
008// distributed under the License is distributed on an "AS IS" BASIS,
009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
010// See the License for the specific language governing permissions and
011// limitations under the License.
012package org.apache.tapestry5.beanmodel;
013
014import java.lang.annotation.Annotation;
015import java.util.HashMap;
016import java.util.Map;
017
018import org.apache.tapestry5.beanmodel.internal.services.BeanModelSourceImpl;
019import org.apache.tapestry5.beanmodel.internal.services.PropertyAccessImpl;
020import org.apache.tapestry5.beanmodel.internal.services.PropertyConduitSourceImpl;
021import org.apache.tapestry5.beanmodel.services.BeanModelSource;
022import org.apache.tapestry5.beanmodel.services.PlasticProxyFactoryImpl;
023import org.apache.tapestry5.beanmodel.services.PropertyConduitSource;
024import org.apache.tapestry5.commons.AnnotationProvider;
025import org.apache.tapestry5.commons.MappedConfiguration;
026import org.apache.tapestry5.commons.ObjectLocator;
027import org.apache.tapestry5.commons.internal.BasicDataTypeAnalyzers;
028import org.apache.tapestry5.commons.internal.BasicTypeCoercions;
029import org.apache.tapestry5.commons.internal.services.StringInterner;
030import org.apache.tapestry5.commons.internal.services.StringInternerImpl;
031import org.apache.tapestry5.commons.internal.services.TypeCoercerImpl;
032import org.apache.tapestry5.commons.internal.util.TapestryException;
033import org.apache.tapestry5.commons.services.CoercionTuple;
034import org.apache.tapestry5.commons.services.CoercionTuple.Key;
035import org.apache.tapestry5.commons.services.DataTypeAnalyzer;
036import org.apache.tapestry5.commons.services.PlasticProxyFactory;
037import org.apache.tapestry5.commons.services.PropertyAccess;
038import org.apache.tapestry5.commons.services.TypeCoercer;
039import org.slf4j.LoggerFactory;
040
041/**
042 * Utility class for creating {@link BeanModelSource} instances without
043 * Tapestry-IoC. Usage of Tapestry-IoC is still recommended.
044 *
045 * The setter methods can be used to customize the BeanModelSource to be created and can be
046 * (and usually are) skipped so <code>BeanModelSource beanModelSource = new BeanModelSourceBuilder().build();</code>
047 * is all you need to do. 
048 */
049public class BeanModelSourceBuilder {
050
051    private TypeCoercer typeCoercer;
052    private PropertyAccess propertyAccess;
053    private PropertyConduitSource propertyConduitSource;
054    private PlasticProxyFactory plasticProxyFactory;
055    private DataTypeAnalyzer dataTypeAnalyzer;
056    private ObjectLocator objectLocator;
057    private StringInterner stringInterner;
058
059    /**
060     * Creates and returns a {@link BeanModelSource} instance.
061     */
062    public BeanModelSource build() 
063    {
064        
065        if (typeCoercer == null) 
066        {
067            createTypeCoercer();
068        }
069        
070        if (propertyAccess == null)
071        {
072            propertyAccess = new PropertyAccessImpl();
073        }
074        
075        if (dataTypeAnalyzer == null)
076        {
077            dataTypeAnalyzer = BasicDataTypeAnalyzers.createDefaultDataTypeAnalyzer();
078        }
079        
080        if (stringInterner == null)
081        {
082            stringInterner = new StringInternerImpl();
083        }
084        
085        if (plasticProxyFactory == null)
086        {
087            plasticProxyFactory = new PlasticProxyFactoryImpl(getClass().getClassLoader(), LoggerFactory.getLogger(PlasticProxyFactory.class));
088        }
089        
090        if (propertyConduitSource == null)
091        {
092            propertyConduitSource = new PropertyConduitSourceImpl(propertyAccess, plasticProxyFactory, typeCoercer, stringInterner);
093        }
094        
095        if (objectLocator == null)
096        {
097            objectLocator = new AutobuildOnlyObjectLocator();
098        }
099        
100        return new BeanModelSourceImpl(typeCoercer, propertyAccess, propertyConduitSource, plasticProxyFactory, dataTypeAnalyzer, objectLocator);
101        
102    }
103    
104    /**
105     * Sets the {@link TypeCoercer} to be used.
106     */
107    public BeanModelSourceBuilder setTypeCoercer(TypeCoercer typeCoercer)
108    {
109        this.typeCoercer = typeCoercer;
110        return this;
111    }
112
113    /**
114     * Sets the {@link PropertyAccess} to be used.
115     */
116    public BeanModelSourceBuilder setPropertyAccess(PropertyAccess propertyAccess)
117    {
118        this.propertyAccess = propertyAccess;
119        return this;
120    }
121
122    /**
123     * Sets the {@link PropertyConduitSource} to be used.
124     */
125    public BeanModelSourceBuilder setPropertyConduitSource(PropertyConduitSource propertyConduitSource)
126    {
127        this.propertyConduitSource = propertyConduitSource;
128        return this;
129    }
130
131    /**
132     * Sets the {@link PlasticProxyFactory} to be used.
133     */
134    public BeanModelSourceBuilder setPlasticProxyFactory(PlasticProxyFactory plasticProxyFactory)
135    {
136        this.plasticProxyFactory = plasticProxyFactory;
137        return this;
138    }
139
140    /**
141     * Sets the {@link DataTypeAnalyzer} to be used.
142     */
143    public BeanModelSourceBuilder setDataTypeAnalyzer(DataTypeAnalyzer dataTypeAnalyzer)
144    {
145        this.dataTypeAnalyzer = dataTypeAnalyzer;
146        return this;
147    }
148
149    /**
150     * Sets the {@link ObjectLocator} to be used. Actually, the only method of it actually used is
151     * {@link ObjectLocator#autobuild(Class)}, for creating objects of the class described by the
152     * {@link BeanModel}.
153     */
154    public BeanModelSourceBuilder setObjectLocator(ObjectLocator objectLocator)
155    {
156        this.objectLocator = objectLocator;
157        return this;
158    }
159
160    /**
161     * Sets the {@link StringInterner} to be used.
162     */
163    public BeanModelSourceBuilder setStringInterner(StringInterner stringInterner)
164    {
165        this.stringInterner = stringInterner;
166        return this;
167    }
168    
169    private void createTypeCoercer() 
170    {
171        CoercionTupleConfiguration configuration = new CoercionTupleConfiguration();
172        BasicTypeCoercions.provideBasicTypeCoercions(configuration);
173        BasicTypeCoercions.provideJSR310TypeCoercions(configuration);
174        typeCoercer = new TypeCoercerImpl(configuration.getTuples());
175    }
176
177    final private static class CoercionTupleConfiguration implements MappedConfiguration<CoercionTuple.Key, CoercionTuple> 
178    {
179
180        final private Map<CoercionTuple.Key, CoercionTuple> tuples = new HashMap<>();
181
182        @Override
183        public void add(CoercionTuple.Key key, CoercionTuple tuple) 
184        {
185            tuples.put(key, tuple);
186        }
187
188        @Override
189        public void addInstance(CoercionTuple.Key key, Class<? extends CoercionTuple> clazz) 
190        {
191            throw new RuntimeException("Not implemented");
192        }
193
194        public Map<CoercionTuple.Key, CoercionTuple> getTuples() 
195        {
196            return tuples;
197        }
198
199        @Override
200        public void override(Key key, CoercionTuple value) 
201        {
202            throw new RuntimeException("Not implemented");            
203        }
204
205        @Override
206        public void overrideInstance(Key key, Class<? extends CoercionTuple> clazz) {
207            throw new RuntimeException("Not implemented");            
208        }
209
210    }
211    
212    final private static class AutobuildOnlyObjectLocator implements ObjectLocator {
213
214        @Override
215        public <T> T getService(String serviceId, Class<T> serviceInterface)
216        {
217            throw new RuntimeException("Not implemented");
218        }
219
220        @Override
221        public <T> T getService(Class<T> serviceInterface)
222        {
223            throw new RuntimeException("Not implemented");
224        }
225
226        @Override
227        public <T> T getService(Class<T> serviceInterface,
228                Class<? extends Annotation>... markerTypes)
229        {
230            throw new RuntimeException("Not implemented");
231        }
232
233        @Override
234        public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider)
235        {
236            throw new RuntimeException("Not implemented");
237        }
238
239        @Override
240        public <T> T autobuild(Class<T> clazz)
241        {
242            try
243            {
244                return clazz.newInstance();
245            }
246            catch (Exception e)
247            {
248                throw new TapestryException("Couldn't instantiate class " + clazz.getName(), e);
249            }
250        }
251
252        @Override
253        public <T> T autobuild(String description, Class<T> clazz)
254        {
255            return autobuild(clazz);
256        }
257
258        public <T> T proxy(Class<T> interfaceClass, Class<? extends T> implementationClass)
259        {
260            throw new RuntimeException("Not implemented");
261        }
262        
263    }
264
265}