/*
* Copyright 2007 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.beanlib.provider;
import java.util.IdentityHashMap;
import java.util.Map;
import net.jcip.annotations.NotThreadSafe;
import net.sf.beanlib.BeanlibException;
import net.sf.beanlib.PropertyInfo;
import net.sf.beanlib.provider.replicator.ArrayReplicator;
import net.sf.beanlib.provider.replicator.BeanReplicator;
import net.sf.beanlib.provider.replicator.CalendarReplicator;
import net.sf.beanlib.provider.replicator.CollectionReplicator;
import net.sf.beanlib.provider.replicator.DateReplicator;
import net.sf.beanlib.provider.replicator.ImmutableReplicator;
import net.sf.beanlib.provider.replicator.MapReplicator;
import net.sf.beanlib.provider.replicator.ReplicatorTemplate;
import net.sf.beanlib.provider.replicator.UnsupportedBlobReplicator;
import net.sf.beanlib.spi.BeanMethodCollector;
import net.sf.beanlib.spi.BeanMethodFinder;
import net.sf.beanlib.spi.BeanPopulationExceptionHandler;
import net.sf.beanlib.spi.BeanPopulatorBaseConfig;
import net.sf.beanlib.spi.BeanPopulatorBaseSpi;
import net.sf.beanlib.spi.BeanPopulatorSpi;
import net.sf.beanlib.spi.BeanSourceHandler;
import net.sf.beanlib.spi.BeanTransformerSpi;
import net.sf.beanlib.spi.ChainedCustomBeanTransformer;
import net.sf.beanlib.spi.CustomBeanTransformerSpi;
import net.sf.beanlib.spi.DetailedPropertyFilter;
import net.sf.beanlib.spi.PropertyFilter;
import net.sf.beanlib.spi.TrivialCustomBeanTransformerFactories;
import net.sf.beanlib.spi.replicator.ArrayReplicatorSpi;
import net.sf.beanlib.spi.replicator.BeanReplicatorSpi;
import net.sf.beanlib.spi.replicator.BlobReplicatorSpi;
import net.sf.beanlib.spi.replicator.CalendarReplicatorSpi;
import net.sf.beanlib.spi.replicator.CollectionReplicatorSpi;
import net.sf.beanlib.spi.replicator.DateReplicatorSpi;
import net.sf.beanlib.spi.replicator.ImmutableReplicatorSpi;
import net.sf.beanlib.spi.replicator.MapReplicatorSpi;
/**
* Default implementation of {@link BeanTransformerSpi}. A Bean Transformer can be used to transform object input
* typically in the form of a property that follows the JavaBean getter/setter convention, on a best attempt basis, into
* an output object typically used to populate the corresponding property of a target JavaBean.
* <p>
* This class extends {@link ReplicatorTemplate} as the implementation typically transforms a JavaBean property by
* replicating it into a separate instance. The replication is typically recursive in that the whole object graph of the
* input object is replicated into an equivalent output object graph, resolving circular references as necessary.
* However, the exact behavior of the replication process including
* <ul>
* <li>whether it's recursive or not; and
* <li>to what extent the input object graph should be traversed and/or replicated</li>
* </ul>
* can be controlled and/or supplemented by the client code via various options:
* <p>
* <ol>
* <li>All the configurable options of {@link BeanPopulatorBaseSpi} are available, as the replication of JavaBean
* properties inevitably involves bean population.</li>
* <p>
* <li>A Bean Transformer adopts a best effort approach to perform object transformation and replication. The default
* implementations for replicating the common data types such as collection, map, dates, etc. can be selectively
* overridden via the replicator factory initialization methods:
* <p>
* <ul>
* <table>
* <tr>
* <th width="20%" align="center">Type</th>
* <th>Configuration method</th>
* <tr>
* <td>
* <li>Immutable (such as enum)</li></td>
* <td>{@link #initImmutableReplicatableFactory(net.sf.beanlib.spi.replicator.ImmutableReplicatorSpi.Factory)
* #initImmutableReplicatableFactory(ImmutableReplicatorSpi.Factory)}</td>
* <tr>
* <td>
* <li>Collection</li></td>
* <td>{@link #initCollectionReplicatableFactory(net.sf.beanlib.spi.replicator.CollectionReplicatorSpi.Factory)
* #initCollectionReplicatableFactory(CollectionReplicatorSpi.Factory)}</td>
* <tr>
* <td>
* <li>Map</li></td>
* <td>{@link #initMapReplicatableFactory(net.sf.beanlib.spi.replicator.MapReplicatorSpi.Factory)
* #initMapReplicatableFactory(MapReplicatorSpi.Factory)}</td>
* <tr>
* <td>
* <li>Primitive array</li></td>
* <td>{@link #initArrayReplicatableFactory(net.sf.beanlib.spi.replicator.ArrayReplicatorSpi.Factory)
* #initArrayReplicatableFactory(ArrayReplicatorSpi.Factory)}</td>
* <tr>
* <td>
* <li>Blob</li></td>
* <td>{@link #initBlobReplicatableFactory(net.sf.beanlib.spi.replicator.BlobReplicatorSpi.Factory)
* #initBlobReplicatableFactory(BlobReplicatorSpi.Factory)}</td>
* <tr>
* <td>
* <li>Date</li></td>
* <td>{@link #initDateReplicatableFactory(net.sf.beanlib.spi.replicator.DateReplicatorSpi.Factory)
* #initDateReplicatableFactory(DateReplicatorSpi.Factory)}</td>
* <tr>
* <td>
* <li>JavaBean (with no-arg constructor)</li></td>
* <td>{@link #initBeanReplicatableFactory(net.sf.beanlib.spi.replicator.BeanReplicatorSpi.Factory)
* #initBeanReplicatableFactory(BeanReplicatorSpi.Factory))}</td>
* </table>
* </ul>
* </li>
* <p>
* <li>For anything else that the existing implementation fails to tranform, client can provide one or multiple custom
* transformer factories via the constructor
* {@link #BeanTransformer(net.sf.beanlib.spi.CustomBeanTransformerSpi.Factory...) new
* BeanTransformer(CustomBeanTransformerSpi.Factory...)}.</li>
* </ol>
*
* @see CustomBeanTransformerSpi
* @author Joe D. Velopar
*/
@NotThreadSafe
public class BeanTransformer extends ReplicatorTemplate implements BeanTransformerSpi {
/** Constructs with the default {@link BeanPopulator#factory}. */
public BeanTransformer() {
this(BeanPopulator.factory);
}
/**
* Convenient constructor that both defaults to use {@link BeanPopulator#factory}, and allows plugging in one or
* more custom bean transformer factories that will be chained together.
*
* @see ChainedCustomBeanTransformer
*/
public BeanTransformer(final CustomBeanTransformerSpi.Factory... customBeanTransformerFactories) {
this(BeanPopulator.factory);
if (customBeanTransformerFactories != null && customBeanTransformerFactories.length > 0) {
this.initCustomTransformerFactory(customBeanTransformerFactories.length == 1 ? customBeanTransformerFactories[0]
: new ChainedCustomBeanTransformer.Factory(customBeanTransformerFactories));
} else
this.initCustomTransformerFactory(null);
}
/**
* Bean populator factory, which is used to create a bean populator which can then be used to determine whether a
* specific JavaBean property should be propagated from a source bean to a target bean.
*/
private final BeanPopulatorSpi.Factory beanPopulatorFactory;
/**
* Constructs with the given bean populator factory, which is used to create a bean populator which can then be used
* to determine whether a specific JavaBean property should be propagated from a source bean to a target bean.
*/
protected BeanTransformer(BeanPopulatorSpi.Factory beanPopulatorFactory) {
this.beanPopulatorFactory = beanPopulatorFactory;
}
@Override
public BeanPopulatorSpi.Factory getBeanPopulatorSpiFactory() {
return beanPopulatorFactory;
}
/** Used to contains those objects that have been replicated. */
private Map<Object, Object> clonedMap = new IdentityHashMap<Object, Object>();
/**
* Used to contain all the configuration options as a single configuration object.
*/
private BeanPopulatorBaseConfig baseConfig = new BeanPopulatorBaseConfig();
/** Custom Transformer. Defaults is a NO_OP. */
private CustomBeanTransformerSpi customTransformer = TrivialCustomBeanTransformerFactories.getNoopCustomTransformer();
/**
* Replicator for immutables.
*/
private ImmutableReplicatorSpi immutableReplicatable = ImmutableReplicator.newImmutableReplicatable(this);
/**
* Replicator for collections.
*/
private CollectionReplicatorSpi collectionReplicatable = CollectionReplicator.newCollectionReplicatable(this);
/**
* Replicator for maps.
*/
private MapReplicatorSpi mapReplicatable = MapReplicator.newMapReplicatable(this);
/**
* Replicator for arrays.
*/
private ArrayReplicatorSpi arrayReplicatable = ArrayReplicator.newArrayReplicatable(this);
/**
* Replicator for blobs.
*/
private BlobReplicatorSpi blobReplicatable = UnsupportedBlobReplicator.newBlobReplicatable(this);
/**
* Replicator for dates.
*/
private DateReplicatorSpi dateReplicatable = DateReplicator.newDateReplicatable(this);
/**
* Replicator for calendars.
*/
private CalendarReplicatorSpi calendarReplicatable = CalendarReplicator.newCalendarReplicatable(this);
/**
* Replicator for JavaBeans.
*/
private BeanReplicatorSpi beanReplicatable = new BeanReplicator(this);
@Override
public final void reset() {
clonedMap = new IdentityHashMap<Object, Object>();
}
@Override
public final <T> T transform(Object from, Class<T> toClass, PropertyInfo propertyInfo) {
try {
if (customTransformer.isTransformable(from, toClass, propertyInfo))
return customTransformer.transform(from, toClass, propertyInfo);
return replicate(from, toClass);
} catch (SecurityException e) {
throw new BeanlibException(e);
}
}
@Override
public final BeanTransformer initCustomTransformerFactory(CustomBeanTransformerSpi.Factory customTransformer) {
this.customTransformer = customTransformer.newCustomBeanTransformer(this);
return this;
}
@Override
public final BeanTransformer initPropertyFilter(PropertyFilter propertyFilter) {
baseConfig.setPropertyFilter(propertyFilter);
return this;
}
@Override
public final BeanTransformer initBeanSourceHandler(BeanSourceHandler beanSourceHandler) {
baseConfig.setBeanSourceHandler(beanSourceHandler);
return this;
}
@Override
public final BeanTransformer initDebug(boolean debug) {
baseConfig.setDebug(debug);
return this;
}
@Override
public final BeanTransformer initDetailedPropertyFilter(DetailedPropertyFilter detailedPropertyFilter) {
baseConfig.setDetailedPropertyFilter(detailedPropertyFilter);
return this;
}
@Override
public final BeanTransformer initReaderMethodFinder(BeanMethodFinder readerMethodFinder) {
baseConfig.setReaderMethodFinder(readerMethodFinder);
return this;
}
@Override
public final BeanTransformer initSetterMethodCollector(BeanMethodCollector setterMethodCollector) {
baseConfig.setSetterMethodCollector(setterMethodCollector);
return this;
}
@Override
public BeanTransformer initCollectionReplicatableFactory(CollectionReplicatorSpi.Factory factory) {
this.collectionReplicatable = factory.newCollectionReplicatable(this);
return this;
}
@Override
public CollectionReplicatorSpi getCollectionReplicatable() {
return collectionReplicatable;
}
@Override
public BeanTransformer initMapReplicatableFactory(MapReplicatorSpi.Factory factory) {
this.mapReplicatable = factory.newMapReplicatable(this);
return this;
}
@Override
public MapReplicatorSpi getMapReplicatable() {
return mapReplicatable;
}
@Override
@SuppressWarnings("unchecked")
public <K, V> Map<K, V> getClonedMap() {
return (Map<K, V>) clonedMap;
}
@Override
public BeanTransformer initImmutableReplicatableFactory(ImmutableReplicatorSpi.Factory immutableReplicatableFactory) {
this.immutableReplicatable = immutableReplicatableFactory.newImmutableReplicatable(this);
return this;
}
@Override
public ImmutableReplicatorSpi getImmutableReplicatable() {
return immutableReplicatable;
}
@Override
public BeanTransformer initArrayReplicatableFactory(ArrayReplicatorSpi.Factory arrayReplicatableFactory) {
this.arrayReplicatable = arrayReplicatableFactory.newArrayReplicatable(this);
return this;
}
@Override
public ArrayReplicatorSpi getArrayReplicatable() {
return arrayReplicatable;
}
@Override
public BeanTransformer initBlobReplicatableFactory(BlobReplicatorSpi.Factory blobReplicatableFactory) {
this.blobReplicatable = blobReplicatableFactory.newBlobReplicatable(this);
return this;
}
@Override
public BlobReplicatorSpi getBlobReplicatable() {
return blobReplicatable;
}
@Override
public BeanTransformer initBeanReplicatableFactory(BeanReplicatorSpi.Factory objectReplicatableFactory) {
this.beanReplicatable = objectReplicatableFactory.newBeanReplicatable(this);
return this;
}
@Override
public BeanReplicatorSpi getBeanReplicatable() {
return beanReplicatable;
}
@Override
public BeanTransformerSpi initDateReplicatableFactory(DateReplicatorSpi.Factory dateReplicatableFactory) {
this.dateReplicatable = dateReplicatableFactory.newDateReplicatable(this);
return this;
}
@Override
public BeanTransformerSpi initCalendarReplicatableFactory(CalendarReplicatorSpi.Factory calendarReplicatableFactory) {
this.calendarReplicatable = calendarReplicatableFactory.newCalendarReplicatable(this);
return this;
}
@Override
public DateReplicatorSpi getDateReplicatable() {
return dateReplicatable;
}
@Override
public CalendarReplicatorSpi getCalendarReplicatable() {
return calendarReplicatable;
}
@Override
public BeanTransformer initBeanPopulationExceptionHandler(BeanPopulationExceptionHandler beanPopulationExceptionHandler) {
baseConfig.setBeanPopulationExceptionHandler(beanPopulationExceptionHandler);
return this;
}
@Override
public BeanTransformerSpi initBeanPopulatorBaseConfig(BeanPopulatorBaseConfig baseConfig) {
this.baseConfig = baseConfig;
return this;
}
@Override
public BeanPopulatorBaseConfig getBeanPopulatorBaseConfig() {
return baseConfig;
}
@Override
public PropertyFilter getPropertyFilter() {
return baseConfig.getPropertyFilter();
}
@Override
public BeanPopulationExceptionHandler getBeanPopulationExceptionHandler() {
return baseConfig.getBeanPopulationExceptionHandler();
}
@Override
public BeanSourceHandler getBeanSourceHandler() {
return baseConfig.getBeanSourceHandler();
}
@Override
public boolean isDebug() {
return baseConfig.isDebug();
}
@Override
public DetailedPropertyFilter getDetailedPropertyFilter() {
return baseConfig.getDetailedPropertyFilter();
}
@Override
public BeanMethodFinder getReaderMethodFinder() {
return baseConfig.getReaderMethodFinder();
}
@Override
public BeanMethodCollector getSetterMethodCollector() {
return baseConfig.getSetterMethodCollector();
}
@Override
public CustomBeanTransformerSpi getCustomBeanTransformer() {
return this.customTransformer;
}
}