diff --git a/kripton-android-core/kripton-android-core.iml b/kripton-android-core/kripton-android-core.iml index b39721097..d6c1687de 100644 --- a/kripton-android-core/kripton-android-core.iml +++ b/kripton-android-core/kripton-android-core.iml @@ -13,10 +13,12 @@ - - + + + + @@ -72,7 +74,7 @@ - + diff --git a/kripton-android-core/pom.xml b/kripton-android-core/pom.xml index b7b92ec38..add19c115 100644 --- a/kripton-android-core/pom.xml +++ b/kripton-android-core/pom.xml @@ -13,7 +13,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -25,7 +25,7 @@ Kripton Persistence Library for Android platform - core module for android modules - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-android-core/src/main/java/com/abubusoft/kripton/androidx/livedata/KriptonXDataSource.java b/kripton-android-core/src/main/java/com/abubusoft/kripton/androidx/livedata/KriptonXDataSource.java index 85621536c..43af2786c 100644 --- a/kripton-android-core/src/main/java/com/abubusoft/kripton/androidx/livedata/KriptonXDataSource.java +++ b/kripton-android-core/src/main/java/com/abubusoft/kripton/androidx/livedata/KriptonXDataSource.java @@ -1,13 +1,14 @@ package com.abubusoft.kripton.androidx.livedata; -import java.util.Collections; -import java.util.List; - import androidx.annotation.NonNull; import androidx.lifecycle.Observer; import androidx.paging.DataSource; import androidx.paging.PositionalDataSource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * A {@link PositionalDataSource} that loads entities based on an ObjectBox * {@link Query} using offset and limit to implement paging support. The data @@ -15,76 +16,76 @@ */ public class KriptonXDataSource extends PositionalDataSource { - private final PagedLiveData query; - - private final Observer observer; - - public static class Factory extends DataSource.Factory { - - private final PagedLiveData query; - - public Factory(PagedLiveData query) { - this.query = query; - } - - @Override - public KriptonXDataSource create() { - return new KriptonXDataSource<>(query); - } - } - - public KriptonXDataSource(PagedLiveData query) { - this.query = query; - - observer = new Observer() { - - @Override - public void onChanged(T t) { - invalidate(); - - } - }; - - query.observeForever(observer); - // observer will be automatically removed once GC'ed - // query. subscribe().onlyChanges().weak().observer(observer); - } - - @Override - public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback callback) { - // note: limiting to int should be fine for Android apps - // TODO ripristinare query.count(); - int totalCount = query.getTotalElements(); - if (totalCount == 0) { - callback.onResult(Collections.emptyList(), 0, 0); - return; - } - - int position = computeInitialLoadPosition(params, totalCount); - int loadSize = computeInitialLoadSize(params, position, totalCount); - - List list = loadRange(position, loadSize); - if (list.size() == loadSize) { - callback.onResult(list, position, totalCount); - } else { - invalidate(); // size doesn't match request - DB modified between - // count and load - } - } - - @Override - public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback callback) { - callback.onResult(loadRange(params.startPosition, params.loadSize)); - } - - private List loadRange(int startPosition, int loadCount) { - /* private void loadRange(int startPosition, int loadCount) { */ - // note: find interprets loadCount 0 as no limit - //query.moveTo(startPosition, loadCount); - - query.createPageRequestBuilder().offset(startPosition).pageSize(loadCount).apply(); - - return null; - } + private final PagedLiveData query; + + private final Observer observer; + + public static class Factory extends DataSource.Factory { + + private final PagedLiveData query; + + public Factory(PagedLiveData query) { + this.query = query; + } + + @Override + public KriptonXDataSource create() { + return new KriptonXDataSource<>(query); + } + } + + public KriptonXDataSource(PagedLiveData query) { + this.query = query; + + observer = new Observer() { + + @Override + public void onChanged(T t) { + invalidate(); + + } + }; + + query.observeForever(observer); + // observer will be automatically removed once GC'ed + // query. subscribe().onlyChanges().weak().observer(observer); + } + + @Override + public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback callback) { + // note: limiting to int should be fine for Android apps + // TODO ripristinare query.count(); + int totalCount = query.getTotalElements(); + if (totalCount == 0) { + callback.onResult(Collections.emptyList(), 0, 0); + return; + } + + int position = computeInitialLoadPosition(params, totalCount); + int loadSize = computeInitialLoadSize(params, position, totalCount); + + List list = loadRange(position, loadSize); + if (list != null && list.size() == loadSize) { + callback.onResult(list, position, totalCount); + } else { + invalidate(); // size doesn't match request - DB modified between + // count and load + } + } + + @Override + public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback callback) { + callback.onResult(loadRange(params.startPosition, params.loadSize)); + } + + private List loadRange(int startPosition, int loadCount) { + /* private void loadRange(int startPosition, int loadCount) { */ + // note: find interprets loadCount 0 as no limit + //query.moveTo(startPosition, loadCount); + + query.createPageRequestBuilder().offset(startPosition).pageSize(loadCount).apply(); + + return new ArrayList<>(); + } } \ No newline at end of file diff --git a/kripton-android-library/pom.xml b/kripton-android-library/pom.xml index 8dd653aef..427799448 100644 --- a/kripton-android-library/pom.xml +++ b/kripton-android-library/pom.xml @@ -14,7 +14,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -31,7 +31,7 @@ 2.2.19 1.0.3 - 7.0.0-rc.8 + 7.0.0-rc.9 3.1.4 4.13.1 3.4.2 diff --git a/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeA.java b/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeA.java index 04fb65ed7..388c7e128 100644 --- a/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeA.java +++ b/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeA.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2016-2019 Francesco Benincasa (info@abubusoft.com) - * + * * 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 @@ -15,70 +15,55 @@ ******************************************************************************/ package sqlite.kripton84; -import java.io.IOException; -import java.util.List; - +import base.BaseAndroidTest; +import com.abubusoft.kripton.android.sqlite.TransactionResult; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import com.abubusoft.kripton.android.sqlite.TransactionResult; - -import base.BaseAndroidTest; -import sqlite.kripton84.BindBean84ADataSource.Transaction; +import java.io.IOException; +import java.util.List; // TODO: Auto-generated Javadoc + /** * The Class Test84RuntimeA. * * @author Francesco Benincasa (info@abubusoft.com) */ -@Config(manifest=Config.NONE) +@Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class Test84RuntimeA extends BaseAndroidTest { - /** - * Test run sqlite. - * - * @throws IOException Signals that an I/O exception has occurred. - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - */ - @Test - public void testRunSqlite() throws IOException, InstantiationException, IllegalAccessException { - Assert.assertNotNull(Bean84ATable.class.getName() != null); - Assert.assertNotNull(Bean84ADaoImpl.class.getName() != null); - - - BindBean84ADataSource dataSource = BindBean84ADataSource.getInstance(); - //dataSource.openWritableDatabase(); - - dataSource.execute(new Transaction() { - - @Override - public TransactionResult onExecute(BindBean84ADaoFactory daoFactory) { - Bean84ADaoImpl dao = daoFactory.getBean84ADao(); + /** + * Test run sqlite. + * + * @throws IOException Signals that an I/O exception has occurred. + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + */ + @Test + public void testRunSqlite() { + BindBean84ADataSource dataSource = BindBean84ADataSource.getInstance(); - Bean84A bean = new Bean84A(); - bean.valueString = "hello"; + dataSource.execute(daoFactory -> { + Bean84ADaoImpl dao = daoFactory.getBean84ADao(); - dao.insertAll(bean); - List list = dao.selectById(bean.id); - Assert.assertEquals("not list ", 1, list.size()); + Bean84A bean = new Bean84A(); + bean.valueString = "hello"; - Assert.assertEquals("not map", 1, list.size()); + dao.insertAll(bean); + List list = dao.selectById(bean.id); + Assert.assertEquals("not list ", 1, list.size()); - // Assert.assertEquals("not set", 1, - // list.get(0).valueSetString.size()); + Assert.assertEquals("not map", 1, list.size()); - return TransactionResult.COMMIT; - } - - }); + return TransactionResult.COMMIT; + }); - } + } } diff --git a/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeB.java b/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeB.java index e9aa38290..1632ad2e9 100644 --- a/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeB.java +++ b/kripton-android-library/src/test/java/sqlite/kripton84/Test84RuntimeB.java @@ -1,12 +1,12 @@ /******************************************************************************* * Copyright 2016-2019 Francesco Benincasa (info@abubusoft.com) - * + * * 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 @@ -15,69 +15,53 @@ ******************************************************************************/ package sqlite.kripton84; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; - -import org.junit.Assert; +import base.BaseAndroidTest; +import com.abubusoft.kripton.android.sqlite.TransactionResult; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; -import com.abubusoft.kripton.android.sqlite.TransactionResult; - -import base.BaseAndroidTest; -import sqlite.kripton84.BindBean84BDataSource.Transaction; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; // TODO: Auto-generated Javadoc + /** * The Class Test84RuntimeB. * * @author Francesco Benincasa (info@abubusoft.com) */ -@Config(manifest=Config.NONE) +@Config(manifest = Config.NONE) @RunWith(RobolectricTestRunner.class) public class Test84RuntimeB extends BaseAndroidTest { - /** - * Test run. - * - * @throws IOException Signals that an I/O exception has occurred. - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - */ - @Test - public void testRun() throws IOException, InstantiationException, IllegalAccessException { - Assert.assertNotNull(Bean84BTable.class.getName() != null); - Assert.assertNotNull(Bean84BDaoImpl.class.getName() != null); - - BindBean84BDataSource dataSource = BindBean84BDataSource.getInstance(); - - dataSource.execute(new Transaction() { + /** + * Test run. + */ + @Test + public void testRun() { + BindBean84BDataSource dataSource = BindBean84BDataSource.getInstance(); - @Override - public TransactionResult onExecute(BindBean84BDaoFactory daoFactory) { - Bean84BDaoImpl dao = daoFactory.getBean84BDao(); + dataSource.execute(daoFactory -> { + Bean84BDaoImpl dao = daoFactory.getBean84BDao(); - Bean84B2 innerBean = new Bean84B2(); - innerBean.columnString = "test01"; + Bean84B2 innerBean = new Bean84B2(); + innerBean.columnString = "test01"; - Bean84B bean = new Bean84B(); - bean.columnBean = innerBean; - dao.insert(bean); + Bean84B bean = new Bean84B(); + bean.columnBean = innerBean; + dao.insert(bean); - Bean84B bean2 = dao.selectById(bean.id); - assertTrue(bean.equals(bean2)); + Bean84B bean2 = dao.selectById(bean.id); + assertEquals(bean, bean2); - Bean84B bean3 = dao.selectByBean(innerBean); - assertTrue(bean.equals(bean3)); + Bean84B bean3 = dao.selectByBean(innerBean); + assertEquals(bean, bean3); - return TransactionResult.COMMIT; - } - - }); + return TransactionResult.COMMIT; + }); - } + } } diff --git a/kripton-arch-integration/kripton-arch-integration.iml b/kripton-arch-integration/kripton-arch-integration.iml index ba7de442b..9807b9bf7 100644 --- a/kripton-arch-integration/kripton-arch-integration.iml +++ b/kripton-arch-integration/kripton-arch-integration.iml @@ -15,7 +15,7 @@ - + @@ -64,14 +64,6 @@ - - - - - - - - \ No newline at end of file diff --git a/kripton-arch-integration/pom.xml b/kripton-arch-integration/pom.xml index 3bc7ebc6c..bb1e11c97 100644 --- a/kripton-arch-integration/pom.xml +++ b/kripton-arch-integration/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml diff --git a/kripton-arch-integration/src/main/java/androidx/arch/core/executor/ArchTaskExecutor.java b/kripton-arch-integration/src/main/java/androidx/arch/core/executor/ArchTaskExecutor.java index a7669454a..f33a1613b 100644 --- a/kripton-arch-integration/src/main/java/androidx/arch/core/executor/ArchTaskExecutor.java +++ b/kripton-arch-integration/src/main/java/androidx/arch/core/executor/ArchTaskExecutor.java @@ -31,23 +31,13 @@ public class ArchTaskExecutor extends TaskExecutor { private TaskExecutor mDelegate; /** The m default task executor. */ - private TaskExecutor mDefaultTaskExecutor; + private final TaskExecutor mDefaultTaskExecutor; /** The Constant sMainThreadExecutor. */ - private static final Executor sMainThreadExecutor = new Executor() { - @Override - public void execute(Runnable command) { - getInstance().postToMainThread(command); - } - }; + private static final Executor sMainThreadExecutor = command -> getInstance().postToMainThread(command); /** The Constant sIOThreadExecutor. */ - private static final Executor sIOThreadExecutor = new Executor() { - @Override - public void execute(Runnable command) { - getInstance().executeOnDiskIO(command); - } - }; + private static final Executor sIOThreadExecutor = command -> getInstance().executeOnDiskIO(command); /** * Instantiates a new arch task executor. diff --git a/kripton-arch-test/pom.xml b/kripton-arch-test/pom.xml index c4b23f7fc..b0e060033 100644 --- a/kripton-arch-test/pom.xml +++ b/kripton-arch-test/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml diff --git a/kripton-core/pom.xml b/kripton-core/pom.xml index 2926a1cac..99a7af734 100644 --- a/kripton-core/pom.xml +++ b/kripton-core/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml diff --git a/kripton-core/src/main/java/com/abubusoft/kripton/common/DateUtils.java b/kripton-core/src/main/java/com/abubusoft/kripton/common/DateUtils.java index b6301cd58..906217dfe 100644 --- a/kripton-core/src/main/java/com/abubusoft/kripton/common/DateUtils.java +++ b/kripton-core/src/main/java/com/abubusoft/kripton/common/DateUtils.java @@ -110,11 +110,7 @@ static String getPattern(String text) { public static class ThreadLocalDateFormatter { /** The Constant FORMATTERS. */ - private static final ThreadLocal> FORMATTERS = new ThreadLocal>() { - protected Map initialValue() { - return new HashMap(); - } - }; + private static final ThreadLocal> FORMATTERS = ThreadLocal.withInitial(HashMap::new); /** * Gets the formatter. @@ -122,7 +118,7 @@ protected Map initialValue() { * @param pattern the pattern * @return the formatter */ - static private final DateFormat getFormatter(final String pattern) { + static private DateFormat getFormatter(final String pattern) { Map formatterMap = FORMATTERS.get(); DateFormat df = formatterMap.get(pattern); if (null == df) { diff --git a/kripton-core/src/main/java/com/abubusoft/kripton/common/DynamicByteBufferHelper.java b/kripton-core/src/main/java/com/abubusoft/kripton/common/DynamicByteBufferHelper.java index a26d77098..c2ea0255d 100644 --- a/kripton-core/src/main/java/com/abubusoft/kripton/common/DynamicByteBufferHelper.java +++ b/kripton-core/src/main/java/com/abubusoft/kripton/common/DynamicByteBufferHelper.java @@ -32,7 +32,6 @@ import com.abubusoft.kripton.exception.KriptonRuntimeException; -// TODO: Auto-generated Javadoc /** * The Class DynamicByteBufferHelper. */ @@ -469,23 +468,9 @@ public static byte[] insert(final byte[] array, final int idx, final byte v) { System.arraycopy(array, 0, newArray, 0, index); } - boolean lastIndex = index == array.length - 1; int remainingIndex = array.length - index; - if (lastIndex) { - /* - * Copy the area after the insert. Make sure we don't write over the - * end. - */ - /* src sbegin dst dbegin length of copy */ - System.arraycopy(array, index, newArray, index + 1, remainingIndex); - - } else { - /* Copy the area after the insert. */ - /* src sbegin dst dbegin length of copy */ - System.arraycopy(array, index, newArray, index + 1, remainingIndex); - - } + System.arraycopy(array, index, newArray, index + 1, remainingIndex); newArray[index] = v; return newArray; @@ -518,25 +503,10 @@ public static byte[] insert(final byte[] array, final int fromIndex, final byte[ System.arraycopy(array, 0, newArray, 0, index); } - boolean lastIndex = index == array.length - 1; - int toIndex = index + values.length; int remainingIndex = newArray.length - toIndex; - if (lastIndex) { - /* - * Copy the area after the insert. Make sure we don't write over the - * end. - */ - /* src sbegin dst dbegin length of copy */ - System.arraycopy(array, index, newArray, index + values.length, remainingIndex); - - } else { - /* Copy the area after the insert. */ - /* src sbegin dst dbegin length of copy */ - System.arraycopy(array, index, newArray, index + values.length, remainingIndex); - - } + System.arraycopy(array, index, newArray, index + values.length, remainingIndex); for (int i = index, j = 0; i < toIndex; i++, j++) { newArray[i] = values[j]; diff --git a/kripton-core/src/main/java/com/abubusoft/kripton/common/StringUtils.java b/kripton-core/src/main/java/com/abubusoft/kripton/common/StringUtils.java index ae2182832..0ef1e41d8 100644 --- a/kripton-core/src/main/java/com/abubusoft/kripton/common/StringUtils.java +++ b/kripton-core/src/main/java/com/abubusoft/kripton/common/StringUtils.java @@ -19,7 +19,6 @@ import java.io.Reader; import java.io.Writer; -// TODO: Auto-generated Javadoc /** * A few string utils. * diff --git a/kripton-dataformat-cbor/pom.xml b/kripton-dataformat-cbor/pom.xml index 7a022b006..bf2e2e538 100644 --- a/kripton-dataformat-cbor/pom.xml +++ b/kripton-dataformat-cbor/pom.xml @@ -6,7 +6,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -16,7 +16,7 @@ jar - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-dataformat-properties/pom.xml b/kripton-dataformat-properties/pom.xml index 19555e4c9..2edbf5fd8 100644 --- a/kripton-dataformat-properties/pom.xml +++ b/kripton-dataformat-properties/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -16,7 +16,7 @@ jar - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-dataformat-smile/pom.xml b/kripton-dataformat-smile/pom.xml index 8e2af82ed..717425f02 100644 --- a/kripton-dataformat-smile/pom.xml +++ b/kripton-dataformat-smile/pom.xml @@ -5,14 +5,14 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml kripton-dataformat-smile - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-dataformat-yaml/pom.xml b/kripton-dataformat-yaml/pom.xml index 688b95b5c..1ae928530 100644 --- a/kripton-dataformat-yaml/pom.xml +++ b/kripton-dataformat-yaml/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -15,7 +15,7 @@ jar - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-example01/pom.xml b/kripton-example01/pom.xml index fe6a2e15f..17fae49d2 100644 --- a/kripton-example01/pom.xml +++ b/kripton-example01/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml diff --git a/kripton-orm/pom.xml b/kripton-orm/pom.xml index 9841544aa..f5a97528a 100644 --- a/kripton-orm/pom.xml +++ b/kripton-orm/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -16,7 +16,7 @@ jar - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlcipher/Database.java b/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlcipher/Database.java index 2a7e213d6..a53aef119 100644 --- a/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlcipher/Database.java +++ b/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlcipher/Database.java @@ -261,13 +261,9 @@ public Cursor query(final SupportSQLiteQuery supportQuery, CancellationSignal si supportQuery.bindTo(hack); - return (safeDb.rawQueryWithFactory(new net.sqlcipher.database.SQLiteDatabase.CursorFactory() { - @Override - public net.sqlcipher.Cursor newCursor(net.sqlcipher.database.SQLiteDatabase db, - SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) { - supportQuery.bindTo(new Program(query)); - return new SQLiteCursor(db, masterQuery, editTable, query); - } + return (safeDb.rawQueryWithFactory((db, masterQuery, editTable, query) -> { + supportQuery.bindTo(new Program(query)); + return new SQLiteCursor(db, masterQuery, editTable, query); }, supportQuery.getSql(), hack.getBindings(), null)); } diff --git a/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlite/AbstractDataSource.java b/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlite/AbstractDataSource.java index b6861815a..c0422290d 100644 --- a/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlite/AbstractDataSource.java +++ b/kripton-orm/src/main/java/com/abubusoft/kripton/android/sqlite/AbstractDataSource.java @@ -1,38 +1,23 @@ -/******************************************************************************* +/** * Copyright 2015, 2017 Francesco Benincasa (info@abubusoft.com). - * + *

* 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 - * + *

+ * 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.abubusoft.kripton.android.sqlite; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import com.abubusoft.kripton.android.KriptonLibrary; -import com.abubusoft.kripton.android.Logger; -import com.abubusoft.kripton.common.Pair; -import com.abubusoft.kripton.exception.KriptonRuntimeException; - import android.content.ContentValues; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -42,6 +27,20 @@ import androidx.sqlite.db.SupportSQLiteOpenHelper; import androidx.sqlite.db.SupportSQLiteOpenHelper.Configuration.Builder; import androidx.sqlite.db.SupportSQLiteStatement; +import com.abubusoft.kripton.android.KriptonLibrary; +import com.abubusoft.kripton.android.Logger; +import com.abubusoft.kripton.common.Pair; +import com.abubusoft.kripton.exception.KriptonRuntimeException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** *

@@ -53,855 +52,846 @@ */ public abstract class AbstractDataSource implements AutoCloseable { - /** - * Interface for database operations. - * - * @param - * the element type - */ - public interface AbstractExecutable { - - /** - * Execute transation. Method need to return - * {@link TransactionResult#COMMIT} to commit results or - * {@link TransactionResult#ROLLBACK} to rollback. If exception is - * thrown, a rollback will be done. - * - * @param daoFactory - * the dao factory - * @return the transaction result - */ - TransactionResult onExecute(E daoFactory); - } - - /** - * The listener interface for receiving onError events. The class that is - * interested in processing a onError event implements this interface, and - * the object created with that class is registered with a component using - * the component's addOnErrorListener method. When the onError - * event occurs, that object's appropriate method is invoked. - * - */ - public interface OnErrorListener { - /** - * Manages error situations. - * - * @param e - * exception - */ - void onError(Throwable e); - } - - /** - * The Enum TypeStatus. - */ - public enum TypeStatus { - - /** The closed. */ - CLOSED, - /** The read and write opened. */ - READ_AND_WRITE_OPENED, - /** The read only opened. */ - READ_ONLY_OPENED - } - - /** The context. */ - protected SQLContextImpl context; - - /** database instance. */ - SupportSQLiteDatabase database; - - /** - *

- * True if dataSource is just created - *

- * . - */ - protected boolean justCreated = false; - - /** The lock access. */ - private final ReentrantReadWriteLock lockAccess = new ReentrantReadWriteLock(); - - /** The lock db. */ - private final ReentrantLock lockDb = new ReentrantLock(); - - /** The lock read access. */ - private final Lock lockReadAccess = lockAccess.readLock(); - - /** The lock read write access. */ - private final Lock lockReadWriteAccess = lockAccess.writeLock(); - - /** The log enabled. */ - protected boolean logEnabled; - - /** - *

- * file name used to save database, - *

- * . - */ - protected final String name; - - /** The on error listener. */ - protected OnErrorListener onErrorListener = (Throwable e) -> { throw (new KriptonRuntimeException(e));}; - - - /** The open counter. */ - private AtomicInteger openCounter = new AtomicInteger(); - - /** The options. */ - protected DataSourceOptions options; - - /** The sqlite helper. */ - protected SupportSQLiteOpenHelper sqliteHelper; - - /** The status. */ - protected ThreadLocal status = new ThreadLocal() { - - @Override - protected TypeStatus initialValue() { - return TypeStatus.CLOSED; - } - - }; - - /** - *

- * database version - *

- * . - */ - protected int version; - - /** if true, database was update during this application run. */ - protected boolean versionChanged; - - /** - * Instantiates a new abstract data source. - * - * @param name - * the name - * @param version - * the version - * @param options - * the options - */ - protected AbstractDataSource(String name, int version, DataSourceOptions options) { - DataSourceOptions optionsValue = (options == null) ? DataSourceOptions.builder().build() : options; - - this.name = optionsValue.inMemory ? null : name; - this.version = version; - - // create new SQLContext - this.context = new SQLContextImpl(this); - - this.options = optionsValue; - this.logEnabled = optionsValue.logEnabled; - - if (this.logEnabled) { - Logger.debug("%s is created with %s", getClass().getName(), options.toString()); - } - } - - protected void beginLock() { - lockDb.lock(); - } - - /** - * Builds the task list. - * - * @param previousVersion - * the previous version - * @param currentVersion - * the current version - * @return the list - */ - protected List buildTaskList(int previousVersion, int currentVersion) { - List result = new ArrayList<>(); - - for (Pair item : this.options.updateTasks) { - if (item.value0 - 1 == previousVersion) { - result.add(item.value1); - previousVersion = item.value0; - } - - if (previousVersion == currentVersion) - break; - } - - if (previousVersion != currentVersion) { - Logger.warn(String.format("Can not find version update task from version %s to version %s", previousVersion, - currentVersion)); - } - - return result; - - } - - /** - * used to clear prepared statements. - */ - public abstract void clearCompiledStatements(); - - /** - * Context. - * - * @return the SQL context - */ - public SQLContext getContext() { - return context; - } - - /* - * (non-Javadoc) - * - * @see android.database.sqlite.SQLiteOpenHelper#close() - */ - @Override - public void close() { - beginLock(); - try { - if (openCounter.decrementAndGet() <= 0) { - - if (!this.options.inMemory) { - // Closing database - if (database != null) { - clearCompiledStatements(); - sqliteHelper.close(); - } - database = null; - } - if (logEnabled) - Logger.info("database CLOSED (%s) (connections: %s)", status.get(), openCounter.intValue()); - } else { - if (logEnabled) - Logger.info("database RELEASED (%s) (connections: %s)", status.get(), openCounter.intValue()); - } - } catch (Exception e) { - e.printStackTrace(); - throw (e); - } finally { - manageStatus(); - endLock(); - } - - } - - protected void closeThreadSafeMode(Pair status) { - if (status.value0==true) { - close(); - } else { - beginLock(); - // we unlock lockReadWriteAccess, so we can include this code in - // lockDb - manageStatus(); - endLock(); - - // unlocked inside - // lockDb.unlock(); - } - } - - /** - * Content values. - * - * @param compiledStatement - * the compiled statement - * @return the kripton content values - */ - protected KriptonContentValues contentValues(SupportSQLiteStatement compiledStatement) { - return context.contentValues(compiledStatement); - } - - /** - * Content values for content provider. - * - * @param values - * the values - * @return the kripton content values - */ - protected KriptonContentValues contentValuesForContentProvider(ContentValues values) { - return context.contentValuesForContentProvider(values); - } - - /** - * Content values for update. - * - * @param compiledStatement - * the compiled statement - * @return the kripton content values - */ - protected KriptonContentValues contentValuesForUpdate(SupportSQLiteStatement compiledStatement) { - return context.contentValuesForUpdate(compiledStatement); - } - - /** - * Creates the helper. - * - * @param options - * the options - * - */ - protected void createHelper() { - if (KriptonLibrary.getContext() == null) - throw new KriptonRuntimeException( - "Kripton library is not properly initialized. Please use KriptonLibrary.init(context) somewhere at application startup"); - - if (this.logEnabled) { - if (options.inMemory) { - Logger.info("In-memory database"); - } else { - File dbFile = KriptonLibrary.getContext().getDatabasePath(name); - Logger.info("Database file %s", dbFile.getAbsolutePath()); - } - } - - Builder config = SupportSQLiteOpenHelper.Configuration.builder(KriptonLibrary.getContext()).name(name) - .callback(new SupportSQLiteOpenHelper.Callback(version) { - - @Override - public void onConfigure(SupportSQLiteDatabase db) { - AbstractDataSource.this.onConfigure(db); - } - - @Override - public void onCorruption(SupportSQLiteDatabase db) { - AbstractDataSource.this.onCorruption(db); - } - - @Override - public void onCreate(SupportSQLiteDatabase db) { - sqliteHelper.setWriteAheadLoggingEnabled(true); - AbstractDataSource.this.onCreate(db); - } - - @Override - public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { - AbstractDataSource.this.onDowngrade(db, oldVersion, newVersion); - } - - @Override - public void onOpen(SupportSQLiteDatabase db) { - sqliteHelper.setWriteAheadLoggingEnabled(true); - AbstractDataSource.this.onOpen(db); - } - - @Override - public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { - AbstractDataSource.this.onUpgrade(db, oldVersion, newVersion); - } - }); - - sqliteHelper = options.openHelperFactory.create(config.build()); - - if (this.logEnabled) { - Logger.debug("Database helper factory class is %s", options.openHelperFactory.getClass().getName()); - Logger.debug("Database helper class is %s", sqliteHelper.getClass().getName()); - } - } - - private void deleteDatabaseFile(String fileName) { - if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) { - return; - } - if (this.logEnabled) { - Logger.fatal("deleting the database file: " + fileName); - } - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - SQLiteDatabase.deleteDatabase(new File(fileName)); - } else { - try { - final boolean deleted = new File(fileName).delete(); - if (!deleted) { - if (this.logEnabled) { - Logger.fatal("Could not delete the database file " + fileName); - } - } - } catch (Exception error) { - if (this.logEnabled) { - Logger.fatal("error while deleting corrupted database file " + error.getMessage()); - } - } - } - } catch (Exception e) { - if (this.logEnabled) { - /* print warning and ignore exception */ - Logger.warn("delete failed: ", e); - } - } - } - - protected void endLock() { - lockDb.unlock(); - } - - /** - * Force close. - */ - void forceClose() { - openCounter.set(0); - } - - /** - *

- * Return database object or runtimeexception if no database is opened. - *

- * - * @return the SQ lite database - */ - public SupportSQLiteDatabase getDatabase() { - if (database == null) - throw (new KriptonRuntimeException( - "No database connection is opened before use " + this.getClass().getCanonicalName())); - return database; - } - - public String getName() { - return name; - } - - // /** - // * Sets the version. - // * - // * @param version - // * the new version - // */ - // public void setVersion(int version) { - // this.version = version; - // } - - /** - * Get error listener, in transations. - * - * @return the on error listener - */ - public OnErrorListener getOnErrorListener() { - return onErrorListener; - } - - /** - * Gets the version. - * - * @return the version - */ - public int getVersion() { - return version; - } - - /** - *

- * True if dataSource is just created - *

- * . - * - * @return true, if is just created - */ - public boolean isJustCreated() { - return justCreated; - } - - /** - * Checks if is log enabled. - * - * @return true, if is log enabled - */ - public boolean isLogEnabled() { - return context.isLogEnabled(); - } - - /** - *

- * return true if database is already opened. - *

- * - * @return true if database is opened, otherwise false - */ - public boolean isOpen() { - return database != null && database.isOpen() && database.isDbLockedByCurrentThread(); - } - - /** - * Return true if any operation is running on datasource, - * false if database is currently closed. - * - * @return - */ - public boolean isAnyPendingOperation() { - return openCounter.get() > 0; - } - - /** - *

- * return true if database is already opened in write mode. - *

- * - * @return true if database is opened, otherwise false - */ - public boolean isOpenInWriteMode() { - // return database != null && database.isOpen() && - // !database.isReadOnly() && database.isDbLockedByCurrentThread(); - return database != null && database.isOpen() && !database.isReadOnly() && database.isDbLockedByCurrentThread(); - } - - /** - * Checks if is upgraded version. - * - * @return the upgradedVersion - */ - public boolean isUpgradedVersion() { - return versionChanged; - } - - /** - * - */ - private void manageStatus() { - switch (status.get()) { - case READ_AND_WRITE_OPENED: - if (database == null) - status.set(TypeStatus.CLOSED); - lockReadWriteAccess.unlock(); - // lockDb.unlock(); - break; - case READ_ONLY_OPENED: - if (database == null) - status.set(TypeStatus.CLOSED); - lockReadAccess.unlock(); - // lockDb.unlock(); - break; - case CLOSED: - // do nothing - // lockDb.unlock(); - break; - } - } - - /** - * Returns true if the database need foreign keys - * - * @return - * - */ - public abstract boolean hasForeignKeys(); - - /** - * On configure. - * - * @param database - * the database - */ - protected void onConfigure(SupportSQLiteDatabase database) { - // configure database - // database.setForeignKeyConstraintsEnabled(true); - if (options.databaseLifecycleHandler != null) { - options.databaseLifecycleHandler.onConfigure(database); - } - } - // protected abstract void onConfigure(SupportSQLiteDatabase database); - - /** - * The method invoked when database corruption is detected. Default - * implementation will delete the database file. - * - * @param db - * the {@link SupportSQLiteDatabase} object representing the - * database on which corruption is detected. - */ - protected void onCorruption(@NonNull SupportSQLiteDatabase db) { - // the following implementation is taken from {@link - // DefaultDatabaseErrorHandler}. - if (this.logEnabled) { - Logger.fatal("Corruption reported by sqlite on database: " + db.getPath()); - } - try { - if (options.databaseLifecycleHandler != null) { - options.databaseLifecycleHandler.onCorruption(db); - } - } catch (Throwable e) { - e.printStackTrace(); - } - - // is the corruption detected even before database could be 'opened'? - if (!db.isOpen()) { - // database files are not even openable. delete this database file. - // NOTE if the database has attached databases, then any of them - // could be corrupt. - // and not deleting all of them could cause corrupted database file - // to remain and - // make the application crash on database open operation. To avoid - // this problem, - // the application should provide its own {@link - // DatabaseErrorHandler} impl class - // to delete ALL files of the database (including the attached - // databases). - deleteDatabaseFile(db.getPath()); - return; - } - List> attachedDbs = null; - try { - // Close the database, which will cause subsequent operations to - // fail. - // before that, get the attached database list first. - try { - attachedDbs = db.getAttachedDbs(); - } catch (SQLiteException e) { - /* ignore */ - } - try { - db.close(); - } catch (IOException e) { - /* ignore */ - } - } finally { - // Delete all files of this corrupt database and/or attached - // databases - if (attachedDbs != null) { - for (android.util.Pair p : attachedDbs) { - deleteDatabaseFile(p.second); - } - } else { - // attachedDbs = null is possible when the database is so - // corrupt that even - // "PRAGMA database_list;" also fails. delete the main database - // file - deleteDatabaseFile(db.getPath()); - } - } - } - - /** - * On create. - * - * @param database - * the database - */ - protected abstract void onCreate(SupportSQLiteDatabase database); - - /** - * On downgrade. - * - * @param db - * the db - * @param oldVersion - * the old version - * @param newVersion - * the new version - */ - protected void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { - if (options.databaseLifecycleHandler != null) { - options.databaseLifecycleHandler.onUpdate(db, oldVersion, newVersion, false); - versionChanged = true; - } - } - - protected void onOpen(SupportSQLiteDatabase db) { - if (AbstractDataSource.this.options.databaseLifecycleHandler != null) { - AbstractDataSource.this.options.databaseLifecycleHandler.onOpen(db); - versionChanged = true; - } - } - - /** - * On session closed. - * - * @return the sets the - */ - protected Set onSessionClosed() { - return this.context.onSessionClosed(); - - } - - /** - * On session opened. - */ - protected void onSessionOpened() { - this.context.onSessionOpened(); - } - - /** - * On upgrade. - * - * @param db - * the db - * @param oldVersion - * the old version - * @param newVersion - * the new version - */ - protected void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { - if (AbstractDataSource.this.options.databaseLifecycleHandler != null) { - AbstractDataSource.this.options.databaseLifecycleHandler.onUpdate(db, oldVersion, newVersion, true); - versionChanged = true; - } - } - - /** - * Open a database, if it is needed in - * - * @param writeMode - * @return - */ - protected Pair openDatabaseThreadSafeMode(boolean writeMode) { - Pair result = new Pair(); - - try { - // lock entire operation set - beginLock(); - boolean needToOpened = writeMode ? !this.isOpenInWriteMode() : !this.isOpen(); - result.value0 = needToOpened; - // in this part we can not lock lockReadWriteAccess, otherwise it - // may be a - // blocking race - // we lock lockReadWriteAccess after we release - if (needToOpened) { - if (writeMode) { - result.value1 = openWritableDatabase(false); - } else { - result.value1 = openReadOnlyDatabase(false); - } - } else { - result.value1 = this.getDatabase(); - } - - } finally { - // unlock entire operation set - endLock(); - - if (writeMode) { - lockReadWriteAccess.lock(); - } else { - lockReadAccess.lock(); - } - - } - - return result; - - } - - public SupportSQLiteDatabase openReadOnlyDatabase() { - return openReadOnlyDatabase(true); - } - - /** - *

- * Open a read only database. - *

- * - * @return read only database - */ - protected SupportSQLiteDatabase openReadOnlyDatabase(boolean lock) { - if (lock) { - // if I lock this in dbLock.. the last one remains locked too - lockReadAccess.lock(); - - beginLock(); - } - - try { - if (sqliteHelper == null) - createHelper(); - - status.set(TypeStatus.READ_ONLY_OPENED); - - if (openCounter.incrementAndGet() == 1) { - // open new read database - if (database == null) { - sqliteHelper.setWriteAheadLoggingEnabled(true); - database = sqliteHelper.getReadableDatabase(); - database.setForeignKeyConstraintsEnabled(hasForeignKeys()); - } - if (logEnabled) - Logger.info("database OPEN %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); - } else { - if (logEnabled) - Logger.info("database REUSE %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); - } - } catch (Throwable e) { - if (logEnabled) { - Logger.fatal("database error during open operation: %s", e.getMessage()); - e.printStackTrace(); - } - throw (e); - } finally { - if (lock) - endLock(); - - } - - return database; - } - - /** - *

- * open a writable database. - *

- * - * @return writable database - */ - public SupportSQLiteDatabase openWritableDatabase() { - return openWritableDatabase(true); - } - - protected SupportSQLiteDatabase openWritableDatabase(boolean lock) { - if (lock) { - lockReadWriteAccess.lock(); - - // if I lock this in dbLock.. the last one remains locked too - beginLock(); - } - - try { - if (sqliteHelper == null) - createHelper(); - - status.set(TypeStatus.READ_AND_WRITE_OPENED); - - if (openCounter.incrementAndGet() == 1) { - // open new write database - if (database == null) { - sqliteHelper.setWriteAheadLoggingEnabled(true); - database = sqliteHelper.getWritableDatabase(); - database.setForeignKeyConstraintsEnabled(hasForeignKeys()); - } - if (logEnabled) - Logger.info("database OPEN %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); - } else { - if (logEnabled) - Logger.info("database REUSE %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); - } - } catch (Throwable e) { - if (logEnabled) { - Logger.fatal("database error during open operation: %s", e.getMessage()); - e.printStackTrace(); - } - throw (e); - } finally { - if (lock) - endLock(); - } - - return database; - } - - /** - * Set error listener for transactions. - * - * @param onErrorListener - * the new on error listener - */ - public void setOnErrorListener(OnErrorListener onErrorListener) { - this.onErrorListener = onErrorListener; - } - - /** - * Sql builder. - * - * @return the string builder - */ - protected StringBuilder sqlBuilder() { - return context.sqlBuilder(); - } + /** + * Interface for database operations. + * + * @param + * the element type + */ + public interface AbstractExecutable { + + /** + * Execute transation. Method need to return + * {@link TransactionResult#COMMIT} to commit results or + * {@link TransactionResult#ROLLBACK} to rollback. If exception is + * thrown, a rollback will be done. + * + * @param daoFactory + * the dao factory + * @return the transaction result + */ + TransactionResult onExecute(E daoFactory); + } + + /** + * The listener interface for receiving onError events. The class that is + * interested in processing a onError event implements this interface, and + * the object created with that class is registered with a component using + * the component's addOnErrorListener method. When the onError + * event occurs, that object's appropriate method is invoked. + * + */ + public interface OnErrorListener { + /** + * Manages error situations. + * + * @param e + * exception + */ + void onError(Throwable e); + } + + /** + * The Enum TypeStatus. + */ + public enum TypeStatus { + + /** The closed. */ + CLOSED, + /** The read and write opened. */ + READ_AND_WRITE_OPENED, + /** The read only opened. */ + READ_ONLY_OPENED + } + + /** The context. */ + protected SQLContextImpl context; + + /** database instance. */ + SupportSQLiteDatabase database; + + /** + *

+ * True if dataSource is just created + *

+ * . + */ + protected boolean justCreated = false; + + /** The lock access. */ + private final ReentrantReadWriteLock lockAccess = new ReentrantReadWriteLock(); + + /** The lock db. */ + private final ReentrantLock lockDb = new ReentrantLock(); + + /** The lock read access. */ + private final Lock lockReadAccess = lockAccess.readLock(); + + /** The lock read write access. */ + private final Lock lockReadWriteAccess = lockAccess.writeLock(); + + /** The log enabled. */ + protected boolean logEnabled; + + /** + *

+ * file name used to save database, + *

+ * . + */ + protected final String name; + + /** The on error listener. */ + protected OnErrorListener onErrorListener = (Throwable e) -> { + throw (new KriptonRuntimeException(e)); + }; + + + /** The open counter. */ + private final AtomicInteger openCounter = new AtomicInteger(); + + /** The options. */ + protected DataSourceOptions options; + + /** The sqlite helper. */ + protected SupportSQLiteOpenHelper sqliteHelper; + + /** The status. */ + protected ThreadLocal status = ThreadLocal.withInitial(() -> TypeStatus.CLOSED); + + /** + *

+ * database version + *

+ * . + */ + protected int version; + + /** if true, database was update during this application run. */ + protected boolean versionChanged; + + /** + * Instantiates a new abstract data source. + * + * @param name + * the name + * @param version + * the version + * @param options + * the options + */ + protected AbstractDataSource(String name, int version, DataSourceOptions options) { + DataSourceOptions optionsValue = (options != null) ? options : DataSourceOptions.builder().build(); + + this.name = optionsValue.inMemory ? null : name; + this.version = version; + + // create new SQLContext + this.context = new SQLContextImpl(this); + + this.options = optionsValue; + this.logEnabled = optionsValue.logEnabled; + + if (this.logEnabled) { + Logger.debug("%s is created with %s", getClass().getName(), optionsValue.toString()); + } + } + + protected void beginLock() { + lockDb.lock(); + } + + /** + * Builds the task list. + * + * @param previousVersion + * the previous version + * @param currentVersion + * the current version + * @return the list + */ + protected List buildTaskList(int previousVersion, int currentVersion) { + List result = new ArrayList<>(); + + for (Pair item : this.options.updateTasks) { + if (item.value0 - 1 == previousVersion) { + result.add(item.value1); + previousVersion = item.value0; + } + + if (previousVersion == currentVersion) + break; + } + + if (previousVersion != currentVersion) { + Logger.warn(String.format("Can not find version update task from version %s to version %s", previousVersion, + currentVersion)); + } + + return result; + + } + + /** + * used to clear prepared statements. + */ + public abstract void clearCompiledStatements(); + + /** + * Context. + * + * @return the SQL context + */ + public SQLContext getContext() { + return context; + } + + /* + * (non-Javadoc) + * + * @see android.database.sqlite.SQLiteOpenHelper#close() + */ + @Override + public void close() { + beginLock(); + try { + if (openCounter.decrementAndGet() <= 0) { + + if (!this.options.inMemory) { + // Closing database + if (database != null) { + clearCompiledStatements(); + sqliteHelper.close(); + } + database = null; + } + if (logEnabled) + Logger.info("database CLOSED (%s) (connections: %s)", status.get(), openCounter.intValue()); + } else { + if (logEnabled) + Logger.info("database RELEASED (%s) (connections: %s)", status.get(), openCounter.intValue()); + } + } catch (Exception e) { + e.printStackTrace(); + throw (e); + } finally { + manageStatus(); + endLock(); + } + + } + + protected void closeThreadSafeMode(Pair status) { + if (status.value0 == true) { + close(); + } else { + beginLock(); + // we unlock lockReadWriteAccess, so we can include this code in + // lockDb + manageStatus(); + endLock(); + + // unlocked inside + // lockDb.unlock(); + } + } + + /** + * Content values. + * + * @param compiledStatement + * the compiled statement + * @return the kripton content values + */ + protected KriptonContentValues contentValues(SupportSQLiteStatement compiledStatement) { + return context.contentValues(compiledStatement); + } + + /** + * Content values for content provider. + * + * @param values + * the values + * @return the kripton content values + */ + protected KriptonContentValues contentValuesForContentProvider(ContentValues values) { + return context.contentValuesForContentProvider(values); + } + + /** + * Content values for update. + * + * @param compiledStatement + * the compiled statement + * @return the kripton content values + */ + protected KriptonContentValues contentValuesForUpdate(SupportSQLiteStatement compiledStatement) { + return context.contentValuesForUpdate(compiledStatement); + } + + /** + * Creates the helper. + */ + protected void createHelper() { + if (KriptonLibrary.getContext() == null) + throw new KriptonRuntimeException( + "Kripton library is not properly initialized. Please use KriptonLibrary.init(context) somewhere at application startup"); + + if (this.logEnabled) { + if (options.inMemory) { + Logger.info("In-memory database"); + } else { + File dbFile = KriptonLibrary.getContext().getDatabasePath(name); + Logger.info("Database file %s", dbFile.getAbsolutePath()); + } + } + + Builder config = SupportSQLiteOpenHelper.Configuration.builder(KriptonLibrary.getContext()).name(name) + .callback(new SupportSQLiteOpenHelper.Callback(version) { + + @Override + public void onConfigure(SupportSQLiteDatabase db) { + AbstractDataSource.this.onConfigure(db); + } + + @Override + public void onCorruption(SupportSQLiteDatabase db) { + AbstractDataSource.this.onCorruption(db); + } + + @Override + public void onCreate(SupportSQLiteDatabase db) { + sqliteHelper.setWriteAheadLoggingEnabled(true); + AbstractDataSource.this.onCreate(db); + } + + @Override + public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { + AbstractDataSource.this.onDowngrade(db, oldVersion, newVersion); + } + + @Override + public void onOpen(SupportSQLiteDatabase db) { + sqliteHelper.setWriteAheadLoggingEnabled(true); + AbstractDataSource.this.onOpen(db); + } + + @Override + public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { + AbstractDataSource.this.onUpgrade(db, oldVersion, newVersion); + } + }); + + sqliteHelper = options.openHelperFactory.create(config.build()); + + if (this.logEnabled) { + Logger.debug("Database helper factory class is %s", options.openHelperFactory.getClass().getName()); + Logger.debug("Database helper class is %s", sqliteHelper.getClass().getName()); + } + } + + private void deleteDatabaseFile(String fileName) { + if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) { + return; + } + if (this.logEnabled) { + Logger.fatal("deleting the database file: " + fileName); + } + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + SQLiteDatabase.deleteDatabase(new File(fileName)); + } else { + try { + final boolean deleted = new File(fileName).delete(); + if (!deleted) { + if (this.logEnabled) { + Logger.fatal("Could not delete the database file " + fileName); + } + } + } catch (Exception error) { + if (this.logEnabled) { + Logger.fatal("error while deleting corrupted database file " + error.getMessage()); + } + } + } + } catch (Exception e) { + if (this.logEnabled) { + /* print warning and ignore exception */ + Logger.warn("delete failed: ", e); + } + } + } + + protected void endLock() { + lockDb.unlock(); + } + + /** + * Force close. + */ + void forceClose() { + openCounter.set(0); + } + + /** + *

+ * Return database object or runtimeexception if no database is opened. + *

+ * + * @return the SQ lite database + */ + public SupportSQLiteDatabase getDatabase() { + if (database == null) + throw (new KriptonRuntimeException( + "No database connection is opened before use " + this.getClass().getCanonicalName())); + return database; + } + + public String getName() { + return name; + } + + // /** + // * Sets the version. + // * + // * @param version + // * the new version + // */ + // public void setVersion(int version) { + // this.version = version; + // } + + /** + * Get error listener, in transations. + * + * @return the on error listener + */ + public OnErrorListener getOnErrorListener() { + return onErrorListener; + } + + /** + * Gets the version. + * + * @return the version + */ + public int getVersion() { + return version; + } + + /** + *

+ * True if dataSource is just created + *

+ * . + * + * @return true, if is just created + */ + public boolean isJustCreated() { + return justCreated; + } + + /** + * Checks if is log enabled. + * + * @return true, if is log enabled + */ + public boolean isLogEnabled() { + return context.isLogEnabled(); + } + + /** + *

+ * return true if database is already opened. + *

+ * + * @return true if database is opened, otherwise false + */ + public boolean isOpen() { + return database != null && database.isOpen() && database.isDbLockedByCurrentThread(); + } + + /** + * Return true if any operation is running on datasource, + * false if database is currently closed. + * + * @return + */ + public boolean isAnyPendingOperation() { + return openCounter.get() > 0; + } + + /** + *

+ * return true if database is already opened in write mode. + *

+ * + * @return true if database is opened, otherwise false + */ + public boolean isOpenInWriteMode() { + // return database != null && database.isOpen() && + // !database.isReadOnly() && database.isDbLockedByCurrentThread(); + return database != null && database.isOpen() && !database.isReadOnly() && database.isDbLockedByCurrentThread(); + } + + /** + * Checks if is upgraded version. + * + * @return the upgradedVersion + */ + public boolean isUpgradedVersion() { + return versionChanged; + } + + /** + * + */ + private void manageStatus() { + switch (status.get()) { + case READ_AND_WRITE_OPENED: + if (database == null) + status.set(TypeStatus.CLOSED); + lockReadWriteAccess.unlock(); + // lockDb.unlock(); + break; + case READ_ONLY_OPENED: + if (database == null) + status.set(TypeStatus.CLOSED); + lockReadAccess.unlock(); + // lockDb.unlock(); + break; + case CLOSED: + // do nothing + // lockDb.unlock(); + break; + } + } + + /** + * Returns true if the database need foreign keys + * + * @return + * + */ + public abstract boolean hasForeignKeys(); + + /** + * On configure. + * + * @param database + * the database + */ + protected void onConfigure(SupportSQLiteDatabase database) { + // configure database + // database.setForeignKeyConstraintsEnabled(true); + if (options.databaseLifecycleHandler != null) { + options.databaseLifecycleHandler.onConfigure(database); + } + } + // protected abstract void onConfigure(SupportSQLiteDatabase database); + + /** + * The method invoked when database corruption is detected. Default + * implementation will delete the database file. + * + * @param db + * the {@link SupportSQLiteDatabase} object representing the + * database on which corruption is detected. + */ + protected void onCorruption(@NonNull SupportSQLiteDatabase db) { + // the following implementation is taken from {@link + // DefaultDatabaseErrorHandler}. + if (this.logEnabled) { + Logger.fatal("Corruption reported by sqlite on database: " + db.getPath()); + } + try { + if (options.databaseLifecycleHandler != null) { + options.databaseLifecycleHandler.onCorruption(db); + } + } catch (Throwable e) { + e.printStackTrace(); + } + + // is the corruption detected even before database could be 'opened'? + if (!db.isOpen()) { + // database files are not even openable. delete this database file. + // NOTE if the database has attached databases, then any of them + // could be corrupt. + // and not deleting all of them could cause corrupted database file + // to remain and + // make the application crash on database open operation. To avoid + // this problem, + // the application should provide its own {@link + // DatabaseErrorHandler} impl class + // to delete ALL files of the database (including the attached + // databases). + deleteDatabaseFile(db.getPath()); + return; + } + List> attachedDbs = null; + try { + // Close the database, which will cause subsequent operations to + // fail. + // before that, get the attached database list first. + try { + attachedDbs = db.getAttachedDbs(); + } catch (SQLiteException e) { + /* ignore */ + } + try { + db.close(); + } catch (IOException e) { + /* ignore */ + } + } finally { + // Delete all files of this corrupt database and/or attached + // databases + if (attachedDbs != null) { + for (android.util.Pair p : attachedDbs) { + deleteDatabaseFile(p.second); + } + } else { + // attachedDbs = null is possible when the database is so + // corrupt that even + // "PRAGMA database_list;" also fails. delete the main database + // file + deleteDatabaseFile(db.getPath()); + } + } + } + + /** + * On create. + * + * @param database + * the database + */ + protected abstract void onCreate(SupportSQLiteDatabase database); + + /** + * On downgrade. + * + * @param db + * the db + * @param oldVersion + * the old version + * @param newVersion + * the new version + */ + protected void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { + if (options.databaseLifecycleHandler != null) { + options.databaseLifecycleHandler.onUpdate(db, oldVersion, newVersion, false); + versionChanged = true; + } + } + + protected void onOpen(SupportSQLiteDatabase db) { + if (AbstractDataSource.this.options.databaseLifecycleHandler != null) { + AbstractDataSource.this.options.databaseLifecycleHandler.onOpen(db); + versionChanged = true; + } + } + + /** + * On session closed. + * + * @return the sets the + */ + protected Set onSessionClosed() { + return this.context.onSessionClosed(); + + } + + /** + * On session opened. + */ + protected void onSessionOpened() { + this.context.onSessionOpened(); + } + + /** + * On upgrade. + * + * @param db + * the db + * @param oldVersion + * the old version + * @param newVersion + * the new version + */ + protected void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { + if (AbstractDataSource.this.options.databaseLifecycleHandler != null) { + AbstractDataSource.this.options.databaseLifecycleHandler.onUpdate(db, oldVersion, newVersion, true); + versionChanged = true; + } + } + + /** + * Open a database, if it is needed in + * + * @param writeMode + * @return + */ + protected Pair openDatabaseThreadSafeMode(boolean writeMode) { + Pair result = new Pair(); + + try { + // lock entire operation set + beginLock(); + boolean needToOpened = writeMode ? !this.isOpenInWriteMode() : !this.isOpen(); + result.value0 = needToOpened; + // in this part we can not lock lockReadWriteAccess, otherwise it + // may be a + // blocking race + // we lock lockReadWriteAccess after we release + if (needToOpened) { + if (writeMode) { + result.value1 = openWritableDatabase(false); + } else { + result.value1 = openReadOnlyDatabase(false); + } + } else { + result.value1 = this.getDatabase(); + } + + } finally { + // unlock entire operation set + endLock(); + + if (writeMode) { + lockReadWriteAccess.lock(); + } else { + lockReadAccess.lock(); + } + + } + + return result; + + } + + public SupportSQLiteDatabase openReadOnlyDatabase() { + return openReadOnlyDatabase(true); + } + + /** + *

+ * Open a read only database. + *

+ * + * @return read only database + */ + protected SupportSQLiteDatabase openReadOnlyDatabase(boolean lock) { + if (lock) { + // if I lock this in dbLock.. the last one remains locked too + lockReadAccess.lock(); + + beginLock(); + } + + try { + if (sqliteHelper == null) + createHelper(); + + status.set(TypeStatus.READ_ONLY_OPENED); + + if (openCounter.incrementAndGet() == 1) { + // open new read database + if (database == null) { + sqliteHelper.setWriteAheadLoggingEnabled(true); + database = sqliteHelper.getReadableDatabase(); + database.setForeignKeyConstraintsEnabled(hasForeignKeys()); + } + if (logEnabled) + Logger.info("database OPEN %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); + } else { + if (logEnabled) + Logger.info("database REUSE %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); + } + } catch (Throwable e) { + if (logEnabled) { + Logger.fatal("database error during open operation: %s", e.getMessage()); + e.printStackTrace(); + } + throw (e); + } finally { + if (lock) + endLock(); + + } + + return database; + } + + /** + *

+ * open a writable database. + *

+ * + * @return writable database + */ + public SupportSQLiteDatabase openWritableDatabase() { + return openWritableDatabase(true); + } + + protected SupportSQLiteDatabase openWritableDatabase(boolean lock) { + if (lock) { + lockReadWriteAccess.lock(); + + // if I lock this in dbLock.. the last one remains locked too + beginLock(); + } + + try { + if (sqliteHelper == null) + createHelper(); + + status.set(TypeStatus.READ_AND_WRITE_OPENED); + + if (openCounter.incrementAndGet() == 1) { + // open new write database + if (database == null) { + sqliteHelper.setWriteAheadLoggingEnabled(true); + database = sqliteHelper.getWritableDatabase(); + database.setForeignKeyConstraintsEnabled(hasForeignKeys()); + } + if (logEnabled) + Logger.info("database OPEN %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); + } else { + if (logEnabled) + Logger.info("database REUSE %s (connections: %s)", status.get(), (openCounter.intValue() - 1)); + } + } catch (Throwable e) { + if (logEnabled) { + Logger.fatal("database error during open operation: %s", e.getMessage()); + e.printStackTrace(); + } + throw (e); + } finally { + if (lock) + endLock(); + } + + return database; + } + + /** + * Set error listener for transactions. + * + * @param onErrorListener + * the new on error listener + */ + public void setOnErrorListener(OnErrorListener onErrorListener) { + this.onErrorListener = onErrorListener; + } + + /** + * Sql builder. + * + * @return the string builder + */ + protected StringBuilder sqlBuilder() { + return context.sqlBuilder(); + } } diff --git a/kripton-parent/pom.xml b/kripton-parent/pom.xml index 55ded1358..1bd76be23 100644 --- a/kripton-parent/pom.xml +++ b/kripton-parent/pom.xml @@ -12,7 +12,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 pom Kripton (Parent) @@ -38,7 +38,7 @@ 3.2.0 3.0.1 - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 @@ -51,7 +51,7 @@ 3.0.1 3.1.4 - 7.0.0-rc.8_r1-robolectric-0 + 7.0.0-rc.9_r1-robolectric-0 1.9.0 diff --git a/kripton-processor/pom.xml b/kripton-processor/pom.xml index 2dd07a37d..5ed7d6f54 100644 --- a/kripton-processor/pom.xml +++ b/kripton-processor/pom.xml @@ -14,7 +14,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -26,7 +26,7 @@ false - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-retrofit-converter/pom.xml b/kripton-retrofit-converter/pom.xml index db19f4f0a..c8e731f90 100644 --- a/kripton-retrofit-converter/pom.xml +++ b/kripton-retrofit-converter/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -16,7 +16,7 @@ 1.8 - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-shared-preferences/kripton-shared-preferences.iml b/kripton-shared-preferences/kripton-shared-preferences.iml index be2e2d436..dbf25cacc 100644 --- a/kripton-shared-preferences/kripton-shared-preferences.iml +++ b/kripton-shared-preferences/kripton-shared-preferences.iml @@ -69,7 +69,7 @@ - + diff --git a/kripton-shared-preferences/pom.xml b/kripton-shared-preferences/pom.xml index 0f2d6c752..57482ad7a 100644 --- a/kripton-shared-preferences/pom.xml +++ b/kripton-shared-preferences/pom.xml @@ -5,7 +5,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -16,7 +16,7 @@ jar - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/kripton-sqlite-test-library/pom.xml b/kripton-sqlite-test-library/pom.xml index 7f4496d78..914d0bbd9 100644 --- a/kripton-sqlite-test-library/pom.xml +++ b/kripton-sqlite-test-library/pom.xml @@ -3,7 +3,7 @@ 4.0.0 com.abubusoft kripton-sqlite-test-library - 7.0.0-rc.8 + 7.0.0-rc.9 Kripton SQLite Test Library Kripton SQLite Test Library @@ -12,7 +12,7 @@ - 7.0.0-rc.8 + 7.0.0-rc.9 1.8 diff --git a/kripton/pom.xml b/kripton/pom.xml index 750c051ab..274e34e72 100644 --- a/kripton/pom.xml +++ b/kripton/pom.xml @@ -14,7 +14,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ../kripton-parent/pom.xml @@ -25,7 +25,7 @@ Kripton Persistence Library - 7.0.0-rc.8 + 7.0.0-rc.9 2.11.2 diff --git a/pom.xml b/pom.xml index cec2041b9..4356698f8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.abubusoft kripton-parent - 7.0.0-rc.8 + 7.0.0-rc.9 ./kripton-parent/pom.xml