package com.indience.iaf.database.core;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;

import com.indience.iaf.core.ApplicationManager;
import com.indience.iaf.core.FrameworkManager;
import com.indience.iaf.core.RuntimeEngine;
import com.indience.iaf.exception.ApplicationException;
import com.indience.iaf.logger.Logger;
import com.indience.iaf.model.AttachmentItem;
import com.indience.iaf.model.InfoMessage;
import com.indience.iaf.model.JSDataStructure;
import com.indience.iaf.model.StructureMeta;
import com.indience.iaf.sync.attachment.Attachment;
import com.indience.iaf.sync.attachment.AttachmentQHelper;
import com.indience.iaf.utils.FrameworkConstants;
import com.indience.iaf.utils.FrameworkHelper;
import com.indience.iaf.utils.RandomStringGenerator;

/**
 */
public class DataManagerImpl implements IDataManager {

	/**
	 * Field database.
	 */
	private SQLiteDatabase database = null;
	private UnviredSQLiteOpenHelper sqliteOpenHelper = null;

	private String databaseName = null;
	private int version;

	/**
	 * Field dataStructureTableMapping.
	 */
	private DataStructureTableMapping dataStructureTableMapping = null;

	private RuntimeEngine runtimeEngine = null;
	private ApplicationManager applicationManager = null;

	private boolean isEmulator = false;

	/**
	 * Constructor for DataManagerImpl.
	 * 
	 * @param path
	 *            String
	 * @param databaseName
	 *            String
	 * @throws DBException
	 */
	public DataManagerImpl(String databaseName, int version) throws DBException {

		if (databaseName == null || databaseName.length() <= 0) {
			throw new DBException(Class.class.getName(), "Constructor", "Database name is invalid: " + databaseName);
		}

		this.databaseName = databaseName;
		this.version = version;

		dataStructureTableMapping = DataStructureTableMapping.getInstance();
		runtimeEngine = RuntimeEngine.getInstance();

		isEmulator = FrameworkHelper.isEmulator();

		sqliteOpenHelper = new UnviredSQLiteOpenHelper(FrameworkHelper.applicationContext, databaseName, this.version);
	}

	/**
	 * Open or create the database.
	 * 
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#openOrCreate()
	 */
	public void openOrCreate(String key) throws DBException {

		if (isEmulator) {
			key = "";
		}

		try {
			// net.sqlcipher.database.SQLiteDatabase.loadLibs(FrameworkHelper.applicationContext);

			database = sqliteOpenHelper.getWritableDatabase();

			// Enable foreign keys
			execute("PRAGMA foreign_keys=ON;");

		} catch (Exception e) {
			Logger.log(Logger.LEVEL_ERROR, this.getClass().getName(), "openOrCreate", "Database open exception: " + e.getMessage());
			throw new DBException(this.getClass().getName(), "openOrCreate", e.getMessage());
		}
	}

	/**
	 * Check if the database exist
	 * 
	 * @return boolean
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#exists()
	 */
	public boolean exists() throws DBException {

		File dbFile = FrameworkHelper.applicationContext.getDatabasePath(databaseName);

		try {
			return dbFile.exists();
		} catch (SecurityException securityException) {
			throw new DBException(Class.class.getName(), "exists", "SecurityException caught while doing database file exist or not, " + securityException.getMessage());
		}
	}

	/**
	 * Method close.
	 * 
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#close()
	 */
	public void close() throws DBException {
		if (database != null) {
			database.close();
		}
	}

	/**
	 * It starts the new transaction.
	 * 
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#beginTransaction()
	 */
	public void beginTransaction() throws DBException {
		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "beginTransaction", "Transaction started.");
		database.beginTransaction();
	}

	/**
	 * It first set current transaction to successful and then ends the current
	 * transaction.
	 * 
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#endTransaction()
	 */
	public void endTransaction() throws DBException {

		try {
			database.setTransactionSuccessful();
		} catch (Exception setTransactionSuccessfulException) {
			throw new DBException(Class.class.getName(), "commitTransaction", "Exception caught while commiting the transaction, " + setTransactionSuccessfulException.getMessage());
		} finally {
			database.endTransaction();
		}

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "commitTransaction", "Transaction commited.");
	}

	/**
	 * This is used to manager synchronization between transactions. 1. Begin
	 * transaction. 2. Callback back the transaction. 3. Commit the transaction.
	 * 
	 * @param callback
	 *            ITransactable
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#executeTransaction(ITransactable)
	 */
	public synchronized void executeTransaction(ITransactable callback) throws DBException {

		Logger.log(Logger.LEVEL_DEBUG, Class.class.getName(), "executeTransaction", "Execute transaction starts.");

		try {
			beginTransaction();

			boolean successful = callback.transaction(this);

			if (successful)
				endTransaction();
		} catch (Throwable throwable) {
			Logger.log(Logger.LEVEL_ERROR, Class.class.getName(), "executeTransaction", "Throwable exception caught while executing transaction, " + throwable.getMessage());
			throw new DBException(Class.class.getName(), "executeTransaction", "Throwable exception caught while executing transaction, " + throwable.getMessage());
		}

		Logger.log(Logger.LEVEL_DEBUG, Class.class.getName(), "executeTransaction", "Transaction Successful done.");
	}

	/**
	 * Method dropDatabase.
	 * 
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#dropDatabase()
	 */
	public void dropDatabase() throws DBException {

		File dbFile = FrameworkHelper.applicationContext.getDatabasePath(databaseName);

		try {
			dbFile.delete();
		} catch (Exception databasePathException) {
			throw new DBException(Class.class.getName(), "dropDatabase", "DatabasePathException - " + databasePathException.getMessage());
		}
	}

	/**
	 * Method dropTable.
	 * 
	 * @param tableName
	 *            String
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#dropTable(String)
	 */
	public void dropTable(String tableName) throws DBException {
		String query = prepareDropTableQuery(tableName);
		Logger.log(Logger.LEVEL_DEBUG, Class.class.getName(), "dropTable", "Query: " + query);
		execute(query);
	}

	/**
	 * Method dropIndex.
	 * 
	 * @param indexName
	 *            String
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#dropIndex(String)
	 */
	public void dropIndex(String indexName) throws DBException {
		String query = prepareDropIndexQuery(indexName);
		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "dropIndex", "Query: " + query);
		execute(query);
	}

	/**
	 * Method createTable.
	 * 
	 * @param tableName
	 *            String
	 * @param columnNames
	 *            String[]
	 * @param columnTypes
	 *            String[]
	 * @param primaryKeys
	 *            String[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#createTable(String,
	 *      String[], String[], String[])
	 */
	public void createTable(String tableName, String[] columnNames, String[] columnTypes, String[] primaryKeys) throws DBException {
		createTable(tableName, columnNames, columnTypes, primaryKeys, null, null, null, null, null);
	}

	/**
	 * Method createTable.
	 * 
	 * @param tableName
	 *            String
	 * @param columnNames
	 *            String[]
	 * @param columnTypes
	 *            String[]
	 * @param primaryKeys
	 *            String[]
	 * @param uniqueKeys
	 *            String[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#createTable(String,
	 *      String[], String[], String[], String[])
	 */
	public void createTable(String tableName, String[] columnNames, String[] columnTypes, String[] primaryKeys, String[] uniqueKeys) throws DBException {
		createTable(tableName, columnNames, columnTypes, primaryKeys, uniqueKeys, null, null, null, null);
	}

	/**
	 * Method createTable.
	 * 
	 * @param tableName
	 *            String
	 * @param columnNames
	 *            String[]
	 * @param columnTypes
	 *            String[]
	 * @param primaryKeys
	 *            String[]
	 * @param uniqueKeys
	 *            String[]
	 * @param mandatoryFields
	 *            boolean[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#createTable(String,
	 *      String[], String[], String[], String[], boolean[])
	 */
	public void createTable(String tableName, String[] columnNames, String[] columnTypes, String[] primaryKeys, String[] uniqueKeys, boolean[] mandatoryFields, String[] foreignKeys, String foreignKeyTableName, String[] foreignKeyTableKeys) throws DBException {

		String query = prepareCreateQuery(tableName, columnNames, columnTypes, primaryKeys, uniqueKeys, mandatoryFields, foreignKeys, foreignKeyTableName, foreignKeyTableKeys);

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "createTable", "Query: " + query);
		execute(query);
	}

	/**
	 * Method createIndex.
	 * 
	 * @param indexName
	 *            String
	 * @param structureName
	 *            String
	 * @param fieldNames
	 *            String[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#createIndex(String,
	 *      String, String[])
	 */
	public void createIndex(String indexName, String structureName, String[] fieldNames) throws DBException {

		String query = prepareCreateIndexQuery(indexName, structureName, fieldNames);

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "createIndex", "Query: " + query);
		execute(query);
	}

	/**
	 * Method checkDuplicateGID.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 * @return IDataStructure
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#checkDuplicateGID(IDataStructure)
	 */
	public IDataStructure checkDuplicateGID(IDataStructure dataStructure) throws DBException {

		String tableName = dataStructure.getTableName();

		// Check for duplicates
		Object[] gidValues = dataStructure.getGids();
		String[] gidFieldNames = dataStructure.getGidFieldNames();
		Object gids = "";

		String whereClauseForDuplicate = "";

		for (int i = 0; i < gidFieldNames.length; i++) {

			if (i == gidFieldNames.length - 1)
				whereClauseForDuplicate = whereClauseForDuplicate + " " + gidFieldNames[i] + " = " + "'" + gidValues[i] + "'";
			else
				whereClauseForDuplicate = whereClauseForDuplicate + " " + gidFieldNames[i] + " = " + "'" + gidValues[i] + "'" + " AND ";

			gids = gids + " - " + gidValues[i];
		}

		IDataStructure[] duplicateDataStructures = get(tableName, whereClauseForDuplicate);

		// Throw exception on duplicate
		if (duplicateDataStructures != null && duplicateDataStructures.length > 0) {
			return duplicateDataStructures[0];
		}

		return null;
	}

	/**
	 * Method insert.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#insert(IDataStructure)
	 */
	public void insert(IDataStructure dataStructure) throws DBException {

		String tableName = dataStructure.getTableName();
		dataStructure.setTimeStamp(Long.valueOf(System.currentTimeMillis()));

		Enumeration<String> keysForParameters = dataStructure.getFieldNames();

		Vector<String> fieldNames = new Vector<String>();

		while (keysForParameters.hasMoreElements()) {
			fieldNames.addElement((String) keysForParameters.nextElement());
		}

		int fieldCount = fieldNames.size();

		Object[] values = new Object[fieldCount];

		for (int i = 0; i < fieldCount; i++) {

			String fieldName = (String) fieldNames.get(i);
			values[i] = dataStructure.getField(fieldName);
		}

		String query = prepareInsertBindQuery(tableName, fieldNames);

		executeBindQuery(query, values);
	}

	/**
	 * Method delete.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#delete(IDataStructure)
	 */
	public void delete(IDataStructure dataStructure) throws DBException {

		String tableName = dataStructure.getTableName();
		Long lid = dataStructure.getLid();

		// // Delete all children and then delete the header
		// if(dataStructure.isHeader()) {
		//
		// Hashtable<String, IDataStructure[]> children =
		// this.getAllChildren(dataStructure);
		//
		// if(children != null) {
		//
		// Enumeration<IDataStructure[]> childrenDataStructures =
		// children.elements();
		//
		// IDataStructure[] itemDataStructures = null;
		//
		// while(childrenDataStructures.hasMoreElements()) {
		//
		// itemDataStructures = (IDataStructure[])
		// childrenDataStructures.nextElement();
		//
		// if(itemDataStructures == null)
		// continue;
		//
		// for(int i = 0; i < itemDataStructures.length; i++) {
		//
		// this.delete(itemDataStructures[i]);
		// }
		// }
		// }
		// }

		String whereClause = IDataStructure.FIELD_LID + " = '" + lid.toString() + "'";

		delete(tableName, whereClause);
	}

	/**
	 * Method delete.
	 * 
	 * @param tableName
	 *            String
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#delete(String)
	 */
	public void delete(String tableName) throws DBException {
		delete(tableName, null);
	}

	/**
	 * Method delete.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#delete(String, String)
	 */
	public void delete(String tableName, String whereClause) throws DBException {

		String query = prepareDeleteQuery(tableName, whereClause);

		if (applicationManager == null) {
			applicationManager = ApplicationManager.getInstance();
		}

		checkIfTableSupportsAttachmentsAndDelete(tableName, whereClause);

		// StructureMeta structureMeta =
		// applicationManager.getStructureMeta(tableName);
		//
		// /*
		// * If header delete the header and its children
		// */
		// if(structureMeta != null && structureMeta.getIsHeader()) {
		//
		// final IDataStructure[] headerDataStructures = get(tableName,
		// whereClause);
		//
		// if(headerDataStructures != null) {
		//
		// ITransactable transaction = new ITransactable() {
		//
		// @Override
		// public boolean transaction(IDataManager dataManager) throws
		// DBException {
		//
		// for(int i = 0; i < headerDataStructures.length; i++) {
		//
		// delete(headerDataStructures[i]);
		//
		// }
		//
		// return true;
		// }
		// };
		//
		// executeTransaction(transaction);
		// }
		//
		// return;
		// }

		execute(query);
	}

	/**
	 * Method insertOrUpdateBasedOnGID.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#insertOrUpdateBasedOnGID(IDataStructure)
	 */
	public void insertOrUpdateBasedOnGID(IDataStructure dataStructure) throws DBException {

		String tableName = dataStructure.getTableName();
		dataStructure.setTimeStamp(Long.valueOf(System.currentTimeMillis()));

		Enumeration<String> keysForParameters = dataStructure.getFieldNames();

		Vector<String> fieldNames = new Vector<String>();

		while (keysForParameters.hasMoreElements()) {
			fieldNames.addElement((String) keysForParameters.nextElement());
		}

		int fieldCount = fieldNames.size();

		Object[] values = new Object[fieldCount];

		for (int i = 0; i < fieldCount; i++) {

			String fieldName = (String) fieldNames.get(i);
			values[i] = dataStructure.getField(fieldName);
		}

		String query = prepareReplaceBindQuery(tableName, fieldNames);

		executeBindQuery(query, values);
	}

	/**
	 * Method update.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#update(IDataStructure)
	 */
	public void update(IDataStructure dataStructure) throws DBException {

		dataStructure.setTimeStamp(Long.valueOf(System.currentTimeMillis()));

		String tableName = dataStructure.getTableName();
		Long lid = dataStructure.getLid();
		String whereClause = IDataStructure.FIELD_LID + " = '" + lid.toString() + "'";

		Enumeration<String> keysForParameters = dataStructure.getFieldNames();

		Vector<String> fieldNames = new Vector<String>();

		while (keysForParameters.hasMoreElements()) {

			String fieldName = (String) keysForParameters.nextElement();

			if (IDataStructure.FIELD_LID.equalsIgnoreCase(fieldName)) {
				continue;
			}

			fieldNames.addElement(fieldName);
		}

		int fieldCount = fieldNames.size();

		String[] columnNames = new String[fieldCount];
		Object[] columnValues = new Object[fieldCount];

		for (int i = 0; i < fieldCount; i++) {

			String fieldName = (String) fieldNames.get(i);
			columnNames[i] = fieldName;
			columnValues[i] = dataStructure.getField(fieldName);
		}

		update(tableName, columnNames, columnValues, whereClause);
	}

	/**
	 * Method update.
	 * 
	 * @param tableName
	 *            String
	 * @param columnNames
	 *            String[]
	 * @param columnValues
	 *            Object[]
	 * @param whereClause
	 *            String
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#update(String, String[],
	 *      Object[], String)
	 */
	public void update(String tableName, String[] columnNames, Object[] columnValues, String whereClause) throws DBException {

		String query = prepareUpdateBindQuery(tableName, columnNames, whereClause);
		executeBindQuery(query, columnValues);
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String)
	 */
	public IDataStructure[] get(String tableName) throws DBException {
		return get(tableName, "");
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, String)
	 */
	public IDataStructure[] get(String tableName, String whereClause) throws DBException {

		String query = prepareSelectQuery(tableName, whereClause, null);
		Cursor cursorToFormDataStructure = executeQuery(query);

		IDataStructure[] dataStructure = getDataStructuresFromCursor(tableName, cursorToFormDataStructure, null);
		return dataStructure;
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param className
	 *            Class
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, Class)
	 */
	public IDataStructure[] get(String tableName, Class className) throws DBException {
		return get(tableName, null, null, className);
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @param className
	 *            Class
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, String,
	 *      Class)
	 */
	public IDataStructure[] get(String tableName, String whereClause, Class className) throws DBException {
		return get(tableName, whereClause, null, className);
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, String[])
	 */
	public IDataStructure[] get(String tableName, String[] orderByFields) throws DBException {
		return get(tableName, null, orderByFields, null);
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, String,
	 *      String[])
	 */
	public IDataStructure[] get(String tableName, String whereClause, String[] orderByFields) throws DBException {
		return get(tableName, whereClause, orderByFields, null);
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @param className
	 *            Class
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, String[],
	 *      Class)
	 */
	public IDataStructure[] get(String tableName, String[] orderByFields, Class className) throws DBException {
		return get(tableName, null, orderByFields, className);
	}

	/**
	 * Method get.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @param className
	 *            Class
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#get(String, String,
	 *      String[], Class)
	 */
	public IDataStructure[] get(String tableName, String whereClause, String[] orderByFields, Class className) throws DBException {

		String query = prepareSelectQuery(tableName, whereClause, orderByFields);

		Cursor cursorToFormDataStructure = executeQuery(query);
		IDataStructure[] dataStructures = getDataStructuresFromCursor(tableName, cursorToFormDataStructure, className);

		return dataStructures;
	}

	public IDataStructure getBasedOnGID(IDataStructure dataStructure) throws DBException {

		String tableName = dataStructure.getTableName();

		Object[] gidValues = dataStructure.getGids();
		String[] gidFieldNames = dataStructure.getGidFieldNames();
		Object gids = "";

		String whereClauseForDataStructureBasedOnGID = "";

		for (int i = 0; i < gidFieldNames.length; i++) {

			if (i == gidFieldNames.length - 1)
				whereClauseForDataStructureBasedOnGID = whereClauseForDataStructureBasedOnGID + " " + gidFieldNames[i] + " = " + "'" + gidValues[i] + "'";
			else
				whereClauseForDataStructureBasedOnGID = whereClauseForDataStructureBasedOnGID + " " + gidFieldNames[i] + " = " + "'" + gidValues[i] + "'" + " AND ";

			gids = gids + " - " + gidValues[i];
		}

		IDataStructure[] dataStructuresBasedOnGID = get(tableName, whereClauseForDataStructureBasedOnGID);

		if (dataStructuresBasedOnGID != null && dataStructuresBasedOnGID.length > 0) {
			return dataStructuresBasedOnGID[0];
		}

		return null;
	}

	/**
	 * Method getChildren.
	 * 
	 * @param tableName
	 *            String
	 * @param headerDataStructure
	 *            IDataStructure
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#getChildren(String,
	 *      IDataStructure)
	 */
	public IDataStructure[] getChildren(String tableName, IDataStructure headerDataStructure) throws DBException {

		String whereClause = IDataStructure.FIELD_FID + " = " + headerDataStructure.getLid();

		IDataStructure[] children = get(tableName, whereClause);

		return children;
	}

	/**
	 * Method getChildren.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @param headerDataStructure
	 *            IDataStructure
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#getChildren(String,
	 *      String, String[], IDataStructure)
	 */
	public IDataStructure[] getChildren(String tableName, String whereClause, String[] orderByFields, IDataStructure headerDataStructure) throws DBException {

		if (whereClause == null || whereClause.equalsIgnoreCase(""))
			whereClause = IDataStructure.FIELD_FID + " = " + headerDataStructure.getLid();
		else
			whereClause = whereClause + " AND " + IDataStructure.FIELD_FID + " = " + headerDataStructure.getLid();

		IDataStructure[] children = get(tableName, whereClause, orderByFields);

		return children;
	}

	/**
	 * Method getChildren.
	 * 
	 * @param tableName
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @param headerDataStructure
	 *            IDataStructure
	 * @return IDataStructure[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#getChildren(String,
	 *      String[], IDataStructure)
	 */
	public IDataStructure[] getChildren(String tableName, String[] orderByFields, IDataStructure headerDataStructure) throws DBException {

		String whereClause = IDataStructure.FIELD_FID + " = " + headerDataStructure.getLid();

		IDataStructure[] children = get(tableName, whereClause, orderByFields);

		return children;
	}

	/**
	 * Method getAllChildren.
	 * 
	 * @param headerDataStructure
	 *            IDataStructure
	 * @return Hashtable<String,IDataStructure[]>
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#getAllChildren(IDataStructure)
	 */
	public Hashtable<String, IDataStructure[]> getAllChildren(IDataStructure headerDataStructure) throws DBException {

		Hashtable<String, IDataStructure[]> allChildren = new Hashtable<String, IDataStructure[]>();

		String[] childrenTableNames = headerDataStructure.getChildrenTableNames();

		IDataStructure[] children = null;

		for (int i = 0; i < childrenTableNames.length; i++) {

			children = getChildren(childrenTableNames[i], headerDataStructure);

			if (children != null && children.length > 0)
				allChildren.put(childrenTableNames[i], children);
		}

		return allChildren;
	}

	/**
	 * Method count.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @return int
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#count(String, String)
	 */
	public int count(String tableName, String whereClause) throws DBException {

		String query = null;

		if (whereClause == null || whereClause.length() <= 0)
			query = "SELECT COUNT(*) FROM " + tableName;
		else
			query = "SELECT COUNT(*) FROM " + tableName + " WHERE " + whereClause;

		Cursor cursor = executeQuery(query);

		try {
			cursor.moveToFirst();
			return cursor.getInt(0);
		} catch (SQLException sqlException) {
			throw new DBException(Class.class.getName(), "count", "SQLException caught while getting count of rows from cursor, " + sqlException.getMessage());
		} catch (Exception exception) {
			throw new DBException(Class.class.getName(), "count", "Exception caught while getting count of rows from cursor, " + exception.getMessage());
		} finally {
			cursor.close();
		}
	}

	/**
	 * Method max.
	 * 
	 * @param tableName
	 *            String
	 * @param fieldName
	 *            String
	 * @param whereClause
	 *            String
	 * @return Object
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#max(String, String,
	 *      String)
	 */
	public Object max(String tableName, String fieldName, String whereClause) throws DBException {

		String query = null;

		if (whereClause == null || whereClause.equalsIgnoreCase(""))
			query = "SELECT MAX(" + fieldName + ") FROM " + tableName;
		else
			query = "SELECT MAX(" + fieldName + ") FROM " + tableName + " WHERE " + whereClause;

		Cursor cursor = executeQuery(query);

		try {
			cursor.moveToFirst();
			return cursor.getInt(0);
		} catch (SQLException sqlException) {
			throw new DBException(Class.class.getName(), "max", "SQLException caught while getting max count, " + sqlException.getMessage());
		} catch (Exception exception) {
			throw new DBException(Class.class.getName(), "max", "Exception caught while getting max count, " + exception.getMessage());
		} finally {
			cursor.close();
		}
	}

	/**
	 * Method min.
	 * 
	 * @param tableName
	 *            String
	 * @param fieldName
	 *            String
	 * @param whereClause
	 *            String
	 * @return Object
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#min(String, String,
	 *      String)
	 */
	public Object min(String tableName, String fieldName, String whereClause) throws DBException {

		String query = null;

		if (whereClause == null || whereClause.equalsIgnoreCase(""))
			query = "SELECT MIN(" + fieldName + ") FROM " + tableName;
		else
			query = "SELECT MIN(" + fieldName + ") FROM " + tableName + " WHERE " + whereClause;

		Cursor cursor = executeQuery(query);

		try {
			cursor.moveToFirst();
			return cursor.getInt(0);
		} catch (SQLException sqlException) {
			throw new DBException(Class.class.getName(), "min", "SQLException caught while getting minimum count, " + sqlException.getMessage());
		} catch (Exception exception) {
			throw new DBException(Class.class.getName(), "min", "SQLException caught while getting minimum count, " + exception.getMessage());
		} finally {
			cursor.close();
		}
	}

	/**
	 * Method total.
	 * 
	 * @param tableName
	 *            String
	 * @param fieldName
	 *            String
	 * @param whereClause
	 *            String
	 * @return Object
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#total(String, String,
	 *      String)
	 */
	public Object total(String tableName, String fieldName, String whereClause) throws DBException {

		String query = null;

		if (whereClause == null || whereClause.equalsIgnoreCase(""))
			query = "SELECT TOTAL(" + fieldName + ") FROM " + tableName;
		else
			query = "SELECT TOTAL(" + fieldName + ") FROM " + tableName + " WHERE " + whereClause;

		Cursor cursor = executeQuery(query);

		try {
			cursor.moveToFirst();
			return cursor.getInt(0);
		} catch (SQLException sqlException) {
			throw new DBException(Class.class.getName(), "total", "SQLException caught while getting total count, " + sqlException.getMessage());
		} catch (Exception exception) {
			throw new DBException(Class.class.getName(), "total", "SQLException caught while getting total count, " + exception.getMessage());
		} finally {
			cursor.close();
		}
	}

	/**
	 * Method execute.
	 * 
	 * @param query
	 *            String
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#execute(String)
	 */
	public void execute(String query) throws DBException {

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "execute", "Query: " + query);

		try {
			database.execSQL(query);
		} catch (Exception sqlException) {
			throw new DBException(Class.class.getName(), "execute", "Exception caught while executing the query, QUERY: " + query + ", " + sqlException.getMessage());
		}
	}

	/**
	 * Method executeBindQuery.
	 * 
	 * @param query
	 *            String
	 * @param columnValues
	 *            Object[]
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#executeBindQuery(String,
	 *      Object[])
	 */
	public void executeBindQuery(String query, Object[] columnValues) throws DBException {

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "executeBindQuery", "Query: " + query);

		SQLiteStatement statement = compileStatement(query);

		try {

			for (int i = 0; i < columnValues.length; i++) {

				if (columnValues[i] == null)
					statement.bindNull(i + 1);
				else if (columnValues[i] instanceof String)
					statement.bindString(i + 1, ((String) columnValues[i]));
				else if (columnValues[i] instanceof Integer)
					statement.bindLong(i + 1, ((Integer) columnValues[i]).intValue());
				else if (columnValues[i] instanceof Long)
					statement.bindLong(i + 1, ((Long) columnValues[i]).longValue());
				else if (columnValues[i] instanceof Float)
					statement.bindDouble(i + 1, ((Float) columnValues[i]).floatValue());
				else if (columnValues[i] instanceof Double)
					statement.bindDouble(i + 1, ((Double) columnValues[i]).doubleValue());
				else if (columnValues[i] instanceof byte[]) {

					byte[] bytes = (byte[]) columnValues[i];
					byte[] compressedBytes = compressBytes(bytes);

					statement.bindBlob(i + 1, compressedBytes);
				}
			}

			statement.execute();
			statement.clearBindings();

		} catch (Exception sqlException) {
			createAndSaveInfoMessage("Exception caught while binding data, " + sqlException.getMessage());
			throw new DBException(Class.class.getName(), "executeBindQuery", "Exception caught while binding data, " + sqlException.getMessage());
		} finally {
			if (statement != null)
				statement.close();
		}
	}

	/**
	 * Method executeQuery.
	 * 
	 * @param sqlStatement
	 *            String
	 * @return Cursor
	 * @throws DBException
	 * @see com.indience.iaf.database.core.IDataManager#executeQuery(String)
	 */
	public Cursor executeQuery(String sqlStatement) throws DBException {

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "executeQuery", "Query: " + sqlStatement);

		try {
			return database.rawQuery(sqlStatement, null);
		} catch (Exception sqlException) {
			throw new DBException(Class.class.getName(), "executeQuery", "Exception caught while executing raw query, " + sqlException.getMessage());
		}
	}

	/**
	 * Method compileStatement.
	 * 
	 * @param query
	 *            String
	 * @return SQLiteStatement
	 * @throws DBException
	 */
	private SQLiteStatement compileStatement(String query) throws DBException {
		try {
			return database.compileStatement(query);
		} catch (Exception sqlException) {
			throw new DBException(Class.class.getName(), "compileStatement", "Exception caught while compiling statement, " + sqlException.getMessage());
		}
	}

	// Unimplemented Methods

	/**
	 * Method prepareCreateQuery.
	 * 
	 * @param tableName
	 *            String
	 * @param tableColumnNames
	 *            String[]
	 * @param tableColumnTypes
	 *            Object[]
	 * @param primaryKeys
	 *            String[]
	 * @param uniqueKeys
	 *            String[]
	 * @param mandatoryFields
	 *            boolean[]
	 * @param foreignKeys
	 * @param foreignKeyTableName
	 * @return String
	 */
	private String prepareCreateQuery(String tableName, String[] tableColumnNames, Object[] tableColumnTypes, String[] primaryKeys, String[] uniqueKeys, boolean[] mandatoryFields, String[] foreignKeys, String foreignKeyTableName, String[] foreignKeyTableKeys) {

		String query = "";

		String primaryKey = "";
		String uniqueKey = "";
		String foreignKey = "";
		String foreignKeyTableKey = "";

		if (primaryKey != null) {
			for (int i = 0; i < primaryKeys.length; i++) {
				if (i == 0) {
					primaryKey += primaryKeys[i];
				} else {
					primaryKey += ", " + primaryKeys[i];
				}
			}
		}

		if (uniqueKeys != null) {
			for (int i = 0; i < uniqueKeys.length; i++) {
				if (i == 0) {
					uniqueKey += uniqueKeys[i];
				} else {
					uniqueKey += ", " + uniqueKeys[i];
				}
			}
		}

		if (foreignKeyTableName != null) {

			for (int i = 0; i < foreignKeys.length; i++) {
				if (i == 0) {
					foreignKey += foreignKeys[i];
				} else {
					foreignKey += ", " + foreignKeys[i];
				}
			}

			for (int i = 0; i < foreignKeyTableKeys.length; i++) {
				if (i == 0) {
					foreignKeyTableKey += foreignKeyTableKeys[i];
				} else {
					foreignKeyTableKey += ", " + foreignKeyTableKeys[i];
				}
			}
		}

		int noOfColumns = 0;

		if (tableColumnNames != null)
			noOfColumns = tableColumnNames.length;

		query = "CREATE TABLE '" + tableName + "' (";

		for (int i = 0; i < noOfColumns; i++) {

			if (i == 0)
				query += "'" + tableColumnNames[i] + "' ";
			else
				query += ", '" + tableColumnNames[i] + "' ";

			if (tableColumnTypes != null) {
				try {
					query += tableColumnTypes[i];
				} catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
					new DBException(Class.class.getName(), "prepareCreateQuery : CreateTable", "ArrayIndexOutOfBoundsException - " + arrayIndexOutOfBoundsException.getMessage());
				}
			}

			if (mandatoryFields != null) {
				boolean isMandatory = mandatoryFields[i];
				if (isMandatory) {
					query += " NOT NULL";
				}
			}
		}

		if (primaryKey != null && !primaryKey.equalsIgnoreCase("")) {
			query += ", " + "PRIMARY KEY(" + primaryKey + ")";
		}

		if (uniqueKey != null && !uniqueKey.equalsIgnoreCase("")) {
			query += ", " + "UNIQUE(" + uniqueKey + ")";
		}

		if (foreignKeyTableName != null && !foreignKeyTableName.equalsIgnoreCase("")) {
			query += ", FOREIGN KEY(" + foreignKey + ") REFERENCES " + foreignKeyTableName + "(" + foreignKeyTableKey + ")" + "ON UPDATE CASCADE ON DELETE CASCADE";
		}

		query = query + ")";

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "prepareCreateQuery", query);

		return query;
	}

	/**
	 * Method prepareCreateIndexQuery.
	 * 
	 * @param indexName
	 *            String
	 * @param structureName
	 *            String
	 * @param fieldNames
	 *            String[]
	 * @return String
	 */
	private String prepareCreateIndexQuery(String indexName, String structureName, String[] fieldNames) {

		String query = "";

		query = "CREATE INDEX " + indexName + " ON " + structureName + "(";

		for (int i = 0; i < fieldNames.length; i++) {

			if (i == 0) {
				query += fieldNames[i];
			} else {
				query += ", " + fieldNames[i];
			}
		}

		query += ")";

		return query;
	}

	/**
	 * Method prepareInsertBindQuery.
	 * 
	 * @param tableName
	 *            String
	 * @param tableColumnNames
	 *            String[]
	 * @return String
	 */
	private String prepareInsertBindQuery(String tableName, Vector<String> tableColumnNames) {

		String query = "";

		int noOfColumns = 0;

		if (tableColumnNames != null)
			noOfColumns = tableColumnNames.size();

		query = "INSERT INTO " + tableName + " (";

		for (int i = 0; i < noOfColumns; i++) {
			String key = tableColumnNames.elementAt(i);
			query = query + key + ",";
		}

		int queryLength = query.length();
		char[] queryChar = query.toCharArray();

		queryChar[queryLength - 1] = ')';

		query = String.valueOf(queryChar);

		query = query + " VALUES (";

		String value = "";

		for (int i = 0; i < noOfColumns; i++) {
			if (value.equalsIgnoreCase(""))
				value = value + "?";
			else
				value = value + ", " + "?";
		}

		query = query + value + ")";

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "prepareInsertBindQuery", query);

		return query;
	}

	/**
	 * Method prepareDropTableQuery.
	 * 
	 * @param tableName
	 *            String
	 * @return String
	 */
	private String prepareDropTableQuery(String tableName) {

		String query = "DROP TABLE " + tableName;
		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "prepareDropTableQuery", query);

		return query;
	}

	/**
	 * Method prepareDropIndexQuery.
	 * 
	 * @param indexName
	 *            String
	 * @return String
	 */
	private String prepareDropIndexQuery(String indexName) {

		String query = "";

		query += "DROP INDEX " + indexName;
		return query;
	}

	/**
	 * Method prepareSelectQuery.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @param orderByFields
	 *            String[]
	 * @return String
	 */
	private String prepareSelectQuery(String tableName, String whereClause, String[] orderByFields) {

		String query = "SELECT * FROM " + tableName + " ";

		if (whereClause != null && !whereClause.equalsIgnoreCase(""))
			query = query + "WHERE " + whereClause;

		if (orderByFields != null && orderByFields.length > 0) {

			query = query + " ORDER BY ";

			for (int i = 0; i < orderByFields.length; i++) {

				if (i == orderByFields.length - 1)
					query = query + orderByFields[i];
				else
					query = query + orderByFields[i] + ", ";
			}

			query = query + " COLLATE NOCASE ASC ";
		}

		// Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(),
		// "prepareSelectQuery", query);

		return query;
	}

	/**
	 * Method prepareDeleteQuery.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @return String
	 */
	private String prepareDeleteQuery(String tableName, String whereClause) {

		String query = "DELETE FROM " + tableName + " ";

		if (whereClause != null && !whereClause.equalsIgnoreCase(""))
			query = query + "WHERE " + whereClause;

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "prepareDeleteQuery", query);

		return query;
	}

	/**
	 * Method prepareUpdateBindQuery.
	 * 
	 * @param tableName
	 *            String
	 * @param tableColumnNames
	 *            String[]
	 * @param whereClause
	 *            String
	 * @return String
	 */
	private String prepareUpdateBindQuery(String tableName, String[] tableColumnNames, String whereClause) {

		String query = "";

		int noOfColumns = 0;

		if (tableColumnNames != null)
			noOfColumns = tableColumnNames.length;

		query = "UPDATE " + tableName + " SET ";

		for (int i = 0; i < noOfColumns; i++) {
			if (i == 0)
				query = query + tableColumnNames[i] + " = ? ";
			else
				query = query + " , " + tableColumnNames[i] + " = ? ";
		}

		if (whereClause != null && !whereClause.equalsIgnoreCase(""))
			query = query + " WHERE " + whereClause;

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "prepareUpdateBindQuery", query);

		return query;
	}

	/**
	 * Method prepareReplaceBindQuery.
	 * 
	 * @param tableName
	 *            String
	 * @param tableColumnNames
	 *            String[]
	 * @return String
	 */
	private String prepareReplaceBindQuery(String tableName, Vector<String> tableColumnNames) {

		String query = "";

		int noOfColumns = 0;

		if (tableColumnNames != null)
			noOfColumns = tableColumnNames.size();

		query = "REPLACE INTO " + tableName + " (";

		for (int i = 0; i < noOfColumns; i++) {
			String key = tableColumnNames.elementAt(i);
			query = query + key + ",";
		}

		int queryLength = query.length();
		char[] queryChar = query.toCharArray();

		queryChar[queryLength - 1] = ')';

		query = String.valueOf(queryChar);

		query = query + " VALUES (";

		String value = "";

		for (int i = 0; i < noOfColumns; i++) {
			if (value.equalsIgnoreCase(""))
				value = value + "?";
			else
				value = value + ", " + "?";
		}

		query = query + value + ")";

		Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "prepareReplaceBindQuery", query);

		return query;
	}

	/**
	 * Method getDataStructuresFromCursor.
	 * 
	 * @param tableName
	 *            String
	 * @param cursor
	 *            Cursor
	 * @param className
	 *            Class
	 * @return IDataStructure[]
	 * @throws DBException
	 */
	@SuppressLint("NewApi")
	private IDataStructure[] getDataStructuresFromCursor(String tableName, Cursor cursor, Class<IDataManager> className) throws DBException {

		Vector<IDataStructure> tempDataStructures = new Vector<IDataStructure>();
		IDataStructure tempDataStructure = null;

		SQLiteCursor sqliteCursor = (SQLiteCursor) cursor;

		try {

			while (sqliteCursor.moveToNext()) {

				// Use reflection for Native framework. For HTML5 Use
				// JSDataStructure
				if (runtimeEngine.isHTML5Framework() && FrameworkManager.getInstance().getDataManager() != this) {

					if (applicationManager == null) {
						applicationManager = ApplicationManager.getInstance();
					}

					StructureMeta structureMeta = applicationManager.getStructureMeta(tableName);

					if (structureMeta == null) {
						throw new DBException(this.getClass().getName(), "getDataStructuresFromCursor", "Structure Meta for table not found: " + tableName);
					}

					tempDataStructure = new JSDataStructure(tableName, structureMeta.getIsHeader());

				} else {

					if (className == null) {

						String tempClassName = dataStructureTableMapping.getClassName(tableName);
						className = (Class) Class.forName(tempClassName);
					}

					tempDataStructure = (IDataStructure) Class.forName(className.getName()).newInstance();
				}

				String[] columnNames = sqliteCursor.getColumnNames();
				Object[] columnValues = new Object[columnNames.length];

				for (int i = 0; i < columnNames.length; i++) {
					String sqlType = tempDataStructure.getFieldType(columnNames[i]);
					if (sqlType == null || sqlType.length() <= 0) {
						throw new DBException(getClass().getName(), "getDataStructuresFromCursor", "NO SQLTYPE FOUND : TABLE-NAME: " + tableName + ", COLUMN-NAME: " + columnNames[i]);
					}

					if (sqlType.equalsIgnoreCase(DataTypes.BLOB)) {
						columnValues[i] = sqliteCursor.getBlob(i);
					} else if (sqlType.equalsIgnoreCase(DataTypes.INTEGER)) {
						columnValues[i] = sqliteCursor.getLong(i);
					} else if (sqlType.equalsIgnoreCase(DataTypes.REAL)) {
						columnValues[i] = sqliteCursor.getDouble(i);
					} else if (sqlType.equalsIgnoreCase(DataTypes.TEXT)) {
						columnValues[i] = sqliteCursor.getString(i);
					} else {
						throw new DBException(getClass().getName(), "getDataStructuresFromCursor", "INVALID SQLTYPE FOUND : TABLE-NAME: " + tableName + ", COLUMN-NAME: " + columnNames[i]);
					}
				}

				populateDataStructure(tempDataStructure, columnNames, columnValues);

				tempDataStructures.addElement(tempDataStructure);
			}
		} catch (SQLException sqlException) {
			throw new DBException(Class.class.getName(), "getDataStructuresFromCursor", "SQLException caught while creating datastructure, " + sqlException.getMessage());
		} catch (Exception exception) {
			throw new DBException(Class.class.getName(), "getDataStructuresFromCursor", "Exception caught while creating datastructure, " + exception.getMessage());
		} finally {
			cursor.close();
		}

		int size = tempDataStructures.size();
		if (size == 0) {
			return null;
		}

		IDataStructure[] dataStructures = new IDataStructure[size];
		for (int i = 0; i < size; i++) {
			dataStructures[i] = (IDataStructure) tempDataStructures.elementAt(i);
		}

		return dataStructures;
	}

	/**
	 * Method populateDataStructure.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 * @param columnNames
	 *            String[]
	 * @param columnValues
	 *            Object[]
	 * @throws DBException
	 */
	private void populateDataStructure(IDataStructure dataStructure, String[] columnNames, Object[] columnValues) throws DBException {

		int noOfColumns = columnNames.length;

		for (int i = 0; i < noOfColumns; i++) {

			if (columnValues[i] instanceof byte[]) {

				byte[] bytes = (byte[]) columnValues[i];
				byte[] uncompressed = uncompressBytes(bytes);

				dataStructure.setField(columnNames[i], uncompressed);

			} else {

				dataStructure.setField(columnNames[i], columnValues[i]);
			}
		}
	}

	/**
	 * Method compressBytes.
	 * 
	 * @param data
	 *            byte[]
	 * @return byte[]
	 */
	private byte[] compressBytes(byte[] data) {

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		GZIPOutputStream gzipStream = null;

		try {
			gzipStream = new GZIPOutputStream(baos);
			gzipStream.write(data);
		} catch (IOException ioException) {
			new DBException(Class.class.getName(), "compressBlob", "IOException caught while compressing data, " + ioException.getMessage());
		} finally {
			try {
				gzipStream.close();
			} catch (IOException ioException) {
				new DBException(Class.class.getName(), "compressBlob", "IOException caught while closing the GZIPOutputStream , " + ioException.getMessage());
			}
		}

		return baos.toByteArray();
	}

	/**
	 * Method uncompressBytes.
	 * 
	 * @param data
	 *            byte[]
	 * @return byte[]
	 * @throws DBException
	 */
	private byte[] uncompressBytes(byte[] data) throws DBException {

		GZIPInputStream gin = null;

		try {
			gin = new GZIPInputStream(new ByteArrayInputStream(data));
		} catch (IOException ioException) {
			throw new DBException(Class.class.getName(), "uncompressBytes", "IOException caught while creating GZIPInputStream object, " + ioException.getMessage());
		}

		return getString(gin).getBytes();
	}

	/**
	 * Method getString.
	 * 
	 * @param inputStream
	 *            InputStream
	 * @return String
	 * @throws DBException
	 */
	public static String getString(InputStream inputStream) throws DBException {

		if (inputStream == null) {
			throw new DBException(Class.class.getName(), "getString", "InputStream found as null.");
		}

		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException ioException) {
			throw new DBException(FrameworkHelper.class.getName(), "getString", "IOException caught while reading input stream, " + ioException.getMessage());
		} finally {
			try {
				inputStream.close();
			} catch (IOException ioException) {
				throw new DBException(FrameworkHelper.class.getName(), "getString", "IOException caught while closing input stream, " + ioException.getMessage());
			}
		}

		return sb.toString();
	}

	/**
	 * Method checkIfTableSupportsAttachmentsAndDelete.
	 * 
	 * @param tableName
	 *            String
	 * @param whereClause
	 *            String
	 * @throws DBException
	 */
	private void checkIfTableSupportsAttachmentsAndDelete(String tableName, String whereClause) throws DBException {

		if (applicationManager == null) {
			applicationManager = ApplicationManager.getInstance();
		}

		// this is a little inefficient as we are calling for the IdataStructure
		// specially after breaking an IdataStructure down to table name and
		// where clause.
		StructureMeta structureMeta = applicationManager.getStructureMeta(tableName);

		if (structureMeta != null) {

			final IDataStructure[] headerDataStructures = get(tableName, whereClause);
			IDataStructure header = null;

			/*
			 * If header, delete the header and its children
			 */
			if (structureMeta.getIsHeader()) {

				// ///////////////////////////////////////////////////////////////////////////
				// if we try to delete a table which is header and supports
				// attachment. we delete all the attachment files on SD card
				// ///////////////////////////////////////////////////////////////////////////
				if (headerDataStructures != null && headerDataStructures.length > 0) {

					header = headerDataStructures[0];

					String beName = header.getBEName();

					// If attachments are not supported do not do anything
					try {
						if (!applicationManager.isAttachmentSupported(beName)) {
							return;
						}
					} catch (ApplicationException e) {
						Logger.log(Logger.LEVEL_ERROR, getClass().getName(), "checkIsTableSupportsAttaqchmentsAnddelete", "ApplicationException while checking is bename " + beName + " supports attachments" + e.getMessage());
						return;
					}

					for (int i = 0; i < headerDataStructures.length; i++) {
						header = headerDataStructures[i];
						AttachmentItem[] items = getAttachments(header);
						if (items != null) {
							int j = 0;
							while (j < items.length) {
								AttachmentItem attachmentItem = items[j++];
								// delete it from the queue
								try {
									String uid = attachmentItem.getUid();
									AttachmentQHelper.getInstance().deleteIfQueuedForDownload(uid);
								} catch (DBException e) {
									Logger.log(Logger.LEVEL_ERROR, Attachment.class.getName(), "checkIsTableSupportsAttaqchmentsAnddelete", "DBException while deleting it from the attachment q: " + e.getMessage());
								}

								// delete related file on SD card
								attachmentItem.delete();

							}
						}

					}
				}

			} else {

				// ///////////////////////////////////////////////////////////////////////////
				// if we try to delete a table which is of type attachment. we
				// delete all the attachment files on SD card as well
				// ///////////////////////////////////////////////////////////////////////////
				if (headerDataStructures != null && headerDataStructures.length > 0) {
					AttachmentItem attachmentItem = null;
					if (attachmentItem instanceof AttachmentItem) {
						attachmentItem = (AttachmentItem) headerDataStructures[0];
						attachmentItem.delete();
					}
				}
			}
		}
	}

	/**
	 * Method getAttachments.
	 * 
	 * @param dataStructure
	 *            IDataStructure
	 */
	private AttachmentItem[] getAttachments(IDataStructure header) {

		ApplicationManager applicationManager = ApplicationManager.getInstance();

		IDataManager applicationDataManager;
		IDataStructure[] dataStructures = null;

		try {
			applicationDataManager = applicationManager.getDataManager();

			String beName = header.getBEName();
			String attachmentStructName = beName + FrameworkConstants.BE_ATTACHMENT;

			dataStructures = applicationDataManager.getChildren(attachmentStructName, null, null, header);

			if (dataStructures == null) {
				String whereClause = AttachmentItem.FIELD_FID + " = '" + header.getLid() + "'";
				dataStructures = applicationDataManager.get(attachmentStructName, whereClause);
			}

		} catch (DBException e) {
			Logger.log(Logger.LEVEL_ERROR, this.getClass().getName(), "getAttachments", "DBException while getting attachment items of a business header: " + e.getMessage());
		}

		if (dataStructures == null || dataStructures.length == 0) {
			Logger.log(Logger.LEVEL_DEBUG, this.getClass().getName(), "getAttachments", "dataStructures is null, while checking if the IdataStructure in question supports attachments and if it has one");
			return null;
		}

		AttachmentItem[] items = new AttachmentItem[dataStructures.length];
		System.arraycopy(dataStructures, 0, items, 0, dataStructures.length);
		return items;
	}

	private class UnviredSQLiteOpenHelper extends SQLiteOpenHelper {

		UnviredSQLiteOpenHelper(Context context, String name, int version) {
			super(context, name, null, version);
		}

		@Override
		public void onCreate(SQLiteDatabase arg0) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
			// TODO Auto-generated method stub

		}
	}

	@Override
	public boolean executeDBOperationsWithSavePoint(ITransactable callback) throws DBException {

		String savePointName = RandomStringGenerator.generateRandomString(5, RandomStringGenerator.Mode.ALPHA);
		createSavepoint(savePointName);

		boolean isSuccess = false;
		try {
			isSuccess = callback.transaction(this);
		} catch (ApplicationException e) {
			Logger.log(Logger.LEVEL_ERROR, getClass().getName(), "executeDBOperationsWithSavePoint", "ApplicationException while executing DB Operations with SavePoint" + e.getMessage());
		}

		if (isSuccess) {
			releaseSavepoint(savePointName);
		} else {
			rollbackToSavepoint(savePointName);
		}

		return isSuccess;
	}

	/**
	 * Method rollbackToSavepoint.
	 * 
	 * @param savePointName
	 *            String
	 * @throws DBException
	 */
	public void rollbackToSavepoint(String savePointName) throws DBException {
		String query = "ROLLBACK TRANSACTION TO SAVEPOINT " + savePointName;
		Logger.log(Logger.LEVEL_DEBUG, Class.class.getName(), "rollbackToSavepoint", "Query: " + query);
		execute(query);

	}

	/**
	 * Method releaseSavepoint.
	 * 
	 * @param savePointName
	 *            String
	 * @throws DBException
	 */
	public void releaseSavepoint(String savePointName) throws DBException {
		String query = "RELEASE SAVEPOINT " + savePointName;
		Logger.log(Logger.LEVEL_DEBUG, Class.class.getName(), "releaseSavepoint", "Query: " + query);
		execute(query);
	}

	/**
	 * Method createSavepoint.
	 * 
	 * @param savePointName
	 *            String
	 * @throws DBException
	 */
	public void createSavepoint(String savePointName) throws DBException {
		String query = "SAVEPOINT " + savePointName;
		Logger.log(Logger.LEVEL_DEBUG, Class.class.getName(), "createSavepoint", "Query: " + query);
		execute(query);
	}

	private void createAndSaveInfoMessage(String message) {
		InfoMessage infoMessage = new InfoMessage();
		infoMessage.setCategory(InfoMessage.CATEGORY_FAILURE);
		infoMessage.setMessage("Message:" + message);

		try {
			FrameworkManager.getInstance().getDataManager().insert(infoMessage);
		} catch (DBException e) {
			Logger.log(Logger.LEVEL_ERROR, this.getClass().getName(), "createAndSaveInfoMessage", "DBException caught while inserting Info Message: " + e.getMessage());
		}
	}
}