/* * 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.replicator; import net.sf.beanlib.BeanlibException; import net.sf.beanlib.provider.BeanTransformer; import net.sf.beanlib.spi.BeanTransformerSpi; import net.sf.beanlib.spi.ChainedCustomBeanTransformer; import net.sf.beanlib.spi.CustomBeanTransformerSpi; import net.sf.beanlib.spi.replicator.BeanReplicatorSpi; /** * Default implementation of {@link BeanReplicatorSpi}. * <p> * A BeanReplicator can be used to replicate JavaBean's. * <h2>Quick Start</h2> To replicate a simple JavaBean with a class definition like: <blockquote> * * <pre> * public class SimpleBean { * private String name; * * public SimpleBean() {} * public SimpleBean(String name) { this.name = name; } * * public String getName() { return name; } * public void setName(String name) { this.name = name; } * } * ... * SimpleBean from = new SimpleBean("foo"); * SimpleBean to = new BeanReplicator().replicateBean(from); * </pre> * * </blockquote> Notes a no-arg constructor is required for the JavaBean at the top level. * <p> * How about a more complex example ? Let's try: * * <pre> * <blockquote>public class ComplexBean { * private String name; * private ComplexBean[] array; * private Collection<ComplexBean> collection; * private Map<String,ComplexBean> map; * * public ComplexBean() {} * public ComplexBean(String name) { this.name = name; } * * public String getName() { return name; } * public void setName(String name) { this.name = name; } * * public Collection<ComplexBean> getCollection() { return collection; } * public void setCollection(Collection<ComplexBean> collection) { this.collection = collection; } * * public ComplexBean[] getArray() { return array; } * public void setArray(ComplexBean[] array) { this.array = array; } * * public Map<String, ComplexBean> getMap() { return map; } * public void setMap(Map<String, ComplexBean> map) { this.map = map; } * } * </blockquote> * </pre> * * First, set up the bean <blockquote> * * <pre> * ComplexBean from = new ComplexBean("foo"); * ComplexBean[] a = { from }; * Collection<ComplexBean> col = Arrays.asList(a); * from.setArray(a); * from.setCollection(col); * Map<String, ComplexBean> map = new HashMap<String, ComplexBean>(); * map.put(from.getName(), from); * from.setMap(map); * </pre> * * </blockquote> And then replicate it in the same way: * * <pre> * <blockquote>ComplexBean to = new BeanReplicator().replicateBean(from); * <blockquote> * </pre> * * Voila! The "to" and "from" beans are different object instances, but the content (ie the entire object graph) has * been replicated. Note this works as long as these objects follow the JavaBean convention. See <a href= * "http://beanlib.svn.sourceforge.net/viewvc/beanlib/trunk/beanlib-test/src/net/sf/beanlib/provider/replicator/BeanReplicatorTest.java?revision=283&view=markup" * >BeanReplicatorTest.java</a> for more details. * * @author Joe D. Velopar */ public class BeanReplicator extends ReplicatorTemplate implements BeanReplicatorSpi { /** * Constructs with a given bean transformer. */ public BeanReplicator(BeanTransformerSpi beanTransformer) { super(beanTransformer); } /** * Convenient constructor that both defaults to use {@link BeanTransformer}, and allows plugging in one or more * custom bean transformer factories that will be chained together. * * @see ChainedCustomBeanTransformer */ public BeanReplicator(CustomBeanTransformerSpi.Factory... customTransformerFactory) { super(new BeanTransformer(customTransformerFactory)); } /** * Constructs with the default {@link BeanTransformer}. */ public BeanReplicator() { super(new BeanTransformer()); } /** * Replicates a given JavaBean object. * * @param <V> from type * @param from from bean to be replicated. */ @SuppressWarnings("unchecked") public <V> V replicateBean(V from) { return replicateBean(from, (Class<V>) from.getClass()); } /** * Replicates the properties of a JavaBean object to an instance of a target class, which is selected from the given * "from" and "to" classes, giving priority to the one which is more specific whenever possible. * * @param <V> from type * @param <T> target type * @param from the original bean to be replicated * @param toClass target class to be instantiated */ @Override public <V, T> T replicateBean(V from, Class<T> toClass) { return this.replicateBean(from, toClass, from); } /** * Replicates the properties of a JavaBean object to an instance of a target class, which is selected from the given * "from" and "to" classes, giving priority to the one which is more specific whenever possible. * * @param <V> from type * @param <T> target type * @param from from bean (after unenhancement) to be replicated * @param toClass target class to be instantiated * @param originalFrom the original from bean before any "unenhancement" * @return an instance of the replicated bean */ protected <V, T> T replicateBean(V from, Class<T> toClass, V originalFrom) { Class<?> fromClass = from.getClass(); String fromClassName = fromClass.getName(); if (fromClassName.startsWith("net.sf.cglib.")) { // Want to skip the cglib stuff. return null; } if (fromClassName.startsWith("java.")) { if (!toClass.isAssignableFrom(fromClass)) return null; // https://sourceforge.net/tracker/?func=detail&aid=3453166&group_id=140152&atid=745598 if (fromClass == Class.class) { // "from" is a class per se @SuppressWarnings("unchecked") T t = (T) from; return t; } // Sorry, don't really know what it is ... soldier on... } T to; try { to = createToInstance(from, toClass); } catch (SecurityException e) { throw new BeanlibException("BeanReplicator.replicateBean failed:" + " toClass=" + toClass + ", from=" + from + ", originalFrom=" + originalFrom, e); } catch (InstantiationException e) { throw new BeanlibException("BeanReplicator.replicateBean failed:" + " toClass=" + toClass + ", from=" + from + ", originalFrom=" + originalFrom, e); } catch (IllegalAccessException e) { throw new BeanlibException("BeanReplicator.replicateBean failed:" + " toClass=" + toClass + ", from=" + from + ", originalFrom=" + originalFrom, e); } catch (NoSuchMethodException e) { throw new BeanlibException("BeanReplicator.replicateBean failed:" + " toClass=" + toClass + ", from=" + from + ", originalFrom=" + originalFrom, e); } putTargetCloned(originalFrom, to); // recursively populate member objects. populateBean(from, to); return to; } /** * Populates the properties of a "from" JavaBean object to a target "to" JavaBean object. * * @param from the bean from which the properties are to be retrieved * @param to the target bean to be populated */ public <V, T> T populate(V from, T to) { putTargetCloned(from, to); // recursively populate member objects. populateBean(from, to); return to; } }