List of usage examples for android.content ContentValues putNull
public void putNull(String key)
From source file:com.akop.bach.parser.PsnEuParser.java
@SuppressLint("DefaultLocale") @Override// ww w .j a v a 2s . c om protected void parseFriends(PsnAccount account) throws ParserException, IOException { synchronized (PsnEuParser.class) { ContentResolver cr = mContext.getContentResolver(); ContentValues cv; List<ContentValues> newCvs = new ArrayList<ContentValues>(100); final long accountId = account.getId(); int rowsInserted = 0; int rowsUpdated = 0; int rowsDeleted = 0; long updated = System.currentTimeMillis(); long started = updated; // Handle pending requests String page = getResponse(URL_FRIENDS); Matcher m; Matcher friendMatcher = PATTERN_FRIENDS_PENDING.matcher(page); while (friendMatcher.find()) { String onlineId = htmlDecode(friendMatcher.group(1)); Cursor c = cr.query(Friends.CONTENT_URI, FRIEND_ID_PROJECTION, Friends.ACCOUNT_ID + "=" + account.getId() + " AND " + Friends.ONLINE_ID + "=?", new String[] { onlineId }, null); long friendId = -1; try { if (c != null && c.moveToFirst()) friendId = c.getLong(0); } finally { if (c != null) c.close(); } cv = new ContentValues(15); cv.put(Friends.DELETE_MARKER, updated); cv.put(Friends.ONLINE_STATUS, PSN.STATUS_PENDING); if (friendId < 0) { // New cv.put(Friends.ONLINE_ID, onlineId); cv.put(Friends.ACCOUNT_ID, accountId); cv.put(Friends.PROGRESS, 0); cv.putNull(Friends.ICON_URL); cv.put(Friends.LEVEL, 0); cv.put(Friends.TROPHIES_PLATINUM, 0); cv.put(Friends.TROPHIES_GOLD, 0); cv.put(Friends.TROPHIES_SILVER, 0); cv.put(Friends.TROPHIES_BRONZE, 0); cv.putNull(Friends.PLAYING); cv.put(Friends.LAST_UPDATED, 0); newCvs.add(cv); } else { cr.update(ContentUris.withAppendedId(Friends.CONTENT_URI, friendId), cv, null, null); rowsUpdated++; } } // Handle rest of friends page = getResponse(URL_FRIENDS_AJAX); friendMatcher = PATTERN_FRIENDS.matcher(page); while (friendMatcher.find()) { String friendData = friendMatcher.group(1); String onlineId; if (!(m = PATTERN_FRIEND_ONLINE_ID.matcher(friendData)).find()) continue; onlineId = htmlDecode(m.group(1)); int level = 0; if ((m = PATTERN_FRIEND_LEVEL.matcher(friendData)).find()) level = Integer.parseInt(m.group(1)); String iconUrl = null; if ((m = PATTERN_FRIEND_AVATAR.matcher(friendData)).find()) iconUrl = getLargeAvatarIcon(resolveImageUrl(URL_FRIENDS_AJAX, m.group(1))); String comment = null; if ((m = PATTERN_FRIEND_COMMENT.matcher(friendData)).find()) { comment = htmlDecode(m.group(1)); if (comment != null && comment.equals("null")) comment = null; } int memberType = PSN.MEMBER_TYPE_FREE; if ((m = PATTERN_FRIEND_IS_PLUS.matcher(friendData)).find() && m.group(1).equalsIgnoreCase("true")) { memberType = PSN.MEMBER_TYPE_PLUS; } int bronze = 0; int silver = 0; int gold = 0; int platinum = 0; m = PATTERN_FRIEND_TROPHY.matcher(friendData); while (m.find()) { String type = m.group(1).toLowerCase(); if ("bronze".equals(type)) bronze = Integer.parseInt(m.group(2)); else if ("silver".equals(type)) silver = Integer.parseInt(m.group(2)); else if ("gold".equals(type)) gold = Integer.parseInt(m.group(2)); else if ("platinum".equals(type)) platinum = Integer.parseInt(m.group(2)); } boolean inGame = false; int status = PSN.STATUS_OTHER; if ((m = PATTERN_FRIEND_STATUS.matcher(friendData)).find()) { String presence = m.group(1).toLowerCase(); if (presence.equals("offline")) status = PSN.STATUS_OFFLINE; else if (presence.equals("online")) status = PSN.STATUS_ONLINE; else if (presence.equals("online-ingame")) { status = PSN.STATUS_ONLINE; inGame = true; } else if (presence.equals("online-away")) status = PSN.STATUS_AWAY; else if (presence.equals("online-ingame-away")) { status = PSN.STATUS_AWAY; inGame = true; } else if (presence.equals("pending")) status = PSN.STATUS_PENDING; } String playing = null; if ((m = PATTERN_FRIEND_PLAYING.matcher(friendData)).find()) { String activity = htmlDecode(m.group(1)).trim(); if (activity != null && activity.length() > 0) { if (inGame) playing = mContext.getString(R.string.playing_f, activity); else playing = activity; } } Cursor c = cr.query(Friends.CONTENT_URI, FRIEND_ID_PROJECTION, Friends.ACCOUNT_ID + "=" + account.getId() + " AND " + Friends.ONLINE_ID + "=?", new String[] { onlineId }, null); long friendId = -1; try { if (c != null && c.moveToFirst()) friendId = c.getLong(0); } finally { if (c != null) c.close(); } cv = new ContentValues(15); cv.put(Friends.ICON_URL, iconUrl); cv.put(Friends.LEVEL, level); cv.put(Friends.MEMBER_TYPE, memberType); cv.put(Friends.COMMENT, comment); cv.put(Friends.LEVEL, level); cv.put(Friends.ONLINE_STATUS, status); cv.put(Friends.TROPHIES_PLATINUM, platinum); cv.put(Friends.TROPHIES_GOLD, gold); cv.put(Friends.TROPHIES_SILVER, silver); cv.put(Friends.TROPHIES_BRONZE, bronze); cv.put(Friends.PLAYING, playing); cv.put(Friends.DELETE_MARKER, updated); if (friendId < 0) { // New cv.put(Friends.ONLINE_ID, onlineId); cv.put(Friends.ACCOUNT_ID, accountId); cv.put(Friends.PROGRESS, 0); cv.put(Friends.LAST_UPDATED, 0); newCvs.add(cv); } else { cr.update(ContentUris.withAppendedId(Friends.CONTENT_URI, friendId), cv, null, null); rowsUpdated++; } } // Remove friends rowsDeleted = cr.delete(Friends.CONTENT_URI, Friends.ACCOUNT_ID + "=" + accountId + " AND " + Friends.DELETE_MARKER + "!=" + updated, null); if (newCvs.size() > 0) { ContentValues[] cvs = new ContentValues[newCvs.size()]; newCvs.toArray(cvs); rowsInserted = cr.bulkInsert(Friends.CONTENT_URI, cvs); } account.refresh(Preferences.get(mContext)); account.setLastFriendUpdate(System.currentTimeMillis()); account.save(Preferences.get(mContext)); cr.notifyChange(Friends.CONTENT_URI, null); if (App.getConfig().logToConsole()) started = displayTimeTaken("Friend page processing [I:" + rowsInserted + ";U:" + rowsUpdated + ";D:" + rowsDeleted + "]", started); } }
From source file:org.opendatakit.tables.utils.CollectUtil.java
/** * This gets a map of values for insertion into a row after returning from a * Collect form. It handles validating the values. Null values are passed back * if the value is not present or is null in the return from ODK Collect (to * support clearing of values)./*from w w w. j a v a 2 s.c o m*/ * * TODO: add support for select-multiple * * @return */ public static ContentValues getMapForInsertion(Context context, String appName, String tableId, ArrayList<ColumnDefinition> orderedDefns, FormValues formValues) { DataUtil du = new DataUtil(Locale.ENGLISH, TimeZone.getDefault()); ContentValues values = new ContentValues(); List<ColumnDefinition> geopointList = GeoColumnUtil.get().getGeopointColumnDefinitions(orderedDefns); List<ColumnDefinition> uriList = RowPathColumnUtil.get().getUriColumnDefinitions(orderedDefns); for (ColumnDefinition cd : orderedDefns) { ColumnDefinition cdContainingElement = cd.getParent(); if (cdContainingElement != null) { if (geopointList.contains(cdContainingElement) || uriList.contains(cdContainingElement)) { // processed by the containing type continue; } // and if this is not a unit of retention, a containing element is // handling it. if (!cd.isUnitOfRetention()) { continue; } } // ok. we are directly processing this... and possibly sucking values // out of sub-elements... ElementType type = cd.getType(); if (geopointList.contains(cd)) { // find its children... List<ColumnDefinition> children = cd.getChildren(); ColumnDefinition[] cparray = new ColumnDefinition[4]; if (!children.isEmpty()) { cparray[0] = children.get(0); } if (children.size() > 1) { cparray[1] = children.get(1); } if (children.size() > 2) { cparray[2] = children.get(2); } if (children.size() > 3) { cparray[3] = children.get(3); } ColumnDefinition cplat = null, cplng = null, cpalt = null, cpacc = null; for (ColumnDefinition scp : cparray) { if (scp.getElementName().equals("latitude")) { cplat = scp; } else if (scp.getElementName().equals("longitude")) { cplng = scp; } else if (scp.getElementName().equals("altitude")) { cpalt = scp; } else if (scp.getElementName().equals("accuracy")) { cpacc = scp; } } // split ODK COLLECT value into the constituent elements String value = formValues.formValues.get(cd.getElementKey()); if (value == null || value.length() == 0) { values.putNull(cplat.getElementKey()); values.putNull(cplng.getElementKey()); values.putNull(cpalt.getElementKey()); values.putNull(cpacc.getElementKey()); } else { String[] parts = value.split(" "); if (parts.length > 0) { values.put(cplat.getElementKey(), parts[0]); } else { values.putNull(cplat.getElementKey()); } if (parts.length > 1) { values.put(cplng.getElementKey(), parts[1]); } else { values.putNull(cplng.getElementKey()); } if (parts.length > 2) { values.put(cpalt.getElementKey(), parts[2]); } else { values.putNull(cpalt.getElementKey()); } if (parts.length > 3) { values.put(cpacc.getElementKey(), parts[3]); } else { values.putNull(cpacc.getElementKey()); } } } else if (uriList.contains(cd)) { // find its children... List<ColumnDefinition> children = cd.getChildren(); ColumnDefinition[] cdarray = new ColumnDefinition[children.size()]; for (int i = 0; i < children.size(); ++i) { cdarray[i] = children.get(i); } // find the uriFragment ColumnDefinition cdfrag = null, cdtype = null; for (ColumnDefinition scp : cdarray) { if (scp.getElementName().equals("uriFragment")) { cdfrag = scp; } else if (scp.getElementName().equals("contentType")) { cdtype = scp; } } // update the uriFragment and contentType elements String value = formValues.formValues.get(cd.getElementKey()); if (value == null || value.length() == 0) { values.putNull(cdfrag.getElementKey()); values.putNull(cdtype.getElementKey()); } else { int dotIdx = value.lastIndexOf("."); String ext = (dotIdx == -1) ? "*" : value.substring(dotIdx + 1); if (ext.length() == 0) { ext = "*"; } String baseContentType = cd.getElementName().substring(0, cd.getElementName().length() - 3); if (baseContentType.equals("mime")) { baseContentType = "*"; } String mimeType = baseContentType + "/" + ext; if (cd.getType().getDataType() == ElementDataType.configpath) { values.put(cdfrag.getElementKey(), ODKFileUtils.asUriFragment(appName, new File(ODKFileUtils.getAppFolder(appName), value))); values.put(cdtype.getElementKey(), mimeType); } else { File ifolder = new File( ODKFileUtils.getInstanceFolder(appName, tableId, formValues.instanceID)); values.put(cdfrag.getElementKey(), ODKFileUtils.asUriFragment(appName, new File(ifolder, value))); values.put(cdtype.getElementKey(), mimeType); } } } else if (cd.isUnitOfRetention()) { ArrayList<Map<String, Object>> choices; SQLiteDatabase db = null; try { db = DatabaseFactory.get().getDatabase(context, appName); choices = (ArrayList<Map<String, Object>>) ColumnUtil.get().getDisplayChoicesList(db, tableId, cd.getElementKey()); } finally { if (db != null) { db.close(); } } String value = formValues.formValues.get(cd.getElementKey()); value = ParseUtil.validifyValue(appName, du, choices, cd, formValues.formValues.get(cd.getElementKey())); if (value != null) { values.put(cd.getElementKey(), value); } else { // don't we want to clear values too? values.putNull(cd.getElementKey()); } } } return values; }
From source file:org.getlantern.firetweet.util.Utils.java
public static boolean setLastSeen(Context context, long userId, long time) { final ContentResolver cr = context.getContentResolver(); final ContentValues values = new ContentValues(); if (time > 0) { values.put(CachedUsers.LAST_SEEN, time); } else {/* w w w .ja v a 2 s. c o m*/ // Zero or negative value means remove last seen values.putNull(CachedUsers.LAST_SEEN); } final Expression where = Expression.equals(CachedUsers.USER_ID, userId); return cr.update(CachedUsers.CONTENT_URI, values, where.getSQL(), null) != 0; }
From source file:com.vegnab.vegnab.MainVNActivity.java
void logPurchaseActivity(Purchase p, IabResult result, boolean isConsumed, String notes) { Uri uri, purchUri = Uri.withAppendedPath(ContentProvider_VegNab.CONTENT_URI, "purchases"); ContentResolver rs = getContentResolver(); ContentValues contentValues = new ContentValues(); if (p == null) { contentValues.put("ProductIdCode", "(purchase object is null)"); contentValues.put("Type", "null"); contentValues.put("PurchaseState", -2); // purchase is null } else {/* w w w .jav a2s . c o m*/ String sku = p.getSku(); contentValues.put("ProductIdCode", sku); // also called 'SKU' contentValues.put("DevPayload", p.getDeveloperPayload()); contentValues.put("Type", p.getItemType()); // "inapp" for an in-app product or "subs" for subscriptions. contentValues.put("OrderIDCode", p.getOrderId()); // corresponds to the Google payments order ID contentValues.put("PkgName", p.getPackageName()); contentValues.put("Signature", p.getSignature()); contentValues.put("Token", p.getToken()); // uniquely identifies a purchase for a given item and user pair contentValues.put("PurchaseState", p.getPurchaseState()); // standard: 0 (purchased), 1 (canceled), or 2 (refunded). or nonstandard: -1 (initiated), -2 (null) SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); long t = p.getPurchaseTime(); contentValues.put("PurchaseTime", dateTimeFormat.format(new Date(t))); contentValues.put("PurchJSON", p.getOriginalJson()); try { // inventory object may not exist yet if (mInventory.hasDetails(sku)) { SkuDetails skuDetails = mInventory.getSkuDetails(sku); contentValues.put("Price", skuDetails.getPrice()); contentValues.put("Description", skuDetails.getDescription()); contentValues.put("Title", skuDetails.getTitle()); } else { contentValues.putNull("Price"); contentValues.putNull("Description"); contentValues.putNull("Title"); } } catch (Exception e) { contentValues.putNull("Price"); contentValues.putNull("Description"); contentValues.putNull("Title"); } } contentValues.put("Consumed", isConsumed ? 1 : 0); if (result == null) { contentValues.putNull("IABResponse"); contentValues.putNull("IABMessage"); } else { contentValues.put("IABResponse", result.getResponse()); contentValues.put("IABMessage", result.getMessage()); } if (notes == null) { contentValues.putNull("Notes"); } else { contentValues.put("Notes", notes); } // create a new record uri = rs.insert(purchUri, contentValues); mNewPurcRecId = Long.parseLong(uri.getLastPathSegment()); if (LDebug.ON) Log.d(LOG_TAG, "mNewPurcRecId of new record stored in DB: " + mNewPurcRecId); }
From source file:org.totschnig.myexpenses.MyApplication.java
/** * 1.check if a planner is configured. If no, nothing to do 2.check if the * configured planner exists on the device 2.1 if yes go through all events * and look for them based on UUID added to description recreate events that * we did not find (2.2 if no, user should have been asked to select a target * calendar where we will store the recreated events) * * @return Result with success true// w w w . j ava 2 s. c o m */ public Result restorePlanner() { ContentResolver cr = getContentResolver(); String TAG = "restorePlanner"; String calendarId = PrefKey.PLANNER_CALENDAR_ID.getString("-1"); String calendarPath = PrefKey.PLANNER_CALENDAR_PATH.getString(""); Log.d(TAG, String.format("restore plans to calendar with id %s and path %s", calendarId, calendarPath)); int restoredPlansCount = 0; if (!(calendarId.equals("-1") || calendarPath.equals(""))) { Cursor c = cr.query(Calendars.CONTENT_URI, new String[] { Calendars._ID }, CALENDAR_FULL_PATH_PROJECTION + " = ?", new String[] { calendarPath }, null); if (c != null) { if (c.moveToFirst()) { mPlannerCalendarId = c.getString(0); Log.d(TAG, String.format("restorePlaner: found calendar with id %s", mPlannerCalendarId)); PrefKey.PLANNER_CALENDAR_ID.putString(mPlannerCalendarId); ContentValues planValues = new ContentValues(), eventValues = new ContentValues(); eventValues.put(Events.CALENDAR_ID, Long.parseLong(mPlannerCalendarId)); Cursor planCursor = cr.query(Template.CONTENT_URI, new String[] { DatabaseConstants.KEY_ROWID, DatabaseConstants.KEY_PLANID, DatabaseConstants.KEY_UUID }, DatabaseConstants.KEY_PLANID + " IS NOT null", null, null); if (planCursor != null) { if (planCursor.moveToFirst()) { do { long templateId = planCursor.getLong(0); long oldPlanId = planCursor.getLong(1); String uuid = planCursor.getString(2); Cursor eventCursor = cr.query(Events.CONTENT_URI, new String[] { Events._ID }, Events.CALENDAR_ID + " = ? AND " + Events.DESCRIPTION + " LIKE ?", new String[] { mPlannerCalendarId, "%" + uuid + "%" }, null); if (eventCursor != null) { if (eventCursor.moveToFirst()) { long newPlanId = eventCursor.getLong(0); Log.d(TAG, String.format( "Looking for event with uuid %s: found id %d. " + "Original event had id %d", uuid, newPlanId, oldPlanId)); if (newPlanId != oldPlanId) { planValues.put(DatabaseConstants.KEY_PLANID, newPlanId); int updated = cr.update( ContentUris.withAppendedId(Template.CONTENT_URI, templateId), planValues, null, null); if (updated > 0) { Log.i(TAG, "updated plan id in template:" + templateId); restoredPlansCount++; } } else { restoredPlansCount++; } continue; } eventCursor.close(); } Log.d(TAG, String.format( "Looking for event with uuid %s did not find, now reconstructing from cache", uuid)); eventCursor = cr.query(TransactionProvider.EVENT_CACHE_URI, buildEventProjection(), Events.DESCRIPTION + " LIKE ?", new String[] { "%" + uuid + "%" }, null); boolean found = false; if (eventCursor != null) { if (eventCursor.moveToFirst()) { found = true; copyEventData(eventCursor, eventValues); if (insertEventAndUpdatePlan(eventValues, templateId)) { Log.i(TAG, "updated plan id in template:" + templateId); restoredPlansCount++; } } eventCursor.close(); } if (!found) { //need to set eventId to null planValues.putNull(DatabaseConstants.KEY_PLANID); getContentResolver().update( ContentUris.withAppendedId(Template.CONTENT_URI, templateId), planValues, null, null); } } while (planCursor.moveToNext()); } planCursor.close(); } } c.close(); } } return new Result(true, R.string.restore_calendar_success, restoredPlansCount); }
From source file:org.opendatakit.services.database.utlities.ODKDatabaseImplUtils.java
/** * Delete any prior server conflict row. * Examine database and incoming server values to determine how to apply the * server values to the database. This might delete the existing row, update * it, or create a conflict row./* w w w . j a va 2 s . c o m*/ * * @param db * @param tableId * @param orderedColumns * @param serverValues field values for this row coming from server. * All fields must have values. The SyncState field * (a local field that does not come from the server) * should be "changed" or "deleted" * @param rowId * @param activeUser * @param rolesList passed in to determine if the current user is a privileged user * @param locale */ public void privilegedPerhapsPlaceRowIntoConflictWithId(OdkConnectionInterface db, String tableId, OrderedColumns orderedColumns, ContentValues serverValues, String rowId, String activeUser, String rolesList, String locale) { AccessContext accessContext = getAccessContext(db, tableId, activeUser, rolesList); // The rolesList of the activeUser does not impact the execution of this action. boolean dbWithinTransaction = db.inTransaction(); try { if (!dbWithinTransaction) { db.beginTransactionNonExclusive(); } // delete any existing server conflict row this.deleteServerConflictRowWithId(db, tableId, rowId); // fetch the current local (possibly-in-conflict) row BaseTable baseTable = this.privilegedGetRowsWithId(db, tableId, rowId, activeUser); if (baseTable.getNumberOfRows() == 0) { throw new IllegalArgumentException("no matching row found for server conflict"); } else if (baseTable.getNumberOfRows() != 1) { throw new IllegalArgumentException("row has checkpoints or database is corrupt"); } Row localRow = baseTable.getRowAtIndex(0); if (localRow.getDataByKey(DataTableColumns.SAVEPOINT_TYPE) == null) { throw new IllegalArgumentException("row has checkpoints"); } String strSyncState = localRow.getDataByKey(DataTableColumns.SYNC_STATE); SyncState state = SyncState.valueOf(strSyncState); int localRowConflictTypeBeforeSync = -1; if (state == SyncState.in_conflict) { // we need to remove the in_conflict records that refer to the // prior state of the server String localRowConflictTypeBeforeSyncStr = localRow.getDataByKey(DataTableColumns.CONFLICT_TYPE); if (localRowConflictTypeBeforeSyncStr == null) { // this row is in conflict. It MUST have a non-null conflict type. throw new IllegalStateException("conflict type is null on an in-conflict row"); } localRowConflictTypeBeforeSync = Integer.parseInt(localRowConflictTypeBeforeSyncStr); if (localRowConflictTypeBeforeSync == ConflictType.SERVER_DELETED_OLD_VALUES || localRowConflictTypeBeforeSync == ConflictType.SERVER_UPDATED_UPDATED_VALUES) { // should be impossible throw new IllegalStateException("only the local conflict record should remain"); } } boolean isServerRowDeleted = serverValues.getAsString(DataTableColumns.SYNC_STATE) .equals(SyncState.deleted.name()); boolean executeDropThrough = false; if (isServerRowDeleted) { if (state == SyncState.synced) { // the server's change should be applied locally. this.privilegedDeleteRowWithId(db, tableId, rowId, activeUser); } else if (state == SyncState.synced_pending_files) { // sync logic may want to UPLOAD local files up to server before calling this routine. // this would prevent loss of attachments that have not yet been pushed to server. // the server's change should be applied locally. this.privilegedDeleteRowWithId(db, tableId, rowId, activeUser); } else if ((state == SyncState.deleted) || ((state == SyncState.in_conflict) && (localRowConflictTypeBeforeSync == ConflictType.LOCAL_DELETED_OLD_VALUES))) { // this occurs if // (1) a delete request was never ACKed but it was performed // on the server. // (2) if there is an unresolved conflict held locally with the // local action being to delete the record, and the prior server // state being a value change, but the newly sync'd state now // reflects a deletion by another party. // // the server's change should be applied locally. this.privilegedDeleteRowWithId(db, tableId, rowId, activeUser); } else { // need to resolve a conflict situation... executeDropThrough = true; } } else if (state == SyncState.synced || state == SyncState.synced_pending_files) { // When a prior sync ends with conflicts, we will not update the table's "lastDataETag" // and when we next sync, we will pull the same server row updates as when the // conflicts were raised (elsewhere in the table). // // Therefore, we can expect many server row updates to have already been locally // applied (for the rows were not in conflict). Detect and ignore these already- // processed changes by testing for the server and device having identical field values. // boolean isDifferent = false; for (int i = 0; i < baseTable.getWidth(); ++i) { String colName = baseTable.getElementKey(i); if (DataTableColumns.ID.equals(colName) || DataTableColumns.CONFLICT_TYPE.equals(colName) || DataTableColumns.EFFECTIVE_ACCESS.equals(colName) || DataTableColumns.SYNC_STATE.equals(colName)) { // these values are ignored during comparisons continue; } String localValue = localRow.getDataByKey(colName); String serverValue = serverValues.containsKey(colName) ? serverValues.getAsString(colName) : null; ElementDataType dt = ElementDataType.string; try { ColumnDefinition cd = orderedColumns.find(colName); dt = cd.getType().getDataType(); } catch (IllegalArgumentException e) { // ignore } if (!identicalValue(localValue, serverValue, dt)) { isDifferent = true; break; } } if (isDifferent) { // Local row needs to be updated with server values. // // detect and handle file attachment column changes and sync state // (need to change serverRow value of this) boolean hasNonNullAttachments = false; for (ColumnDefinition cd : orderedColumns.getColumnDefinitions()) { // todo: does not handle array containing (types containing) rowpath elements if (cd.isUnitOfRetention() && cd.getType().getDataType().equals(ElementDataType.rowpath)) { String uriFragment = serverValues.getAsString(cd.getElementKey()); String localUriFragment = localRow.getDataByKey(cd.getElementKey()); if (uriFragment != null) { if (localUriFragment == null || !localUriFragment.equals(uriFragment)) { hasNonNullAttachments = true; break; } } } } // update the row from the changes on the server ContentValues values = new ContentValues(serverValues); values.put(DataTableColumns.SYNC_STATE, (hasNonNullAttachments || (state == SyncState.synced_pending_files)) ? SyncState.synced_pending_files.name() : SyncState.synced.name()); values.putNull(DataTableColumns.CONFLICT_TYPE); this.privilegedUpdateRowWithId(db, tableId, orderedColumns, values, rowId, activeUser, locale, false); } // and don't execute the drop-through in this case. } else { executeDropThrough = true; } if (executeDropThrough) { // SyncState.deleted and server is not deleting // SyncState.new_row and record exists on server // SyncState.changed and new change (or delete) on server // SyncState.in_conflict and new change (or delete) on server // ALSO: this case can occur when our prior sync attempt pulled down changes that // placed local row(s) in conflict -- which we have since resolved -- and we are now // issuing a sync to push those changes up to the server. // // This is because we do not update our local table's "lastDataETag" until we have // sync'd and applied all changes from the server and have no local conflicts or // checkpoints on the table. Because of this, when we issue a sync after resolving // a conflict, we will get the set of server row changes that include the row(s) // that were previously in conflict. This will appear as one of: // changed | changed // changed | deleted // deleted | changed // deleted | deleted // // BUT, this time, however, the local records will have the same rowETag as the // server record, indicating that they are valid changes (or deletions) on top of // the server's current version of this same row. // // If this is the case (the rowETags match), then we should not place the row into // conflict, but should instead ignore the reported content from the server and push // the local row's change or delete up to the server in the next section. // // If not, when we reach this point in the code, the rowETag of the server row // should **not match** our local row -- indicating that the server has a change from // another source, and that we should place the row into conflict. // String localRowETag = localRow.getDataByKey(DataTableColumns.ROW_ETAG); String serverRowETag = serverValues.getAsString(DataTableColumns.ROW_ETAG); boolean isDifferentRowETag = (localRowETag == null) || !localRowETag.equals(serverRowETag); if (!isDifferentRowETag) { // ignore the server record. // This is an update we will push to the server. // todo: make sure local row is not in_conflict at this point. if (state == SyncState.in_conflict) { // don't think this is logically possible at this point, but we should // transition record to changed or deleted state. ContentValues values = new ContentValues(); for (int i = 0; i < baseTable.getWidth(); ++i) { String colName = baseTable.getElementKey(i); if (DataTableColumns.EFFECTIVE_ACCESS.equals(colName)) { continue; } if (localRow.getDataByIndex(i) == null) { values.putNull(colName); } else { values.put(colName, localRow.getDataByIndex(i)); } } values.put(DataTableColumns.ID, rowId); values.putNull(DataTableColumns.CONFLICT_TYPE); values.put(DataTableColumns.SYNC_STATE, (localRowConflictTypeBeforeSync == ConflictType.LOCAL_DELETED_OLD_VALUES) ? SyncState.deleted.name() : SyncState.changed.name()); this.privilegedUpdateRowWithId(db, tableId, orderedColumns, values, rowId, activeUser, locale, false); } } else { // figure out what the localRow conflict type should be... int localRowConflictType; if (state == SyncState.changed) { // SyncState.changed and new change on server localRowConflictType = ConflictType.LOCAL_UPDATED_UPDATED_VALUES; } else if (state == SyncState.new_row) { // SyncState.new_row and record exists on server // The 'new_row' case occurs if an insert is never ACKed but // completes successfully on the server. localRowConflictType = ConflictType.LOCAL_UPDATED_UPDATED_VALUES; } else if (state == SyncState.deleted) { // SyncState.deleted and server is not deleting localRowConflictType = ConflictType.LOCAL_DELETED_OLD_VALUES; } else if (state == SyncState.in_conflict) { // SyncState.in_conflict and new change on server // leave the local conflict type unchanged (retrieve it and // use it). localRowConflictType = localRowConflictTypeBeforeSync; } else { throw new IllegalStateException("Unexpected state encountered"); } boolean isDifferentPrivilegedFields = false; { String[] privilegedColumns = new String[] { DataTableColumns.FILTER_TYPE, DataTableColumns.FILTER_VALUE }; for (int i = 0; i < privilegedColumns.length; ++i) { String colName = privilegedColumns[i]; String localValue = localRow.getDataByKey(colName); String serverValue = serverValues.containsKey(colName) ? serverValues.getAsString(colName) : null; ElementDataType dt = ElementDataType.string; try { ColumnDefinition cd = orderedColumns.find(colName); dt = cd.getType().getDataType(); } catch (IllegalArgumentException e) { // ignore } boolean sameValue = identicalValue(localValue, serverValue, dt); if (!sameValue) { isDifferentPrivilegedFields = true; break; } } } boolean isDifferentExcludingPrivilegedFields = false; for (int i = 0; i < baseTable.getWidth(); ++i) { String colName = baseTable.getElementKey(i); if (DataTableColumns.ID.equals(colName) || DataTableColumns.CONFLICT_TYPE.equals(colName) || DataTableColumns.EFFECTIVE_ACCESS.equals(colName) || DataTableColumns.SYNC_STATE.equals(colName) || DataTableColumns.ROW_ETAG.equals(colName) || DataTableColumns.FILTER_TYPE.equals(colName) || DataTableColumns.FILTER_VALUE.equals(colName)) { // these values are ignored during this comparison continue; } String localValue = localRow.getDataByKey(colName); String serverValue = serverValues.containsKey(colName) ? serverValues.getAsString(colName) : null; ElementDataType dt = ElementDataType.string; try { ColumnDefinition cd = orderedColumns.find(colName); dt = cd.getType().getDataType(); } catch (IllegalArgumentException e) { // ignore } if (serverValue != null && dt == ElementDataType.bool) { serverValue = Integer .toString(DataHelper.boolToInt(serverValues.getAsBoolean(colName))); } boolean sameValue = identicalValue(localValue, serverValue, dt); if (!sameValue) { isDifferentExcludingPrivilegedFields = true; break; } } boolean hasNonNullDifferingServerAttachments = false; if (isDifferentExcludingPrivilegedFields || (state != SyncState.synced)) { for (ColumnDefinition cd : orderedColumns.getColumnDefinitions()) { // todo: does not handle array containing (types containing) rowpath elements if (cd.isUnitOfRetention() && cd.getType().getDataType().equals(ElementDataType.rowpath)) { String uriFragment = serverValues.getAsString(cd.getElementKey()); String localUriFragment = localRow.getDataByKey(cd.getElementKey()); if (uriFragment != null) { if (localUriFragment == null || !localUriFragment.equals(uriFragment)) { hasNonNullDifferingServerAttachments = true; } } } } } ContentValues values = new ContentValues(serverValues); if (isDifferentExcludingPrivilegedFields || isDifferentPrivilegedFields || isDifferentRowETag || isServerRowDeleted) { if (isDifferentExcludingPrivilegedFields || isServerRowDeleted || (localRowConflictType == ConflictType.LOCAL_DELETED_OLD_VALUES) || (accessContext.isPrivilegedUser && isDifferentPrivilegedFields)) { this.placeRowIntoConflict(db, tableId, rowId, localRowConflictType); serverValues.put(DataTableColumns.SYNC_STATE, SyncState.in_conflict.name()); serverValues.put(DataTableColumns.CONFLICT_TYPE, (isServerRowDeleted ? ConflictType.SERVER_DELETED_OLD_VALUES : ConflictType.SERVER_UPDATED_UPDATED_VALUES)); this.privilegedInsertRowWithId(db, tableId, orderedColumns, serverValues, rowId, activeUser, locale, false); } else { // just apply the server RowETag and filterScope to the local row values.put(DataTableColumns.SYNC_STATE, hasNonNullDifferingServerAttachments ? SyncState.synced_pending_files.name() : SyncState.synced.name()); values.putNull(DataTableColumns.CONFLICT_TYPE); // move the local conflict back into the normal non-conflict (null) state // set the sync state to "changed" temporarily (otherwise we can't update) this.restoreRowFromConflict(db, tableId, rowId, SyncState.changed, localRowConflictTypeBeforeSync); this.privilegedUpdateRowWithId(db, tableId, orderedColumns, values, rowId, activeUser, locale, false); } } else { // data matches -- update row to adjust sync state and conflict type // if needed. SyncState destState = (hasNonNullDifferingServerAttachments || (state == SyncState.synced_pending_files)) ? SyncState.synced_pending_files : SyncState.synced; if ((state != destState) || isDifferentRowETag || (localRow.getDataByKey(DataTableColumns.CONFLICT_TYPE) != null)) { // todo: handle case where local row was in conflict // server has now matched the local row's state. i.e., // update rowEtag, clear conflictType and adjust syncState on row. values.put(DataTableColumns.SYNC_STATE, destState.name()); values.putNull(DataTableColumns.CONFLICT_TYPE); // move the local conflict back into the normal non-conflict (null) state // set the sync state to "changed" temporarily (otherwise we can't update) this.restoreRowFromConflict(db, tableId, rowId, SyncState.changed, localRowConflictTypeBeforeSync); this.privilegedUpdateRowWithId(db, tableId, orderedColumns, values, rowId, activeUser, locale, false); WebLogger.getLogger(db.getAppName()).w(t, "identical rows returned from server -- " + "SHOULDN'T THESE NOT HAPPEN?"); } } } } if (!dbWithinTransaction) { db.setTransactionSuccessful(); } } finally { if (!dbWithinTransaction) { db.endTransaction(); } } }
From source file:org.opendatakit.utilities.test.AbstractODKDatabaseUtilsTest.java
public void testInsertCheckpointRowIntoExistingTableWithIdWithRowConflictType_ExpectFail() throws ActionNotAuthorizedException { String tableId = testTable;/*from w ww.j a v a2s . com*/ String testCol = "testColumn"; String testColType = ElementDataType.string.name(); String testVal = "test"; String rowId = LocalizationUtils.genUUID(); List<Column> columns = new ArrayList<Column>(); columns.add(new Column(testCol, testCol, testColType, "[]")); OrderedColumns orderedColumns = ODKDatabaseImplUtils.get().createOrOpenTableWithColumns(db, tableId, columns); ContentValues cvValues = new ContentValues(); cvValues.put(testCol, testVal); cvValues.putNull(DataTableColumns.CONFLICT_TYPE); boolean thrown = true; try { ODKDatabaseImplUtils.get().insertCheckpointRowWithId(db, tableId, orderedColumns, cvValues, rowId, activeUser, RoleConsts.ADMIN_ROLES_LIST, currentLocale); } catch (ActionNotAuthorizedException ex) { throw ex; } catch (Exception e) { thrown = true; e.printStackTrace(); } assertTrue(thrown); // Drop the table now that the test is done ODKDatabaseImplUtils.get().deleteTableAndAllData(db, tableId); }
From source file:org.opendatakit.utilities.test.AbstractODKDatabaseUtilsTest.java
public void testInsertCheckpointRowIntoExistingTableWithIdWithRowSavepointType_ExpectFail() throws ActionNotAuthorizedException { String tableId = testTable;/*w w w. jav a 2 s . c om*/ String testCol = "testColumn"; String testColType = ElementDataType.string.name(); String testVal = "test"; String rowId = LocalizationUtils.genUUID(); List<Column> columns = new ArrayList<Column>(); columns.add(new Column(testCol, testCol, testColType, "[]")); OrderedColumns orderedColumns = ODKDatabaseImplUtils.get().createOrOpenTableWithColumns(db, tableId, columns); ContentValues cvValues = new ContentValues(); cvValues.put(testCol, testVal); cvValues.putNull(DataTableColumns.SAVEPOINT_TYPE); boolean thrown = true; try { ODKDatabaseImplUtils.get().insertCheckpointRowWithId(db, tableId, orderedColumns, cvValues, rowId, activeUser, RoleConsts.ADMIN_ROLES_LIST, currentLocale); } catch (ActionNotAuthorizedException ex) { throw ex; } catch (Exception e) { thrown = true; e.printStackTrace(); } assertTrue(thrown); // Drop the table now that the test is done ODKDatabaseImplUtils.get().deleteTableAndAllData(db, tableId); }
From source file:org.opendatakit.utilities.test.AbstractODKDatabaseUtilsTest.java
public void testInsertCheckpointRowIntoExistingTableWithIdWithRowSavepointTimestamp_ExpectFail() throws ActionNotAuthorizedException { String tableId = testTable;/*from www .j a va2 s . c om*/ String testCol = "testColumn"; String testColType = ElementDataType.string.name(); String testVal = "test"; String rowId = LocalizationUtils.genUUID(); List<Column> columns = new ArrayList<Column>(); columns.add(new Column(testCol, testCol, testColType, "[]")); OrderedColumns orderedColumns = ODKDatabaseImplUtils.get().createOrOpenTableWithColumns(db, tableId, columns); ContentValues cvValues = new ContentValues(); cvValues.put(testCol, testVal); cvValues.putNull(DataTableColumns.SAVEPOINT_TIMESTAMP); boolean thrown = true; try { ODKDatabaseImplUtils.get().insertCheckpointRowWithId(db, tableId, orderedColumns, cvValues, rowId, activeUser, RoleConsts.ADMIN_ROLES_LIST, currentLocale); } catch (ActionNotAuthorizedException ex) { throw ex; } catch (Exception e) { thrown = true; e.printStackTrace(); } assertTrue(thrown); // Drop the table now that the test is done ODKDatabaseImplUtils.get().deleteTableAndAllData(db, tableId); }
From source file:org.opendatakit.services.database.utilities.ODKDatabaseImplUtils.java
/** * If the latest row-level permissions from the server prevent the activeUser from * performing the modify or delete action on the row, immediately resolve the conflict * by taking the server's changes./* ww w . ja va 2s .co m*/ * * If the latest row-level permissions from the server prevent the activeUser from * altering the permissions on the row, reset all of those permissions to match * the server's latest values. * * And, finally, optimize the conflict -- perhaps immediately resolving it based upon * whether the user actually has the privileges to do anything other than * taking the server changes or if the changes only update the tracking * and (perhaps) the metadata fields. * * @param db * @param tableId * @param orderedColumns * @param rowId * @param initialLocalRowState * @param accessContext * @param locale * @return true if we are still in conflict */ public boolean enforcePermissionsAndOptimizeConflictProcessing(OdkConnectionInterface db, String tableId, OrderedColumns orderedColumns, String rowId, SyncState initialLocalRowState, AccessContext accessContext, String locale) { // we should have two in-conflict records, on is the local, one is the server BaseTable baseTable = this.privilegedGetRowsWithId(db, tableId, rowId, accessContext.activeUser); if (baseTable.getNumberOfRows() != 2) { throw new IllegalStateException( "we should have exactly two rows -- one local-conflict and " + "one server-conflict row"); } Integer idxServerRow = null; int serverRowConflictType = -1; Integer idxLocalRow = null; int localRowConflictType = -1; { for (int idx = 0; idx < 2; ++idx) { String rowConflictTypeStr = baseTable.getRowAtIndex(idx) .getDataByKey(DataTableColumns.CONFLICT_TYPE); if (rowConflictTypeStr == null) { // this row is in conflict. It MUST have a non-null conflict type. throw new IllegalStateException("conflict type is null on an in-conflict row"); } int rowConflictType = Integer.parseInt(rowConflictTypeStr); if (rowConflictType == ConflictType.LOCAL_DELETED_OLD_VALUES || rowConflictType == ConflictType.LOCAL_UPDATED_UPDATED_VALUES) { idxLocalRow = idx; localRowConflictType = rowConflictType; } else if (rowConflictType == ConflictType.SERVER_DELETED_OLD_VALUES || rowConflictType == ConflictType.SERVER_UPDATED_UPDATED_VALUES) { idxServerRow = idx; serverRowConflictType = rowConflictType; } } if (idxServerRow == null) { throw new IllegalStateException( "did not find server conflict row while optimizing " + "the conflict"); } if (idxLocalRow == null) { throw new IllegalStateException( "did not find local conflict row while optimizing " + "the conflict"); } } Row serverRow = baseTable.getRowAtIndex(idxServerRow); String serverDefaultAccess = serverRow.getDataByKey(DataTableColumns.DEFAULT_ACCESS); String serverOwner = serverRow.getDataByKey(DataTableColumns.ROW_OWNER); String serverGroupReadOnly = serverRow.getDataByKey(DataTableColumns.GROUP_READ_ONLY); String serverGroupModify = serverRow.getDataByKey(DataTableColumns.GROUP_MODIFY); String serverGroupPrivileged = serverRow.getDataByKey(DataTableColumns.GROUP_PRIVILEGED); // Part 1: verify the ability to modify or delete the row fields (excluding permissions) // if the server changed privileges in the row such that this user does not have privileges // to modify (if local changed) or delete (if local deleted), then we can immediately // resolve to take server changes. TableSecuritySettings tss = getTableSecuritySettings(db, tableId); try { String updatedSyncState = (localRowConflictType == ConflictType.LOCAL_DELETED_OLD_VALUES) ? SyncState.deleted.name() : SyncState.changed.name(); RowChange rowChange = (localRowConflictType == ConflictType.LOCAL_DELETED_OLD_VALUES) ? RowChange.DELETE_ROW : RowChange.CHANGE_ROW; tss.allowRowChange(accessContext.activeUser, accessContext.rolesArray, updatedSyncState, serverDefaultAccess, serverOwner, serverGroupReadOnly, serverGroupModify, serverGroupPrivileged, rowChange); } catch (ActionNotAuthorizedException e) { Row localRow = baseTable.getRowAtIndex(idxLocalRow); internalResolveServerConflictTakeServerRowWithId(db, tableId, rowId, orderedColumns, initialLocalRowState, serverRow, localRow, accessContext.activeUser, locale); return false; } // Part 2: Test if the permissions fields of the local row are different from any of // those from the server. // // If they are, and the local user is not able to modify permissions fields, silently // update the local in-conflict row to have the same permissions fields as the // server row. Row localRow = baseTable.getRowAtIndex(idxLocalRow); String localDefaultAccess = localRow.getDataByKey(DataTableColumns.DEFAULT_ACCESS); String localOwner = localRow.getDataByKey(DataTableColumns.ROW_OWNER); String localGroupReadOnly = localRow.getDataByKey(DataTableColumns.GROUP_READ_ONLY); String localGroupModify = localRow.getDataByKey(DataTableColumns.GROUP_MODIFY); String localGroupPrivileged = localRow.getDataByKey(DataTableColumns.GROUP_PRIVILEGED); if (!(sameValue(localDefaultAccess, serverDefaultAccess) && sameValue(localOwner, serverOwner) && sameValue(localGroupReadOnly, serverGroupReadOnly) && sameValue(localGroupModify, serverGroupModify) && sameValue(localGroupPrivileged, serverGroupPrivileged))) { // permissions columns have changed // test if we have permissions to make these changes try { tss.canModifyPermissions(accessContext.activeUser, accessContext.rolesArray, serverGroupPrivileged, serverOwner); } catch (ActionNotAuthorizedException e) { // don't have permission to alter permissions columns -- // update the row with all of the local columns as-is, but override all the // permissions fields with the values from the server. ContentValues values = new ContentValues(); for (int i = 0; i < baseTable.getWidth(); ++i) { String colName = baseTable.getElementKey(i); if (DataTableColumns.EFFECTIVE_ACCESS.equals(colName)) { continue; } if (localRow.getDataByIndex(i) == null) { values.putNull(colName); } else { values.put(colName, localRow.getDataByIndex(i)); } } // take the server's permissions fields. if (serverDefaultAccess == null) { values.putNull(DataTableColumns.DEFAULT_ACCESS); } else { values.put(DataTableColumns.DEFAULT_ACCESS, serverDefaultAccess); } if (serverOwner == null) { values.putNull(DataTableColumns.ROW_OWNER); } else { values.put(DataTableColumns.ROW_OWNER, serverOwner); } if (serverGroupReadOnly == null) { values.putNull(DataTableColumns.GROUP_READ_ONLY); } else { values.put(DataTableColumns.GROUP_READ_ONLY, serverGroupReadOnly); } if (serverGroupModify == null) { values.putNull(DataTableColumns.GROUP_MODIFY); } else { values.put(DataTableColumns.GROUP_MODIFY, serverGroupModify); } if (serverGroupPrivileged == null) { values.putNull(DataTableColumns.GROUP_PRIVILEGED); } else { values.put(DataTableColumns.GROUP_PRIVILEGED, serverGroupPrivileged); } this.privilegedUpdateRowWithId(db, tableId, orderedColumns, values, rowId, accessContext.activeUser, locale, false); } } // at this point, all of the local row's changes are confirmed to be // able to be made by the active user. i.e., // // If the local row is deleted, we could push a delete up to the server. // // If the local row is modified, we can push the modification up to the server. // // If the activeUser does not have permission to change the row's permissions, // all of those changes have been reverted to match the values on the server. return true; }