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