+ return new CardResult(local, true, true, serverChanges);
+ }
+
+ /**
+ * Will update the currently selected {@link Card} on the remote server to
+ * be in the same state as <tt>local</tt>, assuming the server is currently
+ * in <tt>original</tt> state.
+ *
+ * @param s
+ * the {@link SimpleSocket} to work on, which <b>MUST</b> be in
+ * SELECT mode
+ * @param original
+ * the original {@link Card} as it was before the client made
+ * changes to it
+ * @param local
+ * the {@link Card} to which state we want the server in
+ *
+ * @return the last modified time from the remote server (which is basically
+ * "now")
+ *
+ * @throws IOException
+ * in case of IO error
+ */
+ private String updateToServer(SimpleSocket s, Card original, Card local)
+ throws IOException {
+ List<Contact> added = new LinkedList<Contact>();
+ List<Contact> removed = new LinkedList<Contact>();
+ List<Contact> from = new LinkedList<Contact>();
+ List<Contact> to = new LinkedList<Contact>();
+ original.compare(local, added, removed, from, to);
+
+ s.sendCommand(Command.PUT_CARD);
+
+ for (Contact c : removed) {
+ s.sendCommand(Command.DELETE_CONTACT, c.getId());
+ }
+ for (Contact c : added) {
+ s.sendCommand(Command.POST_CONTACT, c.getId());
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, c, -1);
+ s.close();
+ }
+ if (from.size() > 0) {
+ for (int index = 0; index < from.size(); index++) {
+ Contact f = from.get(index);
+ Contact t = to.get(index);
+
+ List<Data> subadded = new LinkedList<Data>();
+ List<Data> subremoved = new LinkedList<Data>();
+ f.compare(t, subadded, subremoved, subremoved, subadded);
+ s.sendCommand(Command.PUT_CONTACT, f.getId());
+ for (Data d : subremoved) {
+ s.sendCommand(Command.DELETE_DATA, d.getContentState(true));
+ }
+ for (Data d : subadded) {
+ s.sendCommand(Command.POST_DATA, d.getContentState(true));
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, d);
+ app.close();
+ }
+ s.sendCommand(Command.PUT_CONTACT);
+ }
+ }
+
+ s.sendCommand(Command.PUT_CARD);
+
+ return s.receiveLine();
+ }
+
+ /**
+ * Will update the given {@link Card} object (not {@link File}) to the
+ * currently selected {@link Card} on the remote server.
+ *
+ * @param s
+ * the {@link SimpleSocket} to work on, which <b>MUST</b> be in
+ * SELECT mode
+ * @param local
+ * the {@link Card} to update
+ *
+ * @return the last modified time from the remote server
+ *
+ * @throws IOException
+ * in case of IO error
+ */
+ private String updateFromServer(SimpleSocket s, Card local)
+ throws IOException {
+ s.sendCommand(Command.PUT_CARD);
+
+ s.sendCommand(Command.LIST_CONTACT);
+ Map<String, String> remote = new HashMap<String, String>();
+ for (String line : s.receiveBlock()) {
+ int indexSp = line.indexOf(" ");
+ String hash = line.substring(0, indexSp);
+ String uid = line.substring(indexSp + 1);
+
+ remote.put(uid, hash);
+ }
+
+ List<Contact> deleted = new LinkedList<Contact>();
+ List<Contact> changed = new LinkedList<Contact>();
+ List<String> added = new LinkedList<String>();
+
+ for (Contact c : local) {
+ String hash = remote.get(c.getId());
+ if (hash == null) {
+ deleted.add(c);
+ } else if (!hash.equals(c.getContentState(true))) {
+ changed.add(c);
+ }
+ }
+
+ for (String uid : remote.keySet()) {
+ if (local.getById(uid) == null)
+ added.add(uid);
+ }
+
+ // process:
+
+ for (Contact c : deleted) {
+ c.delete();
+ }
+
+ for (String uid : added) {
+ s.sendCommand(Command.GET_CONTACT, uid);
+ for (Contact cc : Vcard21Parser.parseContact(s.receiveBlock())) {
+ local.add(cc);
+ }
+ }
+
+ for (Contact c : changed) {
+ c.delete();
+ s.sendCommand(Command.GET_CONTACT, c.getId());
+ for (Contact cc : Vcard21Parser.parseContact(s.receiveBlock())) {
+ local.add(cc);
+ }
+ }
+
+ s.sendCommand(Command.PUT_CARD);
+
+ return s.receiveLine();