All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.stephanenicolas.ormgap.OrmLiteConfigUtil Maven / Gradle / Ivy

Go to download

A Gradle plugin for Android to generate an ORMLite configuration file and boost DAOs creations.

There is a newer version: 3.0.5
Show newest version
package com.github.stephanenicolas.ormgap;

import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.db.SqliteAndroidDatabaseType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.DatabaseFieldConfig;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;
import com.j256.ormlite.table.DatabaseTableConfig;
import com.j256.ormlite.table.DatabaseTableConfigLoader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Database configuration file helper class that is used to write a configuration file into the raw resource
 * sub-directory to speed up DAO creation.
 *
 * 

* With help from the user list and especially Ian Dees, we discovered that calls to annotation methods in Android are * _very_ expensive because Method.equals() was doing a huge toString(). This was causing folks to see 2-3 seconds * startup time when configuring 10-15 DAOs because of 1000s of calls to @DatabaseField methods. See this Android bug report. *

* *

* I added this utility class which writes a configuration file into the raw resource "res/raw" directory inside of your * project containing the table and field names and associated details. This file can then be loaded into the * {@link DaoManager} with the help of the * {@link OrmLiteSqliteOpenHelper#OrmLiteSqliteOpenHelper(android.content.Context, String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, int)} * constructor. This means that you can configure your classes _without_ any runtime calls to annotations. It seems * significantly faster. *

* *

* WARNING: Although this is fast, the big problem is that you have to remember to regenerate the config file * whenever you edit one of your database classes. There is no way that I know of to do this automagically. *

* * @author graywatson */ public class OrmLiteConfigUtil { /** * Resource directory name that we are looking for. */ protected static final String RESOURCE_DIR_NAME = "res"; /** * Raw directory name that we are looking for. */ protected static final String RAW_DIR_NAME = "raw"; public static final String HELP_COMMAND = "--help"; /** * Maximum recursion level while we are looking for source files. */ protected static int maxFindSourceLevel = 20; private static final DatabaseType databaseType = new SqliteAndroidDatabaseType(); /** * A call through to {@link #writeConfigFile(String)} taking the file name from the single command line argument. */ public static void main(String[] args) throws Exception { System.out.println("OrmLiteConfigUtil active"); if (args.length > 2) { throw new IllegalArgumentException("TODO review that : Main can take 1 or 2 file-name argument."); } if (args.length == 0 || args[0].equals(HELP_COMMAND)) { System.out.println("OrmLiteConfigUtil is a Java app that can create ORM Lite configuration to boost ORMLite performances.\n"); System.out.println("Usages: .\n"); System.out.println("* ..OrmLiteConfigUtil .\n"); System.out.println(" will generate the ORMLite config file and scan current folder for classes and res/raw dir.\n"); System.out.println("* ..OrmLiteConfigUtil .\n"); System.out.println(" will generate the ORMLite config file and scan the search directory for classes and res/raw dir.\n"); } if (args.length == 1) { String configFileName = args[0]; writeConfigFile(configFileName); } else { File configFile = new File(args[0]); File searchDir = new File(args[1]); writeConfigFile(configFile, searchDir); } System.out.println("OrmLiteConfigUtil done"); } /** * Finds the annotated classes in the current directory or below and writes a configuration file to the file-name in * the raw folder. */ public static void writeConfigFile(String fileName) throws SQLException, IOException { List> classList = new ArrayList>(); findAnnotatedClasses(classList, new File("."), 0); writeConfigFile(fileName, classList.toArray(new Class[classList.size()])); } /** * Writes a configuration fileName in the raw directory with the configuration for classes. */ public static void writeConfigFile(String fileName, Class[] classes) throws SQLException, IOException { File rawDir = findRawDir(new File(".")); if (rawDir == null) { System.err.println("Could not find " + RAW_DIR_NAME + " directory which is typically in the " + RESOURCE_DIR_NAME + " directory"); } else { File configFile = new File(rawDir, fileName); writeConfigFile(configFile, classes); } } /** * Finds the annotated classes in the current directory or below and writes a configuration file. */ public static void writeConfigFile(File configFile) throws SQLException, IOException { writeConfigFile(configFile, new File(".")); } /** * Finds the annotated classes in the specified search directory or below and writes a configuration file. */ public static void writeConfigFile(File configFile, File searchDir) throws SQLException, IOException { List> classList = new ArrayList>(); findAnnotatedClasses(classList, searchDir, 0); writeConfigFile(configFile, classList.toArray(new Class[classList.size()])); } /** * Write a configuration file with the configuration for classes. */ public static void writeConfigFile(File configFile, Class[] classes) throws SQLException, IOException { System.out.println("Writing configurations to " + configFile.getAbsolutePath()); writeConfigFile(new FileOutputStream(configFile), classes); } /** * Write a configuration file to an output stream with the configuration for classes. */ public static void writeConfigFile(OutputStream outputStream, File searchDir) throws SQLException, IOException { List> classList = new ArrayList>(); findAnnotatedClasses(classList, searchDir, 0); writeConfigFile(outputStream, classList.toArray(new Class[classList.size()])); } /** * Write a configuration file to an output stream with the configuration for classes. */ public static void writeConfigFile(OutputStream outputStream, Class[] classes) throws SQLException, IOException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream), 4096); try { for (Class clazz : classes) { writeConfigForTable(writer, clazz); } // NOTE: done is here because this is public System.out.println("Done."); } finally { writer.close(); } } /** * Look for the resource-directory in the current directory or the directories above. Then look for the * raw-directory underneath the resource-directory. */ protected static File findRawDir(File dir) { for (int i = 0; dir != null && i < 20; i++) { File rawDir = findResRawDir(dir); if (rawDir != null) { return rawDir; } dir = dir.getParentFile(); } return null; } private static void findAnnotatedClasses(List> classList, File dir, int level) throws SQLException, IOException { for (File file : dir.listFiles()) { if (file.isDirectory()) { // recurse if we aren't deep enough if (level < maxFindSourceLevel) { findAnnotatedClasses(classList, file, level + 1); } continue; } // skip non .java files if (!file.getName().endsWith(".java")) { continue; } String packageName = getPackageOfClass(file); if (packageName == null) { System.err.println("Could not find package name for: " + file); continue; } // get the filename and cut off the .java String name = file.getName(); name = name.substring(0, name.length() - ".java".length()); String className = packageName + "." + name; Class clazz; try { clazz = Class.forName(className); } catch (Throwable t) { // amazingly, this sometimes throws an Error System.err.println("Could not load class file for: " + file); System.err.println(" " + t); continue; } if (classHasAnnotations(clazz)) { classList.add(clazz); } // handle inner classes try { for (Class innerClazz : clazz.getDeclaredClasses()) { if (classHasAnnotations(innerClazz)) { classList.add(innerClazz); } } } catch (Throwable t) { // amazingly, this sometimes throws an Error System.err.println("Could not load inner classes for: " + clazz); System.err.println(" " + t); continue; } } } private static void writeConfigForTable(BufferedWriter writer, Class clazz) throws SQLException, IOException { String tableName = DatabaseTableConfig.extractTableName(clazz); List fieldConfigs = new ArrayList(); // walk up the classes finding the fields try { for (Class working = clazz; working != null; working = working.getSuperclass()) { for (Field field : working.getDeclaredFields()) { DatabaseFieldConfig fieldConfig = DatabaseFieldConfig.fromField(databaseType, tableName, field); if (fieldConfig != null) { fieldConfigs.add(fieldConfig); } } } } catch (Error e) { System.err.println("Skipping " + clazz + " because we got an error finding its definition: " + e.getMessage()); return; } if (fieldConfigs.isEmpty()) { System.out.println("Skipping " + clazz + " because no annotated fields found"); return; } @SuppressWarnings({"rawtypes", "unchecked"}) DatabaseTableConfig tableConfig = new DatabaseTableConfig(clazz, tableName, fieldConfigs); DatabaseTableConfigLoader.write(writer, tableConfig); writer.append("#################################"); writer.newLine(); System.out.println("Wrote config for " + clazz); } private static boolean classHasAnnotations(Class clazz) { while (clazz != null) { if (clazz.getAnnotation(DatabaseTable.class) != null) { return true; } Field[] fields; try { fields = clazz.getDeclaredFields(); } catch (Throwable t) { // amazingly, this sometimes throws an Error System.err.println("Could not load get delcared fields from: " + clazz); System.err.println(" " + t); return false; } for (Field field : fields) { if (field.getAnnotation(DatabaseField.class) != null || field.getAnnotation(ForeignCollectionField.class) != null) { return true; } } try { clazz = clazz.getSuperclass(); } catch (Throwable t) { // amazingly, this sometimes throws an Error System.err.println("Could not get super class for: " + clazz); System.err.println(" " + t); return false; } } return false; } /** * Returns the package name of a file that has one of the annotations we are looking for. * * @return Package prefix string or null or no annotations. */ private static String getPackageOfClass(File file) throws IOException { BufferedReader reader = new BufferedReader(new FileReader(file)); try { while (true) { String line = reader.readLine(); if (line == null) { return null; } if (line.contains("package")) { String[] parts = line.split("[ \t;]"); if (parts.length > 1 && parts[0].equals("package")) { return parts[1]; } } } } finally { reader.close(); } } /** * Look for the resource directory with raw beneath it. */ private static File findResRawDir(File dir) { for (File file : dir.listFiles()) { if (file.getName().equals(RESOURCE_DIR_NAME) && file.isDirectory()) { File[] rawFiles = file.listFiles(new FileFilter() { public boolean accept(File file) { return file.getName().equals(RAW_DIR_NAME) && file.isDirectory(); } }); if (rawFiles.length == 1) { return rawFiles[0]; } } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy