X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2FContact.java;h=4b46eab8afa3dcc6aa840d8c89952d0c4ef8b3b5;hb=30a4aa17f2141ad80a23447ee2e6303f6c9ef995;hp=8142c29a830c4d6dfc82c6a01f0dddc718e62c62;hpb=78e4af97505df331618f9c13dd5d98440d364764;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/Contact.java b/src/be/nikiroo/jvcard/Contact.java index 8142c29..4b46eab 100644 --- a/src/be/nikiroo/jvcard/Contact.java +++ b/src/be/nikiroo/jvcard/Contact.java @@ -1,13 +1,17 @@ package be.nikiroo.jvcard; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +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.jvcard.resources.StringUtils; /** * A contact is the information that represent a contact person or organisation. @@ -15,12 +19,9 @@ import be.nikiroo.jvcard.tui.StringUtils; * @author niki * */ -public class Contact { - private List datas; +public class Contact extends BaseClass { private int nextBKey = 1; private Map binaries; - private boolean dirty; - private Card parent; /** * Create a new Contact from the given information. Note that the BKeys data @@ -30,57 +31,10 @@ public class Contact { * the information about the contact */ public Contact(List content) { - this.datas = new LinkedList(); - - boolean fn = false; - boolean n = false; - for (Data data : content) { - if (data.getName().equals("N")) { - n = true; - } else if (data.getName().equals("FN")) { - fn = true; - } - - if (!data.getName().equals("VERSION")) { - datas.add(data); - } - } - - // required fields: - if (!n) { - datas.add(new Data(null, "N", "", null)); - } - if (!fn) { - datas.add(new Data(null, "FN", "", null)); - } - + super(load(content)); updateBKeys(true); } - /** - * Return the number of {@link Data} present in this {@link Contact}. - * - * @return the number of {@link Data}s - */ - public int size() { - return datas.size(); - } - - /** - * Return the {@link Data} at index index. - * - * @param index - * the index of the {@link Data} to find - * - * @return the {@link Data} - * - * @throws IndexOutOfBoundsException - * if the index is < 0 or >= {@link Contact#size()} - */ - public Data get(int index) { - return datas.get(index); - } - /** * Return the preferred Data field with the given name, or NULL if none. * @@ -93,13 +47,9 @@ public class Contact { 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 (data.isPreferred()) + return data; } return first; @@ -130,7 +80,7 @@ public class Contact { public List getData(String name) { List found = new LinkedList(); - for (Data data : datas) { + for (Data data : this) { if (data.getName().equals(name)) found.add(data); } @@ -149,36 +99,64 @@ public class Contact { */ public String toString(Format format, int startingBKey) { updateBKeys(false); - return Parser.toString(this, format, startingBKey); + + 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. * + *

* 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: *

    *
  • @x: show only a present/not present info
  • *
  • @n: limit the size to a fixed value 'n'
  • *
  • @+: expand the size of this field as much as possible
  • *
+ *

+ * + *

+ * In case of lists or multiple-fields values, you can select a specific + * list or field with: + *

    + *
  • FIELD@(0): select the first value in a list
  • + *
  • FIELD@[1]: select the second field in a multiple-fields value
  • + *
+ *

* - * Example: "N@10|FN@20|NICK@+|PHOTO@x" + *

+ * You can also add a fixed text if it starts with a simple-quote ('). + *

+ * + *

+ * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x" + *

* * @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, false); } /** * Return a {@link String} representation of this contact formated * accordingly to the given format. * + *

* The format is basically a list of field names separated by a pipe and * optionally parametrised. The parameters allows you to: *

    @@ -186,8 +164,24 @@ public class Contact { *
  • @n: limit the size to a fixed value 'n'
  • *
  • @+: expand the size of this field as much as possible
  • *
+ *

+ * + *

+ * In case of lists or multiple-fields values, you can select a specific + * list or field with: + *

    + *
  • FIELD@(0): select the first value in a list
  • + *
  • FIELD@[1]: select the second field in a multiple-fields value
  • + *
+ *

* - * Example: "N@10|FN@20|NICK@+|PHOTO@x" + *

+ * You can also add a fixed text if it starts with a simple-quote ('). + *

+ * + *

+ * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x" + *

* * @param format * the format to use @@ -219,6 +213,7 @@ public class Contact { * Return a {@link String} representation of this contact formated * accordingly to the given format, part by part. * + *

* The format is basically a list of field names separated by a pipe and * optionally parametrised. The parameters allows you to: *

    @@ -226,8 +221,24 @@ public class Contact { *
  • @n: limit the size to a fixed value 'n'
  • *
  • @+: expand the size of this field as much as possible
  • *
+ *

+ * + *

+ * In case of lists or multiple-fields values, you can select a specific + * list or field with: + *

    + *
  • FIELD@(0): select the first value in a list
  • + *
  • FIELD@[1]: select the second field in a multiple-fields value
  • + *
+ *

+ * + *

+ * You can also add a fixed text if it starts with a simple-quote ('). + *

* - * Example: "N@10|FN@20|NICK@+|PHOTO@x" + *

+ * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x" + *

* * @param format * the format to use @@ -279,6 +290,7 @@ public class Contact { * Return a {@link String} representation of this contact formated * accordingly to the given format, part by part. * + *

* The format is basically a list of field names separated by a pipe and * optionally parametrised. The parameters allows you to: *

    @@ -286,8 +298,24 @@ public class Contact { *
  • @n: limit the size to a fixed value 'n'
  • *
  • @+: expand the size of this field as much as possible
  • *
+ *

+ * + *

+ * In case of lists or multiple-fields values, you can select a specific + * list or field with: + *

    + *
  • FIELD@(0): select the first value in a list
  • + *
  • FIELD@[1]: select the second field in a multiple-fields value
  • + *
+ *

* - * Example: "N@10|FN@20|NICK@+|PHOTO@x" + *

+ * You can also add a fixed text if it starts with a simple-quote ('). + *

+ * + *

+ * Example: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x" + *

* * @param format * the format to use @@ -322,8 +350,11 @@ public class Contact { 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]; @@ -334,16 +365,44 @@ public class Contact { } 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 vv = getPreferredData(field).getValues(); + if (valueNum < vv.size()) { + value = vv.get(valueNum); + } + } else if (fieldNum >= 0) { + List ff = getPreferredData(field).getFields(); + if (fieldNum < ff.size()) { + value = ff.get(fieldNum); + } + } else { + value = getPreferredDataValue(field); + } + if (value == null) { value = ""; } else { @@ -435,7 +494,7 @@ public class Contact { public void updateFrom(Contact vc) { updateBKeys(false); - List newDatas = new LinkedList(vc.datas); + List newDatas = new LinkedList(vc); for (int i = 0; i < newDatas.size(); i++) { Data data = newDatas.get(i); int bkey = Parser.getBKey(data); @@ -446,40 +505,18 @@ public class Contact { } } - this.datas = newDatas; + replaceListContent(newDatas); this.nextBKey = vc.nextBKey; - - setParent(parent); - setDirty(); } - /** - * Delete this {@link Contact} from its parent {@link Card} if any. - * - * @return TRUE in case of success - */ - public boolean delete() { - if (parent != null) { - List list = parent.getContactsList(); - for (int i = 0; i < list.size(); i++) { - if (list.get(i) == this) { - list.remove(i); - parent.setDirty(); - return true; - } - } - } - - return false; + @Override + public String getId() { + return "" + getPreferredDataValue("UID"); } - /** - * Check if this {@link Contact} has unsaved changes. - * - * @return TRUE if it has - */ - public boolean isDirty() { - return dirty; + @Override + public String getState() { + return getId(); } /** @@ -509,7 +546,7 @@ public class Contact { binaries = new HashMap(); } - for (Data data : datas) { + for (Data data : this) { if (data.isBinary() && (data.getB64Key() <= 0 || force)) { binaries.put(nextBKey, data); data.resetB64Key(nextBKey++); @@ -518,37 +555,48 @@ public class Contact { } /** - * Notify that this element has unsaved changes, and notify its parent of - * the same if any. + * Load the data from the given {@link File} under the given {@link Format}. + * + * @param file + * the {@link File} to load from + * @param format + * the {@link Format} to load as + * + * @return the list of elements + * @throws IOException + * in case of IO error */ - protected void setDirty() { - this.dirty = true; - if (this.parent != null) - this.parent.setDirty(); - } + static private List load(List content) { + List datas = new ArrayList(); - /** - * Notify this element and all its descendants that it is in pristine - * state (as opposed to dirty). - */ - void setPristine() { - dirty = false; - for (Data data : datas) { - data.setPristine(); - } - } + boolean fn = false; + boolean n = false; + boolean uid = false; + if (content != null) { + for (Data data : content) { + if (data.getName().equals("N")) { + n = true; + } else if (data.getName().equals("FN")) { + fn = true; + } else if (data.getName().equals("UID")) { + uid = true; + } - /** - * Set the parent of this {@link Contact} and all its descendants. - * - * @param parent - * the new parent - */ - void setParent(Card parent) { - this.parent = parent; - for (Data data : datas) { - data.setParent(this); + if (!data.getName().equals("VERSION")) { + datas.add(data); + } + } } + + // required fields: + if (!n) // required since vCard 3.0, supported in 2.1 + datas.add(new Data(null, "N", "", null)); + if (!fn) // not required anymore but still supported in 4.0 + datas.add(new Data(null, "FN", "", null)); + if (!uid) // supported by vCard, required by this program + datas.add(new Data(null, "UID", UUID.randomUUID().toString(), null)); + + return datas; } /** @@ -582,5 +630,4 @@ public class Contact { list.add(add); return add.length(); } - }