/* * 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 static net.sf.beanlib.utils.ClassUtils.isJavaPackage; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import net.jcip.annotations.ThreadSafe; import net.sf.beanlib.BeanlibException; import net.sf.beanlib.spi.BeanTransformerSpi; import net.sf.beanlib.spi.CustomBeanTransformerSpi; import net.sf.beanlib.spi.replicator.MapReplicatorSpi; /** * Default implementation of {@link net.sf.beanlib.spi.replicator.MapReplicatorSpi}. * * @author Joe D. Velopar */ public class MapReplicator extends ReplicatorTemplate implements MapReplicatorSpi { private static final Factory factory = new Factory(); /** * Factory for {@link MapReplicator} * * @author Joe D. Velopar */ @ThreadSafe private static class Factory implements MapReplicatorSpi.Factory { private Factory() {} @Override public MapReplicator newMapReplicatable(BeanTransformerSpi beanTransformer) { return new MapReplicator(beanTransformer); } } public static MapReplicator newMapReplicatable(BeanTransformerSpi beanTransformer) { return factory.newMapReplicatable(beanTransformer); } protected MapReplicator(BeanTransformerSpi beanTransformer) { super(beanTransformer); } @Override public <K, V, T> T replicateMap(Map<K, V> from, Class<T> toClass) { if (!toClass.isAssignableFrom(from.getClass())) return null; final Map<Object, Object> toMap; try { @SuppressWarnings("unchecked") final Map<Object, Object> m = (Map<Object, Object>) createToMap(from); toMap = m; } catch (SecurityException e) { throw new BeanlibException(e); } catch (InstantiationException e) { throw new BeanlibException(e); } catch (IllegalAccessException e) { throw new BeanlibException(e); } catch (NoSuchMethodException e) { throw new BeanlibException(e); } putTargetCloned(from, toMap); final Map<K, V> fromMap = from; final CustomBeanTransformerSpi customTransformer = getCustomerBeanTransformer(); // recursively populate member objects. for (Map.Entry<K, V> fromEntry : fromMap.entrySet()) { final K fromKey = fromEntry.getKey(); final Object key; setKey: { if (fromKey == null) { key = null; break setKey; } if (super.containsTargetCloned(fromKey)) { final Object targetCloned = super.getTargetCloned(fromKey); if (targetCloned != null) { key = targetCloned; break setKey; } // converting a non-null key to a null key // is likely not what the client really wants; // So let's move on (as a best effort) } if (customTransformer != null) { final Class<?> fromKeyClass = fromKey.getClass(); if (customTransformer.isTransformable(fromKey, fromKeyClass, null)) { final Object transformed = customTransformer.transform(fromKey, fromKeyClass, null); super.putTargetCloned(fromKey, transformed); if (transformed != null) { key = transformed; break setKey; } // converting a non-null key to a null key // is likely not what the client really wants; // So let's move on (as a best effort) } } key = replicate(fromKey); // cloned target is already placed in the target cloned map in replicate } final V fromValue = fromEntry.getValue(); final Object value; setValue: { if (fromValue == null) { value = null; break setValue; } if (super.containsTargetCloned(fromValue)) { value = super.getTargetCloned(fromValue); break setValue; } if (customTransformer != null) { final Class<?> fromValueClass = fromValue.getClass(); if (customTransformer.isTransformable(fromValue, fromValueClass, null)) { value = customTransformer.transform(fromValue, fromValueClass, null); super.putTargetCloned(fromValue, value); break setValue; } } value = replicate(fromValue); // cloned target is already placed in the target cloned map in replicate } try { toMap.put(key, value); } catch (RuntimeException ex) { // probably due to either the key or value being null, but the target map doesn't allow it. log.warn("Failed to put {" + key + "," + value + "} to " + toMap.getClass().getName(), ex); } } return toClass.cast(toMap); } private <K, V> Map<K, V> createToMap(Map<K, V> from) throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException { Class<?> fromClass = from.getClass(); if (isJavaPackage(fromClass)) { if (from instanceof SortedMap) { SortedMap<K, V> fromSortedMap = (SortedMap<K, V>) from; Comparator<K> toComparator = createToComparator(fromSortedMap); if (toComparator != null) return this.createToSortedMapWithComparator(fromSortedMap, toComparator); } return createToInstanceAsMap(from); } if (from instanceof SortedMap) { SortedMap<K, V> fromSortedMap = (SortedMap<K, V>) from; Comparator<K> toComparator = createToComparator(fromSortedMap); return new TreeMap<K, V>(toComparator); } return new HashMap<K, V>(); } private <K, V> Map<K, V> createToInstanceAsMap(Map<K, V> from) throws InstantiationException, IllegalAccessException, NoSuchMethodException { return createToInstance(from); } /** Returns a replicated comparator of the given sorted map, or null if there is no comparator. */ private <K, V> Comparator<K> createToComparator(SortedMap<K, V> fromSortedMap) { Comparator<? super K> fromComparator = fromSortedMap.comparator(); if (fromComparator == null) return null; @SuppressWarnings("unchecked") Comparator<K> toComparator = replicateByBeanReplicatable(fromComparator, Comparator.class); return toComparator; } private <K, V> SortedMap<K, V> createToSortedMapWithComparator(SortedMap<K, V> from, Comparator<? super K> comparator) throws NoSuchMethodException, SecurityException { return createToInstanceWithComparator(from, comparator); } }