/*
* Copyright (C) Tony Green, Litepal Framework Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.litepal.tablemanager;
import java.util.ArrayList;
import java.util.Collection;
import org.litepal.LitePalBase;
import org.litepal.exceptions.DatabaseGenerateException;
import org.litepal.parser.LitePalAttr;
import org.litepal.tablemanager.model.AssociationsModel;
import org.litepal.tablemanager.model.TableModel;
import org.litepal.util.BaseUtility;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
/**
* This class is the basic class for managing database dynamically. It is used
* to create or update tables by the mapping classes from litepal.xml file.
* Generator is a superclass, it just read the fields from classes and format
* the fields types into database types. The analysis job is delegated to the
* subclasses to do. Then subclasses can invoke the execute method to finish the
* job or they can override to do their own logic.
*
* @author Tony Green
* @since 1.0
*/
public abstract class Generator extends LitePalBase {
public static final String TAG = "Generator";
/**
* The collection contains all table models. Use a global variable store
* table model to improve performance. Avoiding look up for table model each
* time.
*/
private Collection<TableModel> mTableModels;
/**
* The collection contains all association models.
*/
private Collection<AssociationsModel> mAllRelationModels;
/**
* This is a shortcut way to get all the table models for each model class
* defined in the mapping list. No need to iterate all the model classes and
* get table model for each one.
*
* @return A collection contains all table models.
*/
protected Collection<TableModel> getAllTableModels() {
if (mTableModels == null) {
mTableModels = new ArrayList<TableModel>();
}
if (!canUseCache()) {
mTableModels.clear();
for (String className : LitePalAttr.getInstance().getClassNames()) {
mTableModels.add(getTableModel(className));
}
}
return mTableModels;
}
/**
* This method is used to get all the association models which in the
* mapping list of litepal.xml file.
*
* @return Collection of RelationModel for all the mapping classes.
*
* @throws DatabaseGenerateException
*/
protected Collection<AssociationsModel> getAllAssociations() {
if (mAllRelationModels == null || mAllRelationModels.isEmpty()) {
mAllRelationModels = getAssociations(LitePalAttr.getInstance().getClassNames());
}
return mAllRelationModels;
}
/**
* Use the parameter SQLiteDatabase to execute the passing SQLs. Subclasses
* can add their own logic when do the executing job by overriding this
* method.
*
* @param sqls
* SQLs that want to execute.
* @param db
* instance of SQLiteDatabase
*
* @throws DatabaseGenerateException
*/
protected void execute(String[] sqls, SQLiteDatabase db) {
String throwSQL = "";
try {
if (sqls != null) {
for (String sql : sqls) {
throwSQL = sql;
db.execSQL(BaseUtility.changeCase(sql));
}
}
} catch (SQLException e) {
throw new DatabaseGenerateException(DatabaseGenerateException.SQL_ERROR + throwSQL);
}
}
/**
* Add association to all the tables based on the associations between class
* models.
*
* @param db
* Instance of SQLiteDatabase.
* @param force
* Drop the table first if it already exists.
*/
private static void addAssociation(SQLiteDatabase db, boolean force) {
AssociationCreator associationsCreator = new Creator();
associationsCreator.addOrUpdateAssociation(db, force);
}
/**
* Update associations to all the associated tables in the database. Remove
* dump foreign key columns and dump intermediate join tables.
*
* @param db
* Instance of SQLiteDatabase.
*/
private static void updateAssociations(SQLiteDatabase db) {
AssociationUpdater associationUpgrader = new Upgrader();
associationUpgrader.addOrUpdateAssociation(db, false);
}
/**
* Upgrade all the tables in the database, including remove dump columns and
* add new columns.
*
* @param db
* Instance of SQLiteDatabase.
*/
private static void upgradeTables(SQLiteDatabase db) {
Upgrader upgrader = new Upgrader();
upgrader.createOrUpgradeTable(db, false);
}
/**
* Create tables based on the class models defined in the litepal.xml file.
* After the tables are created, add association to these tables based on
* the associations between class models.
*
* @param db
* Instance of SQLiteDatabase.
* @param force
* Drop the table first if it already exists.
*/
private static void create(SQLiteDatabase db, boolean force) {
Creator creator = new Creator();
creator.createOrUpgradeTable(db, force);
}
/**
* Drop the tables which are no longer exist in the mapping list but created
* before.
*
* @param db
* Instance of SQLiteDatabase.
*/
private static void drop(SQLiteDatabase db) {
Dropper dropper = new Dropper();
dropper.createOrUpgradeTable(db, false);
}
/**
* If the table models in the collection has the same size as classes
* defined in the mapping list, it means that the table models are exist.
* Can use cache.
*
* @return Can use the cache for the table models or not.
*/
private boolean canUseCache() {
if (mTableModels == null) {
return false;
}
return mTableModels.size() == LitePalAttr.getInstance().getClassNames().size();
}
/**
* Create tables based on the class models defined in the litepal.xml file.
* After the tables are created, add association to these tables based on
* the associations between class models.
*
* @param db
* Instance of SQLiteDatabase.
*/
static void create(SQLiteDatabase db) {
create(db, true);
addAssociation(db, true);
}
/**
* Upgrade tables to make sure when model classes are changed, the
* corresponding tables in the database should be always synchronized with
* them.
*
* @param db
* Instance of SQLiteDatabase.
*/
static void upgrade(SQLiteDatabase db) {
drop(db);
create(db, false);
updateAssociations(db);
upgradeTables(db);
addAssociation(db, false);
}
/**
* Analysis the TableModel by the purpose of subclasses, and generate a SQL
* to do the intention job. The implementation of this method is totally
* delegated to the subclasses.
*
* @param db
* Instance of SQLiteDatabase.
* @param force
* Drop the table first if it already exists.
*/
protected abstract void createOrUpgradeTable(SQLiteDatabase db, boolean force);
/**
* Analysis the {@link AssociationsModel} by the purpose of subclasses, and
* generate a SQL to do the intention job. The implementation of this method
* is totally delegated to the subclasses.
*
* @param db
* Instance of SQLiteDatabase.
* @param force
* Drop the table first if it already exists.
*/
protected abstract void addOrUpdateAssociation(SQLiteDatabase db, boolean force);
}