package de.take_weiland.mods.commons.internal.sync;
import de.take_weiland.mods.commons.serialize.Property;
import de.take_weiland.mods.commons.sync.Syncer;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;
import static java.lang.invoke.MethodHandles.publicLookup;
import static java.lang.invoke.MethodType.methodType;
/**
* @author diesieben07
*/
final class DefaultCompanionFactory implements CompanionFactory {
private static final MethodHandle NULL_COMPANION = MethodHandles.constant(SyncCompanion.class, null);
private final Map<Class<?>, CompanionClassInfo> companionClasses = new HashMap<>();
@Override
public MethodHandle getCompanionConstructor(Class<?> clazz) {
Class<?> companionClass = getCompanionClass(clazz);
if (companionClass == null) {
return NULL_COMPANION;
} else {
try {
return publicLookup().findConstructor(companionClass, methodType(void.class))
.asType(methodType(SyncCompanion.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e); // impossible
}
}
}
Class<?> getCompanionClass(Class<?> clazz) {
CompanionClassInfo info = getInfo(clazz);
return info == null ? null : info.clazz;
}
private CompanionClassInfo getInfo(Class<?> clazz) {
if (clazz == Object.class) {
return null;
}
CompanionClassInfo info;
if (!companionClasses.containsKey(clazz)) {
Map<Property<?, ?>, Syncer<?, ?>> members = SyncCompanions.getSyncedMemberInfo(clazz);
if (members.isEmpty()) {
info = null;
} else {
Class<?> companionClass = new BytecodeEmittingCompanionGenerator(this, clazz, members).generateCompanion();
info = new CompanionClassInfo(companionClass, members.size());
}
companionClasses.put(clazz, info);
} else {
info = companionClasses.get(clazz);
}
return info;
}
int getNextFreeIDFor(Class<?> clazz) {
if (clazz == Object.class) {
return 1; // ID 0 is taken for end of stream
}
Class<?> superclass = clazz.getSuperclass();
CompanionClassInfo info = getInfo(superclass);
return getNextFreeIDFor(superclass) + (info == null ? 0 : info.numSyncedMembers);
}
private static final class CompanionClassInfo {
final Class<?> clazz;
final int numSyncedMembers;
CompanionClassInfo(Class<?> clazz, int numSyncedMembers) {
this.clazz = clazz;
this.numSyncedMembers = numSyncedMembers;
}
}
}