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

com.nhaarman.ellie.Ellie Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 Michael Pardo
 * Copyright (C) 2014 Niek Haarman
 *
 * 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 com.nhaarman.ellie;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.nhaarman.ellie.internal.AdapterHolder;
import com.nhaarman.ellie.internal.DatabaseHelper;
import com.nhaarman.ellie.internal.ModelAdapter;
import com.nhaarman.ellie.internal.RepositoryHolder;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Objects;

/**
 * The main class used in Ellie.
 * This class is responsible for database creation/migrations, and instantiates the {@link ModelRepository} classes.
 * Users must call {@link #init(Context, String, int, int, LogLevel)} or any of its variants before using the class in any other way.
 */
@SuppressWarnings("rawtypes")
public final class Ellie {

    /**
     * The default cache size that is used by each {@link ModelRepository}.
     */
    public static final int DEFAULT_CACHE_SIZE = 1024;

    private static final String TAG = "Ellie";

    @NotNull
    private static Ellie sInstance = new Ellie();

    /**
     * Controls the level of logging.
     */
    public enum LogLevel {

        /**
         * No logging.
         */
        NONE,

        /**
         * Log basic events.
         */
        BASIC,

        /**
         * Log all queries.
         */
        FULL;

        /**
         * Returns whether this level is higher than the given level.
         *
         * @param logLevel The {@code LogLevel} to check against.
         *
         * @return {@code true} if this level is higher than given level.
         */
        public boolean log(final LogLevel logLevel) {
            return ordinal() >= logLevel.ordinal();
        }
    }

    /**
     * The {@link AdapterHolder} instance that is instantiated to access the {@link ModelAdapter}s, {@link TypeAdapter}s, and {@link BaseMigration}s.
     * This value is {@code null} until {@link #init(Context, String, int, int, LogLevel)} is successfully called.
     */
    @Nullable
    private AdapterHolder mAdapterHolder;

    /**
     * The {@link RepositoryHolder} instance that is instantiated to access the {@link ModelRepository} instances.
     * This value is {@code null} until {@link #init(Context, String, int, int, LogLevel)} is successfully called.
     */
    @Nullable
    private RepositoryHolder mRepositoryHolder;

    /**
     * The writable {@link SQLiteDatabase} that is used.
     * This value is {@code null} until {@link #init(Context, String, int, int, LogLevel)} is successfully called.
     */
    @Nullable
    private SQLiteDatabase mSQLiteDatabase;

    /**
     * Indicates whether we're fully initialized.
     */
    private boolean mInitialized;

    /**
     * Returns the main instance of {@code Ellie} that is used in classes when not injected.
     * To have this method return a custom instance, call {@link #useInstance(Ellie)} with your instance.
     *
     * @return The {@code Ellie} instance.
     */
    @NotNull
    public static Ellie getInstance() {
        synchronized (Ellie.class) {
            return sInstance;
        }
    }

    /**
     * Instructs Ellie to use given {@code Ellie} instance whenever the {@code Ellie} instance isn't supplied
     * explicitely to classes that depend on {@code Ellie}.
     * After calling this method, {@link #getInstance()} will return given instance.
     *
     * @param ellie The {@code Ellie} instance to use.
     */
    public static void useInstance(@NotNull final Ellie ellie) {
        synchronized (Ellie.class) {
            sInstance = Objects.requireNonNull(ellie);
        }
    }

    /**
     * Initialize the database. Must be called before interacting with the database.
     *
     * @param context Context
     * @param name    The database name.
     * @param version The database version.
     */
    public void init(@NotNull final Context context, @NotNull final String name, final int version) {
        init(context, name, version, DEFAULT_CACHE_SIZE, LogLevel.NONE);
    }

    /**
     * Initialize the database. Must be called before interacting with the database.
     *
     * @param context   Context
     * @param name      The database name.
     * @param version   The database version.
     * @param cacheSize The cache size.
     */
    public void init(@NotNull final Context context, @NotNull final String name, final int version, final int cacheSize) {
        init(context, name, version, cacheSize, LogLevel.NONE);
    }

    /**
     * Initialize the database. Must be called before interacting with the database.
     *
     * @param context  Context
     * @param name     The database name.
     * @param version  The database version.
     * @param logLevel The logging level.
     */
    public void init(@NotNull final Context context, @NotNull final String name, final int version, @NotNull final LogLevel logLevel) {
        init(context, name, version, DEFAULT_CACHE_SIZE, logLevel);
    }

    /**
     * Initialize the database. Must be called before interacting with the database.
     *
     * @param context   Context
     * @param name      The database name.
     * @param version   The database version.
     * @param cacheSize The cache size.
     * @param logLevel  The logging level.
     */
    public void init(@NotNull final Context context, @NotNull final String name, final int version, final int cacheSize, @NotNull final LogLevel logLevel) {
        Objects.requireNonNull(context);
        Objects.requireNonNull(name);
        Objects.requireNonNull(logLevel);

        if (mInitialized) {
            if (logLevel.log(LogLevel.BASIC)) {
                Log.d(TAG, "Already initialized.");
            }
            return;
        }

        mAdapterHolder = instantiateAdapterHolder();
        mSQLiteDatabase = createSQLiteDatabase(context, name, version, mAdapterHolder, logLevel);
        mRepositoryHolder = instantiateRepositoryHolder(mSQLiteDatabase, cacheSize);

        mInitialized = true;
    }

    /**
     * Uses reflection to instantiate the {@link AdapterHolder} class generated by the compiler module.
     *
     * @throws IllegalStateException if the class could not be instantiated.
     */
    @NotNull
    private AdapterHolder instantiateAdapterHolder() {
        try {
            Class adapterHolderClass = (Class) Class.forName(AdapterHolder.IMPL_CLASS_FQCN);
            Constructor constructor = adapterHolderClass.getConstructor(AdapterHolder.CONSTRUCTOR_CLASSES);
            return constructor.newInstance();
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
            throw new IllegalStateException("Could not instantiate AdapterHolder class. Make sure code generation has completed", e);
        }
    }

    /**
     * Creates the {@link SQLiteDatabase} that is used.
     *
     * @param context       Context
     * @param name          The database name.
     * @param version       The database version.
     * @param adapterHolder The {@link AdapterHolder}.
     * @param logLevel      The logging level.
     *
     * @return the {@code SQLiteDatabase}.
     */
    @NotNull
    private SQLiteDatabase createSQLiteDatabase(@NotNull final Context context, @NotNull final String name, final int version,
                                                @NotNull final AdapterHolder adapterHolder, @NotNull final LogLevel logLevel) {
        DatabaseHelper databaseHelper = new DatabaseHelper(context.getApplicationContext(), name, version, adapterHolder, logLevel);
        return databaseHelper.getWritableDatabase();
    }

    /**
     * Uses reflection to instantiate the {@link RepositoryHolder} class generated by the compiler module.
     *
     * @param database  The {@link SQLiteDatabase} to pass to the {@code RepositoryHolder}.
     * @param cacheSize The size of the cache to use for each {@code RepositoryHolder}.
     *
     * @throws IllegalStateException if the class could not be instantiated.
     */
    @NotNull
    private RepositoryHolder instantiateRepositoryHolder(final SQLiteDatabase database, final int cacheSize) {
        try {
            Class adapterHolderClass = (Class) Class.forName(RepositoryHolder.IMPL_CLASS_FQCN);
            Constructor constructor = adapterHolderClass.getConstructor(RepositoryHolder.CONSTRUCTOR_CLASSES);
            return constructor.newInstance(this, database, cacheSize);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
            throw new IllegalStateException("Could not instantiate RepositoryHolder class. Make sure code generation has completed.", e);
        }
    }

    /**
     * Returns the {@link SQLiteDatabase} instance that is used.
     *
     * @return The {@code SQLiteDatabase} instance.
     */
    @NotNull
    public SQLiteDatabase getDatabase() {
        if (mSQLiteDatabase == null) {
            throw new IllegalStateException("Ellie hasn't been initialized yet. Did you call Ellie#init(...)?");
        }

        return mSQLiteDatabase;
    }

    /**
     * Returns the table name for the model represented by given type class.
     *
     * @param cls The type class.
     * @param  The Model extension to get the table name for.
     *
     * @return The table name.
     */
    @NotNull
    public  String getTableName(final Class cls) {
        if (mAdapterHolder == null) {
            throw new IllegalStateException("Ellie hasn't been initialized yet. Did you call Ellie#init(...)?");
        }

        ModelAdapter modelAdapter = Objects.requireNonNull(
                mAdapterHolder.getModelAdapter(cls),
                cls.getSimpleName() + " has no declared table name! Did you add the @Table annotation?"
        );

        return modelAdapter.getTableName();
    }

    /**
     * Returns the {@link ModelRepository} instance for given type class, if it exists.
     *
     * @param cls The type class.
     * @param  The type of the class extending {@link Model}.
     *
     * @return The {@code ModelRepository} instance, or {@code null} if it doesn't exist.
     */
    @Nullable
    public  ModelRepository getModelRepository(final Class cls) {
        if (mRepositoryHolder == null) {
            throw new IllegalStateException("Ellie hasn't been initialized yet. Did you call Ellie#init(...)?");
        }

        return mRepositoryHolder.getModelRepository(cls);
    }

    /**
     * Returns all instantiated {@link ModelAdapter}s.
     *
     * @return The list of {@code ModelAdapter}s.
     */
    @NotNull
    List getModelAdapters() {
        if (mAdapterHolder == null) {
            throw new IllegalStateException("Ellie hasn't been initialized yet. Did you call Ellie#init(...)?");
        }

        return mAdapterHolder.getModelAdapters();
    }


    /**
     * Returns the {@link TypeAdapter} to use for (de)serializing classes with given type.
     *
     * @param cls The deserialized type class.
     * @param  Deserialized type, i.e. the Java type.
     * @param  Serialized type, i.e. the SQLite type.
     *
     * @return The {@code TypeAdapter}, or {@code null} if there is none for given class.
     */
    @Nullable
     TypeAdapter getTypeAdapter(final Class cls) {
        if (mAdapterHolder == null) {
            throw new IllegalStateException("Ellie hasn't been initialized yet. Did you call Ellie#init(...)?");
        }

        return (TypeAdapter) mAdapterHolder.getTypeAdapter(cls);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy