From a2608c598fa5282f32ca7df92ba410d933e009eb Mon Sep 17 00:00:00 2001
From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca>
Date: Sun, 26 Nov 2023 02:36:24 -0400
Subject: [PATCH 1/6] refactor: Decompose conditional

---
 .../src/main/java/io/objectbox/Box.java       | 20 ++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/objectbox-java/src/main/java/io/objectbox/Box.java b/objectbox-java/src/main/java/io/objectbox/Box.java
index 128ffa80..ffc3e81c 100644
--- a/objectbox-java/src/main/java/io/objectbox/Box.java
+++ b/objectbox-java/src/main/java/io/objectbox/Box.java
@@ -138,14 +138,20 @@ void releaseWriter(Cursor<T> cursor) {
     }
 
     void releaseReader(Cursor<T> cursor) {
-        // NOP if TX is ongoing
-        if (activeTxCursor.get() == null) {
-            Transaction tx = cursor.getTx();
-            if (tx.isClosed() || tx.isRecycled() || !tx.isReadOnly()) {
-                throw new IllegalStateException("Illegal reader TX state");
-            }
-            tx.recycle();
+        if (activeTxCursor.get() != null) {
+            return; // NOP if TX is ongoing
         }
+
+        Transaction tx = cursor.getTx();
+        if (!isValidTransactionToRelease(tx)) {
+            throw new IllegalStateException("Illegal reader TX state");
+        }
+
+        tx.recycle();
+    }
+
+    private boolean isValidTransactionToRelease(Transaction tx) {
+        return !tx.isClosed() && !tx.isRecycled() && tx.isReadOnly();
     }
 
     /**

From e8ea8e84a7d0e370812836b985e0eac366ffbb8d Mon Sep 17 00:00:00 2001
From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca>
Date: Sun, 26 Nov 2023 02:40:22 -0400
Subject: [PATCH 2/6] refactor: Introduce explaining variable

---
 objectbox-java/src/main/java/io/objectbox/BoxStore.java | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/objectbox-java/src/main/java/io/objectbox/BoxStore.java b/objectbox-java/src/main/java/io/objectbox/BoxStore.java
index 04b45240..66cdb50e 100644
--- a/objectbox-java/src/main/java/io/objectbox/BoxStore.java
+++ b/objectbox-java/src/main/java/io/objectbox/BoxStore.java
@@ -373,16 +373,18 @@ static boolean isFileOpen(final String canonicalPath) {
     }
 
     static boolean isFileOpenSync(String canonicalPath, boolean runFinalization) {
+        int Num_Of_Retries = 5;
+        int Timeout = 100; // in millis
         synchronized (openFiles) {
             int tries = 0;
-            while (tries < 5 && openFiles.contains(canonicalPath)) {
+            while (tries < Num_Of_Retries && openFiles.contains(canonicalPath)) {
                 tries++;
                 System.gc();
                 if (runFinalization && tries > 1) System.runFinalization();
                 System.gc();
                 if (runFinalization && tries > 1) System.runFinalization();
                 try {
-                    openFiles.wait(100);
+                    openFiles.wait(Timeout);
                 } catch (InterruptedException e) {
                     // Ignore
                 }

From 81fa2366592fe813b824221c242eab976c084b70 Mon Sep 17 00:00:00 2001
From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca>
Date: Sun, 26 Nov 2023 03:10:43 -0400
Subject: [PATCH 3/6] refactor: Extract openFileOnDiffThread method

---
 .../src/main/java/io/objectbox/BoxStore.java  | 36 ++++++++++---------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/objectbox-java/src/main/java/io/objectbox/BoxStore.java b/objectbox-java/src/main/java/io/objectbox/BoxStore.java
index 66cdb50e..4b34d178 100644
--- a/objectbox-java/src/main/java/io/objectbox/BoxStore.java
+++ b/objectbox-java/src/main/java/io/objectbox/BoxStore.java
@@ -342,6 +342,23 @@ static void verifyNotAlreadyOpen(String canonicalPath) {
         }
     }
 
+
+    private static void openFileOnDiffThread(final String canonicalPath){
+            // Use a thread to avoid finalizers that block us
+        Thread thread = new Thread(() -> {
+                isFileOpenSync(canonicalPath, true);
+                openFilesCheckerThread = null; // Clean ref to itself
+            });
+            thread.setDaemon(true);
+            openFilesCheckerThread = thread;
+            thread.start();
+            try {
+                thread.join(500);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+    }
+
     /** Also retries up to 500ms to improve GC race condition situation. */
     static boolean isFileOpen(final String canonicalPath) {
         synchronized (openFiles) {
@@ -349,27 +366,14 @@ static boolean isFileOpen(final String canonicalPath) {
         }
         Thread checkerThread = BoxStore.openFilesCheckerThread;
         if (checkerThread == null || !checkerThread.isAlive()) {
-            // Use a thread to avoid finalizers that block us
-            checkerThread = new Thread(() -> {
-                isFileOpenSync(canonicalPath, true);
-                BoxStore.openFilesCheckerThread = null; // Clean ref to itself
-            });
-            checkerThread.setDaemon(true);
-
-            BoxStore.openFilesCheckerThread = checkerThread;
-            checkerThread.start();
-            try {
-                checkerThread.join(500);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
+            openFileOnDiffThread(canonicalPath);
+            synchronized (openFiles) {
+                return openFiles.contains(canonicalPath);
             }
         } else {
             // Waiting for finalizers are blocking; only do that in the thread ^
             return isFileOpenSync(canonicalPath, false);
         }
-        synchronized (openFiles) {
-            return openFiles.contains(canonicalPath);
-        }
     }
 
     static boolean isFileOpenSync(String canonicalPath, boolean runFinalization) {

From 3ccf70505b6119851a4d5a3927cf7e6d4e44240e Mon Sep 17 00:00:00 2001
From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca>
Date: Sun, 26 Nov 2023 05:20:10 -0400
Subject: [PATCH 4/6] refactor: Extract cache class

---
 .../flatbuffers/UTF8EncDecCache.java          | 18 ++++++++++++++++
 .../io/objectbox/flatbuffers/Utf8Old.java     | 21 +++++--------------
 2 files changed, 23 insertions(+), 16 deletions(-)
 create mode 100644 objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java

diff --git a/objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java b/objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java
new file mode 100644
index 00000000..384741b4
--- /dev/null
+++ b/objectbox-java/src/main/java/io/objectbox/flatbuffers/UTF8EncDecCache.java
@@ -0,0 +1,18 @@
+package io.objectbox.flatbuffers;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.StandardCharsets;
+
+public class UTF8EncDecCache {
+    final CharsetEncoder encoder;
+    final CharsetDecoder decoder;
+    CharSequence lastInput = null;
+    ByteBuffer lastOutput = null;
+
+    UTF8EncDecCache() {
+        encoder = StandardCharsets.UTF_8.newEncoder();
+        decoder = StandardCharsets.UTF_8.newDecoder();
+    }
+}
diff --git a/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java b/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java
index acb91738..e74a8721 100644
--- a/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java
+++ b/objectbox-java/src/main/java/io/objectbox/flatbuffers/Utf8Old.java
@@ -24,33 +24,22 @@
 import java.nio.charset.CoderResult;
 import java.nio.charset.StandardCharsets;
 
+
 /**
  * This class implements the Utf8 API using the Java Utf8 encoder. Use
  * Utf8.setDefault(new Utf8Old()); to use it.
  */
 public class Utf8Old extends Utf8 {
 
-  private static class Cache {
-    final CharsetEncoder encoder;
-    final CharsetDecoder decoder;
-    CharSequence lastInput = null;
-    ByteBuffer lastOutput = null;
-
-    Cache() {
-      encoder = StandardCharsets.UTF_8.newEncoder();
-      decoder = StandardCharsets.UTF_8.newDecoder();
-    }
-  }
-
-  private static final ThreadLocal<Cache> CACHE =
-      ThreadLocal.withInitial(() -> new Cache());
+  private static final ThreadLocal<UTF8EncDecCache> CACHE =
+      ThreadLocal.withInitial(() -> new UTF8EncDecCache());
 
   // Play some games so that the old encoder doesn't pay twice for computing
   // the length of the encoded string.
 
   @Override
   public int encodedLength(CharSequence in) {
-    final Cache cache = CACHE.get();
+    final UTF8EncDecCache cache = CACHE.get();
     int estimated = (int) (in.length() * cache.encoder.maxBytesPerChar());
     if (cache.lastOutput == null || cache.lastOutput.capacity() < estimated) {
       cache.lastOutput = ByteBuffer.allocate(Math.max(128, estimated));
@@ -73,7 +62,7 @@ public int encodedLength(CharSequence in) {
 
   @Override
   public void encodeUtf8(CharSequence in, ByteBuffer out) {
-    final Cache cache = CACHE.get();
+    final UTF8EncDecCache cache = CACHE.get();
     if (cache.lastInput != in) {
       // Update the lastOutput to match our input, although flatbuffer should
       // never take this branch.

From 9193521d06f6bb36014b6ed854acd24c17aabd56 Mon Sep 17 00:00:00 2001
From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca>
Date: Sun, 26 Nov 2023 05:45:34 -0400
Subject: [PATCH 5/6] refactor: pull-up alias variable to QueryConditionIml
 class

---
 .../io/objectbox/query/PropertyQueryConditionImpl.java |  6 +++---
 .../java/io/objectbox/query/QueryConditionImpl.java    | 10 ++++++++++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java b/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java
index 444fb290..0cf03455 100644
--- a/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java
+++ b/objectbox-java/src/main/java/io/objectbox/query/PropertyQueryConditionImpl.java
@@ -16,7 +16,6 @@
 public abstract class PropertyQueryConditionImpl<T> extends QueryConditionImpl<T> implements PropertyQueryCondition<T> {
     // Note: Expose for DAOcompat
     public final Property<T> property;
-    private String alias;
 
     PropertyQueryConditionImpl(Property<T> property) {
         this.property = property;
@@ -24,7 +23,7 @@ public abstract class PropertyQueryConditionImpl<T> extends QueryConditionImpl<T
 
     @Override
     public QueryCondition<T> alias(String name) {
-        this.alias = name;
+        setAlias(name);
         return this;
     }
 
@@ -32,7 +31,8 @@ public QueryCondition<T> alias(String name) {
     @Override
     public void apply(QueryBuilder<T> builder) {
         applyCondition(builder);
-        if (alias != null && alias.length() != 0) {
+        String alias = getAlias();
+        if (alias != null && !alias.isEmpty()) {
             builder.parameterAlias(alias);
         }
     }
diff --git a/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java b/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java
index 5fe8bc61..e643a755 100644
--- a/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java
+++ b/objectbox-java/src/main/java/io/objectbox/query/QueryConditionImpl.java
@@ -8,6 +8,16 @@
  */
 abstract class QueryConditionImpl<T> implements QueryCondition<T> {
 
+    private String alias;
+
+    public void setAlias(String alias) {
+        this.alias = alias;
+    }
+
+    public String getAlias() {
+        return alias;
+    }
+
     @Override
     public QueryCondition<T> and(QueryCondition<T> queryCondition) {
         return new AndCondition<>(this, (QueryConditionImpl<T>) queryCondition);

From 9d496012aa9b59e682c7934e6bac0a6f159eb2a2 Mon Sep 17 00:00:00 2001
From: Nisarg Sharadkumar Vaghela <ns897912@dal.ca>
Date: Sun, 26 Nov 2023 06:19:27 -0400
Subject: [PATCH 6/6] refactor: move field to supper class

---
 .../objectbox/relation/ArrayListFactory.java  | 16 ++++++++++++++
 .../relation/CopyOnWriteArrayListFactory.java | 16 ++++++++++++++
 .../io/objectbox/relation/ListFactory.java    | 22 +++++--------------
 .../java/io/objectbox/relation/ToMany.java    |  1 -
 4 files changed, 38 insertions(+), 17 deletions(-)
 create mode 100644 objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java
 create mode 100644 objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java

diff --git a/objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java b/objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java
new file mode 100644
index 00000000..21f1dc93
--- /dev/null
+++ b/objectbox-java/src/main/java/io/objectbox/relation/ArrayListFactory.java
@@ -0,0 +1,16 @@
+package io.objectbox.relation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class ArrayListFactory extends ListFactory {
+
+    ArrayListFactory() {
+        super(8247662514375611729L);
+    }
+
+    @Override
+    public <T> List<T> createList() {
+        return new ArrayList<>();
+    }
+}
diff --git a/objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java b/objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java
new file mode 100644
index 00000000..78326c26
--- /dev/null
+++ b/objectbox-java/src/main/java/io/objectbox/relation/CopyOnWriteArrayListFactory.java
@@ -0,0 +1,16 @@
+package io.objectbox.relation;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+class CopyOnWriteArrayListFactory extends ListFactory {
+
+    CopyOnWriteArrayListFactory() {
+        super(1888039726372206411L);
+    }
+
+    @Override
+    public <T> List<T> createList() {
+        return new CopyOnWriteArrayList<>();
+    }
+}
\ No newline at end of file
diff --git a/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java b/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java
index b7a12a98..765d2342 100644
--- a/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java
+++ b/objectbox-java/src/main/java/io/objectbox/relation/ListFactory.java
@@ -24,24 +24,14 @@
 import io.objectbox.annotation.apihint.Experimental;
 
 @Experimental
-public interface ListFactory extends Serializable {
-    <T> List<T> createList();
+public class ListFactory implements Serializable {
+    private final long serialVersionUID;
+    ListFactory(long serialVersionUID){
+        this.serialVersionUID = serialVersionUID;
 
-    class ArrayListFactory implements ListFactory {
-        private static final long serialVersionUID = 8247662514375611729L;
-
-        @Override
-        public <T> List<T> createList() {
-            return new ArrayList<>();
-        }
     }
 
-    class CopyOnWriteArrayListFactory implements ListFactory {
-        private static final long serialVersionUID = 1888039726372206411L;
-
-        @Override
-        public <T> List<T> createList() {
-            return new CopyOnWriteArrayList<>();
-        }
+    <T> List<T> createList(){
+        throw  new RuntimeException("Have to implement createList method");
     }
 }
diff --git a/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java b/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java
index b387c0df..e18089a3 100644
--- a/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java
+++ b/objectbox-java/src/main/java/io/objectbox/relation/ToMany.java
@@ -44,7 +44,6 @@
 import io.objectbox.internal.ToManyGetter;
 import io.objectbox.internal.ToOneGetter;
 import io.objectbox.query.QueryFilter;
-import io.objectbox.relation.ListFactory.CopyOnWriteArrayListFactory;
 
 import static java.lang.Boolean.TRUE;