package org.mef.sprig; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.mef.sprig.json.Reader; import org.mef.sprig.util.TSortNode; import org.mef.sprig.util.TopologicalSort; import play.Logger; /** * Main API class for Sprig. * Loads JSON into model objects and saves them to the database. * */ public class Sprig { protected static String seedDir = "conf/sprig"; protected static Sprig theInstance; protected static Reader reader; public static String version() { return "v0.1.2"; } /** * Sets the directory to be used, when you want JSON files in some other directory * than 'conf/sprig'. * * Used internally for unit testing. * @param dir */ public static void setDir(String dir) { seedDir = dir; } public static void setReader(Reader r) { reader = r; } public static void enableDebugLogging(boolean b) { SprigLogger.debugLoggingEnabled = b; } /** * Load the seed data for the given modes. * @param objs Each object is either a Class of a model to be loaded, or a loader that implements SprigLoader * @return * @throws Exception */ public static int load(Object... objs) throws Exception { return load(null, objs); } /** * Load the seed data for the given modes. * @param dir A sub-directory (under 'conf/sprig') in which to load JSON files. * @param objs Each object is either a Class of a model to be loaded, or a loader that implements SprigLoader * @return * @throws Exception */ @SuppressWarnings("rawtypes") public static int load(String subDir, Object... objs) throws Exception { Sprig self = new Sprig(); theInstance = self; List<Wrapper> L = new ArrayList<Wrapper>(); for(Object obj : objs) { SprigLoader tmp; if (obj instanceof SprigLoader) { tmp = (SprigLoader)obj; } else if (obj instanceof Class) { tmp = new DefaultSprigLoader((Class)obj); } else { throw new IllegalArgumentException("parameter must be Class or a SprigLoader"); } Wrapper wrapper = new Wrapper(tmp, reader); L.add(wrapper); } int n = self.doLoad(subDir, L); //and close for(Wrapper wrapper : L) { wrapper.getLoader().close(); } return n; } @SuppressWarnings("rawtypes") public static List<Object> getLoadedObjects(Class clazz) { List<Object> L = theInstance.resultMap.get(clazz); return L; } //=============================== protected int failCount; @SuppressWarnings("rawtypes") protected Map<Class, List<Object>> resultMap = new HashMap<Class, List<Object>>(); protected Map<String, Wrapper> loaderMap = new HashMap<String, Wrapper>(); protected List<ViaRef> viaL = new ArrayList<ViaRef>(); protected Sprig() { } protected void log(String s) { SprigLogger.log(s); } @SuppressWarnings("rawtypes") protected int doLoad(String subDir, List<Wrapper> wrapperL) throws Exception { int numObjLoaded = 0; List<Object> loadedObjL = null; String dir = getDir(subDir); log("dir: " + dir); for(Wrapper wrapper : wrapperL) { loadedObjL = wrapper.load(dir, viaL); //read the JSON addToResultMap(wrapper.getLoader().getClassBeingLoaded(), loadedObjL); numObjLoaded += loadedObjL.size(); this.loaderMap.put(wrapper.getNameOfClassBeingLoaded(), wrapper); } //sort List<Wrapper> sortedL = tsort(wrapperL); log("and save.."); List<Wrapper> soFarL = new ArrayList<Wrapper>(); failCount = 0; for(Wrapper wrapper : sortedL) { SprigLoader loader = wrapper.getLoader(); log(String.format("SEED saving %s..", wrapper.getNameOfClassBeingLoaded())); List<Object> L = this.resultMap.get(loader.getClassBeingLoaded()); wrapper.save(L); soFarL.add(wrapper); doResolve(soFarL); if (failCount > 0) { throw new IllegalStateException("SEED resolve failed"); } } return numObjLoaded; } private String getDir(String subDir) { if (subDir == null) { return seedDir; } else { return seedDir + "/" + subDir; } } @SuppressWarnings("rawtypes") protected void addToResultMap(Class classBeingLoaded, List<Object> L) { List<Object> storedL = resultMap.get(classBeingLoaded); if (storedL != null) { storedL.addAll(L); } else { resultMap.put(classBeingLoaded, L); } } protected List<Wrapper> tsort(List<Wrapper> wrapperL) { List<TSortNode> L = new ArrayList<TSortNode>(); for(Wrapper wrapper : wrapperL) { TSortNode node = new TSortNode(wrapper); L.add(node); } for(ViaRef via : viaL) { Wrapper srcWrapper = findByClass(wrapperL, via.sourceClazz); Wrapper targetWrapper = findByClassName(wrapperL, via.targetClassName); TSortNode node1 = null; TSortNode node2 = null; for(TSortNode tmp : L) { if (tmp.obj == srcWrapper) { node1 = tmp; } else if (tmp.obj == targetWrapper) { node2 = tmp; } } node1.addDep(node2); } List<TSortNode> sortL = TopologicalSort.sort(L); List<Wrapper> sortedL = new ArrayList<Wrapper>(); for(TSortNode node : sortL) { sortedL.add((Wrapper) node.obj); } return sortedL; } @SuppressWarnings("rawtypes") protected Wrapper findByClass(List<Wrapper> wrapperL, Class clazz) { for(Wrapper wrapper : wrapperL) { SprigLoader loader = wrapper.getLoader(); if (loader.getClassBeingLoaded() == clazz) { return wrapper; } } return null; } protected Wrapper findByClassName(List<Wrapper> wrapperL, String className) { for(Wrapper wrapper : wrapperL) { if (wrapper.getNameOfClassBeingLoaded().equals(className)) { return wrapper; } } return null; } protected boolean doResolve(List<Wrapper> soFarL) { while(doOneRound(soFarL)) { } return (this.viaL.size() == 0); } protected boolean doOneRound(List<Wrapper> soFarL) { for(ViaRef vid : viaL) { for(Wrapper wrapper : soFarL) { //once a loaders objects have been saved to the db, we can resolve references to them String className = wrapper.getNameOfClassBeingLoaded(); if (className.equals(vid.targetClassName)) { //log("a " + className); if (resolveAsDeferredId(vid)) { viaL.remove(vid); return true; } } } } return false; } @SuppressWarnings({ "rawtypes", "unchecked" }) protected boolean resolveAsDeferredId(ViaRef ref) { if (ref.targetField.equals("sprig_id")) { Wrapper w = this.loaderMap.get(ref.targetClassName); Integer sprigId = Integer.parseInt(ref.targetVal); Object obj = w.sprigIdMap.get(sprigId); Wrapper sourceW = this.loaderMap.get(ref.sourceClazz.getSimpleName()); SprigLoader sourceLoader = sourceW.getLoader(); String fieldName = ref.sourceField; //resolve try { sourceLoader.resolve(ref.sourceObj, fieldName, obj); } catch (Exception e) { e.printStackTrace(); failCount++; log(String.format("SEED failed to resolve %s.%s to %s.%s ", ref.sourceClazz.getSimpleName(), fieldName, ref.targetClassName, ref.targetField)); } return true; } return false; } }