package cn.liutils.loading;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.util.ResourceLocation;
import org.apache.commons.io.IOUtils;
import cn.liutils.core.LIUtils;
import cn.liutils.util.generic.RegistryUtils;
import com.google.common.collect.HashBiMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public abstract class Loader<T> {
//'default' element (if any): basic search element. 2nd fallback.
//'parent' element specified in JsonObject: 1st fallback.
Map<String, JsonObject> entries = new HashMap();
HashBiMap<String, T> loadedObjects = HashBiMap.create();
static final JsonParser parser = new JsonParser();
public Loader() {}
public void feed(ResourceLocation loc) {
try {
feed(IOUtils.toString(RegistryUtils.getResourceStream(loc)));
} catch (IOException e) {
e.printStackTrace();
}
}
public void feed(String json) {
feed(parser.parse(json).getAsJsonObject());
}
public void feed(JsonObject root) {
for(Map.Entry<String, JsonElement> entry : root.entrySet()) {
feedEntry(entry.getKey(), entry.getValue().getAsJsonObject());
}
}
public void feedEntry(String name, JsonObject element) {
entries.put(name, element);
}
public void loadAll() {
for(Map.Entry<String, JsonObject> entry : entries.entrySet()) {
if(!entry.getKey().equals("default") && entry.getValue().get("ignore") == null)
doLoad(entry.getKey(), entry.getValue());
}
for(Map.Entry<String, T> entry : loadedObjects.entrySet()) {
finishedLoading(entry.getKey(), entry.getValue(), new ObjectNamespace(entry.getKey()));
}
}
public Collection<T> getEnumeration() {
return loadedObjects.values();
}
public String getName(T obj) {
return loadedObjects.inverse().get(obj);
}
public boolean isLoaded(String name) {
return loadedObjects.containsKey(name);
}
public T getObjectLazy(String name) {
if(!isLoaded(name))
load(name, new ObjectNamespace(name));
return getObject(name);
}
public T getObject(String name) {
return loadedObjects.get(name);
}
private void doLoad(String name, JsonObject object) {
try {
T obj = load(name, new ObjectNamespace(name));
if(obj == null) {
LIUtils.log.error("Didn't load the element " + name + " correctly.");
return;
}
loadedObjects.put(name, obj);
} catch(Exception e) {
LIUtils.log.error("An error occured when loading element " + name + ".");
e.printStackTrace();
}
}
protected abstract T load(String name, ObjectNamespace ns);
/**
* Called when all objects' loading are completed.
*/
protected abstract void finishedLoading(String name, T object, ObjectNamespace ns);
/**
* @throws NumberFormatException
*/
public Double getDouble(String name, Object ...searchRule) {
JsonPrimitive jp = getProp(name, searchRule);
return jp == null ? null : jp.getAsDouble();
}
/**
* @throws NumberFormatException
*/
public Float getFloat(String name, Object ...searchRule) {
JsonPrimitive jp = getProp(name, searchRule);
return jp == null ? null : jp.getAsFloat();
}
/**
* @throws NumberFormatException
*/
public Integer getInt(String name, Object ...searchRule) {
JsonPrimitive jp = getProp(name, searchRule);
return jp == null ? null : jp.getAsInt();
}
public Boolean getBoolean(String name, Object ...searchRule) {
JsonPrimitive jp = getProp(name, searchRule);
return jp == null ? null : jp.getAsBoolean();
}
public String getString(String name, Object ...searchRule) {
JsonPrimitive jp = getProp(name, searchRule);
return jp == null ? null : jp.getAsString();
}
/**
*
* @param name element name
* @param searchRule <br/>
* * Do a tree locating on the jsonObject. <br/>
* * type=String: name to lookup, parent=JsonObject <br/>
* * type=int: array index, parent=JsonArray <br/>
* * finally the result must be a JsonPrimitive. otherwise you'll get a null. <br/>
* * Parent fallback: Self->Parent(if any)->Default(if any)
* @return null if the object does not exist, or didn't find the primitive.
* @throws IllegalArgumentException if given a wrong search rule
*/
public JsonPrimitive getProp(String name, Object ...searchRule) {
JsonObject object = entries.get(name);
if(object == null)
return null;
JsonObject defObject = entries.get("default");
JsonObject parObject = null;
JsonPrimitive par = (JsonPrimitive) object.get("parent");
if(par != null && par.isString()) {
parObject = entries.get(par.getAsString());
}
JsonObject[] falls = new JsonObject[] { object, defObject, parObject };
for(JsonObject o : falls) {
if(o == null)
continue;
JsonElement current = o;
int searchIndex = 0;
boolean fail = false;
while(searchIndex < searchRule.length && current != null) {
Object obj = searchRule[searchIndex];
if(obj instanceof String) {
if(!current.isJsonObject()) {
fail = true;
break;
}
current = current.getAsJsonObject().get((String) obj);
} else {
int i = 0;
try {
i = (int) obj;
} catch(Exception e) {
throw new IllegalArgumentException("Not a string nor an int!");
}
if(!current.isJsonArray())
break;
current = current.getAsJsonArray().get(i);
}
++searchIndex;
}
if(!fail && current != null && current.isJsonPrimitive()) {
return (JsonPrimitive) current;
}
}
return null;
}
public class ObjectNamespace {
public final String name;
public final Loader<T> loader;
ObjectNamespace(String n) {
name = n;
loader = Loader.this;
}
/**
* @throws NumberFormatException
*/
public Double getDouble(Object ...searchRule) {
return Loader.this.getDouble(name, searchRule);
}
/**
* @throws NumberFormatException
*/
public Float getFloat(Object ...searchRule) {
return Loader.this.getFloat(name, searchRule);
}
/**
* @throws NumberFormatException
*/
public Integer getInt(Object ...searchRule) {
return Loader.this.getInt(name, searchRule);
}
public Boolean getBoolean(Object ...searchRule) {
return Loader.this.getBoolean(name, searchRule);
}
public String getString(Object ...searchRule) {
return Loader.this.getString(name, searchRule);
}
}
}