Source code

KittyORM mig v.4 implementation sources
  1. Click to view MigrationDBv4.class:
     1@KITTY_DATABASE(
     2        isLoggingOn = false,
     3        isProductionOn = true,
     4        isKittyDexUtilLoggingEnabled = false,
     5        databaseName = "mig",
     6        domainPackageNames = {"net.akaish.kittyormdemo.sqlite.migrations.migv4"},
     7        databaseVersion = 4,
     8        logTag = MigrationDBv4.LTAG
     9)
    10@KITTY_DATABASE_REGISTRY(
    11        domainModels = {
    12                net.akaish.kittyormdemo.sqlite.migrations.migv4.MigTwoModel.class,
    13                net.akaish.kittyormdemo.sqlite.migrations.migv4.MigThreeModel.class,
    14                net.akaish.kittyormdemo.sqlite.migrations.migv4.MigFourModel.class
    15        }
    16)
    17@KITTY_DATABASE_HELPER(
    18        onUpgradeBehavior = KITTY_DATABASE_HELPER.UpgradeBehavior.USE_FILE_MIGRATIONS
    19)
    20public class MigrationDBv4 extends KittyDatabase {
    21
    22    public static final String LTAG = "MIGv4";
    23
    24    /**
    25     * KittyORM main database class that represents bootstrap and holder for all related with database
    26     * components.
    27     *
    28     * @param ctx
    29     */
    30    public MigrationDBv4(Context ctx) {
    31        super(ctx);
    32    }
    33}

  2. Click to view MigTwoModel.class:
     1@KITTY_TABLE(
     2        tableName = "mig_two"
     3)
     4public class MigTwoModel extends KittyModel {
     5
     6    @KITTY_COLUMN(
     7            columnOrder = 0,
     8            isIPK = true
     9    )
    10    public Long id;
    11
    12    @KITTY_COLUMN(
    13            columnOrder = 1
    14    )
    15    public Long migOneReference;
    16
    17    @KITTY_COLUMN(columnOrder = 2)
    18    public Animals someAnimal;
    19
    20    @KITTY_COLUMN(
    21            columnOrder = 3,
    22            columnAffinity = TypeAffinities.TEXT
    23    )
    24    @KITTY_COLUMN_SERIALIZATION
    25    public AnimalSounds someAnimalSound;
    26
    27    String someAnimalSoundSerialize() {
    28        if(someAnimalSound == null) return null;
    29        return new GsonBuilder().create().toJson(someAnimalSound);
    30    }
    31
    32    AnimalSounds someAnimalSoundDeserialize(String cvData) {
    33        if(cvData == null) return null;
    34        if(cvData.length() == 0) return null;
    35        return new GsonBuilder().create().fromJson(cvData, AnimalSounds.class);
    36    }
    37
    38    @Override
    39    public String toString() {
    40        return new StringBuilder(64)
    41                .append("[ id = ")
    42                .append(id)
    43                .append(" ; migOneReference = ")
    44                .append(migOneReference)
    45                .append(" ; someAnimal = ")
    46                .append(someAnimal)
    47                .append(" ; someAnimalSound = ")
    48                .append(someAnimalSoundSerialize())
    49                .append(" ] ").toString();
    50    }
    51}

  3. Click to view MigThreeModel.class:
     1@KITTY_TABLE(tableName = "mig_three")
     2public class MigThreeModel extends KittyModel {
     3
     4    @KITTY_COLUMN(
     5            columnOrder = 0,
     6            isIPK = true
     7    )
     8    public Long id;
     9
    10    @KITTY_COLUMN(
    11            columnOrder = 1,
    12            columnName = "new_sv_name"
    13    )
    14    @NOT_NULL
    15    @DEFAULT(
    16            literalValue = "'Something random'"
    17    )
    18    public String someValue;
    19
    20    @KITTY_COLUMN(columnOrder = 2)
    21    @DEFAULT(signedInteger = 22)
    22    @ONE_COLUMN_INDEX(indexName = "m3_rnd_long")
    23    public Long randomLong;
    24
    25    public String toString() {
    26        return new StringBuilder(32)
    27                .append("[ id = ")
    28                .append(id)
    29                .append(" ; someValue = ")
    30                .append(someValue)
    31                .append(" ; randomLong = ")
    32                .append(randomLong)
    33                .append(" ]").toString();
    34    }
    35}

  4. Click to view MigFourModel.class:
     1@KITTY_TABLE(tableName = "mig_four")
     2public class MigFourModel extends KittyModel {
     3
     4    @KITTY_COLUMN(
     5            columnOrder = 0,
     6            isIPK = true)
     7    public Long id;
     8
     9    @KITTY_COLUMN(columnOrder = 1)
    10    @FOREIGN_KEY(
    11            reference = @FOREIGN_KEY_REFERENCE(
    12                    foreignTableName = "mig_three",
    13                    foreignTableColumns = {"id"}
    14            )
    15    )
    16    @NOT_NULL
    17    public Long migThreeReference;
    18
    19    @KITTY_COLUMN(columnOrder = 2)
    20    @FOREIGN_KEY(
    21            reference = @FOREIGN_KEY_REFERENCE(
    22                    foreignTableName = "mig_two",
    23                    foreignTableColumns = {"id"}
    24            )
    25    )
    26    @NOT_NULL
    27    public Long migTwoReference;
    28
    29    @KITTY_COLUMN(columnOrder = 3)
    30    @NOT_NULL
    31    @DEFAULT(
    32            predefinedLiteralValue = LiteralValues.CURRENT_DATE
    33    )
    34    public Date creationDate;
    35
    36    @Override
    37    public String toString() {
    38        return new StringBuilder(64)
    39                .append("[ id = ")
    40                .append(id)
    41                .append(" ; migThreeReferecnde = ")
    42                .append(migThreeReference)
    43                .append(" ; migTwoReference = ")
    44                .append(migTwoReference)
    45                .append(" ; creationDate = ")
    46                .append(creationDate)
    47                .append(" ]").toString();
    48    }
    49}

Fragment and utility code used in this tutorial
  1. Click to view MigV4RandomModelFactory.class:
     1public class MigV4RandomModelFactory {
     2
     3    final Context context;
     4    final Random rnd;
     5
     6    private final SparseArray<String> randomAnimalSays = new SparseArray<>();
     7    private final SparseArray<String> randomAnimalLocalizedName = new SparseArray<>();
     8
     9    public MigV4RandomModelFactory(Context ctx) {
    10        this.context = ctx;
    11        this.rnd = new Random();
    12
    13        // Lol, getContext().getString() method is fucking slow, calling for each new random model this method twice causes 55% of all execution time of generating new random model (!)
    14        // Right now getting those string causes only 14% of execution time
    15        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.BEAR), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.BEAR)));
    16        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.CAT), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.CAT)));
    17        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.DOG), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.DOG)));
    18        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.GOAT), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.GOAT)));
    19        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.LION), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.LION)));
    20        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.SHEEP), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.SHEEP)));
    21        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.TIGER), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.TIGER)));
    22        randomAnimalSays.append(Animals.getLocalizedAnimalSaysResource(Animals.WOLF), context.getString(Animals.getLocalizedAnimalSaysResource(Animals.WOLF)));
    23
    24
    25        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.BEAR), context.getString(Animals.getLocalizedAnimalNameResource(Animals.BEAR)));
    26        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.CAT), context.getString(Animals.getLocalizedAnimalNameResource(Animals.CAT)));
    27        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.DOG), context.getString(Animals.getLocalizedAnimalNameResource(Animals.DOG)));
    28        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.GOAT), context.getString(Animals.getLocalizedAnimalNameResource(Animals.GOAT)));
    29        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.LION), context.getString(Animals.getLocalizedAnimalNameResource(Animals.LION)));
    30        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.SHEEP), context.getString(Animals.getLocalizedAnimalNameResource(Animals.SHEEP)));
    31        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.TIGER), context.getString(Animals.getLocalizedAnimalNameResource(Animals.TIGER)));
    32        randomAnimalLocalizedName.append(Animals.getLocalizedAnimalNameResource(Animals.WOLF), context.getString(Animals.getLocalizedAnimalNameResource(Animals.WOLF)));
    33    }
    34
    35    public MigTwoModel newM2RndModel() {
    36        MigTwoModel model = new MigTwoModel();
    37        model.someAnimal = Animals.rndAnimal(rnd);
    38        model.migOneReference = rnd.nextLong();
    39        AnimalSounds animalSounds = new AnimalSounds();
    40        animalSounds.animalName = randomAnimalLocalizedName.get(Animals.getLocalizedAnimalNameResource(model.someAnimal));
    41        animalSounds.animalSounds = randomAnimalSays.get(Animals.getLocalizedAnimalSaysResource(model.someAnimal));
    42        model.someAnimalSound = animalSounds;
    43        return model;
    44    }
    45
    46    static final String[] M3_SOME_VALUES = {"One", "Apple", "Wolf", "Plane", "Name", "Fear of being alone", "Despair", "Death", "Do not look for meaning where it is not"};
    47
    48    public MigThreeModel newM3RndModel() {
    49        return newM3RndModel(rnd.nextBoolean());
    50    }
    51
    52    public MigThreeModel newM3RndModel(boolean setDefaultValue) {
    53        MigThreeModel model = new MigThreeModel();
    54        if(setDefaultValue)
    55            model.setFieldExclusion("someValue");
    56        else
    57            model.someValue = M3_SOME_VALUES[rnd.nextInt(M3_SOME_VALUES.length)];
    58        model.randomLong = rnd.nextLong();
    59        return model;
    60    }
    61
    62    public MigFourModel newM4RndModel(List<MigThreeModel> mig3Models, List<MigTwoModel> mig2Models) {
    63        if(mig2Models == null || mig3Models == null) {
    64            throw new IllegalArgumentException("M4RMF#newM4RndModel bad collections provided!");
    65        }
    66        if(mig2Models.size() == 0 || mig3Models.size() == 0) {
    67            throw new IllegalArgumentException("M4RMF#newM4RndModel bad collections provided!");
    68        }
    69        return newM4RndModel(
    70                rnd.nextBoolean(),
    71                mig2Models.get(rnd.nextInt(mig2Models.size())).id,
    72                mig3Models.get(rnd.nextInt(mig3Models.size())).id
    73        );
    74    }
    75
    76    public MigFourModel newM4RndModel(boolean setDefaultValue, Long mig2reference, Long mig3reference) {
    77        if(mig2reference == null || mig3reference == null)
    78            throw new IllegalArgumentException("M4RMF#newM4RndModel bad references id provided!");
    79        MigFourModel model = new MigFourModel();
    80        model.migThreeReference = mig3reference;
    81        model.migTwoReference = mig2reference;
    82        if(setDefaultValue)
    83            model.setFieldExclusion("creationDate");
    84        else
    85            model.creationDate = new Date(System.currentTimeMillis());
    86        return model;
    87    }
    88}

  2. Click to view Lesson5Tab4FilescriptMigration.class:
      1public class Lesson5Tab4FilescriptMigration extends Lesson5BaseFragment {
      2
      3    private MigrationDBv4 databaseV4;
      4    private SharedPreferencesMigDB sf;
      5
      6    private Button insertRandomButton;
      7    private Button clearTableButton;
      8    private Button deleteDatabaseButton;
      9
     10    private ListView eventsListView;
     11
     12    private TextView statusTV;
     13
     14    private MigDatabaseState mdbState;
     15
     16    final static int DB_IMPLEMENTATION_VERSION = 4;
     17    final static int TABLE_AMOUNT = 3;
     18
     19    public Lesson5Tab4FilescriptMigration() {}
     20
     21    @Override
     22    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     23        View rootView = inflater.inflate(R.layout.lesson5_tab4_file_migrations, container, false);
     24
     25        insertRandomButton = rootView.findViewById(R.id.l5_t4_go_button);
     26        clearTableButton = rootView.findViewById(R.id.l5_t4_clear_button);
     27        deleteDatabaseButton = rootView.findViewById(R.id.l5_t4_delete_database_button);
     28
     29        eventsListView = rootView.findViewById(R.id.l5_t4_actions);
     30
     31        insertRandomButton.setOnClickListener(new View.OnClickListener() {
     32            @Override
     33            public void onClick(View v) {
     34                insert25RND();
     35            }
     36        });
     37
     38        clearTableButton.setOnClickListener(new View.OnClickListener() {
     39            @Override
     40            public void onClick(View v) {
     41                clearTable();
     42            }
     43        });
     44
     45        deleteDatabaseButton.setOnClickListener(new View.OnClickListener() {
     46            @Override
     47            public void onClick(View v) {
     48                deleteDatabase();
     49            }
     50        });
     51
     52        statusTV = rootView.findViewById(R.id.l5_t4_status);
     53
     54
     55        setUpExpandedList(
     56                rootView,
     57                R.id._l5_t4_expanded_panel_list,
     58                R.id._l5_t4_expanded_panel_text,
     59                R.string._l5_t4_expanded_text_pattern
     60        );
     61
     62        reloadTableExpandedList();
     63        reloadStatus();
     64        return rootView;
     65    }
     66
     67    public MigDatabaseState getMdbState(Context context, int implVersion, String[] tables) {
     68        if(mdbState != null) return mdbState;
     69        mdbState = new MigDatabaseState(implVersion, tables, context, getSf());
     70        return mdbState;
     71    }
     72
     73    public void reloadStatus() {
     74        if(statusTV != null) {
     75            statusTV.setText(getMdbState(getContext(), DB_IMPLEMENTATION_VERSION, new String[] {M1M2TN, M1M3TN, M1M4TN}).toString());
     76        }
     77    }
     78
     79
     80    @Override
     81    public void onVisible() {
     82        reloadTableExpandedList();
     83        reloadStatus();
     84    }
     85
     86    private SharedPreferencesMigDB getSf() {
     87        if(sf != null) return sf;
     88        sf = new SharedPreferencesMigDB(getContext());
     89        return sf;
     90    }
     91
     92    private MigrationDBv4 getDatabase() {
     93        // retrieving existing database after upgrade -> downgrade would cause onUpgrade() script would be run after mapper fetching
     94        databaseV4 = new MigrationDBv4(getContext());
     95        return databaseV4;
     96    }
     97
     98    private void insert25RND() {
     99        new InsertRandomAsync().execute(0l);
    100        //new TestCPK().execute();
    101    }
    102
    103    private void clearTable() {
    104        new WipeAsync().execute(0l);
    105    }
    106
    107    private void deleteDatabase() {
    108        new DeleteDatabaseAsync().execute(0l);
    109    }
    110
    111    private void reloadTableExpandedList() {
    112        new ReloadTableAsync().execute(0l);
    113    }
    114
    115    @Override
    116    protected int snackbarMessageResource() {
    117        return R.string._l5_t4_snackbar_message;
    118    }
    119
    120
    121
    122    // Asyncs
    123
    124    class ReloadTableAsync extends AsyncTask<Long, Long, List<String>> {
    125
    126        @Override
    127        protected List<String> doInBackground(Long... params) {
    128            LinkedList<String> toListView = new LinkedList<>();
    129            if(getSf().isDatabaseCreated() && !getSf().isDatabaseDeletedManually() && getSf().currentMigDBVersion() == DB_IMPLEMENTATION_VERSION) {
    130                // T4
    131                KittyMapper mapper = getDatabase().getMapper(MigFourModel.class);
    132                List<MigFourModel> m1Models = mapper.findAll();
    133                mapper.close();
    134                // T2
    135                KittyMapper mapperT2 = getDatabase().getMapper(MigTwoModel.class);
    136                List<MigTwoModel> m2Models = mapperT2.findAll();
    137                mapper.close();
    138                // T3
    139                KittyMapper mapperT3 = getDatabase().getMapper(MigThreeModel.class);
    140                List<MigThreeModel> m3Models = mapperT3.findAll();
    141                mapper.close();
    142
    143                if(m1Models == null) {
    144                    toListView.addLast(format(getContext().getString(R.string._l5_t4_m4_db), 0));
    145                } else {
    146                    toListView.addLast(format(getContext().getString(R.string._l5_t4_m4_db), m1Models.size()));
    147                    Iterator<MigFourModel> mI = m1Models.iterator();
    148                    while (mI.hasNext()) {
    149                        toListView.addLast(mI.next().toString());
    150                    }
    151                }
    152                if(m2Models == null) {
    153                    toListView.addLast(format(getContext().getString(R.string._l5_t4_m2_db), 0));
    154                } else {
    155                    toListView.addLast(format(getContext().getString(R.string._l5_t4_m2_db), m2Models.size()));
    156                    Iterator<MigTwoModel> mI = m2Models.iterator();
    157                    while (mI.hasNext()) {
    158                        toListView.addLast(mI.next().toString());
    159                    }
    160                }
    161                if(m3Models == null) {
    162                    toListView.addLast(format(getContext().getString(R.string._l5_t4_m3_db), 0));
    163                } else {
    164                    toListView.addLast(format(getContext().getString(R.string._l5_t4_m3_db), m2Models.size()));
    165                    Iterator<MigThreeModel> mI = m3Models.iterator();
    166                    while (mI.hasNext()) {
    167                        toListView.addLast(mI.next().toString());
    168                    }
    169                }
    170                return toListView;
    171            } else {
    172                if(!getSf().isDatabaseCreated() || getSf().isDatabaseDeletedManually()) {
    173                    toListView.addLast(getString(R.string._l5_t4_m1_db_doesnt_exist));
    174                    return toListView;
    175                } else {
    176                    toListView.addLast(format(getString(R.string._l5_t4_m1_db_has_different_version), getSf().currentMigDBVersion()));
    177                    return toListView;
    178                }
    179            }
    180        }
    181
    182        @Override
    183        protected void onPostExecute(List<String> result) {
    184            int tableAmount = TABLE_AMOUNT;
    185            if(getSf().isDatabaseDeletedManually() || !getSf().isDatabaseCreated() || getSf().currentMigDBVersion() != DB_IMPLEMENTATION_VERSION)
    186                tableAmount = 0;
    187            if(result != null) {
    188                events.setAdapter(new MigAdapter(getContext(), result));
    189                int recordsAmount = result.size() - tableAmount;
    190                if(tableAmount == 0)
    191                    recordsAmount = 0;
    192                expandedTitle.setText(format(expandeddTitlePattern, recordsAmount, tableAmount));
    193            } else {
    194                events.setAdapter(new MigAdapter(getContext(), new LinkedList<String>()));
    195                expandedTitle.setText(format(expandeddTitlePattern, 0, tableAmount));
    196            }
    197        }
    198    }
    199
    200    private static final String ERR_STRING_WIPE = "Lesson5tab4WipeDataError, see exception details!";
    201
    202    class WipeAsync extends AsyncTask<Long, Long, WipeAsyncResult> {
    203
    204        ProgressDialog dialog;
    205
    206        @Override
    207        protected void onPreExecute() {
    208            dialog = ProgressDialog.show(
    209                    getLessonActivity(),
    210                    getString(R.string._l5_t4_running_requested_operation_pg_title),
    211                    getString(R.string._l5_t4_running_requested_operation_pg_body)
    212            );
    213            dialog.setCancelable(false);
    214        }
    215
    216        @Override
    217        protected WipeAsyncResult doInBackground(Long... params) {
    218            if(getSf().isDatabaseCreated() && !getSf().isDatabaseDeletedManually() && getSf().currentMigDBVersion() == DB_IMPLEMENTATION_VERSION) {
    219                try {
    220                    KittyMapper mapper4 = getDatabase().getMapper(MigFourModel.class);
    221                    KittyMapper mapper2 = getDatabase().getMapper(MigTwoModel.class);
    222                    KittyMapper mapper3 = getDatabase().getMapper(MigThreeModel.class);
    223                    long recordsCount = mapper4.countAll() + mapper2.countAll() + mapper3.countAll();
    224                    long affected = mapper4.deleteAll() + mapper2.deleteAll() + mapper3.deleteAll();
    225                    mapper4.close(); mapper2.close(); mapper3.close();
    226                    return new WipeAsyncResult(true, false, DB_IMPLEMENTATION_VERSION, affected, recordsCount);
    227                } catch (Exception e) {
    228                    Log.e(MigrationDBv4.LTAG, ERR_STRING_WIPE, e);
    229                    if (e instanceof KittyRuntimeException) {
    230                        if (((KittyRuntimeException) e).getNestedException() != null) {
    231                            Log.e(MigrationDBv4.LTAG, ERR_STRING_WIPE, ((KittyRuntimeException) e).getNestedException());
    232                        }
    233                    }
    234                    return new WipeAsyncResult(true, false, DB_IMPLEMENTATION_VERSION, -1l, -1l);
    235                }
    236            } else {
    237                return new WipeAsyncResult(
    238                        getSf().isDatabaseCreated(),
    239                        getSf().isDatabaseDeletedManually(),
    240                        getSf().currentMigDBVersion(),
    241                        -1l, -1l);
    242            }
    243        }
    244
    245        @Override
    246        protected void onPostExecute(WipeAsyncResult result) {
    247            dialog.cancel();
    248
    249            if (eventsListView != null) {
    250                eventsListView.setAdapter(new BasicArrayAdapter(getContext(), new LinkedList<String>()));
    251                eventsListView.setOnTouchListener(new View.OnTouchListener() {
    252
    253                    // Setting on Touch Listener for handling the touch inside ScrollView
    254                    @Override
    255                    public boolean onTouch(View v, MotionEvent event) {
    256                        // Disallow the touch request for parent scroll on touch of child view
    257                        v.getParent().requestDisallowInterceptTouchEvent(true);
    258                        return false;
    259                    }
    260                });
    261
    262                if(!result.isCreated) {
    263                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_op_not_existing));
    264                } else if (result.isDeleted) {
    265                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_op_deleted));
    266                } else if (result.dbVersion != DB_IMPLEMENTATION_VERSION) {
    267                    if(result.dbVersion < 1) {
    268                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_op_mig_version_is_lower), result.dbVersion, DB_IMPLEMENTATION_VERSION));
    269                    } else {
    270                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_op_mig_version_is_higher), result.dbVersion, DB_IMPLEMENTATION_VERSION));
    271                    }
    272                } else if (result.recordsCount > -1 && result.affectedRows > -1) {
    273                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_count_to_events), result.recordsCount));
    274                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_deleted_to_events), result.affectedRows));
    275                } else {
    276                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_t4_error_event));
    277                }
    278                ((BasicArrayAdapter) eventsListView.getAdapter()).notifyDataSetChanged();
    279                reloadTableExpandedList();
    280                reloadStatus();
    281            }
    282        }
    283    }
    284
    285    class WipeAsyncResult {
    286        boolean isCreated;
    287        boolean isDeleted;
    288        int dbVersion;
    289        Long affectedRows;
    290        Long recordsCount;
    291
    292        public WipeAsyncResult(boolean isCreated, boolean isDeleted, int dbVersion,
    293                               Long affectedRows, Long recordsCount) {
    294            this.isCreated = isCreated;
    295            this.isDeleted = isDeleted;
    296            this.dbVersion = dbVersion;
    297            this.affectedRows = affectedRows;
    298            this.recordsCount = recordsCount;
    299        }
    300    }
    301
    302    static final int INSERT_AMOUNT = 25;
    303    static final int INSERT_FK_AMOUNT = 10;
    304
    305    static final String ERR_INSERT_RND = "Lesson5tab4InsertRNDDataError, see exception details!";
    306
    307    // TEST COMPLEX PK
    308    class TestCPK extends AsyncTask<Void, Void, Long> {
    309
    310        /**
    311         * Override this method to perform a computation on a background thread. The
    312         * specified parameters are the parameters passed to {@link #execute}
    313         * by the caller of this task.
    314         * <p>
    315         * This method can call {@link #publishProgress} to publish updates
    316         * on the UI thread.
    317         *
    318         * @param voids The parameters of the task.
    319         * @return A result, defined by the subclass of this task.
    320         * @see #onPreExecute()
    321         * @see #onPostExecute
    322         * @see #publishProgress
    323         */
    324        @Override
    325        protected Long doInBackground(Void... voids) {
    326            KittyMapper mapper = getDatabase().getMapper(MigFiveModel.class);
    327            MigFiveModel m1 = new MigFiveModel();
    328            MigFiveModel m2 = new MigFiveModel();
    329            MigFiveModel m3 = new MigFiveModel();
    330
    331            m1.ipkUniqueString = UUID.randomUUID().toString();
    332            m2.ipkUniqueString = UUID.randomUUID().toString();
    333            m3.ipkUniqueString = UUID.randomUUID().toString();
    334
    335            m1.someStr = "STR1";
    336            m2.someStr = "STR2";
    337            m3.someStr = "STR3";
    338
    339            mapper.save(m1);
    340            mapper.save(m2);
    341            mapper.save(m3);
    342
    343            Log.e("CPK test 0", " count = "+mapper.countAll());
    344
    345            SQLiteCondition sqLiteCondition = new SQLiteConditionBuilder()
    346                                                .addField("ipk_str")
    347                                                .addSQLOperator(SQLiteOperator.EQUAL)
    348                                                .addValue(m3.ipkUniqueString)
    349                                                .build();
    350            MigFiveModel m3FromDB = (MigFiveModel) mapper.findWhere(sqLiteCondition).get(0);
    351            Log.e("CPK test 1", m3FromDB.toString());
    352            m3FromDB.someStr = "modified";
    353
    354            mapper.save(m3FromDB);
    355
    356            SQLiteCondition sqLiteCondition2 = new SQLiteConditionBuilder()
    357                                                    .addField("some_str")
    358                                                    .addSQLOperator(SQLiteOperator.EQUAL)
    359                                                    .addValue("modified")
    360                                                    .build();
    361            MigFiveModel m3FromDBM = (MigFiveModel) mapper.findWhere(sqLiteCondition2).get(0);
    362            Log.e("CPK test 2", m3FromDB.toString());
    363
    364            Log.e("CPK test 3", " count = "+mapper.countAll());
    365
    366            mapper.deleteAll();
    367            return null;
    368        }
    369    }
    370
    371    class InsertRandomAsync extends AsyncTask<Long, Long, InsertRandomResults> {
    372        ProgressDialog dialog;
    373
    374        @Override
    375        protected void onPreExecute() {
    376            dialog = ProgressDialog.show(
    377                    getLessonActivity(),
    378                    getString(R.string._l5_t4_running_requested_operation_pg_title),
    379                    getString(R.string._l5_t4_running_requested_operation_pg_body)
    380            );
    381            dialog.setCancelable(false);
    382        }
    383
    384        @Override
    385        protected InsertRandomResults doInBackground(Long... strings) {
    386//            File destinationFile = getContext().getFilesDir();
    387//            KittyUtils.copyDirectoryFromAssetsToFS(getContext(), KittyNamingUtils.ASSETS_URI_START + "kittysqliteorm", destinationFile);
    388//            if(true) return null;
    389            if(getSf().currentMigDBVersion() > DB_IMPLEMENTATION_VERSION) {
    390                return new InsertRandomResults(
    391                        null,
    392                        null,
    393                        null,
    394                        -1l,
    395                        -1l,
    396                        -1l,
    397                        false,
    398                        getSf().currentMigDBVersion()
    399                );
    400            } else {
    401                try {
    402                    KittyMapper mapper4 = getDatabase().getMapper(MigFourModel.class);
    403                    KittyMapper mapper2 = getDatabase().getMapper(MigTwoModel.class);
    404                    KittyMapper mapper3 = getDatabase().getMapper(MigThreeModel.class);
    405                    long recordsCount = mapper4.countAll() + mapper2.countAll() + mapper3.countAll();
    406
    407                    boolean deleteData = getSf().currentMigDBVersion() == DB_IMPLEMENTATION_VERSION;
    408
    409                    long affected;
    410
    411                    if(deleteData)
    412                        affected = mapper4.deleteAll() + mapper2.deleteAll() + mapper3.deleteAll();
    413                    else
    414                        affected = 0l;
    415
    416                    LinkedList<MigFourModel> modelsToInsert = new LinkedList<>();
    417                    LinkedList<MigTwoModel> models2ToInsert = new LinkedList<>();
    418                    LinkedList<MigThreeModel> models3ToInsert = new LinkedList<>();
    419
    420                    getSf().setDatabaseCreated(true);
    421                    getSf().setCurrentMigDBVersion(DB_IMPLEMENTATION_VERSION);
    422                    getSf().setDatabaseDeletedManually(false);
    423
    424                    MigV4RandomModelFactory factory = new MigV4RandomModelFactory(getContext());
    425
    426                    for (int i = 0; i < INSERT_AMOUNT; i++) {
    427                        MigTwoModel m = factory.newM2RndModel();
    428                        models2ToInsert.addLast(m);
    429                    }
    430                    mapper2.insertInTransaction(models2ToInsert);
    431                    List<MigTwoModel> models2 = mapper2.findAll();
    432                    LinkedList<String> out2 = new LinkedList<>();
    433                    Iterator<MigTwoModel> mI2 = models2.iterator();
    434                    while (mI2.hasNext()) {
    435                        out2.addLast(mI2.next().toString());
    436                    }
    437
    438                    for (int i = 0; i < INSERT_AMOUNT; i++) {
    439                        MigThreeModel m = factory.newM3RndModel();
    440                        models3ToInsert.addLast(m);
    441                    }
    442                    mapper3.insertInTransaction(models3ToInsert);
    443                    List<MigThreeModel> models3 = mapper3.findAll();
    444                    LinkedList<String> out3 = new LinkedList<>();
    445                    Iterator<MigThreeModel> mI3 = models3.iterator();
    446                    while (mI3.hasNext()) {
    447                        out3.addLast(mI3.next().toString());
    448                    }
    449
    450                    for (int i = 0; i < INSERT_FK_AMOUNT; i++) {
    451                        MigFourModel m = factory.newM4RndModel(models3, models2);
    452                        modelsToInsert.addLast(m);
    453                    }
    454                    mapper4.insertInTransaction(modelsToInsert);
    455                    List<MigFourModel> models = mapper4.findAll();
    456                    Iterator<MigFourModel> mI = models.iterator();
    457                    LinkedList<String> out4 = new LinkedList<>();
    458                    while (mI.hasNext()) {
    459                        out4.addLast(mI.next().toString());
    460                    }
    461
    462                    long recordsCountAfter = mapper4.countAll() + mapper2.countAll() + mapper3.countAll();
    463                    mapper4.close(); mapper2.close(); mapper3.close();
    464                    return new InsertRandomResults(out4, out2, out3, affected, recordsCount, recordsCountAfter, true, getSf().currentMigDBVersion());
    465                } catch (Exception e) {
    466                    Log.e(MigrationDBv4.LTAG, ERR_INSERT_RND, e);
    467                    if (e instanceof KittyRuntimeException) {
    468                        if (((KittyRuntimeException) e).getNestedException() != null) {
    469                            Log.e(MigrationDBv4.LTAG, ERR_INSERT_RND, ((KittyRuntimeException) e).getNestedException());
    470                        }
    471                    }
    472                    return new InsertRandomResults(
    473                            null,
    474                            null,
    475                            null,
    476                            -1l,
    477                            -1l,
    478                            -1l,
    479                            false,
    480                            getSf().currentMigDBVersion()
    481                    );
    482                }
    483            }
    484        }
    485
    486        @Override
    487        protected void onPostExecute(InsertRandomResults result) {
    488            dialog.cancel();
    489            if (eventsListView != null) {
    490                eventsListView.setAdapter(new BasicArrayAdapter(getContext(), new LinkedList<String>()));
    491                eventsListView.setOnTouchListener(new View.OnTouchListener() {
    492
    493                    // Setting on Touch Listener for handling the touch inside ScrollView
    494                    @Override
    495                    public boolean onTouch(View v, MotionEvent event) {
    496                        // Disallow the touch request for parent scroll on touch of child view
    497                        v.getParent().requestDisallowInterceptTouchEvent(true);
    498                        return false;
    499                    }
    500                });
    501                if (result.operationSuccess) {
    502                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_count_to_events), result.modelsCountBefore));
    503                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_deleted_to_events), result.deletedModelsAffectedRows));
    504                    for (String modelString : result.modelInsertionsM4) {
    505                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_inserted_to_events), M1M4TN, modelString));
    506                    }
    507                    for (String modelString2 : result.modelInsertionsM2) {
    508                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_inserted_to_events), M1M2TN, modelString2));
    509                    }
    510                    for (String modelString3 : result.modelInsertionsM3) {
    511                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_inserted_to_events), M1M3TN, modelString3));
    512                    }
    513                    ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_count_to_events), result.modelsCountAfter));
    514                } else {
    515                    if(getSf().currentMigDBVersion() > DB_IMPLEMENTATION_VERSION) {
    516                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_op_mig_version_is_higher), result.dbVersion, DB_IMPLEMENTATION_VERSION));
    517                    } else {
    518                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(getString(R.string._l5_t4_error_event));
    519                    }
    520                }
    521                ((BasicArrayAdapter) eventsListView.getAdapter()).notifyDataSetChanged();
    522                reloadTableExpandedList();
    523                reloadStatus();
    524            }
    525        }
    526
    527
    528    }
    529
    530    class InsertRandomResults {
    531        List<String> modelInsertionsM4;
    532        List<String> modelInsertionsM2;
    533        List<String> modelInsertionsM3;
    534        long deletedModelsAffectedRows;
    535        long modelsCountBefore;
    536        long modelsCountAfter;
    537        boolean operationSuccess;
    538        int dbVersion;
    539
    540        public InsertRandomResults(List<String> modelInsertionsM4, List<String> modelInsertionsM2,
    541                                   List<String> modelInsertionsM3, long deletedModelsAffectedRows,
    542                                   long modelsCountBefore, long modelsCountAfter, boolean opSuccess,
    543                                   int dbVersion) {
    544            this.modelInsertionsM4 = modelInsertionsM4;
    545            this.modelInsertionsM2 = modelInsertionsM2;
    546            this.modelInsertionsM3 = modelInsertionsM3;
    547            this.deletedModelsAffectedRows = deletedModelsAffectedRows;
    548            this.modelsCountBefore = modelsCountBefore;
    549            this.modelsCountAfter = modelsCountAfter;
    550            this.operationSuccess = opSuccess;
    551            this.dbVersion = dbVersion;
    552        }
    553    }
    554
    555    static final String ERR_DELETION = "Lesson5tab4DBDeleteError, see exception details!";
    556
    557    class DeleteDatabaseAsync extends AsyncTask<Long, Long, Integer> {
    558        ProgressDialog dialog;
    559
    560        final int DELETED = 1;
    561        final int NOT_DELETED = 2;
    562        final int ERROR = 3;
    563
    564        @Override
    565        protected void onPreExecute() {
    566            dialog = ProgressDialog.show(
    567                    getLessonActivity(),
    568                    getString(R.string._l5_t4_running_requested_operation_pg_title),
    569                    getString(R.string._l5_t4_running_requested_operation_pg_body)
    570            );
    571            dialog.setCancelable(false);
    572        }
    573
    574        @Override
    575        protected Integer doInBackground(Long... strings) {
    576            try {
    577                boolean deleted = getDatabase().deleteDatabase();
    578                getSf().setDatabaseDeletedManually(true);
    579                getSf().setDatabaseCreated(false);
    580                getSf().setCurrentMigDBVersion(-1);
    581                if(deleted)
    582                    return DELETED;
    583                else
    584                    return NOT_DELETED;
    585            } catch (Exception e) {
    586                Log.e(MigrationDBv4.LTAG, ERR_DELETION, e);
    587                if (e instanceof KittyRuntimeException) {
    588                    if (((KittyRuntimeException) e).getNestedException() != null) {
    589                        Log.e(MigrationDBv4.LTAG, ERR_DELETION, ((KittyRuntimeException) e).getNestedException());
    590                    }
    591                }
    592                return ERROR;
    593            }
    594        }
    595
    596        @Override
    597        protected void onPostExecute(Integer result) {
    598            dialog.cancel();
    599            if (eventsListView != null) {
    600                eventsListView.setAdapter(new BasicArrayAdapter(getContext(), new LinkedList<String>()));
    601                eventsListView.setOnTouchListener(new View.OnTouchListener() {
    602
    603                    // Setting on Touch Listener for handling the touch inside ScrollView
    604                    @Override
    605                    public boolean onTouch(View v, MotionEvent event) {
    606                        // Disallow the touch request for parent scroll on touch of child view
    607                        v.getParent().requestDisallowInterceptTouchEvent(true);
    608                        return false;
    609                    }
    610                });
    611                switch (result) {
    612                    case DELETED:
    613                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_delete_db_success)));
    614                        break;
    615                    case NOT_DELETED:
    616                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_delete_db_fail)));
    617                        break;
    618                    case ERROR:
    619                        ((BasicArrayAdapter) eventsListView.getAdapter()).addItemLast(format(getString(R.string._l5_t4_error_event)));
    620                        break;
    621                }
    622                ((BasicArrayAdapter) eventsListView.getAdapter()).notifyDataSetChanged();
    623                reloadTableExpandedList();
    624                reloadStatus();
    625            }
    626        }
    627    }
    628
    629    // Expanded list
    630    MigAdapter migAdapter;
    631
    632    @Override
    633    protected void setUpExpandedList(View rootView, int eventsId, int eventsTitleId, int eventTitleStringPattern) {
    634        events = (ListView) rootView.findViewById(eventsId);
    635        expandedTitle = (TextView) rootView.findViewById(eventsTitleId);
    636        expandeddTitlePattern = getString(eventTitleStringPattern);
    637
    638        expandedTitle.setText(format(expandeddTitlePattern, 0));
    639
    640        if(expandedAdapter == null) {
    641            migAdapter = new MigAdapter(getContext(), new LinkedList<String>());
    642        }
    643
    644        events.setAdapter(migAdapter);
    645        events.setOnTouchListener(new View.OnTouchListener() {
    646
    647            // Setting on Touch Listener for handling the touch inside ScrollView
    648            @Override
    649            public boolean onTouch(View v, MotionEvent event) {
    650                // Disallow the touch request for parent scroll on touch of child view
    651                v.getParent().requestDisallowInterceptTouchEvent(true);
    652                return false;
    653            }
    654        });
    655    }
    656
    657    // Fab menu section
    658
    659    @Override
    660    public View.OnClickListener helpFabMenuAction() {
    661        return new View.OnClickListener() {
    662
    663            /**
    664             * Called when a view has been clicked.
    665             *
    666             * @param v The view that was clicked.
    667             */
    668            @Override
    669            public void onClick(View v) {
    670                ((KittyTutorialActivity) getParentFragment().getActivity()).showWebViewDialog(L5_T4_TUTORIAL);
    671            }
    672        };
    673    }
    674
    675    @Override
    676    public View.OnClickListener sourceFabMenuAction() {
    677        return new View.OnClickListener() {
    678
    679            /**
    680             * Called when a view has been clicked.
    681             *
    682             * @param v The view that was clicked.
    683             */
    684            @Override
    685            public void onClick(View v) {
    686                ((KittyTutorialActivity) getParentFragment().getActivity()).showWebViewDialog(L5_T4_SOURCE);
    687            }
    688        };
    689    }
    690
    691    @Override
    692    public View.OnClickListener schemaFabMenuAction() {
    693        return new View.OnClickListener() {
    694
    695            /**
    696             * Called when a view has been clicked.
    697             *
    698             * @param v The view that was clicked.
    699             */
    700            @Override
    701            public void onClick(View v) {
    702                ((KittyTutorialActivity) getParentFragment().getActivity()).showWebViewDialog(L5_T4_SCHEMA);
    703            }
    704        };
    705    }
    706}