/*
* Copyright 2015 Odnoklassniki Ltd, Mail.Ru Group
*
* 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 one.nio.serial;
import one.nio.serial.gen.StubGenerator;
import java.io.ObjectInput;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;
public class MapSerializer extends Serializer<Map> {
private Constructor constructor;
MapSerializer(Class cls) {
super(cls);
this.constructor = findConstructor();
this.constructor.setAccessible(true);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
try {
super.readExternal(in);
} catch (ClassNotFoundException e) {
if ((Repository.getOptions() & Repository.MAP_STUBS) == 0) throw e;
this.cls = StubGenerator.generateRegular(uniqueName("Map"), "java/util/HashMap", null);
this.origin = Origin.GENERATED;
}
this.constructor = findConstructor();
this.constructor.setAccessible(true);
}
@Override
public void calcSize(Map obj, CalcSizeStream css) throws IOException {
css.count += 4;
for (Map.Entry e : ((Map<?, ?>) obj).entrySet()) {
css.writeObject(e.getKey());
css.writeObject(e.getValue());
}
}
@Override
public void write(Map obj, DataStream out) throws IOException {
out.writeInt(obj.size());
for (Map.Entry e : ((Map<?, ?>) obj).entrySet()) {
out.writeObject(e.getKey());
out.writeObject(e.getValue());
}
}
@Override
@SuppressWarnings("unchecked")
public Map read(DataStream in) throws IOException, ClassNotFoundException {
Map result;
try {
result = (Map) constructor.newInstance();
in.register(result);
} catch (Exception e) {
throw new IOException(e);
}
int length = in.readInt();
for (int i = 0; i < length; i++) {
result.put(in.readObject(), in.readObject());
}
return result;
}
@Override
public void skip(DataStream in) throws IOException, ClassNotFoundException {
int length = in.readInt();
for (int i = 0; i < length; i++) {
in.readObject();
in.readObject();
}
}
@Override
public void toJson(Map obj, StringBuilder builder) throws IOException {
builder.append('{');
boolean firstWritten = false;
for (Map.Entry<?, ?> entry : ((Map<?, ?>) obj).entrySet()) {
if (firstWritten) builder.append(','); else firstWritten = true;
Json.appendString(builder, entry.getKey().toString());
builder.append(':');
Json.appendObject(builder, entry.getValue());
}
builder.append('}');
}
@SuppressWarnings("unchecked")
private Constructor findConstructor() {
try {
return cls.getConstructor();
} catch (NoSuchMethodException e) {
Class implementation = SortedMap.class.isAssignableFrom(cls) ? TreeMap.class : HashMap.class;
generateUid();
Repository.log.warn("[" + Long.toHexString(uid) + "] No default constructor for " + descriptor +
", changed type to " + implementation.getName());
try {
return implementation.getConstructor();
} catch (NoSuchMethodException e1) {
return null;
}
}
}
static boolean isValidType(Class<?> type) {
try {
type.getConstructor();
return (type.getModifiers() & Modifier.ABSTRACT) == 0;
} catch (NoSuchMethodException e) {
return type == Map.class || type == SortedMap.class || type == NavigableMap.class;
}
}
}