
org.droitateddb.BaseContentProvider Maven / Gradle / Ivy
/*
* Copyright (C) 2014 The droitated DB Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.droitateddb;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import org.droitateddb.cursor.CombinedCursor;
import org.droitateddb.cursor.CombinedCursorImpl;
import org.droitateddb.entity.Entity;
import org.droitateddb.schema.AbstractAttribute;
import org.droitateddb.schema.EntityInfo;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The {@link BaseContentProvider} is the base class of all generated {@link ContentProvider} for {@link Entity} classes.
*
* For each {@link Entity} which allows {@link ContentProvider} generation a separate {@link ContentProvider} will be generated. The generated
* {@link ContentProvider} is also referenced in the AndroidManifest.xml to use an own Implementation remove the attribute
* droitateddb:generated="true"
in the provider
element of the AndroidManifest.xml
* The authority of the {@link ContentProvider} is therefore defined within the {@link Entity} annotation.
*
* The base {@link Uri} for calling the {@link ContentProvider} is build following this schema: content://[authority]/[
* {@link BaseContentProvider#getEntityURIPart}].
* If not overridden {@link BaseContentProvider#getEntityURIPart} returns entity
*
* For easier use the {@link BaseContentProvider} provides the methods {@link BaseContentProvider#uri(String)} and
* {@link BaseContentProvider#uriForItem(String, long)}. These methods allow getting the {@link Uri} referencing the Provider with the table name of the
* {@link Entity} and the primary key.
*
* {@link BaseContentProvider#uri(String)}: Returns a {@link Uri} for accessing multiple {@link Entity} elements.
*
* {@link BaseContentProvider#uriForItem(String, long)}: Returns a {@link Uri} for accessing a specific {@link Entity} element
*
* @author Falk Appel
* @author Alexander Frank
*/
public abstract class BaseContentProvider extends ContentProvider {
private static final int MATCH_DIR = 0;
private static final int MATCH_ITEM = 1;
private static final String PROTOCOL = "content://";
private static final UriRegistry REGISTRY = new UriRegistry();
private final String contentUri;
private final String dirContentType;
private final String itemContentType;
private DbCreator dbCreator;
private final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
protected BaseContentProvider() {
String authority = getAuthority();
String base = getEntityURIPart();
String typeDescription = "/vnd." + authority + "." + base;
contentUri = PROTOCOL + authority + "/" + base;
dirContentType = ContentResolver.CURSOR_DIR_BASE_TYPE + typeDescription;
itemContentType = ContentResolver.CURSOR_ITEM_BASE_TYPE + typeDescription;
uriMatcher.addURI(authority, base, MATCH_DIR);
uriMatcher.addURI(authority, base + "/#", MATCH_ITEM);
REGISTRY.add(getTableName(), contentUri);
}
/**
* Returns a {@link Uri} for the using it with a {@link ContentResolver} to access this {@link ContentProvider}. The {@link Uri} allows CRUD operations on
* multiple {@link Entity} elements.
*
* @param tableName Name of the Table (simple {@link Entity} name) the data should be retrieved from.
* @return Uri to access the tables data
*/
public static Uri uri(final String tableName) {
return REGISTRY.getDictionaryUri(tableName);
}
/**
* Returns a {@link Uri} for the using it with a {@link ContentResolver} to access this {@link ContentProvider}. The {@link Uri} allows CRUD operations
* on a
* single {@link Entity} element, qualified by its primary key.
*
* @param tableName Name of the Table (simple {@link Entity} name) the data should be retrieved from.
* @param id Primary key of the {@link Entity} to be accessed
* @return Uri to access the specific item in the table
*/
public static Uri uriForItem(final String tableName, final long id) {
return REGISTRY.getItemUri(tableName, Long.toString(id));
}
/**
* Has to return the authority to be used for this {@link ContentProvider}.
* If generated this is the authority defined in the {@link Entity} class
*
* @return The authority used for the ContentProvider
*/
protected abstract String getAuthority();
/**
* Has to return the attribute describing the primary key of the {@link Entity}.
* Id generated this is the {@link org.droitateddb.entity.PrimaryKey} annotated {@link org.droitateddb.entity.Column}.
*
* @return The description of the primary key of the table access by this ContentProvider
*/
protected abstract AbstractAttribute getIdAttribute();
/**
* Has to return metadata of the {@link Entity} this {@link ContentProvider} is for.
*
* @return Metadata to the {@link Entity} being accessed
*/
protected abstract EntityInfo getEntityInfo();
protected String getEntityURIPart() {
return "entity";
}
private String getTableName() {
return getEntityInfo().table();
}
private String getIdName() {
return getIdAttribute().fieldName();
}
@Override
public boolean onCreate() {
dbCreator = DbCreator.getInstance(getContext());
return true;
}
@Override
public String getType(final Uri uri) {
switch (uriMatcher.match(uri)) {
case MATCH_DIR:
return dirContentType;
case MATCH_ITEM:
return itemContentType;
default:
throw new UnsupportedOperationException("No type for Uri " + uri + " present");
}
}
@Override
public Uri insert(final Uri uri, final ContentValues values) {
return dbCreator.functionOnDatabase(new DbFunction() {
@Override
public Uri apply(SQLiteDatabase db) {
switch (uriMatcher.match(uri)) {
case MATCH_DIR:
long id = db.insertWithOnConflict(getTableName(), null, values, SQLiteDatabase.CONFLICT_REPLACE);
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(contentUri + "/" + id);
default:
throw new UnsupportedOperationException("Insert not allowed with specific note uri");
}
}
});
}
@Override
public Cursor query(final Uri uri, final String[] projection, final String selection, final String[] selectionArgs, final String sortOrder) {
SQLiteDatabase db = dbCreator.getDatabaseConnection();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(getTableName());
switch (uriMatcher.match(uri)) {
case MATCH_DIR:
return wrap(builder.query(db, projection, selection, selectionArgs, null, null, sortOrder), uri);
case MATCH_ITEM:
int id = Integer.parseInt(uri.getLastPathSegment());
builder.appendWhere(getIdName() + " = " + id);
return wrap(builder.query(db, projection, selection, selectionArgs, null, null, sortOrder), uri);
default:
throw new UnsupportedOperationException("Unsupported query Uri " + uri);
}
}
@Override
public int update(final Uri uri, final ContentValues values, final String selection, final String[] selectionArgs) {
return dbCreator.functionOnDatabase(new DbFunction() {
@Override
public Integer apply(SQLiteDatabase db) {
switch (uriMatcher.match(uri)) {
case MATCH_DIR:
return db.update(getTableName(), values, selection, selectionArgs);
case MATCH_ITEM:
String selectId = getIdName() + " = " + Integer.parseInt(uri.getLastPathSegment());
return db.update(getTableName(), values, getEffectivSelection(selection, selectId), selectionArgs);
default:
throw new UnsupportedOperationException(
"Update not supported for the given Uri + " + uri + ". Only single item updates are allowed right " +
"now");
}
}
});
}
@Override
public int delete(final Uri uri, final String selection, final String[] selectionArgs) {
return dbCreator.functionOnDatabase(new DbFunction() {
@Override
public Integer apply(SQLiteDatabase db) {
switch (uriMatcher.match(uri)) {
case MATCH_DIR:
return db.delete(getTableName(), selection, selectionArgs);
case MATCH_ITEM:
String selectId = getIdName() + " = " + Integer.parseInt(uri.getLastPathSegment());
return db.delete(getTableName(), getEffectivSelection(selection, selectId), selectionArgs);
default:
throw new UnsupportedOperationException(
"Delete not supported for the given Uri + " + uri + ". Only single item deletes are allowed right now");
}
}
});
}
private Cursor wrap(final Cursor cursor, final Uri uri) {
try {
EntityInfo entityInfo = getEntityInfo();
CombinedCursor> magicCursor = CombinedCursorImpl.create(getContext(), cursor, entityInfo, Class.forName(entityInfo.className()));
magicCursor.setNotificationUri(getContext().getContentResolver(), uri);
return magicCursor;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private String getEffectivSelection(final String submitedSelection, final String idSelection) {
String effectivSelection;
if (submitedSelection == null || submitedSelection.equals("")) {
effectivSelection = idSelection;
} else {
effectivSelection = submitedSelection + " AND " + idSelection;
}
return effectivSelection;
}
private static class UriRegistry {
private final Map registry = new ConcurrentHashMap();
public void add(final String tableName, final String uri) {
registry.put(tableName, uri);
}
public Uri getDictionaryUri(final String tableName) {
return Uri.parse(getRegisteredUri(tableName));
}
public Uri getItemUri(final String tableName, final String id) {
return Uri.parse(getRegisteredUri(tableName) + "/" + id);
}
private String getRegisteredUri(final String tableName) {
String uri = registry.get(tableName);
if (uri == null) {
throw new IllegalStateException("No ContentProvider Uri was registered for table " + tableName +
". Did you forget to set \"contentProvider=true\" on the corresponding @Entity annotation?");
}
return uri;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy