1/*
2 * Copyright (c) 2009-2010 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.jme3.network.serializing.serializers;
34
35import com.jme3.network.serializing.Serializer;
36import com.jme3.network.serializing.SerializerRegistration;
37import java.io.IOException;
38import java.nio.ByteBuffer;
39import java.util.HashMap;
40import java.util.Iterator;
41import java.util.Map;
42import java.util.Map.Entry;
43import java.util.Set;
44import java.util.logging.Level;
45
46public class MapSerializer extends Serializer {
47
48    /*
49
50    Structure:
51
52    struct Map {
53        INT length
54        BYTE flags = { 0x01 = all keys have the same type,
55                       0x02 = all values have the same type }
56        if (flags has 0x01 set)
57            SHORT keyType
58        if (flags has 0x02 set)
59            SHORT valType
60
61        struct MapEntry[length] entries {
62            if (flags does not have 0x01 set)
63                SHORT keyType
64            OBJECT key
65
66            if (flags does not have 0x02 set)
67                SHORT valType
68            OBJECT value
69        }
70    }
71
72     */
73
74    @SuppressWarnings("unchecked")
75    public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
76        int length = data.getInt();
77
78        Map map;
79        try {
80            map = (Map)c.newInstance();
81        } catch (Exception e) {
82            log.log(Level.WARNING, "[Serializer][???] Could not determine map type. Using HashMap.");
83            map = new HashMap();
84        }
85
86        if (length == 0) return (T)map;
87
88        int flags = data.get() & 0xff;
89        boolean uniqueKeys = (flags & 0x01) == 0;
90        boolean uniqueVals = (flags & 0x02) == 0;
91
92        Class keyClazz = null;
93        Class valClazz = null;
94        Serializer keySerial = null;
95        Serializer valSerial = null;
96        if (!uniqueKeys){
97            SerializerRegistration reg = Serializer.readClass(data);
98            keyClazz = reg.getType();
99            keySerial = reg.getSerializer();
100        }
101        if (!uniqueVals){
102            SerializerRegistration reg = Serializer.readClass(data);
103            valClazz = reg.getType();
104            valSerial = reg.getSerializer();
105        }
106
107        for (int i = 0; i < length; i++){
108            Object key;
109            Object value;
110            if (uniqueKeys){
111                key = Serializer.readClassAndObject(data);
112            }else{
113                key = keySerial.readObject(data, keyClazz);
114            }
115            if (uniqueVals){
116                value = Serializer.readClassAndObject(data);
117            }else{
118                value = valSerial.readObject(data, valClazz);
119            }
120
121            map.put(key, value);
122        }
123
124        return (T)map;
125    }
126
127    @SuppressWarnings("unchecked")
128    public void writeObject(ByteBuffer buffer, Object object) throws IOException {
129        Map map = (Map)object;
130        int length = map.size();
131
132        buffer.putInt(length);
133        if (length == 0) return;
134
135
136        Set<Entry> entries = map.entrySet();
137
138        Iterator<Entry> it = entries.iterator();
139
140        Entry entry = it.next();
141        Class keyClass = entry.getKey().getClass();
142        Class valClass = entry.getValue().getClass();
143        while (it.hasNext()) {
144            entry = it.next();
145
146            if (entry.getKey().getClass() != keyClass){
147                keyClass = null;
148                if (valClass == null)
149                    break;
150            }
151            if (entry.getValue().getClass() != valClass){
152                valClass = null;
153                if (keyClass == null)
154                    break;
155            }
156        }
157
158        boolean uniqueKeys = keyClass == null;
159        boolean uniqueVals = valClass == null;
160        int flags = 0;
161        if (!uniqueKeys) flags |= 0x01;
162        if (!uniqueVals) flags |= 0x02;
163        buffer.put( (byte) flags );
164
165        Serializer keySerial = null, valSerial = null;
166        if (!uniqueKeys){
167            Serializer.writeClass(buffer, keyClass);
168            keySerial = Serializer.getSerializer(keyClass);
169        }
170        if (!uniqueVals){
171            Serializer.writeClass(buffer, valClass);
172            valSerial = Serializer.getSerializer(valClass);
173        }
174
175        it = entries.iterator();
176        while (it.hasNext()) {
177            entry = it.next();
178            if (uniqueKeys){
179                Serializer.writeClassAndObject(buffer, entry.getKey());
180            }else{
181                keySerial.writeObject(buffer, entry.getKey());
182            }
183            if (uniqueVals){
184                Serializer.writeClassAndObject(buffer, entry.getValue());
185            }else{
186                valSerial.writeObject(buffer, entry.getValue());
187            }
188        }
189    }
190}
191