package xapi.inject.api;
import xapi.annotation.inject.InstanceDefault;
import xapi.annotation.inject.InstanceOverride;
import xapi.annotation.inject.SingletonDefault;
import xapi.annotation.inject.SingletonOverride;
import xapi.except.NotConfiguredCorrectly;
import xapi.platform.AndroidPlatform;
import xapi.platform.DevPlatform;
import xapi.platform.GwtDevPlatform;
import xapi.platform.GwtPlatform;
import xapi.platform.JavaFxPlatform;
import xapi.platform.JrePlatform;
import xapi.platform.Platform;
import xapi.util.X_Properties;
import xapi.util.X_Util;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Created by James X. Nelson (james @wetheinter.net) on 7/18/16.
*/
@JrePlatform
@GwtPlatform
@DevPlatform
@GwtDevPlatform
@AndroidPlatform
@JavaFxPlatform
public class PlatformChecker {
private final Set<String> runtime;
private boolean needsInject;
private Map<String, Set<String>> knownPlatforms;
private Map<Class<? extends Annotation>, Integer> platformScore;
private Map<String, Class<? extends Annotation>> allPlatforms;
public PlatformChecker() {
runtime = new HashSet<>();
allPlatforms = new HashMap<>();
platformScore = new HashMap<>();
for (String allowed : X_Properties.platform.get().split(",")) {
runtime.add(allowed);
}
knownPlatforms = getKnownPlatforms();
for (String choice : runtime) {
if (!knownPlatforms.containsKey(choice)) {
needsInject = true;
break;
}
}
sort();
}
public boolean needsInject() {
return needsInject;
}
public void addPlatform(ClassLoader loader, String name, Iterable<String> choices) {
final Set<String> result = knownPlatforms.computeIfAbsent(name, ignored->new LinkedHashSet<>());
for (String choice : choices) {
result.add(choice);
String type = knownPlatforms.computeIfAbsent(choice, ignored->{
final Set<String> set = new LinkedHashSet<>();
set.add(choice);
return set;
}).iterator().next();
try {
allPlatforms.put(choice, (Class<? extends Annotation>) loader.loadClass(type));
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw X_Util.rethrow(e);
}
}
sort();
}
private void sort() {
int score = 0;
for (String current : runtime) {
score++;
for (String platform : knownPlatforms.get(current)) {
final Class<? extends Annotation> type = allPlatforms.get(platform);
platformScore.put(type, score);
}
}
for (Class<? extends Annotation> platform : allPlatforms.values()) {
platformScore.computeIfAbsent(platform, i->Integer.MIN_VALUE);
}
}
public Class<?> findBest(Map<Class<?>, Integer> cls) {
int best = Integer.MIN_VALUE;
Class<?> result = null;
for (Entry<Class<?>, Integer> entry : cls.entrySet()) {
final Class<?> cl = entry.getKey();
int score = entry.getValue() == null ? classScore(cl) : entry.getValue();
if (score > best) {
result = cl;
best = score;
}
}
return result;
}
public int classScore(Class<?> cl) {
int score = Integer.MIN_VALUE;
int bump = -1;
for (Annotation annotation : cl.getAnnotations()) {
if (annotation instanceof SingletonOverride) {
score = ((SingletonOverride)annotation).priority();
} else if (annotation instanceof InstanceOverride) {
score = ((InstanceOverride)annotation).priority();
} else if (annotation instanceof SingletonDefault || annotation instanceof InstanceDefault) {
score = Integer.MIN_VALUE + 1;
} else {
Integer maybe = platformScore.get(annotation.annotationType());
if (maybe != null) {
if (maybe != Integer.MIN_VALUE) {
if (maybe > bump) {
bump = maybe;
}
}
}
}
}
if (bump > -1) {
score += bump;
}
return score;
}
public Map<String, Set<String>> getAllPlatforms() {
return knownPlatforms;
}
protected Map<String, Set<String>> getKnownPlatforms() {
final Annotation[] annos = getClass().getAnnotations();
Map<String, Set<String>> results = new HashMap<>();
for (Annotation anno : annos) {
String type = anno.annotationType().getName();
String simpleType = type.substring(type.lastIndexOf('.') + 1);
final Set<String> into = results.computeIfAbsent(type, ignored->new LinkedHashSet());
results.put(simpleType, into);
into.add(type);
collectFallbacks(into, anno.annotationType());
allPlatforms.put(type, anno.annotationType());
allPlatforms.put(simpleType, anno.annotationType());
}
return results;
}
private void collectFallbacks(Set<String> into, Class<? extends Annotation> cls) {
Platform platform = cls.getAnnotation(Platform.class);
if (platform == null) {
throw new NotConfiguredCorrectly("Platform / fallback type " + cls + " did not have an @Platform annotation!");
}
for (Class<? extends Annotation> fallback : platform.fallback()) {
if (into.add(fallback.getName())) {
collectFallbacks(into, fallback);
}
}
}
public String[] getRuntime() {
return runtime.toArray(new String[runtime.size()]);
}
}