// Path: src/main/java/no/gorman/database/DBFunctions.java
// public static Object get(Field f, Object instance) {
//     try {
//         return f.get(instance);
//     }catch (IllegalAccessException e) {
//         throw new RuntimeException(e);
//     }
// }
// 
// Path: src/main/java/no/gorman/database/DBFunctions.java
// public static List<Field> getMappedFields(Class<?> clazz) {
//     List<Field> columnFields = asList(clazz.getDeclaredFields()).stream()
//             .filter(f -> f.isAnnotationPresent(Column.class))
//             .collect(toList());
//     columnFields.forEach(f->f.setAccessible(true));
// 
//     if (clazz.getSuperclass() != null) {
//         columnFields.addAll(getMappedFields(clazz.getSuperclass()));
//     }
//     return columnFields;
// }
// 
// Path: src/main/java/no/gorman/database/DBFunctions.java
// public static <T> Field getPrimaryKeyField(T obj) {
//     return getMappedFields(obj.getClass()).stream().filter(f-> getColumn(f).getType() == PrimaryKey).findFirst().orElseThrow(() -> new IllegalArgumentException(obj.getClass().getName() + " has no primary key defined"));
// }


import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static no.gorman.database.DBFunctions.get;
import static no.gorman.database.DBFunctions.getMappedFields;
import static no.gorman.database.DBFunctions.getPrimaryKeyField;
package no.gorman.database;



public class BigBrother {

    public static interface Spy { void suspectAltered(Object suspect); }

    private static final ConcurrentMap<RowIdentifier, WeakHashMap<Spy, Boolean>> spies = new ConcurrentHashMap<>();
    private static List<WeakReference<Spy>> spyRefs = Collections.synchronizedList(new ArrayList<>());
    //Keep track of all weak references created, listen for when they are garbage collected,
    // so the main spy-map doesn't fill up with keys that have no living spies left.
    private static final ReferenceQueue<Spy> terminatedSpies = new ReferenceQueue<>();

    static {
        new Thread(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        terminatedSpies.remove();
// Path: src/main/java/no/gorman/database/DBFunctions.java
// public static Object get(Field f, Object instance) {
//     try {
//         return f.get(instance);
//     }catch (IllegalAccessException e) {
//         throw new RuntimeException(e);
//     }
// }
// 
// Path: src/main/java/no/gorman/database/DBFunctions.java
// public static List<Field> getMappedFields(Class<?> clazz) {
//     List<Field> columnFields = asList(clazz.getDeclaredFields()).stream()
//             .filter(f -> f.isAnnotationPresent(Column.class))
//             .collect(toList());
//     columnFields.forEach(f->f.setAccessible(true));
// 
//     if (clazz.getSuperclass() != null) {
//         columnFields.addAll(getMappedFields(clazz.getSuperclass()));
//     }
//     return columnFields;
// }
// 
// Path: src/main/java/no/gorman/database/DBFunctions.java
// public static <T> Field getPrimaryKeyField(T obj) {
//     return getMappedFields(obj.getClass()).stream().filter(f-> getColumn(f).getType() == PrimaryKey).findFirst().orElseThrow(() -> new IllegalArgumentException(obj.getClass().getName() + " has no primary key defined"));
// }


// Path: src/main/java/no/gorman/database/BigBrother.java
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static no.gorman.database.DBFunctions.get;
import static no.gorman.database.DBFunctions.getMappedFields;
import static no.gorman.database.DBFunctions.getPrimaryKeyField;

package no.gorman.database;



public class BigBrother {

    public static interface Spy { void suspectAltered(Object suspect); }

    private static final ConcurrentMap<RowIdentifier, WeakHashMap<Spy, Boolean>> spies = new ConcurrentHashMap<>();
    private static List<WeakReference<Spy>> spyRefs = Collections.synchronizedList(new ArrayList<>());
    //Keep track of all weak references created, listen for when they are garbage collected,
    // so the main spy-map doesn't fill up with keys that have no living spies left.
    private static final ReferenceQueue<Spy> terminatedSpies = new ReferenceQueue<>();

    static {
        new Thread(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        terminatedSpies.remove();
                        spyRefs.removeIf(spy -> spy.get() == null);