Remote: jdoc + description + some fixes:
authorNiki Roo <niki@nikiroo.be>
Sun, 20 Mar 2016 10:05:04 +0000 (11:05 +0100)
committerNiki Roo <niki@nikiroo.be>
Sun, 20 Mar 2016 10:05:04 +0000 (11:05 +0100)
- remote is now done
- sync is partially done (post/get, no sync yet)

src/be/nikiroo/jvcard/BaseClass.java
src/be/nikiroo/jvcard/Card.java
src/be/nikiroo/jvcard/Contact.java
src/be/nikiroo/jvcard/Data.java
src/be/nikiroo/jvcard/TypeInfo.java
src/be/nikiroo/jvcard/remote/Command.java
src/be/nikiroo/jvcard/remote/Server.java
src/be/nikiroo/jvcard/remote/SimpleSocket.java
src/be/nikiroo/jvcard/remote/package-info.java

index 0aa18e6ff66336a3f2afecacc9ba6fbe1b4a380b..df0fcf00de78f998ad87d03842dc4947f86a966a 100644 (file)
@@ -251,7 +251,14 @@ public abstract class BaseClass<E extends BaseClass<?>> implements List<E> {
 
        /**
         * Get the recursive state of the current object, i.e., its children. It
-        * represents the full state information about this object's children.
+        * represents the full state information about this object's children. It
+        * may not contains spaces nor new lines.
+        * 
+        * <p>
+        * Not that this state is <b>lossy</b>. You cannot retrieve the data from
+        * the state, it can only be used as an ID to check if thw data are
+        * identical.
+        * </p>
         * 
         * @return a {@link String} representing the current content state of this
         *         object, i.e., its children included
@@ -295,7 +302,14 @@ public abstract class BaseClass<E extends BaseClass<?>> implements List<E> {
        /**
         * Get the state of the current object, children <b>not included</b>. It
         * represents the full state information about this object, but do not check
-        * its children (see {@link BaseClass#getContentState()} for that).
+        * its children (see {@link BaseClass#getContentState()} for that). It may
+        * not contains spaces nor new lines.
+        * 
+        * <p>
+        * Not that this state is <b>lossy</b>. You cannot retrieve the data from
+        * the state, it can only be used as an ID to check if thw data are
+        * identical.
+        * </p>
         * 
         * @return a {@link String} representing the current state of this object,
         *         children not included
index 7939263bfc60b3201458810a735c3690df041077..d2567a496237c159546fa40f46015cebc241223b 100644 (file)
@@ -228,6 +228,6 @@ public class Card extends BaseClass<Contact> {
 
        @Override
        public String getState() {
-               return "" + name + format;
+               return ("" + name + format).replace(' ', '_');
        }
 }
index ce701a26a60ad88031881a13b71291b72c1ef627..08c2a515e563cdfb39aadc7ab84795b1ec1153c6 100644 (file)
@@ -418,7 +418,7 @@ public class Contact extends BaseClass<Data> {
 
        @Override
        public String getState() {
-               return "" + getPreferredDataValue("UID");
+               return getId();
        }
 
        /**
index a0ef2e955889d3bd5ea7f5455ccd5ca0a36f48fb..5c09a13419c1177167f4b2b1cb7bcea1fab68dbb 100644 (file)
@@ -153,6 +153,6 @@ public class Data extends BaseClass<TypeInfo> {
 
        @Override
        public String getState() {
-               return "" + name + value + group;
+               return ("" + name + value + group).replace(' ', '_');
        }
 }
index 2585cfbdd4deb68c12152cfc592cc4274f543c71..c5b812cc89939ae556f3b62f48bb7c234ce6ac11 100644 (file)
@@ -52,6 +52,6 @@ public class TypeInfo extends BaseClass {
 
        @Override
        public String getState() {
-               return "" + name + value;
+               return ("" + name + value).replace(' ', '_');
        }
 }
\ No newline at end of file
index 6ab044e8ffd09afdc510241f54d27d852508915c..3704c26936923ac16f9797ad63d2bb033b7ecec2 100644 (file)
 package be.nikiroo.jvcard.remote;
 
+/**
+ * This enum list all the possible {@link Command}s you can send to a jVCard
+ * remote servrer.
+ * 
+ * @author niki
+ *
+ */
 public enum Command {
-       /** VERSION of the protocol */
+       /**
+        * Protocol version.
+        * 
+        * @return the version of the protocol used by this server
+        */
        VERSION,
-       /** TIME of the remote server in milliseconds since the Unix epoch */
+       /**
+        * Server time.
+        * 
+        * @return the TIME of the remote server with the format yyyy-MM-dd HH:mm:ss
+        */
        TIME,
-       /** STOP the communication (client stops) */
+       /**
+        * STOP the client communication (the server will now close the
+        * communication)
+        */
        STOP,
        /**
-        * LIST all the cards on the remote server that contain the search term,
-        * or all contacts if no search term given; also add their timestamps
+        * LIST the cards available on this server.
+        * 
+        * @param parameter
+        *            (optional) a search term that, if given, must be in the name
+        *            of the card for it to be returned (case insensitive)
+        * 
+        * @return a LIST of all the cards on the remote server that contain the
+        *         search term, or all cards if no search term given
+        * 
+        * @note The cards are listed each on their own line, preceded by their last
+        *       modified time and a space character (" ")
+        *       <p>
+        *       Example: <tt>
+        * <p>
+        * 2016-03-19 11:13:23 Family.vcf
+        * <p>
+        * 2016-03-19 11:13:23 CoWorkers.vcf
         */
        LIST_CARD,
-       /** HELP about the protocol for interactive access */
+       /**
+        * Internationalised help message.
+        * 
+        * @return some HELP about the protocol for interactive access
+        */
        HELP,
-       /** SELECT a resource (a card) to work on */
+       /**
+        * SELECT a resource (a card) to work on (you can issue *_CARD commands when
+        * in SELECT mode), or leave SELECT mode if already enabled.
+        * 
+        * @param parameter
+        *            the resource name (the card name) to work on to enter SELECT
+        *            mode, or nothing to leave it
+        * 
+        * @return the last modified date of the selected card
+        */
        SELECT,
-       /** GET a remote card */
+       /**
+        * GET a remote card and return it as VCF data.
+        * 
+        * @return VCF data
+        */
        GET_CARD,
        /**
-        * PUT mode activation toggle for a card on the remote server (you can issue
-        * *_CONTACT commands when in PUT mode)
+        * Enter into PUT_CARD mode for the selected card (you can issue *_CONTACT
+        * commands when in PUT_CARD mode), or leave PUT_CARD mode if already
+        * enabled.
+        * 
+        * @requires SELECT mode must be enabled before
         */
        PUT_CARD,
-       /** POST a new card to the remote server */
+       /**
+        * POST a new card to the remote server as the selected resource.
+        * 
+        * @input will wait for the card content as VCF data
+        * 
+        * @requires SELECT mode must be enabled before
+        */
        POST_CARD,
-       /** DELETE an existing contact from the remote server */
+       /**
+        * DELETE the selected contact from the remote server.
+        * 
+        * @requires SELECT mode must be enabled before
+        */
        DELETE_CARD,
-       /** HASH the given contact and return the hash, or empty if not found */
+       /**
+        * HASH the selected contact and return the hash, or return empty if not
+        * found.
+        * 
+        * @param parameter
+        *            the UID of the contact to hash
+        * 
+        * @return the hash, or an empty message if not found
+        * 
+        * @requires PUT_CARD mode must be enabled before
+        */
        HASH_CONTACT,
-       /** LIST all the contacts of the current card; also add their hashes */
+       /**
+        * LIST the contacts available in the selected card.
+        * 
+        * @param parameter
+        *            (optional) a search term that, if given, must be present in
+        *            the N or FN property of the contact for the contact to be
+        *            returned (case insensitive)
+        * 
+        * @return a LIST of all the contacts in the selected card that contain the
+        *         search term, or all contacts if no search term given
+        * 
+        * @requires PUT_CARD mode must be enabled before
+        * 
+        * @note The contacts are listed each on their own line, preceded by their
+        *       hashes and a space character (" ")
+        *       <p>
+        *       Example: <tt>
+        * <p>
+        * 5d1db4f26410eae670852b53e6ea80be 6pXXHy8T3b
+        * <p>
+        * 477eef8e57a12dffeeb4063d5a138c9a FoYJUyDOwM
+        */
        LIST_CONTACT,
-       /** GET a remote contact */
+       /**
+        * GET a remote contact if found.
+        * 
+        * @param parameter
+        *            the UID of the contact to return
+        * 
+        * @return the contact as VCF data or an empty message if the UID was not
+        *         found
+        * 
+        * @requires PUT_CARD mode must be enabled before
+        * 
+        */
        GET_CONTACT,
        /**
-        * PUT mode activation toggle for a contact on the remote server (you can
-        * issue *_DATA commands when in PUT mode), param = uid
+        * Select the given contact by UID and enter into PUT_CONTACT mode (you can
+        * issue *_DATA commands when in PUT_CONTACT mode), or leave PUT_CONTACT
+        * mode if already enabled.
+        * 
+        * @param parameter
+        *            the UID of the contact to select to enter PUT_CONTACT mode, or
+        *            nothing to leave it
+        * 
+        * @requires PUT_CARD mode must be enabled before
         */
        PUT_CONTACT,
-       /** POST a new contact to the remote server */
+       /**
+        * POST a new contact to the remote server in the selected card.
+        * 
+        * @input will wait for the contact VCF data
+        * 
+        * @requires PUT_CARD mode must be enabled before
+        */
        POST_CONTACT,
-       /** DELETE an existing contact from the remote server */
+       /**
+        * DELETE an existing contact from the remote server.
+        * 
+        * @param parameter
+        *            the UID of the contact to delete
+        * 
+        * @requires PUT_CARD mode must be enabled before
+        */
        DELETE_CONTACT,
-       /** HASH the data(s) with the given name */
+       /**
+        * HASH the data(s) with the given name.
+        * 
+        * @param parameter
+        *            the name of the data(s) you want
+        * 
+        * @return the hashes of all the datas that correspond to the given name
+        * 
+        * @requires PUT_CONTACT mode must be enabled before
+        */
        HASH_DATA,
-       /** LIST all the datas of the current contact; also add their hashes */
+       /**
+        * LIST the datas available in the selected contact.
+        * 
+        * @param parameter
+        *            (optional) a search term that, if given, must be present in
+        *            the name of the data for it to be returned (case insensitive)
+        * 
+        * @return a LIST of all the datas in the selected contact that contain the
+        *         search term, or all datas if no search term given
+        * 
+        * @requires PUT_CONTACT mode must be enabled before
+        * 
+        * @note The datas' names are listed each on their own line, preceded by
+        *       their hashes and a space character (" ")
+        *       <p>
+        *       Example: <tt>
+        * <p>
+        * 5d1db4f26410eae670852b53e6ea80be FN
+        * <p>
+        * 477eef8e57a12dffeeb4063d5a138c9a TEL
+        */
        LIST_DATA,
-       /** GET a (or more) remote data(s) by name */
+       /**
+        * GET one or more remote data(s) by name.
+        * 
+        * @param parameter
+        *            the name of the data(s) to return
+        * 
+        * @return the datas as VCF data or an empty message if no data were found
+        *         with that name
+        * 
+        * @requires PUT_CONTACT mode must be enabled before
+        * 
+        */
        GET_DATA,
-       /** POST a new data to the remote server */
+       /**
+        * POST a new data to the remote server in the selected contact.
+        * 
+        * @input will wait for the data VCF data
+        * 
+        * @requires PUT_CONTACT mode must be enabled before
+        */
        POST_DATA,
-       /** DELETE an existing data from the remote server */
+       /**
+        * DELETE an existing data from the remote server.
+        * 
+        * @param parameter
+        *            the hash of the data to delete
+        * 
+        * @requires PUT_CONTACT mode must be enabled before
+        */
        DELETE_DATA,
 }
\ No newline at end of file
index 567df69c83f12b735d33a2c330ff6b4a96a657f3..08091f6ea352e59cd96e5ec2a59032650eaea65c 100644 (file)
@@ -201,7 +201,7 @@ public class Server implements Runnable {
                        break;
                }
                case VERSION: {
-                       s.sendCommand(Command.VERSION);
+                       s.sendLine("" + SimpleSocket.CURRENT_VERSION);
                        break;
                }
                case TIME: {
@@ -259,8 +259,10 @@ public class Server implements Runnable {
                }
                case LIST_CARD: {
                        for (File file : dataDir.listFiles()) {
-                               if (cmd.getParam() == null || cmd.getParam().length() == 0
-                                               || file.getName().contains(cmd.getParam())) {
+                               if (cmd.getParam() == null
+                                               || cmd.getParam().length() == 0
+                                               || file.getName().toLowerCase()
+                                                               .contains(cmd.getParam().toLowerCase())) {
                                        s.send(StringUtils.fromTime(file.lastModified()) + " "
                                                        + file.getName());
                                }
@@ -273,8 +275,8 @@ public class Server implements Runnable {
                        s.send("The following commands are available:");
                        s.send("- TIME: get the server time");
                        s.send("- HELP: this help screen");
-                       s.send("- LIST: list the available cards on this server");
-                       s.send("- VERSION/GET/PUT/POST/DELETE/STOP: TODO");
+                       s.send("- LIST_CARD: list the available cards on this server");
+                       s.send("- VERSION/GET_*/PUT_*/POST_*/DELETE_*/STOP: TODO");
                        s.sendBlock();
                        break;
                }
@@ -364,7 +366,8 @@ public class Server implements Runnable {
                        break;
                }
                default: {
-                       throw new InvalidParameterException("command invalid here");
+                       throw new InvalidParameterException("command invalid here: "
+                                       + command);
                }
                }
 
@@ -409,16 +412,16 @@ public class Server implements Runnable {
                        break;
                }
                case POST_CONTACT: {
-                       String uid = cmd.getParam();
-                       Contact contact = card.getById(uid);
-                       if (contact != null)
-                               contact.delete();
                        List<Contact> list = Vcard21Parser.parseContact(s.receiveBlock());
                        if (list.size() > 0) {
-                               contact = list.get(0);
-                               contact.getPreferredData("UID").setValue(uid);
-                               card.add(contact);
+                               Contact newContact = list.get(0);
+                               String uid = newContact.getPreferredDataValue("UID");
+                               Contact oldContact = card.getById(uid);
+                               if (oldContact != null)
+                                       oldContact.delete();
+                               card.add(newContact);
                        }
+
                        break;
                }
                case PUT_CONTACT: {
@@ -456,7 +459,13 @@ public class Server implements Runnable {
                }
                case LIST_CONTACT: {
                        for (Contact contact : card) {
-                               s.send(contact.getContentState() + " " + contact.getId());
+                               if (cmd.getParam() == null
+                                               || cmd.getParam().length() == 0
+                                               || (contact.getPreferredDataValue("FN") + contact
+                                                               .getPreferredDataValue("N")).toLowerCase()
+                                                               .contains(cmd.getParam().toLowerCase())) {
+                                       s.send(contact.getContentState() + " " + contact.getId());
+                               }
                        }
                        s.sendBlock();
                        break;
@@ -466,7 +475,8 @@ public class Server implements Runnable {
                        break;
                }
                default: {
-                       throw new InvalidParameterException("command invalid here");
+                       throw new InvalidParameterException("command invalid here: "
+                                       + command);
                }
                }
 
@@ -557,7 +567,12 @@ public class Server implements Runnable {
                }
                case LIST_DATA: {
                        for (Data data : contact) {
-                               s.send(data.getContentState() + " " + data.getName());
+                               if (cmd.getParam() == null
+                                               || cmd.getParam().length() == 0
+                                               || data.getName().toLowerCase()
+                                                               .contains(cmd.getParam().toLowerCase())) {
+                                       s.send(data.getContentState() + " " + data.getName());
+                               }
                        }
                        s.sendBlock();
                        break;
@@ -567,7 +582,8 @@ public class Server implements Runnable {
                        break;
                }
                default: {
-                       throw new InvalidParameterException("command invalid here");
+                       throw new InvalidParameterException("command invalid here: "
+                                       + command);
                }
                }
 
@@ -593,7 +609,7 @@ public class Server implements Runnable {
                if (vcf != null && vcf.exists()) {
                        Card card = new Card(vcf, Format.VCard21);
 
-                       // timestamp:
+                       // timestamp + data
                        lines.add(StringUtils.fromTime(card.getLastModified()));
                        lines.addAll(Vcard21Parser.toStrings(card));
                }
index acc43b295c352bad99f3e09103b632397a27e03a..d9dcb6007f8c6050126b97960c4a755ab8e305c2 100644 (file)
@@ -22,7 +22,7 @@ public class SimpleSocket {
        /**
         * The current version of the network protocol.
         */
-       static private final int CURRENT_VERSION = 1;
+       static public final int CURRENT_VERSION = 1;
 
        /**
         * The end of block marker.
index 5cb9de128394e7f078f5b245724e2c8e3ecccc75..4c39be6d8afd2e81c73fec0c1cbb2ee508870ad8 100644 (file)
@@ -1,5 +1,45 @@
 /**
- * TODO: describe protocol here.
+ * jVCard remote uses a simple plain text protocol to 
+ * transfer/retrieve/delete information from/to the server.
+ * 
+ * <p>
+ * 
+ * The protocol is based around <b>lines</b> of text and <b>blocks</b> of 
+ * <b>lines</b>. The client sends <b>commands</b> while the server only 
+ * answers with text data.
+ * 
+ * <p>
+ * 
+ * Some definitions:
+ * <ul>
+ *             <li>A <b>line</b> is the text between two new-line characters (\n).
+ *             <li>A <b>block</b> is all the <b>lines</b> between two end-of-block 
+ * <b>lines</b>.
+ *             <li>An end-of-block <b>line</b> is a special <b>line</b> 
+ * containing only a dot (.).
+ *             <li>A <b>command</b> is <b>block</b> whose first <b>line</b> 
+ * contains the command in upper case text and an optional text
+ * argument, optionally followed by a second <b>block</b> of "input" data
+ * </ul>
+ * 
+ * <p>
+ * 
+ * Upon connection, the first thing that happen is that the server sends 
+ * a VERSION <b>command</b> (a <b>block</b> whose first <b>line</b> is 
+ * <b><tt>VERSION 1</tt></b>, possibly followed by some help text 
+ * <b>lines</b>).
+ * 
+ * <p>
+ * 
+ * The client <b>MUST</b> answer with another VERSION <b>command</b>.
+ * 
+ * <p>
+ * 
+ * From that time on, the client is allowed to send <b>commands</b>
+ * as described by {@link be.nikiroo.jvcard.remote.Command}.
+ * If the client doesn't follow the rules in 
+ * {@link be.nikiroo.jvcard.remote.Command}, the server will close
+ * the connection.
  * 
  * @author niki
  */