<commit_before>package org.commcare.models.database;

import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.util.Log;

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;

import org.commcare.models.AndroidPrototypeFactory;
import org.commcare.modern.database.DatabaseHelper;
import org.javarosa.core.util.PrefixTree;
import org.javarosa.core.util.externalizable.PrototypeFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import dalvik.system.DexFile;

public class DbUtil {
    private static final String TAG = DbUtil.class.getSimpleName();
    public final static String orphanFileTableName = "OrphanedFiles";

    private static PrototypeFactory factory;

    public static void setDBUtilsPrototypeFactory(PrototypeFactory factory) {
        DbUtil.factory = factory;
    }

    /**
     * Basically this is our PrototypeManager for Android
     */
    public static PrototypeFactory getPrototypeFactory(Context c) {
        if (factory != null) {
            return factory;
        }

        PrefixTree tree = new PrefixTree();

        try {
            List<String> classes = getClasses(new String[]{"org.javarosa", "org.commcare"}, c);
            for (String cl : classes) {
                tree.addString(cl);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        factory = new AndroidPrototypeFactory(tree);
        return factory;

    }

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     */
    @SuppressWarnings("unchecked")
    private static List<String> getClasses(String[] packageNames, Context c)
            throws IOException {
        ArrayList<String> classNames = new ArrayList<>();

        String zpath = c.getApplicationInfo().sourceDir;


        if (zpath == null) {
            zpath = "/data/app/org.commcare.android.apk";
        }

        DexFile df = new DexFile(new File(zpath));
        for (Enumeration<String> en = df.entries(); en.hasMoreElements(); ) {
            String cn = en.nextElement();
            try {
                for (String packageName : packageNames) {

                    if (cn.startsWith(packageName) && !cn.startsWith("org.commcare.dalvik") && !cn.contains(".test.") && !cn.contains("readystatesoftware")) {

                        //TODO: These optimize by preventing us from statically loading classes we don't need, but they take a _long_ time to run.
                        //Maybe we should skip this and/or roll it into initializing the factory itself.
                        Class prototype = Class.forName(cn);
                        if (prototype.isInterface()) {
                            continue;
                        }
                        boolean emptyc = false;
                        for (Constructor<?> cons : prototype.getConstructors()) {
                            if (cons.getParameterTypes().length == 0) {
                                emptyc = true;
                            }
                        }
                        if (!emptyc) {
                            continue;
                        }
                        classNames.add(cn);
                    }
                }
            } catch (Error | Exception e) {

            }
        }

        return classNames;
    }

    /**
     * Provides a hook for Sqllite databases to be able to try to migrate themselves in place
     * from the writabledatabase method. Required due to SqlCipher making it incredibly difficult
     * and obnoxious to determine when your databases need an upgrade, so we'll just try to run
     * one any time the method would have crashed anyway.
     *
     * Will crash if this update doesn't work, so no return is needed
     */
    public static void trySqlCipherDbUpdate(String key, Context context, String dbName) {
        //There's no clear way how to tell whether this call is the invalid db version
        //because SqlLite didn't actually provide that info (thanks!), but we can
        //test manually
<commit_msg>Include odk in prototype factory class setup<commit_after>package org.commcare.models.database;

import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.util.Log;

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;

import org.commcare.models.AndroidPrototypeFactory;
import org.commcare.modern.database.DatabaseHelper;
import org.javarosa.core.util.PrefixTree;
import org.javarosa.core.util.externalizable.Externalizable;
import org.javarosa.core.util.externalizable.PrototypeFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import dalvik.system.DexFile;

public class DbUtil {
    private static final String TAG = DbUtil.class.getSimpleName();
    public final static String orphanFileTableName = "OrphanedFiles";

    private static PrototypeFactory factory;

    public static void setDBUtilsPrototypeFactory(PrototypeFactory factory) {
        DbUtil.factory = factory;
    }

    /**
     * Basically this is our PrototypeManager for Android
     */
    public static PrototypeFactory getPrototypeFactory(Context c) {
        if (factory != null) {
            return factory;
        }

        PrefixTree tree = new PrefixTree();

        try {
            List<String> classes = getClasses(new String[]{"org.javarosa", "org.commcare", "org.odk.collect"}, c);
            for (String cl : classes) {
                tree.addString(cl);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        factory = new AndroidPrototypeFactory(tree);
        return factory;

    }

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     */
    @SuppressWarnings("unchecked")
    private static List<String> getClasses(String[] packageNames, Context c)
            throws IOException {
        ArrayList<String> classNames = new ArrayList<>();

        String zpath = c.getApplicationInfo().sourceDir;


        if (zpath == null) {
            zpath = "/data/app/org.commcare.android.apk";
        }

        DexFile df = new DexFile(new File(zpath));
        for (Enumeration<String> en = df.entries(); en.hasMoreElements(); ) {
            String cn = en.nextElement();
            try {
                for (String packageName : packageNames) {
                    if (cn.startsWith(packageName) && !cn.contains(".test.") && !cn.contains("readystatesoftware")) {
                        //TODO: These optimize by preventing us from statically loading classes we don't need, but they take a _long_ time to run.
                        //Maybe we should skip this and/or roll it into initializing the factory itself.
                        Class prototype = Class.forName(cn);
                        if (prototype.isInterface()) {
                            continue;
                        }
                        boolean emptyc = false;
                        for (Constructor<?> cons : prototype.getConstructors()) {
                            if (cons.getParameterTypes().length == 0) {
                                emptyc = true;
                            }
                        }
                        if (!emptyc) {
                            continue;
                        }
                        if (Externalizable.class.isAssignableFrom(prototype)) {
                            classNames.add(cn);
                        }
                    }
                }
            } catch (Error | Exception e) {

            }
        }

        return classNames;
    }

    /**
     * Provides a hook for Sqllite databases to be able to try to migrate themselves in place
     * from the writabledatabase method. Required due to SqlCipher making it incredibly difficult
     * and obnoxious to determine when your databases need an upgrade, so we'll just try to run
     * one any time the method would have crashed anyway.
     *
     * Will crash if this update doesn't work, so no return is needed
     */
    public static void trySqlCipherDbUpdate(String key, Context context, String dbName) {
        //There's no clear way how to tell whether this call is the invalid db version
        //because SqlLite didn't actually provide that info (thanks!), but we can
        //test manually

{'fraction_non_alphanumeric': 0.08448942460768706, 'fraction_numerical': 0.00022742779167614282, 'mean_word_length': 2.0017064846416384, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5037120', 'n_tokens_mistral': 2241, 'n_tokens_neox': 2144, 'n_words': 674}