/*
* Odoo, Open Source Management Solution
* Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http:www.gnu.org/licenses/>
*
*/
package com.odoo.support.provider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
import org.json.JSONArray;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import com.odoo.orm.OColumn;
import com.odoo.orm.OColumn.RelationType;
import com.odoo.orm.OModel;
import com.odoo.orm.OModel.Command;
import com.odoo.orm.SelectionBuilder;
import com.odoo.util.JSONUtils;
/**
* The Class OContentProvider.
*/
public abstract class OContentProvider extends ContentProvider implements
OContentProviderHelper {
private final int COLLECTION = 1;
private final int SINGLE_ROW = 2;
private UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
/** The database model. */
private OModel model = null;
public static Uri buildURI(String authority, String path) {
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.authority(authority);
uriBuilder.appendPath(path);
uriBuilder.scheme("content");
return uriBuilder.build();
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
reInitModel();
final SQLiteDatabase db = model.getWritableDatabase();
assert db != null;
final int match = matcher.match(uri);
int count = 0;
SelectionBuilder builder = new SelectionBuilder();
switch (match) {
case COLLECTION:
count = builder.table(model.getTableName()).where(where, whereArgs)
.delete(db);
break;
case SINGLE_ROW:
String id = uri.getLastPathSegment();
count = builder.table(model.getTableName())
.where(OColumn.ROW_ID + "=?", id).where(where, whereArgs)
.delete(db);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// Send broadcast to registered ContentObservers, to refresh UI.
Context ctx = getContext();
assert ctx != null;
ctx.getContentResolver().notifyChange(uri, null, false);
return count;
}
@Override
public String getType(Uri uri) {
return uri().toString();
}
private void handleManyToMany(HashMap<String, List<Integer>> record_ids,
int _id) {
if (record_ids.size() > 0) {
for (String key : record_ids.keySet()) {
List<Integer> ids = record_ids.get(key);
OColumn column = model.getColumn(key);
OModel rel_model = model.createInstance(column.getType());
model.manageManyToManyRecords(model.getWritableDatabase(),
rel_model, ids, _id, Command.Replace);
}
}
}
private HashMap<String, List<Integer>> getManyToManyRecords(
ContentValues values) {
HashMap<String, List<Integer>> ids = new HashMap<String, List<Integer>>();
for (OColumn col : model.getRelationColumns()) {
if (col.getRelationType() == RelationType.ManyToMany) {
if (values.containsKey(col.getName())) {
List<Integer> record_ids = new ArrayList<Integer>();
try {
record_ids.addAll(JSONUtils
.<Integer> toList(new JSONArray(values.get(
col.getName()).toString())));
} catch (Exception e) {
e.printStackTrace();
}
ids.put(col.getName(), record_ids);
values.remove(col.getName());
}
}
}
return ids;
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
reInitModel();
HashMap<String, List<Integer>> manyToManyIds = getManyToManyRecords(initialValues);
final SQLiteDatabase db = model.getWritableDatabase();
assert db != null;
final int match = matcher.match(uri);
Uri result;
switch (match) {
case COLLECTION:
long id = db.insertOrThrow(model.getTableName(), null,
initialValues);
handleManyToMany(manyToManyIds, (int) id);
result = Uri.parse(uri() + "/" + id);
break;
case SINGLE_ROW:
throw new UnsupportedOperationException(
"Insert not supported on URI: " + uri);
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// Send broadcast to registered ContentObservers, to refresh UI.
Context ctx = getContext();
assert ctx != null;
ctx.getContentResolver().notifyChange(uri, null, false);
return result;
}
@Override
public boolean onCreate() {
model = model(getContext());
matcher.addURI(authority(), path(), COLLECTION);
matcher.addURI(authority(), path() + "/#", SINGLE_ROW);
return ((model == null) ? false : true);
}
public void addURI(String authority, String path, int type) {
matcher.addURI(authority, path, type);
}
private void reInitModel() {
if (model.getDatabaseName().length() == 0) {
onCreate();
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sort) {
projection = validateProjections(projection);
Cursor c = createQuery(uri, projection, selection, selectionArgs, sort);
Context ctx = getContext();
assert ctx != null;
c.setNotificationUri(ctx.getContentResolver(), uri);
return c;
}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
reInitModel();
SelectionBuilder builder = new SelectionBuilder();
final SQLiteDatabase db = model.getWritableDatabase();
final int match = matcher.match(uri);
int count;
switch (match) {
case COLLECTION:
Cursor cr = query(uri, new String[] { OColumn.ROW_ID }, where,
whereArgs, null);
while (cr.moveToNext()) {
int id = cr.getInt(cr.getColumnIndex(OColumn.ROW_ID));
handleManyToMany(getManyToManyRecords(values), id);
}
cr.close();
count = builder.table(model.getTableName()).where(where, whereArgs)
.update(db, values);
break;
case SINGLE_ROW:
String id = uri.getLastPathSegment();
handleManyToMany(getManyToManyRecords(values), Integer.parseInt(id));
count = builder.table(model.getTableName()).where(where, whereArgs)
.where(OColumn.ROW_ID + "=?", id).where(where, whereArgs)
.update(db, values);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
Context ctx = getContext();
assert ctx != null;
ctx.getContentResolver().notifyChange(uri, null, false);
return count;
}
private String[] validateProjections(String[] projection) {
List<String> columns = new ArrayList<String>();
columns.addAll(Arrays.asList(projection));
columns.addAll(Arrays.asList(new String[] { OColumn.ROW_ID, "is_dirty",
"is_active", "odoo_name" }));
if (model.getColumn("id") != null)
columns.add("id");
return columns.toArray(new String[columns.size()]);
}
private Cursor createQuery(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sort) {
reInitModel();
SQLiteQueryBuilder query = new SQLiteQueryBuilder();
boolean withAlias = (projection.length < model.projection().length);
StringBuffer joins = new StringBuffer();
String base_table = model.getTableName();
String base_alias = base_table + "_base";
HashMap<String, String> projectionMap = new HashMap<String, String>();
List<String> mJoinTables = new ArrayList<String>();
for (String col_name : projection) {
String col = col_name;
if (col_name.contains(".")) {
col = col_name.split("\\.")[0];
}
OColumn column = model.getColumn(col);
String display_col = col;
if (withAlias) {
display_col = base_alias + "." + col + " AS " + col;
boolean many2oneJoin = col_name.contains(".");
if (column.getRelationType() != null && many2oneJoin) {
OModel rel_model = model.createInstance(column.getType());
String table = rel_model.getTableName();
String alias = table;
alias = table + "_self";
table += " AS " + alias;
if (!mJoinTables.contains(alias)) {
mJoinTables.add(alias);
joins.append(" JOIN ");
joins.append(table);
joins.append(" ON ");
joins.append(base_alias + "." + column.getName());
joins.append(" = ");
joins.append(alias + "." + OColumn.ROW_ID);
joins.append(" ");
}
String rel_col = col;
String rel_col_name = "";
if (col_name.contains(".")) {
rel_col += "_" + col_name.split("\\.")[1];
rel_col_name = col_name.split("\\.")[1];
}
projectionMap.put(rel_col, alias + "." + rel_col_name
+ " AS " + rel_col);
}
}
projectionMap.put(col, display_col);
}
StringBuffer tables = new StringBuffer();
tables.append(base_table + ((withAlias) ? " AS " + base_alias : " "));
tables.append(joins.toString());
query.setTables(tables.toString());
query.setProjectionMap(projectionMap);
StringBuffer whr = new StringBuffer();
String where = null;
if (selection != null && selectionArgs != null) {
if (withAlias) {
// Check for and
Pattern pattern = Pattern.compile(" and | AND ");
String[] data = pattern.split(selection);
StringBuffer or_string = new StringBuffer();
for (String token : data) {
if (token.contains("OR") || token.contains("or")) {
or_string.append(token.trim());
or_string.append(" OR ");
} else {
whr.append(base_alias + "." + token.trim());
whr.append(" AND ");
}
}
if (whr.length() > 0)
whr.delete(whr.length() - 5, whr.length());
// Check for or
if (or_string.length() > 0) {
if (whr.length() > 0)
whr.append(" AND ");
pattern = Pattern.compile(" or | OR ");
data = pattern.split(or_string.toString());
for (String token : data) {
if (!token.contains(base_alias)) {
if (token.contains("(")) {
whr.append("(");
token = token.replaceAll("\\(", "");
whr.append(base_alias + "." + token.trim());
} else if (token.contains(")")) {
token = token.replaceAll("\\)", "");
whr.append(base_alias + "." + token.trim());
whr.append(")");
} else {
whr.append(base_alias + "." + token.trim());
}
} else {
whr.append(token.trim());
}
whr.append(" OR ");
}
if (whr.length() > 0)
whr.delete(whr.length() - 4, whr.length());
}
} else {
whr.append(selection);
}
where = whr.toString();
}
Cursor c = null;
int uriMatch = matcher.match(uri);
switch (uriMatch) {
case SINGLE_ROW:
// Return a single entry, by ID.
String id = uri.getLastPathSegment();
query.appendWhere(base_alias + "." + OColumn.ROW_ID + " = " + id);
case COLLECTION:
c = query.query(model.getReadableDatabase(), null, where,
selectionArgs, null, null, sort);
return c;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
public int matchURI(Uri uri) {
return matcher.match(uri);
}
public OModel getModel() {
return model;
}
}