diff --git a/pom.xml b/pom.xml index 89c87e6b..062ed078 100644 --- a/pom.xml +++ b/pom.xml @@ -146,6 +146,24 @@ 1.5 1.5 + + + default-compile + + -proc:none + + com/j256/ormlite/android/processor/OrmLiteAnnotationProcessor.java + + + + + compile-everything-else + compile + + compile + + + org.apache.maven.plugins @@ -248,6 +266,33 @@ $1${version}$2 + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + package + + shade + + + + + com.squareup:javapoet + + + true + + + com.squareup.javapoet + com.j256.ormlite.android.processor.javapoet + + + + + + @@ -330,6 +375,11 @@ ${android-support-version} true + + com.squareup + javapoet + 1.0.0 + @@ -386,5 +436,11 @@ ${easymock-version} test + + com.google.testing.compile + compile-testing + 0.6 + test + diff --git a/src/main/java/com/j256/ormlite/android/annotations/Database.java b/src/main/java/com/j256/ormlite/android/annotations/Database.java new file mode 100644 index 00000000..f9623658 --- /dev/null +++ b/src/main/java/com/j256/ormlite/android/annotations/Database.java @@ -0,0 +1,135 @@ +package com.j256.ormlite.android.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This class is applied to a class derived from + * {@link com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper} and lists + * the classes representing the tables in the database. Compile-time annotation + * processing will generate a method that can be called from your constructor to + * cache table information to avoid slow reflection on Android. This + * functionality replaces table configuration files which achieved the same goal + * by manually creating a text file at build time and parsing it at runtime. + * + * Add a call to YOUR_CLASS_NAME_TableConfig.cacheTableConfigurations() to your + * constructor to make use of this functionality. You can also call + * YOUR_CLASS_NAME_TableConfig.createTables(connectionSource) from your onCreate + * to create all tables included in this annotation. + * + * For inner/nested classes, the generated class name will use underscores to + * separate the classes (e.g. package.Outer.Inner will result in + * package.Outer_Inner_TableConfig begin generated). + * + *

+ * Example (Table.java) + *

+ * + *

+ *

+ * + *
+ * @DatabaseTable
+ * class Table {
+ * 	@DatabaseField
+ * 	int field;
+ * }
+ * 
+ * + *
+ *

+ * + *

+ * Example (Outer.java) + *

+ * + *

+ *

+ * + *
+ * class Outer {
+ * 	@DatabaseTable
+ * 	class Inner {
+ * 		@DatabaseField
+ * 		int field;
+ * 	}
+ * }
+ * 
+ * + *
+ *

+ * + *

+ * Example (OpenHelper.java) + *

+ * + *

+ *

+ * + *
+ * @Database({ Table.class, Outer.Inner.class })
+ * class OpenHelper extends OrmLiteSqliteOpenHelper {
+ * 	OpenHelper(Context context, String databaseName, CursorFactory factory,
+ * 			int databaseVersion) {
+ * 		super(context, databaseName, factory, databaseVersion);
+ * 		OpenHelper_TableConfig.cacheTableConfigurations();
+ * 	}
+ * 
+ * 	@Override
+ * 	public void onCreate(SQLiteDatabase database,
+ * 			ConnectionSource connectionSource) {
+ * 		try {
+ * 			OpenHelper_TableConfig.createTables(connectionSource);
+ * 		} catch (SQLException e) {
+ * 			throw new RuntimeException(e);
+ * 		}
+ * 	}
+ * }
+ * 
+ * + *
+ *

+ * + * * + *

+ * Example (InnerOpenHelper.java) + *

+ * + *

+ *

+ * + *
+ * class Outer {
+ * 	@Database({ Table.class, Outer.Inner.class })
+ * 	class OpenHelper extends OrmLiteSqliteOpenHelper {
+ * 		OpenHelper(Context context, String databaseName, CursorFactory factory,
+ * 				int databaseVersion) {
+ * 			super(context, databaseName, factory, databaseVersion);
+ * 			Outer_OpenHelper_TableConfig.cacheTableConfigurations();
+ * 		}
+ * 
+ * 		@Override
+ * 		public void onCreate(SQLiteDatabase database,
+ * 				ConnectionSource connectionSource) {
+ * 			try {
+ * 				Outer_OpenHelper_TableConfig.createTables(connectionSource);
+ * 			} catch (SQLException e) {
+ * 				throw new RuntimeException(e);
+ * 			}
+ * 		}
+ * 	}
+ * }
+ * 
+ * + *
+ *

+ * + * @author nathancrouther + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface Database { + Class[] value(); +} diff --git a/src/main/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtil.java b/src/main/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtil.java index 52c45966..d2513305 100644 --- a/src/main/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtil.java +++ b/src/main/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtil.java @@ -52,7 +52,12 @@ *

* * @author graywatson + * + * @deprecated As of version 4.49 configuration files have been replaced by + * automatic annotation processing at compile time. + * @see com.j256.ormlite.android.annotations.Database */ +@Deprecated public class OrmLiteConfigUtil { /** diff --git a/src/main/java/com/j256/ormlite/android/apptools/OrmLiteSqliteOpenHelper.java b/src/main/java/com/j256/ormlite/android/apptools/OrmLiteSqliteOpenHelper.java index 62a3f457..323c30cc 100644 --- a/src/main/java/com/j256/ormlite/android/apptools/OrmLiteSqliteOpenHelper.java +++ b/src/main/java/com/j256/ormlite/android/apptools/OrmLiteSqliteOpenHelper.java @@ -70,7 +70,14 @@ public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFacto * called if the stored database is a different version. * @param configFileId * file-id which probably should be a R.raw.ormlite_config.txt or some static value. + * + * @deprecated As of version 4.49 configuration files have been replaced by + * automatic annotation processing at compile time. Add an + * {@link com.j256.ormlite.android.annotations.Database} + * annotation to the class that inherits from this class to + * activate annotation processing. */ + @Deprecated public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion, int configFileId) { this(context, databaseName, factory, databaseVersion, openFileId(context, configFileId)); @@ -90,7 +97,14 @@ public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFacto * called if the stored database is a different version. * @param configFile * Configuration file to be loaded. + * + * @deprecated As of version 4.49 configuration files have been replaced by + * automatic annotation processing at compile time. Add an + * {@link com.j256.ormlite.android.annotations.Database} + * annotation to the class that inherits from this class to + * activate annotation processing. */ + @Deprecated public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion, File configFile) { this(context, databaseName, factory, databaseVersion, openFile(configFile)); @@ -111,7 +125,14 @@ public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFacto * called if the stored database is a different version. * @param stream * Stream opened to the configuration file to be loaded. It will be closed when this method returns. + * + * @deprecated As of version 4.49 configuration files have been replaced by + * automatic annotation processing at compile time. Add an + * {@link com.j256.ormlite.android.annotations.Database} + * annotation to the class that inherits from this class to + * activate annotation processing. */ + @Deprecated public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion, InputStream stream) { super(context, databaseName, factory, databaseVersion); diff --git a/src/main/java/com/j256/ormlite/android/processor/FieldBindings.java b/src/main/java/com/j256/ormlite/android/processor/FieldBindings.java new file mode 100644 index 00000000..6a99b4df --- /dev/null +++ b/src/main/java/com/j256/ormlite/android/processor/FieldBindings.java @@ -0,0 +1,21 @@ +package com.j256.ormlite.android.processor; + +import java.util.List; + +class FieldBindings { + private final String fieldName; + private final List setters; + + FieldBindings(String fieldName, List setters) { + this.fieldName = fieldName; + this.setters = setters; + } + + String getFieldName() { + return fieldName; + } + + List getSetters() { + return setters; + } +} diff --git a/src/main/java/com/j256/ormlite/android/processor/OrmLiteAnnotationProcessor.java b/src/main/java/com/j256/ormlite/android/processor/OrmLiteAnnotationProcessor.java new file mode 100644 index 00000000..e6918adb --- /dev/null +++ b/src/main/java/com/j256/ormlite/android/processor/OrmLiteAnnotationProcessor.java @@ -0,0 +1,764 @@ +package com.j256.ormlite.android.processor; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.field.ForeignCollectionField; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTable; +import com.j256.ormlite.table.DatabaseTableConfig; +import com.j256.ormlite.table.TableUtils; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.WildcardTypeName; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.FilerException; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.SourceVersion; +import javax.lang.model.type.MirroredTypeException; +import javax.lang.model.type.MirroredTypesException; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +/** + * Class that is automatically run when compiling client code that automatically + * generates code to call DaoManager.addCachedDatabaseConfigs() without needing + * a config file. + * + * @author nathancrouther + */ +/* + * Eclipse doesn't like the link to rt.jar and classes therein. This is a + * spurious warning that can be ignored. It is intended to prevent referencing + * com.sun packages that may not be in every JVM, but the annotation processing + * stuff is part of JSR-269, so will always be present. + */ +@SuppressWarnings("restriction") +public final class OrmLiteAnnotationProcessor extends AbstractProcessor { + private static final String FQCN_Object = "java.lang.Object"; + private static final String FQCN_Class = "java.lang.Class"; + private static final String FQCN_OpenHelper = "com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper"; + private static final String CN_OpenHelper = "OrmLiteSqliteOpenHelper"; + + private Map> foundDatabases = new HashMap>(); + private Set foundTables = new HashSet(); + + @Override + public Set getSupportedAnnotationTypes() { + Set types = new LinkedHashSet(); + types.add(DatabaseTable.class.getCanonicalName()); + types.add(Database.class.getCanonicalName()); + return types; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + @Override + public boolean process(Set elements, + RoundEnvironment env) { + for (Element element : env + .getElementsAnnotatedWith(DatabaseTable.class)) { + DatabaseTable annotation = element + .getAnnotation(DatabaseTable.class); + if (annotation == null) { + continue; + } + + raiseNote(String.format("Processing %s class: %s", + DatabaseTable.class.getSimpleName(), + ((TypeElement) element).getQualifiedName())); + + ParsedClassName parsedClassName = new ParsedClassName(element); + + String tableName; + if (annotation.tableName() != null + && annotation.tableName().length() > 0) { + tableName = annotation.tableName(); + } else { + tableName = element.getSimpleName().toString().toLowerCase(); + } + + TableBindings table = new TableBindings(parsedClassName, tableName); + + // get all fields from this and all parents until we hit Object + TypeElement tableClassElement = (TypeElement) element; + foundTables.add(tableClassElement); + do { + for (Element child : tableClassElement.getEnclosedElements()) { + if (child.getKind().isField()) { + DatabaseField databaseField = child + .getAnnotation(DatabaseField.class); + ForeignCollectionField foreignCollectionField = child + .getAnnotation(ForeignCollectionField.class); + + if (databaseField != null) { + if (databaseField.persisted()) { + table.addField(new FieldBindings(child + .getSimpleName().toString(), + getSettersForDatabaseField( + databaseField, child.asType() + .toString()))); + } + if (foreignCollectionField != null) { + raiseError( + String.format( + "Fields cannot be annotated with both %s and %s", + DatabaseField.class + .getSimpleName(), + ForeignCollectionField.class + .getSimpleName()), + child); + } + } else if (foreignCollectionField != null) { + table.addField(new FieldBindings( + child.getSimpleName().toString(), + getSettersForForeignCollectionField(foreignCollectionField))); + } + + if (databaseField != null + && foreignCollectionField != null) { + raiseError( + String.format( + "Fields cannot be annotated with both %s and %s", + DatabaseField.class.getSimpleName(), + ForeignCollectionField.class + .getSimpleName()), child); + } + } + } + + tableClassElement = (TypeElement) processingEnv.getTypeUtils() + .asElement(tableClassElement.getSuperclass()); + } while (!tableClassElement.getQualifiedName().toString() + .equals(FQCN_Object)); + + if (table.getFields().isEmpty()) { + raiseWarning( + String.format( + "No fields annotated with %s found for class annotated with %s", + DatabaseField.class.getSimpleName(), + DatabaseTable.class.getSimpleName()), element); + } + + createTableConfigSourceFile(table, element); + } + + Set tablesCollectionElements = env + .getElementsAnnotatedWith(Database.class); + for (Element element : tablesCollectionElements) { + Database annotation = element.getAnnotation(Database.class); + if (annotation == null) { + continue; + } + + raiseNote(String.format("Processing %s class: %s", + Database.class.getSimpleName(), + ((TypeElement) element).getQualifiedName())); + + boolean derivedFromOpenHelper = false; + TypeElement annotatedClassElement = (TypeElement) element; + do { + if (annotatedClassElement.getQualifiedName().toString() + .equals(FQCN_OpenHelper)) { + derivedFromOpenHelper = true; + } + annotatedClassElement = (TypeElement) processingEnv + .getTypeUtils().asElement( + annotatedClassElement.getSuperclass()); + } while (!annotatedClassElement.getQualifiedName().toString() + .equals(FQCN_Object)); + if (!derivedFromOpenHelper) { + raiseError( + String.format( + "%s annotation must be applied to a class deriving from %s", + Database.class.getSimpleName(), CN_OpenHelper), + element); + } + + List tableTypes = new ArrayList(); + try { + Class[] classes = annotation.value(); + if (classes != null) { + for (int i = 0; i < classes.length; ++i) { + TypeElement typeElement; + try { + typeElement = processingEnv.getElementUtils() + .getTypeElement( + classes[i].getCanonicalName()); + } catch (MirroredTypeException mte) { + typeElement = (TypeElement) processingEnv + .getTypeUtils().asElement( + mte.getTypeMirror()); + } + tableTypes.add(typeElement); + } + } else { + // eclipse populates this with null if annotation populated + // with scalar (no {}) even though this is a legal shortcut + raiseError(String.format( + "%s annotation must enclose values array with {}", + Database.class.getSimpleName()), element); + continue; + } + } catch (MirroredTypesException mte) { + for (TypeMirror m : mte.getTypeMirrors()) { + tableTypes.add((TypeElement) processingEnv.getTypeUtils() + .asElement(m)); + } + } + + if (tableTypes.isEmpty()) { + raiseError( + String.format( + "%s annotation must contain at least one class annotated with %s", + Database.class.getSimpleName(), + DatabaseTable.class.getSimpleName()), element); + } + + foundDatabases.put((TypeElement) element, tableTypes); + + createDatabaseConfigSourceFile((TypeElement) element, tableTypes); + } + + if (env.processingOver()) { + raiseNote(String.format( + "Finished processing %d %s class(es) and %d %s class(es)", + foundTables.size(), DatabaseTable.class.getSimpleName(), + foundDatabases.size(), Database.class.getSimpleName())); + + final StandardLocation savedDatabaseInfoLocation = StandardLocation.SOURCE_OUTPUT; + final String savedDatabaseInfoPackageName = getClass().getPackage() + .getName(); + final String savedDatabaseInfoFileName = "savedDatabaseInfo"; + + // Try to read a file from before + Map> savedDatabaseInfo; + try { + FileObject file = processingEnv.getFiler() + .getResource(savedDatabaseInfoLocation, + savedDatabaseInfoPackageName, + savedDatabaseInfoFileName); + ObjectInputStream reader = new ObjectInputStream( + file.openInputStream()); + try { + // We created the file previously, so the cast is safe + @SuppressWarnings("unchecked") + Map> oldDatabaseInfo = (Map>) reader + .readObject(); + raiseNote(String.format( + "Loaded %d Database-to-DatabaseTable mappings", + oldDatabaseInfo.size())); + savedDatabaseInfo = oldDatabaseInfo; + } finally { + reader.close(); + } + } catch (FilerException e) { + // This file will only exist and have content during a round of + // incremental compilation, there is no clean way to detect this + // without trying and failing to read the file, so we catch the + // error and initialize with empty contents. + savedDatabaseInfo = new HashMap>(); + } catch (IOException e) { + // This file will only exist and have content during a round of + // incremental compilation, there is no clean way to detect this + // without trying and failing to read the file, so we catch the + // error and initialize with empty contents. + savedDatabaseInfo = new HashMap>(); + } catch (ClassNotFoundException e) { + // Built-in Java classes will always be available so this should + // never happen + throw new RuntimeException(e); + } + + // Verify each Database annotation only contains valid tables and + // add it to the saved list for future rounds of incremental + // compilation + for (Entry> databaseAndTables : foundDatabases + .entrySet()) { + List tableNames = new ArrayList(); + + for (TypeElement table : databaseAndTables.getValue()) { + if (table.getAnnotation(DatabaseTable.class) == null) { + raiseError( + String.format( + "%s annotation contains class %s not annotated with %s", + Database.class.getSimpleName(), + table.getSimpleName(), + DatabaseTable.class.getSimpleName()), + databaseAndTables.getKey()); + } + + tableNames.add(table.getQualifiedName().toString()); + } + + savedDatabaseInfo.put(databaseAndTables.getKey() + .getQualifiedName().toString(), tableNames); + } + + // Save the updated information for future rounds of incremental + // compilation + try { + FileObject file = processingEnv.getFiler() + .createResource(savedDatabaseInfoLocation, + savedDatabaseInfoPackageName, + savedDatabaseInfoFileName); + ObjectOutputStream writer = new ObjectOutputStream( + file.openOutputStream()); + writer.writeObject(savedDatabaseInfo); + writer.close(); + raiseNote(String.format( + "Stored %d Database-to-DatabaseTable mappings", + savedDatabaseInfo.size())); + } catch (FilerException e) { + // should never happen, but if it does, it shouldn't be fatal + // since the worst consequence would be a spurious warning + // during future incremental compilations that would be cleared + // with a full rebuild. + raiseNote("FilerException while saving Database-to-DatabaseTable mappings: " + + e.toString()); + } catch (IOException e) { + // should never happen, but if it does, it shouldn't be fatal + // since the worst consequence would be a spurious warning + // during future incremental compilations that would be cleared + // with a full rebuild. + raiseNote("IOException while saving Database-to-DatabaseTable mappings: " + + e.toString()); + } + + // Verify that every table is in a database (try to enforce using + // the database annotation for better performance) + Set tablesIncludedInDatabases = new HashSet(); + for (List tableNames : savedDatabaseInfo.values()) { + for (String tableName : tableNames) { + tablesIncludedInDatabases.add(tableName); + } + } + for (TypeElement foundTable : foundTables) { + if (!tablesIncludedInDatabases.contains(foundTable + .getQualifiedName().toString())) { + raiseWarning( + String.format( + "Class annotated with %s is not included in any %s annotation", + DatabaseTable.class.getSimpleName(), + Database.class.getSimpleName()), foundTable); + } + } + } + + return true; + } + + private void createDatabaseConfigSourceFile( + TypeElement openHelperClassElement, List tableClasses) { + ParsedClassName openHelperClassName = new ParsedClassName( + openHelperClassElement); + + try { + JavaFileObject javaFileObject = processingEnv + .getFiler() + .createSourceFile( + openHelperClassName + .getGeneratedFullyQualifiedClassName(), + openHelperClassElement); + + Writer writer = javaFileObject.openWriter(); + + try { + MethodSpec.Builder constructor = MethodSpec + .constructorBuilder().addModifiers(Modifier.PRIVATE); + + MethodSpec.Builder cacheTableConfigurations = MethodSpec + .methodBuilder("cacheTableConfigurations") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC); + cacheTableConfigurations.addStatement( + "$T tableConfigs = new $T()", + ParameterizedTypeName.get(ClassName.get(List.class), + ParameterizedTypeName.get(ClassName + .get(DatabaseTableConfig.class), + WildcardTypeName + .subtypeOf(Object.class))), + ParameterizedTypeName.get(ClassName + .get(ArrayList.class), ParameterizedTypeName + .get(ClassName.get(DatabaseTableConfig.class), + WildcardTypeName + .subtypeOf(Object.class)))); + for (TypeElement tableClass : tableClasses) { + ParsedClassName tableClassName = new ParsedClassName( + tableClass); + cacheTableConfigurations.addStatement( + "tableConfigs.add($T.createConfig())", ClassName + .get(tableClassName.getPackageName(), + tableClassName + .getGeneratedClassName())); + } + cacheTableConfigurations.addStatement( + "$T.addCachedDatabaseConfigs(tableConfigs)", + DaoManager.class); + + MethodSpec.Builder createTables = MethodSpec + .methodBuilder("createTables") + .addParameter(ConnectionSource.class, + "connectionSource") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addException(SQLException.class); + for (TypeElement tableClass : tableClasses) { + createTables.addStatement( + "$T.createTable(connectionSource, $T.class)", + TableUtils.class, tableClass.asType()); + } + + TypeSpec.Builder openHelperClass = TypeSpec + .classBuilder( + openHelperClassName.getGeneratedClassName()) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(constructor.build()) + .addMethod(cacheTableConfigurations.build()) + .addMethod(createTables.build()); + + JavaFile openHelperFile = JavaFile.builder( + openHelperClassName.getPackageName(), + openHelperClass.build()).build(); + openHelperFile.writeTo(writer); + } finally { + writer.close(); + } + } catch (FilerException e) { + // if multiple classes are in the same file (e.g. inner/nested + // classes), eclipse will do an incremental compilation for all of + // them. The unchanged ones' generated files will not be deleted, so + // we can ignore this benign error. + raiseNote(String + .format("Skipping file generation for %s since file already exists", + openHelperClassName + .getGeneratedFullyQualifiedClassName())); + } catch (IOException e) { + // We should always be able to generate the source files and if we + // can't we should bail out + throw new RuntimeException(e); + } + } + + private List getSettersForDatabaseField( + DatabaseField annotation, String fullyQualifiedFieldType) { + List output = new ArrayList(); + addSetterIfNotDefault(annotation, "columnName", "setColumnName($S)", + output); + addSetterIfNotDefault(annotation, "dataType", + "setDataType(com.j256.ormlite.field.DataType.$L)", output); + addSetterIfNotDefault(annotation, "defaultValue", + "setDefaultValue($S)", output); + addSetterIfNotDefault(annotation, "width", "setWidth($L)", output); + addSetterIfNotDefault(annotation, "canBeNull", "setCanBeNull($L)", + output); + addSetterIfNotDefault(annotation, "id", "setId($L)", output); + addSetterIfNotDefault(annotation, "generatedId", "setGeneratedId($L)", + output); + addSetterIfNotDefault(annotation, "generatedIdSequence", + "setGeneratedIdSequence($S)", output); + addSetterIfNotDefault(annotation, "foreign", "setForeign($L)", output); + addSetterIfNotDefault(annotation, "useGetSet", "setUseGetSet($L)", + output); + addSetterIfNotDefault(annotation, "unknownEnumName", + "setUnknownEnumValue(" + fullyQualifiedFieldType + ".$L)", + output); + addSetterIfNotDefault(annotation, "throwIfNull", "setThrowIfNull($L)", + output); + addSetterIfNotDefault(annotation, "format", "setFormat($S)", output); + addSetterIfNotDefault(annotation, "unique", "setUnique($L)", output); + addSetterIfNotDefault(annotation, "uniqueCombo", "setUniqueCombo($L)", + output); + addSetterIfNotDefault(annotation, "index", "setIndex($L)", output); + addSetterIfNotDefault(annotation, "uniqueIndex", "setUniqueIndex($L)", + output); + addSetterIfNotDefault(annotation, "indexName", "setIndexName($S)", + output); + addSetterIfNotDefault(annotation, "uniqueIndexName", + "setUniqueIndexName($S)", output); + addSetterIfNotDefault(annotation, "foreignAutoRefresh", + "setForeignAutoRefresh($L)", output); + addSetterIfNotDefault(annotation, "maxForeignAutoRefreshLevel", + "setMaxForeignAutoRefreshLevel($L)", output); + addSetterIfNotDefault(annotation, "persisterClass", + "setPersisterClass($T.class)", output); + addSetterIfNotDefault(annotation, "allowGeneratedIdInsert", + "setAllowGeneratedIdInsert($L)", output); + addSetterIfNotDefault(annotation, "columnDefinition", + "setColumnDefinition($S)", output); + addSetterIfNotDefault(annotation, "foreignAutoCreate", + "setForeignAutoCreate($L)", output); + addSetterIfNotDefault(annotation, "version", "setVersion($L)", output); + addSetterIfNotDefault(annotation, "foreignColumnName", + "setForeignColumnName($S)", output); + addSetterIfNotDefault(annotation, "readOnly", "setReadOnly($L)", output); + return output; + } + + private List getSettersForForeignCollectionField( + ForeignCollectionField annotation) { + List output = new ArrayList(); + addSetterIfNotDefault(annotation, "columnName", "setColumnName($S)", + output); + addSetter("setForeignCollection($L)", true, output); + addSetterIfNotDefault(annotation, "eager", + "setForeignCollectionEager($L)", output); + addSetterIfNotDefault(annotation, "maxEagerLevel", + "maxEagerForeignCollectionLevel", + "setForeignCollectionMaxEagerLevel($L)", output); + addSetterIfNotDefault(annotation, "columnName", + "setForeignCollectionColumnName($S)", output); + addSetterIfNotDefault(annotation, "orderColumnName", + "setForeignCollectionOrderColumnName($S)", output); + addSetterIfNotDefault(annotation, "orderAscending", + "setForeignCollectionOrderAscending($L)", output); + addSetterIfNotDefault(annotation, "foreignFieldName", + "foreignColumnName", + "setForeignCollectionForeignFieldName($S)", output); + return output; + } + + private void addSetterIfNotDefault(Annotation annotation, String name, + String format, List output) { + addSetterIfNotDefault(annotation, name, null, format, output); + } + + private void addSetterIfNotDefault(Annotation annotation, String name, + String fallbackName, String format, List output) { + Object value = getValueIfNotDefault(annotation, name); + if (value != null) { + addSetter(format, value, output); + } else if (fallbackName != null) { + value = getValueIfNotDefault(annotation, fallbackName); + if (value != null) { + addSetter(format, value, output); + } + } + } + + private void addSetter(String format, Object value, + List output) { + output.add(new SetterBindings(format, value)); + } + + /** + * This function examines a single field in an annotation and if the current + * value doesn't match the default returns the current value. If the current + * value matches the default, it returns null. + * + * @param annotation + * the annotation containing the field of interest + * @param name + * the name of the field in the annotation to read + * @return the current value if it is not the default, null otherwise + */ + private Object getValueIfNotDefault(Annotation annotation, String name) { + try { + Method method = annotation.annotationType().getMethod(name); + + Object actualValue; + Object defaultValue; + if (method.getReturnType().getCanonicalName().equals(FQCN_Class)) { + try { + actualValue = getClassNameFromClassObject(method + .invoke(annotation)); + } catch (Exception ex) { + actualValue = getMirroredClassNameFromException(ex); + } + try { + defaultValue = getClassNameFromClassObject(method + .getDefaultValue()); + } catch (Exception ex) { + defaultValue = getMirroredClassNameFromException(ex); + } + } else { + actualValue = method.invoke(annotation); + defaultValue = method.getDefaultValue(); + } + + if (defaultValue.equals(actualValue)) { + return null; + } else { + return actualValue; + } + } catch (Exception e) { + // All possible annotation properties are unit tested, so it is not + // possible to get an exception here + throw new RuntimeException(e); + } + } + + private TypeName getClassNameFromClassObject(Object object) { + return TypeName.get((Class) object); + } + + private TypeName getMirroredClassNameFromException(Exception ex) + throws Exception { + Throwable t = ex; + do { + if (t instanceof MirroredTypeException) { + return TypeName + .get(((MirroredTypeException) t).getTypeMirror()); + } + t = t.getCause(); + } while (t != null); + + throw ex; + } + + private void createTableConfigSourceFile(TableBindings table, + Element tableClassElement) { + try { + JavaFileObject javaFileObject = processingEnv.getFiler() + .createSourceFile( + table.getParsedClassName() + .getGeneratedFullyQualifiedClassName(), + tableClassElement); + + Writer writer = javaFileObject.openWriter(); + try { + writeTable(writer, table); + } finally { + writer.close(); + } + } catch (FilerException e) { + // if multiple classes are in the same file (e.g. inner/nested + // classes), eclipse will do an incremental compilation for all of + // them. The unchanged ones' generated files will not be deleted, so + // we can ignore this benign error. + raiseNote(String + .format("Skipping file generation for %s since file already exists", + table.getParsedClassName() + .getGeneratedFullyQualifiedClassName())); + } catch (IOException e) { + // We should always be able to generate the source files and if we + // can't we should bail out + throw new RuntimeException(e); + } + } + + private void writeTable(Writer writer, TableBindings table) + throws IOException { + MethodSpec.Builder constructor = MethodSpec.constructorBuilder() + .addModifiers(Modifier.PRIVATE); + + MethodSpec.Builder createConfig = MethodSpec + .methodBuilder("createConfig") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns( + ParameterizedTypeName.get( + ClassName.get(DatabaseTableConfig.class), + WildcardTypeName.subtypeOf(Object.class))); + createConfig.addStatement("$T databaseFieldConfigs = new $T()", + ParameterizedTypeName.get(ClassName.get(List.class), + TypeName.get(DatabaseFieldConfig.class)), + ParameterizedTypeName.get(ClassName.get(ArrayList.class), + TypeName.get(DatabaseFieldConfig.class))); + + for (FieldBindings field : table.getFields()) { + createConfig.addStatement("$T $LFieldConfig = new $T($S)", + DatabaseFieldConfig.class, field.getFieldName(), + DatabaseFieldConfig.class, field.getFieldName()); + + for (SetterBindings setter : field.getSetters()) { + createConfig.addStatement( + "$LFieldConfig." + setter.getFormat(), + field.getFieldName(), setter.getParameter()); + } + + createConfig.addStatement( + "databaseFieldConfigs.add($LFieldConfig)", + field.getFieldName()); + } + + createConfig.addStatement( + "return new $T<$T>($T.class, $S, databaseFieldConfigs)", + DatabaseTableConfig.class, ClassName.get(table + .getParsedClassName().getPackageName(), table + .getParsedClassName().getInputClassName()), ClassName + .get(table.getParsedClassName().getPackageName(), table + .getParsedClassName().getInputClassName()), + table.getTableName()); + + TypeSpec.Builder tableConfigClass = TypeSpec + .classBuilder( + table.getParsedClassName().getGeneratedClassName()) + .addModifiers(Modifier.PUBLIC, Modifier.FINAL) + .addMethod(constructor.build()).addMethod(createConfig.build()); + + JavaFile tableConfigFile = JavaFile.builder( + table.getParsedClassName().getPackageName(), + tableConfigClass.build()).build(); + tableConfigFile.writeTo(writer); + } + + private void raiseNote(String message) { + this.processingEnv.getMessager().printMessage(Kind.NOTE, message); + } + + /* + * During incremental compiles in eclipse, if the same element raises + * multiple warnings, the internal message printing code can throw a NPE. + * When this happens, all warnings are displayed properly, so we should + * ignore the error. We will validate our arguments ourself to ensure that + * this code doesn't mask a real bug. + */ + + private void raiseWarning(String message, Element element) { + if (message == null || element == null) { + throw new NullPointerException(); + } + try { + this.processingEnv.getMessager().printMessage(Kind.WARNING, + message, element); + } catch (NullPointerException e) { + // ignore to workaround issues with eclipse incremental compilation + raiseNote("NullPointerException while raising a warning: " + + e.toString()); + } + } + + private void raiseError(String message, Element element) { + if (message == null || element == null) { + throw new NullPointerException(); + } + try { + this.processingEnv.getMessager().printMessage(Kind.ERROR, message, + element); + } catch (NullPointerException e) { + // ignore to workaround issues with eclipse incremental compilation + raiseNote("NullPointerException while raising an error: " + + e.toString()); + } + } +} diff --git a/src/main/java/com/j256/ormlite/android/processor/ParsedClassName.java b/src/main/java/com/j256/ormlite/android/processor/ParsedClassName.java new file mode 100644 index 00000000..bc136c2e --- /dev/null +++ b/src/main/java/com/j256/ormlite/android/processor/ParsedClassName.java @@ -0,0 +1,80 @@ +package com.j256.ormlite.android.processor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; + +/* + * Eclipse doesn't like the link to rt.jar and classes therein. This is a + * spurious warning that can be ignored. It is intended to prevent referencing + * com.sun packages that may not be in every JVM, but the annotation processing + * stuff is part of JSR-269, so will always be present. + */ +@SuppressWarnings("restriction") +class ParsedClassName { + private String packageName; + private List nestedClasses = new ArrayList(); + + ParsedClassName(Element element) { + Element elementIterator = element; + do { + nestedClasses.add(elementIterator.getSimpleName().toString()); + elementIterator = elementIterator.getEnclosingElement(); + } while (elementIterator.getKind().isClass()); + Collections.reverse(nestedClasses); + packageName = ((PackageElement) elementIterator).getQualifiedName() + .toString(); + } + + String getPackageName() { + return packageName; + } + + String getInputFullyQualifiedClassName() { + StringBuilder sb = new StringBuilder(); + if (!packageName.isEmpty()) { + sb.append(packageName); + sb.append('.'); + } + sb.append(getInputClassName()); + return sb.toString(); + } + + String getInputClassName() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < nestedClasses.size(); ++i) { + if (i != 0) { + sb.append('.'); + } + sb.append(nestedClasses.get(i)); + } + return sb.toString(); + } + + String getGeneratedClassName() { + final String SUFFIX = "_TableConfig"; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < nestedClasses.size(); ++i) { + if (i != 0) { + sb.append('_'); + } + sb.append(nestedClasses.get(i)); + } + sb.append(SUFFIX); + return sb.toString(); + } + + String getGeneratedFullyQualifiedClassName() { + StringBuilder sb = new StringBuilder(); + if (!packageName.isEmpty()) { + sb.append(packageName); + sb.append('.'); + } + sb.append(getGeneratedClassName()); + return sb.toString(); + } +} diff --git a/src/main/java/com/j256/ormlite/android/processor/SetterBindings.java b/src/main/java/com/j256/ormlite/android/processor/SetterBindings.java new file mode 100644 index 00000000..0bf22b4e --- /dev/null +++ b/src/main/java/com/j256/ormlite/android/processor/SetterBindings.java @@ -0,0 +1,19 @@ +package com.j256.ormlite.android.processor; + +class SetterBindings { + private final String format; + private final Object parameter; + + SetterBindings(String format, Object parameter) { + this.format = format; + this.parameter = parameter; + } + + String getFormat() { + return format; + } + + Object getParameter() { + return parameter; + } +} diff --git a/src/main/java/com/j256/ormlite/android/processor/TableBindings.java b/src/main/java/com/j256/ormlite/android/processor/TableBindings.java new file mode 100644 index 00000000..5c98fb3b --- /dev/null +++ b/src/main/java/com/j256/ormlite/android/processor/TableBindings.java @@ -0,0 +1,31 @@ +package com.j256.ormlite.android.processor; + +import java.util.ArrayList; +import java.util.List; + +class TableBindings { + private final ParsedClassName parsedClassName; + private final String tableName; + private final List fields = new ArrayList(); + + TableBindings(ParsedClassName parsedClassName, String tableName) { + this.parsedClassName = parsedClassName; + this.tableName = tableName; + } + + void addField(FieldBindings field) { + fields.add(field); + } + + ParsedClassName getParsedClassName() { + return parsedClassName; + } + + String getTableName() { + return tableName; + } + + List getFields() { + return fields; + } +} diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 00000000..a63788c0 --- /dev/null +++ b/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +com.j256.ormlite.android.processor.OrmLiteAnnotationProcessor diff --git a/src/test/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtilTest.java b/src/test/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtilTest.java index 0859bc6e..52ec0bfc 100644 --- a/src/test/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtilTest.java +++ b/src/test/java/com/j256/ormlite/android/apptools/OrmLiteConfigUtilTest.java @@ -11,6 +11,7 @@ import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.field.ForeignCollectionField; +@SuppressWarnings("deprecation") public class OrmLiteConfigUtilTest { private static final String lineSeparator = System.getProperty("line.separator"); diff --git a/src/test/java/com/j256/ormlite/android/processor/OrmLiteAnnotationProcessorTest.java b/src/test/java/com/j256/ormlite/android/processor/OrmLiteAnnotationProcessorTest.java new file mode 100644 index 00000000..73e1d2d6 --- /dev/null +++ b/src/test/java/com/j256/ormlite/android/processor/OrmLiteAnnotationProcessorTest.java @@ -0,0 +1,160 @@ +package com.j256.ormlite.android.processor; + +import static com.google.common.truth.Truth.assert_; +import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; + +import org.junit.Test; + +import com.google.testing.compile.JavaFileObjects; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.field.ForeignCollectionField; +import com.j256.ormlite.table.DatabaseTable; + +public class OrmLiteAnnotationProcessorTest { + + @Test + public void testDatabaseFieldAllDefaults() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/UnnamedTableWithDefaultDatabaseField.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .compilesWithoutError() + .and() + .generatesSources( + JavaFileObjects + .forResource("outputs/UnnamedTableWithDefaultDatabaseField_TableConfig.java"), + JavaFileObjects + .forResource("outputs/UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig.java")); + } + + @Test + public void testDatabaseFieldAllSpecified() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/NamedTableWithSpecifiedDatabaseField.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .compilesWithoutError() + .and() + .generatesSources( + JavaFileObjects + .forResource("outputs/NamedTableWithSpecifiedDatabaseField_TableConfig.java"), + JavaFileObjects + .forResource("outputs/NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig.java")); + } + + @Test + public void testForeignCollectionFieldAllDefaults() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/UnnamedTableWithDefaultForeignCollectionField.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .compilesWithoutError() + .and() + .generatesSources( + JavaFileObjects + .forResource("outputs/UnnamedTableWithDefaultForeignCollectionField_TableConfig.java"), + JavaFileObjects + .forResource("outputs/UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig.java")); + } + + @Test + public void testForeignCollectionFieldAllSpecified() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/NamedTableWithSpecifiedForeignCollectionField.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .compilesWithoutError() + .and() + .generatesSources( + JavaFileObjects + .forResource("outputs/NamedTableWithSpecifiedForeignCollectionField_TableConfig.java"), + JavaFileObjects + .forResource("outputs/NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig.java")); + } + + @Test + public void testInnerClasses() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/InnerClassTable.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .compilesWithoutError() + .and() + .generatesSources( + JavaFileObjects + .forResource("outputs/InnerClassTable_InnerClass_TableConfig.java"), + JavaFileObjects + .forResource("outputs/InnerClassTable_OtherInnerClass_TableConfig.java"), + JavaFileObjects + .forResource("outputs/InnerClassTable_OpenHelper_TableConfig.java")); + } + + @Test + public void testErrorBothAnnotationsOnField() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/TableWithFieldWithBothAnnotations.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .failsToCompile() + .withErrorContaining( + String.format( + "Fields cannot be annotated with both %s and %s", + DatabaseField.class.getSimpleName(), + ForeignCollectionField.class.getSimpleName())); + } + + @Test + public void testErrorDatabaseWithNoTables() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/DatabaseWithNoTables.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .failsToCompile() + .withErrorContaining( + String.format( + "%s annotation must contain at least one class annotated with %s", + Database.class.getSimpleName(), + DatabaseTable.class.getSimpleName())); + } + + @Test + public void testErrorDatabaseDerivedFromWrongClass() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/DatabaseDerivedFromWrongClass.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .failsToCompile() + .withErrorContaining( + String.format( + "%s annotation must be applied to a class deriving from %s", + Database.class.getSimpleName(), + OrmLiteSqliteOpenHelper.class.getSimpleName())); + } + + @Test + public void testErrorDatabaseWithNonTable() { + assert_() + .about(javaSource()) + .that(JavaFileObjects + .forResource("inputs/DatabaseWithNonTable.java")) + .processedWith(new OrmLiteAnnotationProcessor()) + .failsToCompile() + .withErrorContaining( + String.format( + "%s annotation contains class %s not annotated with %s", + Database.class.getSimpleName(), + String.class.getSimpleName(), + DatabaseTable.class.getSimpleName())); + } +} diff --git a/src/test/resources/inputs/DatabaseDerivedFromWrongClass.java b/src/test/resources/inputs/DatabaseDerivedFromWrongClass.java new file mode 100644 index 00000000..50149f2e --- /dev/null +++ b/src/test/resources/inputs/DatabaseDerivedFromWrongClass.java @@ -0,0 +1,15 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable +class DatabaseDerivedFromWrongClass { + @DatabaseField + int field; + + @Database({ DatabaseDerivedFromWrongClass.class }) + static abstract class OpenHelper { + } +} diff --git a/src/test/resources/inputs/DatabaseWithNoTables.java b/src/test/resources/inputs/DatabaseWithNoTables.java new file mode 100644 index 00000000..f051a610 --- /dev/null +++ b/src/test/resources/inputs/DatabaseWithNoTables.java @@ -0,0 +1,15 @@ +package com.j256.ormlite.android.processor.inputs; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; + +@Database({}) +abstract class DatabaseWithNoTables extends OrmLiteSqliteOpenHelper { + DatabaseWithNoTables(Context context, String databaseName, + CursorFactory factory, int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + } +} diff --git a/src/test/resources/inputs/DatabaseWithNonTable.java b/src/test/resources/inputs/DatabaseWithNonTable.java new file mode 100644 index 00000000..3f3cc082 --- /dev/null +++ b/src/test/resources/inputs/DatabaseWithNonTable.java @@ -0,0 +1,23 @@ +package com.j256.ormlite.android.processor.inputs; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable +class DatabaseWithNonTable { + @DatabaseField + int field; + + @Database({ DatabaseWithNonTable.class, String.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + } + } +} diff --git a/src/test/resources/inputs/InnerClassTable.java b/src/test/resources/inputs/InnerClassTable.java new file mode 100644 index 00000000..45d0c638 --- /dev/null +++ b/src/test/resources/inputs/InnerClassTable.java @@ -0,0 +1,47 @@ +package com.j256.ormlite.android.processor.inputs; + +import java.sql.SQLException; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTable; + +class InnerClassTable { + @DatabaseTable + static class InnerClass { + @DatabaseField + int field; + } + + @DatabaseTable + static class OtherInnerClass { + @DatabaseField + int field; + } + + @Database({ InnerClass.class, OtherInnerClass.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + InnerClassTable_OpenHelper_TableConfig.cacheTableConfigurations(); + } + + @Override + public void onCreate(SQLiteDatabase database, + ConnectionSource connectionSource) { + try { + InnerClassTable_OpenHelper_TableConfig + .createTables(connectionSource); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/test/resources/inputs/NamedTableWithSpecifiedDatabaseField.java b/src/test/resources/inputs/NamedTableWithSpecifiedDatabaseField.java new file mode 100644 index 00000000..d2ebb6bb --- /dev/null +++ b/src/test/resources/inputs/NamedTableWithSpecifiedDatabaseField.java @@ -0,0 +1,170 @@ +package com.j256.ormlite.android.processor.inputs; + +import java.lang.reflect.Field; +import java.sql.SQLException; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.DataPersister; +import com.j256.ormlite.field.DataType; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.field.FieldType; +import com.j256.ormlite.field.SqlType; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.support.DatabaseResults; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "table") +class NamedTableWithSpecifiedDatabaseField { + + public static enum FieldTypeEnum { + VALUE, OTHER_VALUE; + } + + public static class CustomPersister implements DataPersister { + public Object parseDefaultString(FieldType fieldType, String defaultStr) + throws SQLException { + return null; + } + + public Object javaToSqlArg(FieldType fieldType, Object obj) + throws SQLException { + return null; + } + + public Object resultToSqlArg(FieldType fieldType, + DatabaseResults results, int columnPos) throws SQLException { + return null; + } + + public Object resultToJava(FieldType fieldType, + DatabaseResults results, int columnPos) throws SQLException { + return null; + } + + public Object sqlArgToJava(FieldType fieldType, Object sqlArg, + int columnPos) throws SQLException { + return null; + } + + public SqlType getSqlType() { + return null; + } + + public boolean isStreamType() { + return false; + } + + public Object resultStringToJava(FieldType fieldType, + String stringValue, int columnPos) throws SQLException { + return null; + } + + public Class[] getAssociatedClasses() { + return null; + } + + public String[] getAssociatedClassNames() { + return null; + } + + public Object makeConfigObject(FieldType fieldType) throws SQLException { + return null; + } + + public Object convertIdNumber(Number number) { + return null; + } + + public boolean isValidGeneratedType() { + return false; + } + + public boolean isValidForField(Field field) { + return false; + } + + public Class getPrimaryClass() { + return null; + } + + public boolean isEscapedDefaultValue() { + return false; + } + + public boolean isEscapedValue() { + return false; + } + + public boolean isPrimitive() { + return false; + } + + public boolean isComparable() { + return false; + } + + public boolean isAppropriateId() { + return false; + } + + public boolean isArgumentHolderRequired() { + return false; + } + + public boolean isSelfGeneratedId() { + return false; + } + + public Object generateId() { + return null; + } + + public int getDefaultWidth() { + return 0; + } + + public boolean dataIsEqual(Object obj1, Object obj2) { + return false; + } + + public boolean isValidForVersion() { + return false; + } + + public Object moveToNextValue(Object currentValue) throws SQLException { + return null; + } + } + + @DatabaseField(columnName = "column", dataType = DataType.ENUM_INTEGER, defaultValue = "VALUE", width = 100, canBeNull = false, id = true, generatedId = true, generatedIdSequence = "id_sequence", foreign = true, useGetSet = true, unknownEnumName = "OTHER_VALUE", throwIfNull = true, format = "%f", unique = true, uniqueCombo = true, index = true, uniqueIndex = true, indexName = "index", uniqueIndexName = "unique_index", foreignAutoRefresh = true, maxForeignAutoRefreshLevel = 5, persisterClass = CustomPersister.class, allowGeneratedIdInsert = true, columnDefinition = "INT NOT NULL", foreignAutoCreate = true, version = true, foreignColumnName = "foreign", readOnly = true) + FieldTypeEnum field; + + @DatabaseField(persisted = false) + int ignored; + + @Database({ NamedTableWithSpecifiedDatabaseField.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig + .cacheTableConfigurations(); + } + + @Override + public void onCreate(SQLiteDatabase database, + ConnectionSource connectionSource) { + try { + NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig + .createTables(connectionSource); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/test/resources/inputs/NamedTableWithSpecifiedForeignCollectionField.java b/src/test/resources/inputs/NamedTableWithSpecifiedForeignCollectionField.java new file mode 100644 index 00000000..d1257b56 --- /dev/null +++ b/src/test/resources/inputs/NamedTableWithSpecifiedForeignCollectionField.java @@ -0,0 +1,44 @@ +package com.j256.ormlite.android.processor.inputs; + +import java.sql.SQLException; +import java.util.List; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.ForeignCollectionField; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable(tableName = "table") +class NamedTableWithSpecifiedForeignCollectionField { + @ForeignCollectionField(eager = true, maxEagerLevel = 5, columnName = "column", orderColumnName = "order_column", orderAscending = false, foreignFieldName = "foreign_field") + List numbers; + + @ForeignCollectionField(maxEagerForeignCollectionLevel = 5, foreignColumnName = "foreign_field") + List numbers_deprecated; + + @Database({ NamedTableWithSpecifiedForeignCollectionField.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig + .cacheTableConfigurations(); + } + + @Override + public void onCreate(SQLiteDatabase database, + ConnectionSource connectionSource) { + try { + NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig + .createTables(connectionSource); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/test/resources/inputs/TableWithFieldWithBothAnnotations.java b/src/test/resources/inputs/TableWithFieldWithBothAnnotations.java new file mode 100644 index 00000000..ad3543d1 --- /dev/null +++ b/src/test/resources/inputs/TableWithFieldWithBothAnnotations.java @@ -0,0 +1,27 @@ +package com.j256.ormlite.android.processor.inputs; + +import java.util.List; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.field.ForeignCollectionField; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable +class TableWithFieldWithBothAnnotations { + @DatabaseField + @ForeignCollectionField + List field; + + @Database({ TableWithFieldWithBothAnnotations.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + } + } +} diff --git a/src/test/resources/inputs/UnnamedTableWithDefaultDatabaseField.java b/src/test/resources/inputs/UnnamedTableWithDefaultDatabaseField.java new file mode 100644 index 00000000..a8a46b29 --- /dev/null +++ b/src/test/resources/inputs/UnnamedTableWithDefaultDatabaseField.java @@ -0,0 +1,40 @@ +package com.j256.ormlite.android.processor.inputs; + +import java.sql.SQLException; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable +class UnnamedTableWithDefaultDatabaseField { + @DatabaseField + int field; + + @Database({ UnnamedTableWithDefaultDatabaseField.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig + .cacheTableConfigurations(); + } + + @Override + public void onCreate(SQLiteDatabase database, + ConnectionSource connectionSource) { + try { + UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig + .createTables(connectionSource); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/test/resources/inputs/UnnamedTableWithDefaultForeignCollectionField.java b/src/test/resources/inputs/UnnamedTableWithDefaultForeignCollectionField.java new file mode 100644 index 00000000..a8d94f65 --- /dev/null +++ b/src/test/resources/inputs/UnnamedTableWithDefaultForeignCollectionField.java @@ -0,0 +1,41 @@ +package com.j256.ormlite.android.processor.inputs; + +import java.sql.SQLException; +import java.util.List; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; + +import com.j256.ormlite.android.annotations.Database; +import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper; +import com.j256.ormlite.field.ForeignCollectionField; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTable; + +@DatabaseTable +class UnnamedTableWithDefaultForeignCollectionField { + @ForeignCollectionField + List numbers; + + @Database({ UnnamedTableWithDefaultForeignCollectionField.class }) + static abstract class OpenHelper extends OrmLiteSqliteOpenHelper { + OpenHelper(Context context, String databaseName, CursorFactory factory, + int databaseVersion) { + super(context, databaseName, factory, databaseVersion); + UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig + .cacheTableConfigurations(); + } + + @Override + public void onCreate(SQLiteDatabase database, + ConnectionSource connectionSource) { + try { + UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig + .createTables(connectionSource); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/test/resources/outputs/InnerClassTable_InnerClass_TableConfig.java b/src/test/resources/outputs/InnerClassTable_InnerClass_TableConfig.java new file mode 100644 index 00000000..9adc54bc --- /dev/null +++ b/src/test/resources/outputs/InnerClassTable_InnerClass_TableConfig.java @@ -0,0 +1,20 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.table.DatabaseTableConfig; +import java.util.ArrayList; +import java.util.List; + +public final class InnerClassTable_InnerClass_TableConfig { + private InnerClassTable_InnerClass_TableConfig() { + } + + public static DatabaseTableConfig createConfig() { + List databaseFieldConfigs = new ArrayList(); + DatabaseFieldConfig fieldFieldConfig = new DatabaseFieldConfig("field"); + databaseFieldConfigs.add(fieldFieldConfig); + return new DatabaseTableConfig( + InnerClassTable.InnerClass.class, "innerclass", + databaseFieldConfigs); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/InnerClassTable_OpenHelper_TableConfig.java b/src/test/resources/outputs/InnerClassTable_OpenHelper_TableConfig.java new file mode 100644 index 00000000..f1e21202 --- /dev/null +++ b/src/test/resources/outputs/InnerClassTable_OpenHelper_TableConfig.java @@ -0,0 +1,30 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTableConfig; +import com.j256.ormlite.table.TableUtils; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public final class InnerClassTable_OpenHelper_TableConfig { + private InnerClassTable_OpenHelper_TableConfig() { + } + + public static void cacheTableConfigurations() { + List> tableConfigs = new ArrayList>(); + tableConfigs.add(InnerClassTable_InnerClass_TableConfig.createConfig()); + tableConfigs.add(InnerClassTable_OtherInnerClass_TableConfig + .createConfig()); + DaoManager.addCachedDatabaseConfigs(tableConfigs); + } + + public static void createTables(ConnectionSource connectionSource) + throws SQLException { + TableUtils.createTable(connectionSource, + InnerClassTable.InnerClass.class); + TableUtils.createTable(connectionSource, + InnerClassTable.OtherInnerClass.class); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/InnerClassTable_OtherInnerClass_TableConfig.java b/src/test/resources/outputs/InnerClassTable_OtherInnerClass_TableConfig.java new file mode 100644 index 00000000..da900b17 --- /dev/null +++ b/src/test/resources/outputs/InnerClassTable_OtherInnerClass_TableConfig.java @@ -0,0 +1,20 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.table.DatabaseTableConfig; +import java.util.ArrayList; +import java.util.List; + +public final class InnerClassTable_OtherInnerClass_TableConfig { + private InnerClassTable_OtherInnerClass_TableConfig() { + } + + public static DatabaseTableConfig createConfig() { + List databaseFieldConfigs = new ArrayList(); + DatabaseFieldConfig fieldFieldConfig = new DatabaseFieldConfig("field"); + databaseFieldConfigs.add(fieldFieldConfig); + return new DatabaseTableConfig( + InnerClassTable.OtherInnerClass.class, "otherinnerclass", + databaseFieldConfigs); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig.java b/src/test/resources/outputs/NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig.java new file mode 100644 index 00000000..399d4612 --- /dev/null +++ b/src/test/resources/outputs/NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig.java @@ -0,0 +1,27 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTableConfig; +import com.j256.ormlite.table.TableUtils; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public final class NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig { + private NamedTableWithSpecifiedDatabaseField_OpenHelper_TableConfig() { + } + + public static void cacheTableConfigurations() { + List> tableConfigs = new ArrayList>(); + tableConfigs.add(NamedTableWithSpecifiedDatabaseField_TableConfig + .createConfig()); + DaoManager.addCachedDatabaseConfigs(tableConfigs); + } + + public static void createTables(ConnectionSource connectionSource) + throws SQLException { + TableUtils.createTable(connectionSource, + NamedTableWithSpecifiedDatabaseField.class); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/NamedTableWithSpecifiedDatabaseField_TableConfig.java b/src/test/resources/outputs/NamedTableWithSpecifiedDatabaseField_TableConfig.java new file mode 100644 index 00000000..95f86bec --- /dev/null +++ b/src/test/resources/outputs/NamedTableWithSpecifiedDatabaseField_TableConfig.java @@ -0,0 +1,51 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.table.DatabaseTableConfig; +import java.util.ArrayList; +import java.util.List; + +public final class NamedTableWithSpecifiedDatabaseField_TableConfig { + private NamedTableWithSpecifiedDatabaseField_TableConfig() { + } + + public static DatabaseTableConfig createConfig() { + List databaseFieldConfigs = new ArrayList(); + DatabaseFieldConfig fieldFieldConfig = new DatabaseFieldConfig("field"); + fieldFieldConfig.setColumnName("column"); + fieldFieldConfig + .setDataType(com.j256.ormlite.field.DataType.ENUM_INTEGER); + fieldFieldConfig.setDefaultValue("VALUE"); + fieldFieldConfig.setWidth(100); + fieldFieldConfig.setCanBeNull(false); + fieldFieldConfig.setId(true); + fieldFieldConfig.setGeneratedId(true); + fieldFieldConfig.setGeneratedIdSequence("id_sequence"); + fieldFieldConfig.setForeign(true); + fieldFieldConfig.setUseGetSet(true); + fieldFieldConfig + .setUnknownEnumValue(com.j256.ormlite.android.processor.inputs.NamedTableWithSpecifiedDatabaseField.FieldTypeEnum.OTHER_VALUE); + fieldFieldConfig.setThrowIfNull(true); + fieldFieldConfig.setFormat("%f"); + fieldFieldConfig.setUnique(true); + fieldFieldConfig.setUniqueCombo(true); + fieldFieldConfig.setIndex(true); + fieldFieldConfig.setUniqueIndex(true); + fieldFieldConfig.setIndexName("index"); + fieldFieldConfig.setUniqueIndexName("unique_index"); + fieldFieldConfig.setForeignAutoRefresh(true); + fieldFieldConfig.setMaxForeignAutoRefreshLevel(5); + fieldFieldConfig + .setPersisterClass(NamedTableWithSpecifiedDatabaseField.CustomPersister.class); + fieldFieldConfig.setAllowGeneratedIdInsert(true); + fieldFieldConfig.setColumnDefinition("INT NOT NULL"); + fieldFieldConfig.setForeignAutoCreate(true); + fieldFieldConfig.setVersion(true); + fieldFieldConfig.setForeignColumnName("foreign"); + fieldFieldConfig.setReadOnly(true); + databaseFieldConfigs.add(fieldFieldConfig); + return new DatabaseTableConfig( + NamedTableWithSpecifiedDatabaseField.class, "table", + databaseFieldConfigs); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig.java b/src/test/resources/outputs/NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig.java new file mode 100644 index 00000000..2c74f06e --- /dev/null +++ b/src/test/resources/outputs/NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig.java @@ -0,0 +1,28 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTableConfig; +import com.j256.ormlite.table.TableUtils; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public final class NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig { + private NamedTableWithSpecifiedForeignCollectionField_OpenHelper_TableConfig() { + } + + public static void cacheTableConfigurations() { + List> tableConfigs = new ArrayList>(); + tableConfigs + .add(NamedTableWithSpecifiedForeignCollectionField_TableConfig + .createConfig()); + DaoManager.addCachedDatabaseConfigs(tableConfigs); + } + + public static void createTables(ConnectionSource connectionSource) + throws SQLException { + TableUtils.createTable(connectionSource, + NamedTableWithSpecifiedForeignCollectionField.class); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/NamedTableWithSpecifiedForeignCollectionField_TableConfig.java b/src/test/resources/outputs/NamedTableWithSpecifiedForeignCollectionField_TableConfig.java new file mode 100644 index 00000000..d2fc7447 --- /dev/null +++ b/src/test/resources/outputs/NamedTableWithSpecifiedForeignCollectionField_TableConfig.java @@ -0,0 +1,37 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.table.DatabaseTableConfig; +import java.util.ArrayList; +import java.util.List; + +public final class NamedTableWithSpecifiedForeignCollectionField_TableConfig { + private NamedTableWithSpecifiedForeignCollectionField_TableConfig() { + } + + public static DatabaseTableConfig createConfig() { + List databaseFieldConfigs = new ArrayList(); + DatabaseFieldConfig numbersFieldConfig = new DatabaseFieldConfig( + "numbers"); + numbersFieldConfig.setColumnName("column"); + numbersFieldConfig.setForeignCollection(true); + numbersFieldConfig.setForeignCollectionEager(true); + numbersFieldConfig.setForeignCollectionMaxEagerLevel(5); + numbersFieldConfig.setForeignCollectionColumnName("column"); + numbersFieldConfig.setForeignCollectionOrderColumnName("order_column"); + numbersFieldConfig.setForeignCollectionOrderAscending(false); + numbersFieldConfig + .setForeignCollectionForeignFieldName("foreign_field"); + databaseFieldConfigs.add(numbersFieldConfig); + DatabaseFieldConfig numbers_deprecatedFieldConfig = new DatabaseFieldConfig( + "numbers_deprecated"); + numbers_deprecatedFieldConfig.setForeignCollection(true); + numbers_deprecatedFieldConfig.setForeignCollectionMaxEagerLevel(5); + numbers_deprecatedFieldConfig + .setForeignCollectionForeignFieldName("foreign_field"); + databaseFieldConfigs.add(numbers_deprecatedFieldConfig); + return new DatabaseTableConfig( + NamedTableWithSpecifiedForeignCollectionField.class, "table", + databaseFieldConfigs); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig.java b/src/test/resources/outputs/UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig.java new file mode 100644 index 00000000..c847816d --- /dev/null +++ b/src/test/resources/outputs/UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig.java @@ -0,0 +1,27 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTableConfig; +import com.j256.ormlite.table.TableUtils; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public final class UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig { + private UnnamedTableWithDefaultDatabaseField_OpenHelper_TableConfig() { + } + + public static void cacheTableConfigurations() { + List> tableConfigs = new ArrayList>(); + tableConfigs.add(UnnamedTableWithDefaultDatabaseField_TableConfig + .createConfig()); + DaoManager.addCachedDatabaseConfigs(tableConfigs); + } + + public static void createTables(ConnectionSource connectionSource) + throws SQLException { + TableUtils.createTable(connectionSource, + UnnamedTableWithDefaultDatabaseField.class); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/UnnamedTableWithDefaultDatabaseField_TableConfig.java b/src/test/resources/outputs/UnnamedTableWithDefaultDatabaseField_TableConfig.java new file mode 100644 index 00000000..fa5836f4 --- /dev/null +++ b/src/test/resources/outputs/UnnamedTableWithDefaultDatabaseField_TableConfig.java @@ -0,0 +1,20 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.table.DatabaseTableConfig; +import java.util.ArrayList; +import java.util.List; + +public final class UnnamedTableWithDefaultDatabaseField_TableConfig { + private UnnamedTableWithDefaultDatabaseField_TableConfig() { + } + + public static DatabaseTableConfig createConfig() { + List databaseFieldConfigs = new ArrayList(); + DatabaseFieldConfig fieldFieldConfig = new DatabaseFieldConfig("field"); + databaseFieldConfigs.add(fieldFieldConfig); + return new DatabaseTableConfig( + UnnamedTableWithDefaultDatabaseField.class, + "unnamedtablewithdefaultdatabasefield", databaseFieldConfigs); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig.java b/src/test/resources/outputs/UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig.java new file mode 100644 index 00000000..c0a61c6a --- /dev/null +++ b/src/test/resources/outputs/UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig.java @@ -0,0 +1,28 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.support.ConnectionSource; +import com.j256.ormlite.table.DatabaseTableConfig; +import com.j256.ormlite.table.TableUtils; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public final class UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig { + private UnnamedTableWithDefaultForeignCollectionField_OpenHelper_TableConfig() { + } + + public static void cacheTableConfigurations() { + List> tableConfigs = new ArrayList>(); + tableConfigs + .add(UnnamedTableWithDefaultForeignCollectionField_TableConfig + .createConfig()); + DaoManager.addCachedDatabaseConfigs(tableConfigs); + } + + public static void createTables(ConnectionSource connectionSource) + throws SQLException { + TableUtils.createTable(connectionSource, + UnnamedTableWithDefaultForeignCollectionField.class); + } +} \ No newline at end of file diff --git a/src/test/resources/outputs/UnnamedTableWithDefaultForeignCollectionField_TableConfig.java b/src/test/resources/outputs/UnnamedTableWithDefaultForeignCollectionField_TableConfig.java new file mode 100644 index 00000000..c58675df --- /dev/null +++ b/src/test/resources/outputs/UnnamedTableWithDefaultForeignCollectionField_TableConfig.java @@ -0,0 +1,23 @@ +package com.j256.ormlite.android.processor.inputs; + +import com.j256.ormlite.field.DatabaseFieldConfig; +import com.j256.ormlite.table.DatabaseTableConfig; +import java.util.ArrayList; +import java.util.List; + +public final class UnnamedTableWithDefaultForeignCollectionField_TableConfig { + private UnnamedTableWithDefaultForeignCollectionField_TableConfig() { + } + + public static DatabaseTableConfig createConfig() { + List databaseFieldConfigs = new ArrayList(); + DatabaseFieldConfig numbersFieldConfig = new DatabaseFieldConfig( + "numbers"); + numbersFieldConfig.setForeignCollection(true); + databaseFieldConfigs.add(numbersFieldConfig); + return new DatabaseTableConfig( + UnnamedTableWithDefaultForeignCollectionField.class, + "unnamedtablewithdefaultforeigncollectionfield", + databaseFieldConfigs); + } +} \ No newline at end of file