001/**
002 *   GRANITE DATA SERVICES
003 *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 *   This file is part of the Granite Data Services Platform.
006 *
007 *   Granite Data Services is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   Granite Data Services is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 *   General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public
018 *   License along with this library; if not, write to the Free Software
019 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 *   USA, or see <http://www.gnu.org/licenses/>.
021 */
022package org.granite.messaging.amf.io.convert.impl;
023
024import java.lang.reflect.Type;
025import java.lang.reflect.WildcardType;
026import java.util.Map;
027import java.util.Map.Entry;
028
029import org.granite.messaging.amf.io.convert.Converter;
030import org.granite.messaging.amf.io.convert.Converters;
031import org.granite.messaging.amf.io.convert.IllegalConverterArgumentException;
032import org.granite.util.TypeUtil;
033import org.granite.util.MapUtil;
034
035/**
036 * @author Franck WOLFF
037 */
038public class Map2Map extends Converter {
039
040    public Map2Map(Converters converters) {
041        super(converters);
042    }
043
044    @Override
045        protected boolean internalCanConvert(Object value, Type targetType) {
046
047        Type[] targetComponentTypes = MapUtil.getComponentTypes(targetType);
048        if (targetComponentTypes == null)
049            return false; // not a map.
050
051        if (value == null)
052            return true;
053
054        if (!(value instanceof Map<?, ?>))
055            return false;
056
057        Type keyType = targetComponentTypes[0];
058        Type valueType = targetComponentTypes[1];
059
060        if ((keyType.equals(Object.class) || keyType instanceof WildcardType) &&
061            (valueType.equals(Object.class) || valueType instanceof WildcardType))
062            return true;
063
064        Converter keyConverter = null;
065        Converter valueConverter = null;
066        for (Map.Entry<?, ?> item : ((Map<?, ?>)value).entrySet()) {
067
068            if (keyConverter == null)
069                keyConverter = converters.getConverter(item.getKey(), keyType);
070            else if (!keyConverter.canConvert(item.getKey(), keyType))
071                keyConverter = converters.getConverter(item.getKey(), keyType);
072            if (keyConverter == null)
073                return false;
074
075            if (valueConverter == null)
076                valueConverter = converters.getConverter(item.getValue(), valueType);
077            else if (!valueConverter.canConvert(item.getValue(), valueType))
078                valueConverter = converters.getConverter(item.getValue(), valueType);
079            if (valueConverter == null)
080                return false;
081        }
082
083        return true;
084    }
085
086    @Override
087        protected Object internalConvert(Object value, Type targetType) {
088
089        if (value == null)
090            return null;
091
092        if (value instanceof Map<?, ?>) {
093            Map<?, ?> map = (Map<?, ?>)value;
094
095            Type[] targetComponentTypes = MapUtil.getComponentTypes(targetType);
096            if (targetComponentTypes != null) {
097                Type keyType = targetComponentTypes[0];
098                Type valueType = targetComponentTypes[1];
099
100                Class<?> targetClass = TypeUtil.classOfType(targetType);
101                if (targetClass.isInstance(value) &&
102                    (keyType.equals(Object.class) || keyType instanceof WildcardType) &&
103                    (valueType.equals(Object.class) || valueType instanceof WildcardType))
104                    return value;
105                
106                // Check if all keys and values are compatible
107                if (targetClass.isInstance(value)) {
108                        Class<?> keyClass = TypeUtil.classOfType(keyType);
109                        Class<?> valueClass = TypeUtil.classOfType(valueType);
110                        
111                        boolean compatible = true;                      
112                        for (Entry<?, ?> entry : ((Map<?, ?>)value).entrySet()) {
113                                if (!keyClass.isInstance(entry.getKey()) || !valueClass.isInstance(entry.getValue())) {
114                                        compatible = false;
115                                        break;
116                                }
117                        }
118                        if (compatible)
119                                return value;
120                }
121                
122                Map<Object, Object> targetInstance = null;
123                try {
124                    targetInstance = MapUtil.newMap(targetClass, map.size());
125                } catch (Exception e) {
126                    throw new IllegalConverterArgumentException(this, value, targetType, e);
127                }
128
129                Converter keyConverter = null;
130                Converter valueConverter = null;
131                for (Map.Entry<?, ?> item : ((Map<?, ?>)value).entrySet()) {
132
133                    if (keyConverter == null)
134                        keyConverter = converters.getConverter(item.getKey(), keyType);
135                    else if (!keyConverter.canConvert(item.getKey(), keyType))
136                        keyConverter = converters.getConverter(item.getKey(), keyType);
137                    if (keyConverter == null)
138                        throw new IllegalConverterArgumentException(this, value, targetType);
139
140                    if (valueConverter == null)
141                        valueConverter = converters.getConverter(item.getValue(), valueType);
142                    else if (!valueConverter.canConvert(item.getValue(), valueType))
143                        valueConverter = converters.getConverter(item.getValue(), valueType);
144                    if (valueConverter == null)
145                        throw new IllegalConverterArgumentException(this, value, targetType);
146
147                    targetInstance.put(
148                        keyConverter.convert(item.getKey(), keyType),
149                        valueConverter.convert(item.getValue(), valueType)
150                    );
151                }
152
153                return targetInstance;
154            }
155        }
156
157        throw new IllegalConverterArgumentException(this, value, targetType);
158    }
159}