diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml new file mode 100644 index 0000000..3cd3e9d --- /dev/null +++ b/.idea/dbnavigator.xml @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..ce1c62c --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..c3dfb30 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..d64cd49 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/src/main/java/fr/doap/jdb/JDB.java b/src/main/java/fr/doap/jdb/JDB.java index 04ec531..41dbac6 100644 --- a/src/main/java/fr/doap/jdb/JDB.java +++ b/src/main/java/fr/doap/jdb/JDB.java @@ -1,16 +1,13 @@ package fr.doap.jdb; +import fr.doap.jdb.utils.*; import fr.doap.slog.*; -import javax.sql.rowset.serial.*; -import javax.swing.plaf.*; import java.lang.reflect.*; import java.sql.*; import java.util.*; import java.util.concurrent.*; -import static fr.doap.jdb.Utils.*; - public class JDB { /* public static */ static public void init(String aConn, String aUser, String aPass) { @@ -32,12 +29,16 @@ public class JDB { Optional> lOpt = mDBWrap.query("SELECT MAX("+DBWrap.sJID+")+1 AS NEXTID FROM OBJECTS").findFirst(); Object lNID = lOpt.get().get("NEXTID"); - if (lNID != null) mNextID = (int)lNID; - else mNextID = 1; + if (lNID != null) mDBWOC.setNextID((int)lNID); + else mDBWOC.setNextID(1); } protected void _save(Object aObject) { - new ObjectMapBuilder(aObject); + Objects2Maps lO2M = new Objects2Maps(mDBWOC); + lO2M.add(aObject); + mSQLTypes.putAll(lO2M.getColumnSQLTypes()); + lO2M.getInserts().values().forEach(aProps -> mDBWrap.insert(aProps)); + lO2M.getUpdates().values().forEach(aProps -> mDBWrap.update(aProps)); } // aConds = String: le SQL et ensuite les paramètres @@ -63,7 +64,7 @@ public class JDB { mRS.next(); int lID = mRS.getInt(mDBWrap.sJID); L.i("ID: {}", lID); - Object lCached = mWeakObjectCache.getObject(lID); + Object lCached = mDBWOC.getObject(lID); if (lCached != null) return (T)lCached; L.i("NOT CACHED ! Loading..."); @@ -85,7 +86,7 @@ L.i("Props: {}", lMap); byte[] lBA = mRS.getBytes("_SUBS_"); if (lBA != null) { int[] lIDs = (int[]) Utils.fromByteArray(lBA); - for (int i=0; i aID.toString()).toList())+")").get(); @@ -103,10 +104,10 @@ L.i(" --> Props: {}", lMap); Class lClass = Class.forName(lLoadRS.getString("_CLASS_")); Object lO = lClass.getDeclaredConstructor().newInstance(); - mWeakObjectCache.put(lSID, lO); + mDBWOC.put(lSID, lO); } lProps.forEach((aID, aProps) -> { - Object lO = mWeakObjectCache.getId(aID); + Object lO = mDBWOC.getId(aID); if ((lO instanceof Collection)&&(lO.getClass().getCanonicalName().startsWith("java."))) { } else { @@ -122,7 +123,7 @@ L.i(" --> Props: {}", lMap); } }); - return (T)mWeakObjectCache.getObject(lID); + return (T) mDBWOC.getObject(lID); } catch (Exception aE) { L.w(aE); } return null; } @@ -142,146 +143,7 @@ L.i(" --> Props: {}", lMap); return mDBWrap._query(lSQL, lParams); } - static final Map, String> sTypeMap = Utils.HashMapOf( - Boolean.class, "BOOLEAN", - Integer.class, "INTEGER", - Long.class, "BIGINT", - byte[].class, "VARBINARY", - Blob.class, "BLOB", - String.class, "VARCHAR"); - - class ObjectMapBuilder { - ObjectMapBuilder(Object aObject) { - Map> lReach=new HashMap<>(); - Map> lInserts=new HashMap<>(); - Map> lUpdates=new HashMap<>(); - - add(aObject); - - while (mPos < mProcessingList.size()) { - Object lO = mProcessingList.get(mPos); - try { - Constructor lCons = lO.getClass().getDeclaredConstructor(); // ensure at least a default constructor... - } catch (Exception aE) { - throw new RuntimeException("No constructor for class "+lO.getClass().getCanonicalName()); - } - - Set lObjects = new HashSet<>(); - lReach.put(lO, lObjects); - - Map lProps = getProperties(lO); - for (Map.Entry iME: lProps.entrySet()) { - Object lV = iME.getValue(); - if (!sTypeMap.containsKey(lV.getClass())) { - add(lV); - iME.setValue(mWeakObjectCache.getId(lV)); - mSQLTypes.put(iME.getKey(), "INTEGER"); - lObjects.add(lV); - } else mSQLTypes.put(iME.getKey(), sTypeMap.get(lV.getClass())); - } - - lProps.put(DBWrap.sJID, mWeakObjectCache.getId(lO)); - lProps.put("_CLASS_", lO.getClass().getCanonicalName()); - - if (mInserts.contains(lO)) lInserts.put(lO, lProps); - else lUpdates.put(lO, lProps); - - mPos++; - } - - // saturate - Set lReachComplete = new HashSet<>(); - lReachComplete.add(this); - while (!lReachComplete.isEmpty()) { - lReachComplete.clear(); - lReach.values().forEach(aSet -> { - Set lNew = new HashSet<>(aSet); - aSet.forEach(aE -> { - if (lNew.addAll(lReach.get(aE))) lReachComplete.add(aE); - }); - aSet.clear(); - aSet.addAll(lNew); - }); - } - - lReach.forEach((aO, aSet) -> { - if (aSet.size() > 0) { - Object[] lObs = aSet.toArray(); - int lNew[] = new int[lObs.length]; - for (int i = 0; i < lNew.length; i++) lNew[i] = mWeakObjectCache.getId(lObs[i]); -L.i("Reach {} -> {}", aSet, lNew); - if (lInserts.containsKey(aO)) lInserts.get(aO).put("_SUBS_", Utils.toByteArray(lNew)); - else lUpdates.get(aO).put("_SUBS_", Utils.toByteArray(lNew)); - } - }); - - L.i("Reach: {}", lReach); - lInserts.values().forEach(aProps -> mDBWrap.insert(aProps)); - lUpdates.values().forEach(aProps -> mDBWrap.update(aProps)); - } - - private void add(Object aObject) { - if (!mProcessingList.contains(aObject)) { - mProcessingList.add(aObject); - if (mWeakObjectCache.getId(aObject) == -1) { - mWeakObjectCache.put(mNextID, aObject); - mNextID++; - mInserts.add(aObject); - } - } - } - - protected Map getProperties(Object aObject) { - Map lProps = new HashMap<>(); - boolean isCollection = aObject instanceof Collection; - Class lC = aObject.getClass(); - while (lC != Object.class) { - lProps.put(getShortClassname(lC), true); - if (isCollection && lC.getCanonicalName().startsWith("java.")) { - Collection lColl = (Collection) aObject; - Object[] lObjects = new Object[2*lColl.size()]; - Iterator iIt = lColl.iterator(); - for (int i=0; i mProcessingList = new ArrayList<>(); - Set mInserts = new HashSet<>(); - } - protected DBWrap mDBWrap; - protected WeakObjectCache mWeakObjectCache = new WeakObjectCache(); - protected int mNextID; + protected DBWOC mDBWOC = new DBWOC(); protected Map mSQLTypes = new HashMap<>(); } \ No newline at end of file diff --git a/src/main/java/fr/doap/jdb/Objects2Maps.java b/src/main/java/fr/doap/jdb/Objects2Maps.java new file mode 100644 index 0000000..b8f2290 --- /dev/null +++ b/src/main/java/fr/doap/jdb/Objects2Maps.java @@ -0,0 +1,152 @@ +package fr.doap.jdb; + +import fr.doap.jdb.impl.*; +import fr.doap.jdb.utils.*; +import fr.doap.slog.*; + +import java.lang.reflect.*; +import java.sql.*; +import java.util.*; + +import static fr.doap.jdb.utils.Utils.*; + +public class Objects2Maps extends ObjectVisitor { + + public Objects2Maps(DBWOC aWOC) { mDBWOC =aWOC; } + + public Map> getInserts() { return mInserts; } + public Map> getUpdates() { return mUpdates; } + public Map getColumnSQLTypes() { return mColumnSQLType; } + + @Override + protected void onObjectAdded(Object aObject, int aPos) { + if (mDBWOC.getId(aObject) == -1) { + mDBWOC.addNew(aObject); + mNewObjects.add(aObject); + } + } + + @Override + protected void onProcessingObject(Object aObject, int aPos) { + Class lClass = aObject.getClass(); + try { + Constructor lCons = lClass.getDeclaredConstructor(); // ensure at least a default constructor... + } catch (Exception aE) { + throw new RuntimeException("No constructor for class "+lClass.getCanonicalName()); + } + + Set lReachableObjects = new HashSet<>(); + mReach.put(aObject, lReachableObjects); + + Map lProps = getProperties(aObject); + + for (Map.Entry iME: lProps.entrySet()) { + Object lV = iME.getValue(); + if (!sTypeMap.containsKey(lV.getClass())) { + add(lV); // process object if necessary + iME.setValue(mDBWOC.getId(lV)); // replace object by ID + mColumnSQLType.put(iME.getKey(), "INTEGER"); // column type + lReachableObjects.add(lV); // add object to reachable list + } else mColumnSQLType.put(iME.getKey(), sTypeMap.get(lV.getClass())); + } + + lProps.put(DBWrap.sJID, mDBWOC.getId(aObject)); + lProps.put("_CLASS_", lClass.getCanonicalName()); + + if (mNewObjects.contains(aObject)) mInserts.put(aObject, lProps); + else mUpdates.put(aObject, lProps); + } + + @Override + protected void onEndingRun(int aStart, int aPos) { + // saturate + Set lReachChanged = new HashSet<>(mReach.keySet()); + while (lReachChanged.size() > 0) { + Set lNewReachChanged = new HashSet<>(); + for (Object iKey : lReachChanged) { + Set lSet = mReach.get(iKey); + boolean lSetUpdated = false; + Set lNew = new HashSet<>(lSet); + for (Object iO : lSet) if (lNew.addAll(mReach.get(iO))) lSetUpdated = true; + if (lSetUpdated) { + lSet.clear(); + lSet.addAll(lNew); + lNewReachChanged.add(iKey); + } + } + lReachChanged = lNewReachChanged; + } + + mReach.forEach((aO, aSet) -> { + if (aSet.size() > 0) { + Object[] lObs = aSet.toArray(); + int lNew[] = new int[lObs.length]; + for (int i = 0; i < lNew.length; i++) lNew[i] = mDBWOC.getId(lObs[i]); + L.i("Reach {} -> {}", aSet, lNew); + if (mInserts.containsKey(aO)) mInserts.get(aO).put("_SUBS_", Utils.toByteArray(lNew)); + else mUpdates.get(aO).put("_SUBS_", Utils.toByteArray(lNew)); + } + }); + + L.i("Reach: {}", mReach); + } + + protected Map getProperties(Object aObject) { + Map lProps = new HashMap<>(); + Class lC = aObject.getClass(); + while (lC != Object.class) { + lProps.put(getShortClassname(lC), true); + if (aObject instanceof Collection && lC.getCanonicalName().startsWith("java.")) { + Collection lColl = (Collection) aObject; + Object[] lObjects = new Object[2*lColl.size()]; + Iterator iIt = lColl.iterator(); + for (int i=0; i mNewObjects = new HashSet<>(); + Map> mReach =new HashMap<>(); + Map> mInserts =new HashMap<>(); + Map> mUpdates =new HashMap<>(); + Map mColumnSQLType =new HashMap<>(); + + static final Map, String> sTypeMap = Utils.HashMapOf( + Boolean.class, "BOOLEAN", + Integer.class, "INTEGER", + Long.class, "BIGINT", + byte[].class, "VARBINARY", + Blob.class, "BLOB", + String.class, "VARCHAR" ); +} diff --git a/src/main/java/fr/doap/jdb/TestSupport.java b/src/main/java/fr/doap/jdb/TestSupport.java deleted file mode 100644 index 604e813..0000000 --- a/src/main/java/fr/doap/jdb/TestSupport.java +++ /dev/null @@ -1,5 +0,0 @@ -package fr.doap.jdb; - -public class TestSupport { - -} diff --git a/src/main/java/fr/doap/jdb/impl/ClassAnalyzer.java b/src/main/java/fr/doap/jdb/impl/ClassAnalyzer.java index 38d40a7..420909c 100644 --- a/src/main/java/fr/doap/jdb/impl/ClassAnalyzer.java +++ b/src/main/java/fr/doap/jdb/impl/ClassAnalyzer.java @@ -8,31 +8,39 @@ import java.util.*; abstract public class ClassAnalyzer { - abstract public Map analyze(Class aClass); - public boolean isApplicable(Class aClass) { return true; } + public boolean processSuperclass(Class aClass) { return true; } + + abstract public void analyze(Class aClass, Map aMap); + static public class FieldCA extends ClassAnalyzer { @Override - public Map analyze(Class aClass) { - Map lMap = new HashMap<>(); + public void analyze(Class aClass, Map aMap) { try { Field[] lFields = aClass.getDeclaredFields(); - for (Field iF: lFields) lMap.put(iF.getName(),new PropGetterSetter.FieldGS(iF)); + for (Field iF: lFields) { + if (iF.trySetAccessible()) { + PropGetterSetter lPGS = new PropGetterSetter.FieldGS(iF); + if (lPGS != null) aMap.put(iF.getName(), lPGS); + } + } } catch (Exception aE) { L.t(aE); } - return lMap; } } static public class CollectionCA extends ClassAnalyzer { - @Override - public Map analyze(Class aClass) { - return null; - } - @Override public boolean isApplicable(Class aClass) { - return Collection.class.isAssignableFrom(aClass); + return (Collection.class.isAssignableFrom(aClass) && aClass.getCanonicalName().startsWith("java.")); + } + + @Override + public boolean processSuperclass(Class aClass) { return false; } + + @Override + public void analyze(Class aClass, Map aMap) { + aMap.put("_CONTENT_", new PropGetterSetter.CollectionGS()); } } } diff --git a/src/main/java/fr/doap/jdb/impl/ClassInfos.java b/src/main/java/fr/doap/jdb/impl/ClassInfos.java index 5bc1a6c..db202d8 100644 --- a/src/main/java/fr/doap/jdb/impl/ClassInfos.java +++ b/src/main/java/fr/doap/jdb/impl/ClassInfos.java @@ -9,21 +9,19 @@ public class ClassInfos { static public Map getProps(Object aObject) { Map lMap = new HashMap<>(); ClassInfos lCI = get(aObject.getClass()); - if (lCI != null) { - lCI.mPropsGetterSetter.forEach((aKey, aGS) -> { - lMap.put(aKey, aGS.get(aObject)) - }); - } + if (lCI != null) lCI.mPropsGetterSetter.forEach((aKey, aGS) -> lMap.put(aKey, aGS.get(aObject))); return lMap; } + static public void setProps(Map aMap, Object aObject) { + ClassInfos lCI = get(aObject.getClass()); + if (lCI != null) lCI.mPropsGetterSetter.forEach((aKey, aGS) -> aGS.set(aObject, aMap.get(aKey))); + } + static public ClassInfos get(Class aClass) { if ((aClass != null)&&(aClass != Object.class)) { ClassInfos lCI = sInfosMap.get(aClass); - if (lCI == null) { - lCI = new ClassInfos(aClass); - sInfosMap.put(aClass, lCI); - } + if (lCI == null) lCI = new ClassInfos(aClass); return lCI; } return null; @@ -32,24 +30,30 @@ public class ClassInfos { ClassInfos(Class aClass) { try { sInfosMap.put(aClass, this); - Class lSuper = aClass.getSuperclass(); - if (lSuper != Object.class) { - ClassInfos lCI = get(lSuper); - mPropsGetterSetter.putAll(lCI.mPropsGetterSetter); - } + ClassAnalyzer lCA = null; for (ClassAnalyzer iCA: sClassAnalyzers) { if (iCA.isApplicable(aClass)) { - mPropsGetterSetter.putAll(iCA.analyze(aClass)); + lCA = iCA; break; } } + if (lCA != null) { + if (lCA.processSuperclass(aClass)) { + Class lSuper = aClass.getSuperclass(); + if ((lSuper != null)&&(lSuper != Object.class)) { + ClassInfos lCI = get(lSuper); + mPropsGetterSetter.putAll(lCI.mPropsGetterSetter); + } + } + lCA.analyze(aClass, mPropsGetterSetter); + } } catch (Exception aE) { L.t(aE); } } + public Map getGS() { return mPropsGetterSetter; } + private Map mPropsGetterSetter = new HashMap<>(); - private Map mPropsGetterSetter = new HashMap<>(); - - static Map, ClassInfos> sInfosMap = new HashMap<>(); - static List sClassAnalyzers = new ArrayList<>(List.of(new ClassAnalyzer.Collection(), new ClassAnalyzer.Field())); + static Map, ClassInfos> sInfosMap = new HashMap<>(); + static List sClassAnalyzers = new ArrayList<>(List.of(new ClassAnalyzer.CollectionCA(), new ClassAnalyzer.FieldCA())); } diff --git a/src/main/java/fr/doap/jdb/impl/PropGetterSetter.java b/src/main/java/fr/doap/jdb/impl/PropGetterSetter.java index 654a6bf..dfb13bd 100644 --- a/src/main/java/fr/doap/jdb/impl/PropGetterSetter.java +++ b/src/main/java/fr/doap/jdb/impl/PropGetterSetter.java @@ -1,25 +1,46 @@ package fr.doap.jdb.impl; +import fr.doap.jdb.utils.*; import fr.doap.slog.*; import java.lang.reflect.*; +import java.util.*; -interface PropGetterSetter { +abstract +public class PropGetterSetter { - Class getType(); + abstract public Class getType(); - Object get(Object aObject); - void set(Object aObject, Object aValue); + abstract public Object get(Object aObject); + abstract public void set(Object aObject, Object aValue); - static public class FieldGS implements PropGetterSetter { - FieldGS(Field aF) { mField=aF; } + static public class FieldGS extends PropGetterSetter { + + public FieldGS(Field aF) { mField=aF; } public Class getType() { return mField.getType(); } public Object get(Object aObject) { try { return mField.get(aObject); } catch (Exception aE) { L.t(aE); } return null; } + public void set(Object aObject, Object aValue) { try { mField.set(aObject, aValue); } catch (Exception aE) { L.t(aE); }} Field mField; } -} + + + static public class CollectionGS extends PropGetterSetter { + + public Class getType() { return byte[].class; } + + public Object get(Object aObject) { + Iterator lIt = ((Collection)aObject).iterator(); + return null; + } + + + public void set(Object aObject, Object aValue) { + + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/doap/jdb/TestC.java b/src/main/java/fr/doap/jdb/tests/TestC.java similarity index 83% rename from src/main/java/fr/doap/jdb/TestC.java rename to src/main/java/fr/doap/jdb/tests/TestC.java index 50551be..f05e6b8 100644 --- a/src/main/java/fr/doap/jdb/TestC.java +++ b/src/main/java/fr/doap/jdb/tests/TestC.java @@ -1,4 +1,4 @@ -package fr.doap.jdb; +package fr.doap.jdb.tests; public class TestC { int mInt = 3; diff --git a/src/main/java/fr/doap/jdb/TestC2.java b/src/main/java/fr/doap/jdb/tests/TestC2.java similarity index 86% rename from src/main/java/fr/doap/jdb/TestC2.java rename to src/main/java/fr/doap/jdb/tests/TestC2.java index 0b84215..5b3f112 100644 --- a/src/main/java/fr/doap/jdb/TestC2.java +++ b/src/main/java/fr/doap/jdb/tests/TestC2.java @@ -1,4 +1,4 @@ -package fr.doap.jdb; +package fr.doap.jdb.tests; import java.util.*; diff --git a/src/main/java/fr/doap/jdb/TestD.java b/src/main/java/fr/doap/jdb/tests/TestD.java similarity index 65% rename from src/main/java/fr/doap/jdb/TestD.java rename to src/main/java/fr/doap/jdb/tests/TestD.java index b7b9163..5080aa2 100644 --- a/src/main/java/fr/doap/jdb/TestD.java +++ b/src/main/java/fr/doap/jdb/tests/TestD.java @@ -1,4 +1,4 @@ -package fr.doap.jdb; +package fr.doap.jdb.tests; public class TestD { String mS = "mon test D"; diff --git a/src/main/java/fr/doap/jdb/utils/DBWOC.java b/src/main/java/fr/doap/jdb/utils/DBWOC.java new file mode 100644 index 0000000..935cb23 --- /dev/null +++ b/src/main/java/fr/doap/jdb/utils/DBWOC.java @@ -0,0 +1,24 @@ +package fr.doap.jdb.utils; + +public class DBWOC extends WeakObjectCache { + + public DBWOC setNextID(int aNextID) { + mNextID =aNextID; + return this; + } + + public int nextID() { return mNextID; } + + public int addNew(Object aObject) { + int lID = getId(aObject); + if (lID == -1) { + lID = mNextID; + put(lID, aObject); + mNextID++; + } + return lID; + } + + + protected int mNextID; +} diff --git a/src/main/java/fr/doap/jdb/utils/ObjectVisitor.java b/src/main/java/fr/doap/jdb/utils/ObjectVisitor.java new file mode 100644 index 0000000..71db979 --- /dev/null +++ b/src/main/java/fr/doap/jdb/utils/ObjectVisitor.java @@ -0,0 +1,79 @@ +package fr.doap.jdb.utils; + +import java.util.*; +import java.util.function.*; + +public class ObjectVisitor { + + protected boolean onCheckObjectBeforeAdding(Object aObject, int aPos) { return true; } + protected void onObjectAdded(Object aObject, int aPos) {} + + protected void onStartingRun(int aPos) {} + protected void onProcessingObject(Object aObject, int aPos) {} + protected void onEndingRun(int aStart, int aPos) {} + + public void add(Consumer aCallback, Object ... aObjects) { + boolean lNeedRun = false; + if (aObjects != null) { + for (Object iO : aObjects) { + if (iO != null) { + if (!mProcessingList.contains(iO)) { + if (onCheckObjectBeforeAdding(iO, mProcessingList.size())) { + lNeedRun = true; + mProcessingList.add(iO); + onObjectAdded(iO, mProcessingList.size()-1); + } + } + Set> lCallbacks = mPostProcessCallback.get(iO); + if (lCallbacks == null) { + lCallbacks = new HashSet<>(); + mPostProcessCallback.put(iO, lCallbacks); + } + lCallbacks.add(aCallback); + } + } + if (lNeedRun) run(); + } + } + + public void add(Object ... aObjects) { + boolean lNeedRun = false; + if (aObjects != null) { + for (Object iO : aObjects) { + if (iO != null) { + if (!mProcessingList.contains(iO)) { + if (onCheckObjectBeforeAdding(iO, mProcessingList.size())) { + lNeedRun = true; + mProcessingList.add(iO); + onObjectAdded(iO, mProcessingList.size()-1); + } + } + } + } + if (lNeedRun && !mRunning) run(); + } + } + + protected void run() { + mRunning = true; + int lStartPos = mPos; + onStartingRun(mPos); + while (mPos < mProcessingList.size()) { + Object lO = mProcessingList.get(mPos); + onProcessingObject(lO, mPos); + mPos++; + } + for (int iP=mPos-1; iP>=lStartPos; iP--) { + Object lO = mProcessingList.get(iP); + Set> lCallbacks = mPostProcessCallback.get(lO); + if (lCallbacks != null) lCallbacks.forEach(aCallback -> aCallback.accept(lO)); + } + onEndingRun(lStartPos, mPos); + mRunning = false; + } + + protected boolean mRunning = false; + protected int mPos = 0; + protected List mProcessingList = new ArrayList<>(); + protected Map>> mPostProcessCallback = new HashMap<>(); +} diff --git a/src/main/java/fr/doap/jdb/Utils.java b/src/main/java/fr/doap/jdb/utils/Utils.java similarity index 89% rename from src/main/java/fr/doap/jdb/Utils.java rename to src/main/java/fr/doap/jdb/utils/Utils.java index 8675686..55e4dcc 100644 --- a/src/main/java/fr/doap/jdb/Utils.java +++ b/src/main/java/fr/doap/jdb/utils/Utils.java @@ -1,4 +1,4 @@ -package fr.doap.jdb; +package fr.doap.jdb.utils; import fr.doap.slog.*; @@ -22,7 +22,7 @@ public class Utils { */ } - static byte[] toByteArray(final Object obj) { + static public byte[] toByteArray(final Object obj) { ByteArrayOutputStream lBAOS = new ByteArrayOutputStream(); try (ObjectOutputStream lOOS = new ObjectOutputStream(lBAOS)) { lOOS.writeObject(obj); @@ -32,7 +32,7 @@ public class Utils { return null; } - static Object fromByteArray(byte[] bytes) { + static public Object fromByteArray(byte[] bytes) { ByteArrayInputStream lBAIS = new ByteArrayInputStream(bytes); try (ObjectInput lOI = new ObjectInputStream(lBAIS)) { return lOI.readObject(); diff --git a/src/main/java/fr/doap/jdb/WeakObjectCache.java b/src/main/java/fr/doap/jdb/utils/WeakObjectCache.java similarity index 79% rename from src/main/java/fr/doap/jdb/WeakObjectCache.java rename to src/main/java/fr/doap/jdb/utils/WeakObjectCache.java index 21815a8..9a64bbb 100644 --- a/src/main/java/fr/doap/jdb/WeakObjectCache.java +++ b/src/main/java/fr/doap/jdb/utils/WeakObjectCache.java @@ -1,4 +1,4 @@ -package fr.doap.jdb; +package fr.doap.jdb.utils; import java.lang.ref.*; import java.util.*; @@ -29,6 +29,6 @@ public class WeakObjectCache { mObjectCache.clear(); } - Map mIDs = new WeakHashMap<>(); - Map> mObjectCache = new TreeMap<>(); + Map mIDs = new WeakHashMap<>(); + Map> mObjectCache = new TreeMap<>(); } \ No newline at end of file diff --git a/src/main/java/fr/doap/jdb/utils/proxies/ListProxy.java b/src/main/java/fr/doap/jdb/utils/proxies/ListProxy.java new file mode 100644 index 0000000..6510cb2 --- /dev/null +++ b/src/main/java/fr/doap/jdb/utils/proxies/ListProxy.java @@ -0,0 +1,27 @@ +package fr.doap.jdb.utils.proxies; + +import fr.doap.slog.*; + +import java.lang.reflect.*; +import java.util.*; + +public class ListProxy implements InvocationHandler { + + static public T getProxy(T aObject) { + return (T) Proxy.newProxyInstance(aObject.getClass().getClassLoader(), new Class[]{ List.class }, new ListProxy(aObject)); + } + + private ListProxy(List aList) { mList=aList; } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (!mLoaded) { + mLoaded = true; + L.i("Loading list values... DONE !"); + } else L.t("Values already loaded"); + return method.invoke(mList, args); + } + + private boolean mLoaded = false; + private List mList; +} diff --git a/src/main/java/fr/doap/trash/DBWrapper.java b/src/main/java/fr/doap/trash/DBWrapper.java index 91d003f..bc748d0 100644 --- a/src/main/java/fr/doap/trash/DBWrapper.java +++ b/src/main/java/fr/doap/trash/DBWrapper.java @@ -1,6 +1,6 @@ package fr.doap.trash; -import fr.doap.jdb.*; +import fr.doap.jdb.utils.*; import fr.doap.slog.*; import java.sql.*; @@ -50,7 +50,7 @@ public class DBWrapper { } protected Connection mConnection; - protected WeakObjectCache mWeakObjectCache = new WeakObjectCache(); + protected WeakObjectCache mWeakObjectCache = new WeakObjectCache(); protected ProcessingQueue mSQLQueue; interface SQLAbstractCommand { diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 2ed22bc..460d1cf 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -8,4 +8,6 @@ module fr.doap.jdb { exports fr.doap.jdb; exports fr.doap.trash; exports fr.doap.jdb.impl; + exports fr.doap.jdb.tests; + exports fr.doap.jdb.utils; } \ No newline at end of file diff --git a/src/test/java/fr/doap/jdb/ClassStructTest.java b/src/test/java/fr/doap/jdb/ClassStructTest.java new file mode 100644 index 0000000..b2c8892 --- /dev/null +++ b/src/test/java/fr/doap/jdb/ClassStructTest.java @@ -0,0 +1,20 @@ +package fr.doap.jdb; + +import fr.doap.jdb.tests.*; +import fr.doap.jdb.utils.*; +import fr.doap.slog.*; +import org.junit.jupiter.api.*; + +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ClassStructTest { + + @Test + public void test1() { + TestC lTC2 = new TestC2(); + Objects2Maps lO2M = new Objects2Maps(new DBWOC()); + lO2M.add(lTC2); + L.i("SQL Types: {}", lO2M.getColumnSQLTypes()); + L.i("Inserts: {}", lO2M.getInserts()); + L.i("Updates: {}", lO2M.getUpdates()); + } +} diff --git a/src/test/java/fr/doap/jdb/JDBTest.java b/src/test/java/fr/doap/jdb/JDBTest.java index fd2c89c..26903bb 100644 --- a/src/test/java/fr/doap/jdb/JDBTest.java +++ b/src/test/java/fr/doap/jdb/JDBTest.java @@ -1,5 +1,6 @@ package fr.doap.jdb; +import fr.doap.jdb.tests.*; import fr.doap.slog.*; import org.junit.jupiter.api.*; @@ -19,7 +20,7 @@ public class JDBTest { JDB.save(new TestC()); JDB.save(new TestD()); - JDB.sJDB.mWeakObjectCache.clear(); + JDB.sJDB.mDBWOC.clear(); JDB.sJDB.mDBWrap.query("SELECT * FROM OBJECTS").forEach(aMap -> L.i(aMap.toString())); diff --git a/src/test/java/fr/doap/jdb/ProxyTest.java b/src/test/java/fr/doap/jdb/ProxyTest.java new file mode 100644 index 0000000..2e7c31f --- /dev/null +++ b/src/test/java/fr/doap/jdb/ProxyTest.java @@ -0,0 +1,22 @@ +package fr.doap.jdb; + +import fr.doap.jdb.utils.proxies.*; +import fr.doap.slog.*; +import org.junit.jupiter.api.*; + +import java.util.*; + +public class ProxyTest { + + @Test + public void test1() { + List lS = new ArrayList<>(List.of("test 1", "test 2", "test 3")); + List lProx = ListProxy.getProxy(lS); + List lProx2 = ListProxy.getProxy(lS); + L.i("Original #1: {}", lS.get(1)); + L.i("Proxy1 empty: {}", lProx.isEmpty()); + L.i("Proxy1 #1: {}", lProx.get(1)); + L.i("Proxy2 size: {}", lProx2.size()); + L.i("Proxy2 #2: {}", lProx2.get(2)); + } +}