import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
import be.nikiroo.jvcard.Card;
import be.nikiroo.jvcard.Contact;
import be.nikiroo.jvcard.Data;
+import be.nikiroo.jvcard.launcher.CardResult;
+import be.nikiroo.jvcard.launcher.CardResult.MergeCallback;
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.parsers.Vcard21Parser;
-import be.nikiroo.jvcard.resources.Bundles;
import be.nikiroo.jvcard.resources.StringUtils;
+import be.nikiroo.jvcard.resources.bundles.RemoteBundle;
+import be.nikiroo.jvcard.resources.enums.RemotingOption;
/**
* This class will synchronise {@link Card}s between a local instance an a
*
* @param force
* force the synchronisation to occur
+ * @param callback
+ * the {@link MergeCallback} to call in case of conflict
*
* @return the synchronised (or not) {@link Card}
*
* @throws IOException
* in case of IO error
*/
- public Card sync(boolean force) throws UnknownHostException, IOException {
-
+ public CardResult sync(boolean force, MergeCallback callback)
+ throws UnknownHostException, IOException {
long tsOriginal = getLastModified();
Card local = new Card(getCache(cacheDir), Format.VCard21);
- local.setRemote(true);
// do NOT update unless we are in autoSync or forced mode or we don't
// have the file on cache
if (!autoSync && !force && tsOriginal != -1) {
- return local;
+ return new CardResult(local, true, false, false);
}
SimpleSocket s = new SimpleSocket(new Socket(host, port), "sync client");
// get the server time stamp
long tsServer = -1;
+ boolean serverChanges = false;
try {
s.open(true);
s.sendCommand(Command.LIST_CARD);
}
// Check changes
- boolean serverChanges = (tsServer - tsOriginal) > GRACE_TIME;
+ serverChanges = (tsServer - tsOriginal) > GRACE_TIME;
boolean localChanges = false;
Card original = null;
if (tsOriginal != -1) {
System.err.println("DEBUG: it changed. retry.");
s.sendCommand(Command.SELECT);
s.close();
- return sync(force);
+ return sync(force, callback);
}
switch (action) {
s.sendCommand(Command.GET_CARD);
List<String> data = s.receiveBlock();
setLastModified(data.remove(0));
- Card server = new Card(Vcard21Parser.parseContact(data));
- local.replaceListContent(server);
+ local.replaceListContent(Vcard21Parser.parseContact(data));
if (local.isDirty())
local.save();
break;
}
case HELP: {
- if (true)
- throw new IOException("two-way sync not supported yet");
-
// note: we are holding the server here, so it could throw
// us away if we take too long
+ // TODO: check if those files are deleted
File mergeF = File.createTempFile("contact-merge", ".vcf");
File serverF = File
.createTempFile("contact-server", ".vcf");
Card server = new Card(serverF, Format.VCard21);
updateFromServer(s, server);
- // TODO: auto merge into mergeF (from original, local,
- // server)
- local.saveAs(mergeF, Format.VCard21);
+ // Do an auto sync
+ server.saveAs(mergeF, Format.VCard21);
Card merge = new Card(mergeF, Format.VCard21);
-
- // TODO: ask client if ok or to change it herself
-
- String serverLastModifTime = updateToServer(s, original,
- merge);
+ List<Contact> added = new LinkedList<Contact>();
+ List<Contact> removed = new LinkedList<Contact>();
+ original.compare(local, added, removed, removed, added);
+ for (Contact c : removed)
+ merge.getById(c.getId()).delete();
+ for (Contact c : added)
+ merge.add(Vcard21Parser.clone(c));
+
+ merge.save();
+
+ // defer to client:
+ if (callback == null) {
+ throw new IOException(
+ "Conflicting changes detected and merge operation not allowed");
+ }
+
+ merge = callback.merge(original, local, server, merge);
+ if (merge == null) {
+ throw new IOException(
+ "Conflicting changes detected and merge operation cancelled");
+ }
+
+ // TODO: something like:
+ // String serverLastModifTime = updateToServer(s, original,
+ // merge);
+ // ...but without starting with original since it is not
+ // true here
+ s.sendCommand(Command.POST_CARD);
+ s.sendBlock(Vcard21Parser.toStrings(merge));
+ String serverLastModifTime = s.receiveLine();
+ //
merge.saveAs(getCache(cacheDir), Format.VCard21);
merge.saveAs(getCache(cacheDirOrig), Format.VCard21);
break;
}
+ default:
+ // will not happen
+ break;
}
s.sendCommand(Command.SELECT);
}
} catch (IOException e) {
- throw e;
+ return new CardResult(e);
} catch (Exception e) {
- e.printStackTrace();
- return local;
+ return new CardResult(new IOException(e));
} finally {
s.close();
}
- return local;
+ return new CardResult(local, true, true, serverChanges);
}
/**
List<Data> subadded = new LinkedList<Data>();
List<Data> subremoved = new LinkedList<Data>();
f.compare(t, subadded, subremoved, subremoved, subadded);
- s.sendCommand(Command.PUT_CONTACT, name);
+ s.sendCommand(Command.PUT_CONTACT, f.getId());
for (Data d : subremoved) {
- s.sendCommand(Command.DELETE_DATA, d.getContentState());
+ s.sendCommand(Command.DELETE_DATA, d.getContentState(true));
}
for (Data d : subadded) {
- s.sendCommand(Command.POST_DATA, d.getContentState());
+ s.sendCommand(Command.POST_DATA, d.getContentState(true));
s.sendBlock(Vcard21Parser.toStrings(d));
}
+ s.sendCommand(Command.PUT_CONTACT);
}
}
String hash = remote.get(c.getId());
if (hash == null) {
deleted.add(c);
- } else if (!hash.equals(c.getContentState())) {
+ } else if (!hash.equals(c.getContentState(true))) {
changed.add(c);
}
}
*/
static private void config() {
String dir = null;
- ResourceBundle bundle = Bundles.getBundle("remote");
+ RemoteBundle bundle = new RemoteBundle();
try {
- dir = bundle.getString("CLIENT_CACHE_DIR").trim();
+ dir = bundle.getString(RemotingOption.CLIENT_CACHE_DIR);
cacheDir = new File(dir + File.separator + "current");
cacheDir.mkdir();
+ dir);
}
- String autoStr = bundle.getString("CLIENT_AUTO_SYNC");
- if (autoStr != null && autoStr.trim().equalsIgnoreCase("true")) {
- autoSync = true;
- }
-
- } catch (MissingResourceException e) {
- throw new InvalidParameterException(
- "Cannot access remote.properties configuration file");
+ autoSync = bundle
+ .getBoolean(RemotingOption.CLIENT_AUTO_SYNC, false);
} catch (Exception e) {
throw new InvalidParameterException(
"Cannot open or create cache store at: " + dir);