/*
* OTABackupRestoreContactCollection.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* 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.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.otabackuprestoredemo;
import java.io.EOFException;
import java.util.Vector;
import net.rim.device.api.collection.CollectionEventSource;
import net.rim.device.api.collection.CollectionListener;
import net.rim.device.api.i18n.Locale;
import net.rim.device.api.synchronization.OTASyncCapable;
import net.rim.device.api.synchronization.SyncCollection;
import net.rim.device.api.synchronization.SyncCollectionSchema;
import net.rim.device.api.synchronization.SyncConverter;
import net.rim.device.api.synchronization.SyncObject;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import net.rim.device.api.system.RuntimeStore;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.util.CloneableVector;
import net.rim.device.api.util.DataBuffer;
import net.rim.device.api.util.ListenerUtilities;
/**
* A collection enabled for OTA backup/restore. Basically a serially syncable
* collection with few added interfaces.
*/
public class OTABackupRestoreContactCollection implements SyncConverter,
SyncCollection, OTASyncCapable, CollectionEventSource {
private PersistentObject _persist; // The persistable object for the
// contacts
private Vector _contacts; // The actual contacts
private Vector _listeners; // Listeners to generate events when contacts are
// added
private final SyncCollectionSchema _schema; // Lets us know about the data
// we are backing up
private static final long PERSISTENT_KEY = 0x266babf899b20b56L; // com.rim.samples.device.otabackuprestoredemo.OTABackupRestoreContactCollection._persist
private static final long AR_KEY = 0xef780e08b3a7cf07L; // com.rim.samples.device.otabackuprestoredemo.OTABackupRestoreContactCollection
private static final int FIELDTAG_FIRST_NAME = 1;
private static final int FIELDTAG_LAST_NAME = 2;
private static final int FIELDTAG_EMAIL_ADDRESS = 3;
private static final int DEFAULT_RECORD_TYPE = 1; // The id for the default
// (and the only) record
// type
// Key fields - lets the server know which fields uniquely define a record
private static final int[] KEY_FIELD_IDS = new int[] { FIELDTAG_FIRST_NAME,
FIELDTAG_LAST_NAME };
// Creates a new OTABackupRestoreContactCollection object
private OTABackupRestoreContactCollection() {
_persist = PersistentStore.getPersistentObject(PERSISTENT_KEY);
_contacts = (Vector) _persist.getContents();
if (_contacts == null) {
_contacts = new Vector();
_persist.setContents(_contacts);
_persist.commit();
}
_listeners = new CloneableVector();
// Set up the schema for the collection
_schema = new SyncCollectionSchema();
_schema.setDefaultRecordType(DEFAULT_RECORD_TYPE);
_schema.setKeyFieldIds(DEFAULT_RECORD_TYPE, KEY_FIELD_IDS);
}
/**
* Get the singleton instance of the OTABackupRestoreContactCollection class
*
* @return The singleton instance of the OTABackupRestoreContactCollection
* class
*/
static OTABackupRestoreContactCollection getInstance() {
final RuntimeStore rs = RuntimeStore.getRuntimeStore();
synchronized (rs) {
OTABackupRestoreContactCollection collection =
(OTABackupRestoreContactCollection) rs.get(AR_KEY);
if (collection == null) {
collection = new OTABackupRestoreContactCollection();
rs.put(AR_KEY, collection);
}
return collection;
}
}
// SyncConverter
// methods-----------------------------------------------------
/**
* @see net.rim.device.api.synchronization.SyncConverter#convert(SyncObject,DataBuffer,int)
*/
public boolean convert(final SyncObject object, final DataBuffer buffer,
final int version) {
if (version == getSyncVersion()) {
if (object instanceof OTAContactData) {
final String first = ((OTAContactData) object).getFirst();
final String last = ((OTAContactData) object).getLast();
final String email = ((OTAContactData) object).getEmail();
// In compliance with desktop sync format
buffer.writeShort(first.length() + 1);
buffer.writeByte(FIELDTAG_FIRST_NAME);
buffer.write(first.getBytes());
buffer.writeByte(0);
buffer.writeShort(last.length() + 1);
buffer.writeByte(FIELDTAG_LAST_NAME);
buffer.write(last.getBytes());
buffer.writeByte(0);
buffer.writeShort(email.length() + 1);
buffer.writeByte(FIELDTAG_EMAIL_ADDRESS);
buffer.write(email.getBytes());
buffer.writeByte(0);
return true;
}
}
return false;
}
/**
* @see net.rim.device.api.synchronization.SyncConverter#convert(DataBuffer,int,int)
*/
public SyncObject convert(final DataBuffer data, final int version,
final int UID) {
try {
final OTAContactData contact = new OTAContactData(UID);
while (data.available() > 0) {
final int length = data.readShort();
final byte[] bytes = new byte[length];
switch (data.readByte()) {
case FIELDTAG_FIRST_NAME:
data.readFully(bytes);
// Trim null-terminator.
contact.setFirst(new String(bytes).trim());
break;
case FIELDTAG_LAST_NAME:
data.readFully(bytes);
contact.setLast(new String(bytes).trim());
break;
case FIELDTAG_EMAIL_ADDRESS:
data.readFully(bytes);
contact.setEmail(new String(bytes).trim());
break;
default:
data.readFully(bytes);
// other fields not supported
break;
}
}
return contact;
} catch (final EOFException e) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert(e.toString());
}
});
}
return null;
}
// SyncCollection
// methods----------------------------------------------------
/**
* @see net.rim.device.api.synchronization.SyncCollection#addSyncObject(SyncObject)
*/
public boolean addSyncObject(final SyncObject object) {
// Add a contact to the persistent store
_contacts.addElement(object);
PersistentObject.commit(_contacts);
// Use the CollectionListeners to let the server know the add was
// successful
for (int i = 0; i < _listeners.size(); i++) {
final CollectionListener cl =
(CollectionListener) _listeners.elementAt(i);
cl.elementAdded(this, object);
}
return true;
}
/**
* Insert an element at the specified index
*
* @param index
* The index at which to insert
* @param object
* The SyncObject to insert
*/
public boolean insertSyncObjectAt(final int index, final SyncObject object) {
// Add a contact to the persistent store
_contacts.insertElementAt(object, index);
PersistentObject.commit(_contacts);
// Use the CollectionListeners to let the server know the add was
// successful
for (int i = 0; i < _listeners.size(); i++) {
final CollectionListener cl =
(CollectionListener) _listeners.elementAt(i);
cl.elementAdded(this, object);
}
return true;
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#updateSyncObject(SyncObject,
* SyncObject)
*/
public boolean updateSyncObject(final SyncObject oldObject,
final SyncObject newObject) {
// Update a contact in the store
if (_contacts.contains(oldObject)) {
_contacts.setElementAt(newObject, _contacts.indexOf(oldObject));
PersistentObject.commit(_contacts);
}
// Use the CollectionListeners to let the server know the update was
// successful
for (int i = 0; i < _listeners.size(); i++) {
final CollectionListener cl =
(CollectionListener) _listeners.elementAt(i);
cl.elementUpdated(this, oldObject, newObject);
}
return true;
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#removeSyncObject(SyncObject)
*/
public boolean removeSyncObject(final SyncObject object) {
// Remove a contact from the persistent store
_contacts.removeElement(object);
PersistentObject.commit(_contacts);
// Use the CollectionListeners to let the server know the remove was
// successful
for (int i = 0; i < _listeners.size(); i++) {
final CollectionListener cl =
(CollectionListener) _listeners.elementAt(i);
cl.elementAdded(this, object);
}
return true;
}
public boolean removeAllSyncObjects() {
return false; // NA
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncObjects()
*/
public SyncObject[] getSyncObjects() {
final SyncObject[] contactArray = new SyncObject[_contacts.size()];
for (int i = _contacts.size() - 1; i >= 0; --i) {
contactArray[i] = (SyncObject) _contacts.elementAt(i);
}
return contactArray;
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncObjects()
*/
public SyncObject getSyncObject(final int uid) {
for (int i = _contacts.size() - 1; i >= 0; --i) {
final SyncObject so = (SyncObject) _contacts.elementAt(i);
if (so.getUID() == uid) {
return so;
}
}
return null;
}
/**
* Return the SyncObject at the specified index
*
* @param index
* The index of the SyncObject to retrieve
* @return The requested SyncObject
*/
public SyncObject getSyncObjectAt(final int index) {
return (SyncObject) _contacts.elementAt(index);
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#isSyncObjectDirty(SyncObject)
*/
public boolean isSyncObjectDirty(final SyncObject object) {
return false; // NA
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#setSyncObjectDirty(SyncObject)
*/
public void setSyncObjectDirty(final SyncObject object) {
// NA
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#clearSyncObjectDirty(SyncObject)
*/
public void clearSyncObjectDirty(final SyncObject object) {
// NA
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncObjectCount()
*/
public int getSyncObjectCount() {
_persist = PersistentStore.getPersistentObject(PERSISTENT_KEY);
_contacts = (Vector) _persist.getContents();
return _contacts.size();
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncVersion()
*/
public int getSyncVersion() {
return 1;
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncName()
*/
public String getSyncName() {
return "OTABackupRestoreContacts";
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncName(Locale)
*/
public String getSyncName(final Locale locale) {
return null;
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#getSyncConverter()
*/
public SyncConverter getSyncConverter() {
return this;
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#beginTransaction()
*/
public void beginTransaction() {
_persist = PersistentStore.getPersistentObject(PERSISTENT_KEY);
_contacts = (Vector) _persist.getContents();
}
/**
* @see net.rim.device.api.synchronization.SyncCollection#endTransaction()
*/
public void endTransaction() {
_persist.setContents(_contacts);
_persist.commit();
}
// OTASyncCapable methods
// ---------------------------------------------------
/**
* @see net.rim.device.api.synchronization.OTASyncCapable#getSchema()
*/
public SyncCollectionSchema getSchema() {
// Returns our schema.
return _schema;
}
// CollectionEventSource methods
// --------------------------------------------
/**
* @see net.rim.device.api.collection.CollectionEventSource#addCollectionListener(Object)
*/
public void addCollectionListener(final Object listener) {
_listeners = ListenerUtilities.fastAddListener(_listeners, listener);
}
/**
* @see net.rim.device.api.collection.CollectionEventSource#removeCollectionListener(Object)
*/
public void removeCollectionListener(final Object listener) {
_listeners = ListenerUtilities.removeListener(_listeners, listener);
}
/**
* Gets the size of the contact list.
*
* @return The size of the contact list
*/
int size() {
return _contacts.size();
}
}