jDoc, possible leak on crash, new depth option
[jvcard.git] / src / be / nikiroo / jvcard / Contact.java
index 87c1286a77093d436148a1563015f789e1b9beb6..4b4446de67c7ab0b9d4fed2650024a7274913421 100644 (file)
@@ -11,13 +11,17 @@ import java.util.UUID;
 
 import be.nikiroo.jvcard.parsers.Format;
 import be.nikiroo.jvcard.parsers.Parser;
-import be.nikiroo.jvcard.tui.StringUtils;
+import be.nikiroo.utils.StringUtils;
 
 /**
  * A contact is the information that represent a contact person or organisation.
+ * <p>
+ * Each {@link Data} inside can be binary encoded or not: if it is binary
+ * encoded, it has an active BKey number (not -1) associated to it (of value 0
+ * if still not sorted, or unique for the whole {@link Contact} if already
+ * processed).
  * 
  * @author niki
- * 
  */
 public class Contact extends BaseClass<Data> {
        private int nextBKey = 1;
@@ -36,32 +40,32 @@ public class Contact extends BaseClass<Data> {
        }
 
        /**
-        * Return the preferred Data field with the given name, or NULL if none.
+        * Return the preferred Data field with the given name, the first one if
+        * none is preferred, or NULL if none at all.
         * 
         * @param name
         *            the name to look for
-        * @return the Data field, or NULL
+        * 
+        * @return the {@link Data} field, or NULL
         */
        public Data getPreferredData(String name) {
-               Data first = null;
+               Data pref = null;
+               int ipref = Integer.MAX_VALUE;
                for (Data data : getData(name)) {
-                       if (first == null)
-                               first = data;
-                       for (int index = 0; index < data.size(); index++) {
-                               TypeInfo type = data.get(index);
-                               if (type.getName().equals("TYPE")
-                                               && type.getValue().equals("pref")) {
-                                       return data;
-                               }
-                       }
+                       if (pref == null)
+                               pref = data;
+
+                       if (data.getPreferred() < ipref)
+                               pref = data;
                }
 
-               return first;
+               return pref;
        }
 
        /**
         * Return the value of the preferred data field with this name, or NULL if
-        * none (you cannot differentiate a NULL value and no value).
+        * none (you cannot differentiate a NULL value and no value with this method
+        * -- for that, check {@link Contact#getPreferredData(String)}).
         * 
         * @param name
         *            the name to look for
@@ -78,7 +82,7 @@ public class Contact extends BaseClass<Data> {
         * Get the Data fields that share the given name.
         * 
         * @param name
-        *            the name to ook for
+        *            the name to look for
         * @return a list of Data fields with this name
         */
        public List<Data> getData(String name) {
@@ -92,63 +96,79 @@ public class Contact extends BaseClass<Data> {
                return found;
        }
 
-       /**
-        * Return a {@link String} representation of this contact.
-        * 
-        * @param format
-        *            the {@link Format} to use
-        * @param startingBKey
-        *            the starting BKey or -1 for no BKeys
-        * @return the {@link String} representation
-        */
-       public String toString(Format format, int startingBKey) {
-               updateBKeys(false);
-
-               StringBuilder builder = new StringBuilder();
-               for (String line : Parser.toStrings(this, format, startingBKey)) {
-                       builder.append(line);
-                       builder.append("\r\n");
-               }
-
-               return builder.toString();
-       }
-
        /**
         * Return a {@link String} representation of this contact formated
         * accordingly to the given format.
         * 
+        * <p>
         * The format is basically a list of field names separated by a pipe and
-        * optionally parametrised. The parameters allows you to:
+        * optionally parametrised with the 'at' (@) symbol. The parameters allows
+        * you to:
         * <ul>
         * <li>@x: show only a present/not present info</li>
         * <li>@n: limit the size to a fixed value 'n'</li>
         * <li>@+: expand the size of this field as much as possible</li>
         * </ul>
+        * </p>
+        * 
+        * <p>
+        * In case of lists or multiple-fields values, you can select a specific
+        * list or field with:
+        * <ul>
+        * <li>FIELD@(0): select the first value in a list</li>
+        * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+        * </ul>
+        * </p>
+        * 
+        * <p>
+        * You can also add a fixed text if it starts with a simple-quote (').
+        * </p>
         * 
-        * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+        * <p>
+        * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x"
+        * </p>
         * 
         * @param format
         *            the format to use
+        * @param separator
+        *            the separator {@link String} to use between fields
         * 
         * @return the {@link String} representation
         */
-       public String toString(String format) {
-               return toString(format, "|", null, -1, true, false);
+       public String toString(String format, String separator) {
+               return toString(format, separator, null, -1, true);
        }
 
        /**
         * Return a {@link String} representation of this contact formated
         * accordingly to the given format.
         * 
+        * <p>
         * The format is basically a list of field names separated by a pipe and
         * optionally parametrised. The parameters allows you to:
         * <ul>
-        * <li>@x: (the 'x' is the letter 'x') show only a present/not present info</li>
+        * <li>@x: show only a present/not present info</li>
         * <li>@n: limit the size to a fixed value 'n'</li>
         * <li>@+: expand the size of this field as much as possible</li>
         * </ul>
+        * </p>
         * 
-        * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+        * <p>
+        * In case of lists or multiple-fields values, you can select a specific
+        * list or field with:
+        * <ul>
+        * <li>FIELD@(0): select the first value in a list</li>
+        * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+        * </ul>
+        * </p>
+        * 
+        * <p>
+        * You can also add a fixed text if it starts with a simple-quote (').
+        * </p>
+        * 
+        * <p>
+        * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x"
+        * </p>
         * 
         * @param format
         *            the format to use
@@ -158,14 +178,13 @@ public class Contact extends BaseClass<Data> {
         *            the {@link String} to use for left and right padding
         * @param width
         *            a fixed width or -1 for "as long as needed"
-        * 
         * @param unicode
-        *            allow Uniode or only ASCII characters
+        *            allow Unicode or only ASCII characters
         * 
         * @return the {@link String} representation
         */
        public String toString(String format, String separator, String padding,
-                       int width, boolean unicode, boolean removeAccents) {
+                       int width, boolean unicode) {
                StringBuilder builder = new StringBuilder();
 
                for (String str : toStringArray(format, separator, padding, width,
@@ -180,6 +199,7 @@ public class Contact extends BaseClass<Data> {
         * Return a {@link String} representation of this contact formated
         * accordingly to the given format, part by part.
         * 
+        * <p>
         * The format is basically a list of field names separated by a pipe and
         * optionally parametrised. The parameters allows you to:
         * <ul>
@@ -187,8 +207,24 @@ public class Contact extends BaseClass<Data> {
         * <li>@n: limit the size to a fixed value 'n'</li>
         * <li>@+: expand the size of this field as much as possible</li>
         * </ul>
+        * </p>
+        * 
+        * <p>
+        * In case of lists or multiple-fields values, you can select a specific
+        * list or field with:
+        * <ul>
+        * <li>FIELD@(0): select the first value in a list</li>
+        * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+        * </ul>
+        * </p>
         * 
-        * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+        * <p>
+        * You can also add a fixed text if it starts with a simple-quote (').
+        * </p>
+        * 
+        * <p>
+        * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x"
+        * </p>
         * 
         * @param format
         *            the format to use
@@ -240,6 +276,7 @@ public class Contact extends BaseClass<Data> {
         * Return a {@link String} representation of this contact formated
         * accordingly to the given format, part by part.
         * 
+        * <p>
         * The format is basically a list of field names separated by a pipe and
         * optionally parametrised. The parameters allows you to:
         * <ul>
@@ -247,8 +284,24 @@ public class Contact extends BaseClass<Data> {
         * <li>@n: limit the size to a fixed value 'n'</li>
         * <li>@+: expand the size of this field as much as possible</li>
         * </ul>
+        * </p>
+        * 
+        * <p>
+        * In case of lists or multiple-fields values, you can select a specific
+        * list or field with:
+        * <ul>
+        * <li>FIELD@(0): select the first value in a list</li>
+        * <li>FIELD@[1]: select the second field in a multiple-fields value</li>
+        * </ul>
+        * </p>
         * 
-        * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+        * <p>
+        * You can also add a fixed text if it starts with a simple-quote (').
+        * </p>
+        * 
+        * <p>
+        * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x"
+        * </p>
         * 
         * @param format
         *            the format to use
@@ -256,7 +309,7 @@ public class Contact extends BaseClass<Data> {
         *            a fixed width or -1 for "as long as needed"
         * @param unicode
         *            allow Uniode or only ASCII characters
-        *
+        * 
         * @return the {@link String} representation
         */
        public String[] toStringArray(String format, int width, boolean unicode) {
@@ -283,8 +336,11 @@ public class Contact extends BaseClass<Data> {
                        int size = -1;
                        boolean binary = false;
                        boolean expand = false;
+                       int fieldNum = -1;
+                       int valueNum = -1;
 
-                       if (field.contains("@")) {
+                       if (field.length() > 0 && field.charAt(0) != '\''
+                                       && field.contains("@")) {
                                String[] opts = field.split("@");
                                if (opts.length > 0)
                                        field = opts[0];
@@ -295,16 +351,48 @@ public class Contact extends BaseClass<Data> {
                                        } else if (opt.equals("+")) {
                                                expand = true;
                                                numOfFieldsToExpand++;
+                                       } else if (opt.length() > 0 && opt.charAt(0) == '(') {
+                                               try {
+                                                       opt = opt.substring(1, opt.length() - 1);
+                                                       valueNum = Integer.parseInt(opt);
+                                               } catch (Exception e) {
+                                               }
+                                       } else if (opt.length() > 0 && opt.charAt(0) == '[') {
+                                               try {
+                                                       opt = opt.substring(1, opt.length() - 1);
+                                                       fieldNum = Integer.parseInt(opt);
+                                               } catch (Exception e) {
+                                               }
                                        } else {
                                                try {
                                                        size = Integer.parseInt(opt);
-                                               } catch (Exception e) {
+                                               } catch (NumberFormatException e) {
                                                }
                                        }
                                }
                        }
 
-                       String value = getPreferredDataValue(field);
+                       String value = null;
+                       if (field.length() > 0 && field.charAt(0) == '\'') {
+                               value = field.substring(1);
+                       } else if (valueNum >= 0) {
+                               List<String> vv = getPreferredData(field).getValues();
+                               if (valueNum < vv.size()) {
+                                       value = vv.get(valueNum);
+                               }
+                       } else if (fieldNum >= 0) {
+                               List<String> ff = getPreferredData(field).getFields();
+                               if (fieldNum < ff.size()) {
+                                       value = ff.get(fieldNum);
+                               }
+                       } else {
+                               // we don't need the *data* in binary mode...
+                               if (binary)
+                                       value = getData(field).size() > 0 ? "x" : null;
+                               else
+                                       value = getPreferredDataValue(field);
+                       }
+
                        if (value == null) {
                                value = "";
                        } else {
@@ -418,18 +506,18 @@ public class Contact extends BaseClass<Data> {
 
        @Override
        public String getState() {
-               return "" + getPreferredDataValue("UID");
+               return getId();
        }
 
        /**
-        * Return a {@link String} representation of this contact, in vCard 2.1,
-        * without BKeys.
+        * Return a simple {@link String} representation of this contact without
+        * BKeys.
         * 
         * @return the {@link String} representation
         */
        @Override
        public String toString() {
-               return toString(Format.VCard21, -1);
+               return "[Contact: " + getPreferredDataValue("FN") + "]";
        }
 
        /**
@@ -504,12 +592,26 @@ public class Contact extends BaseClass<Data> {
        /**
         * Add a {@link String} to the given {@link List}, but make sure it does not
         * exceed the maximum size, and truncate it if needed to fit.
+        * <p>
+        * Will always add one and only one {@link String} (potentially empty) at
+        * the end of <tt>list</tt>.
         * 
         * @param list
+        *            the list to add to
         * @param add
+        *            the {@link String} to (either fully or partially) add
         * @param currentSize
+        *            the current total size (managed outside of this method)
         * @param maxSize
-        * @return
+        *            the maximum size that cannot be exceeded (or -1 for
+        *            "no maximum") -- if the maximum size would be exceeded by
+        *            adding this {@link String}, only a part of it will be added;
+        *            if the maximum size is already reached or exceeded (should not
+        *            happen because of this method), an empty {@link String} will
+        *            be added
+        * 
+        * @return the number of characters added (the size of the last
+        *         {@link String} in <tt>list</tt>)
         */
        static private int addToList(List<String> list, String add,
                        int currentSize, int maxSize) {