X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2FContact.java;h=4b46eab8afa3dcc6aa840d8c89952d0c4ef8b3b5;hb=e3fe983485916e0f3dbae73394f9dd8271623639;hp=f2a8b01b5ac4be470383f2b29934fde412bbeca8;hpb=9c8baf0c360173b864683176c567757429c4fb12;p=jvcard.git
diff --git a/src/be/nikiroo/jvcard/Contact.java b/src/be/nikiroo/jvcard/Contact.java
index f2a8b01..4b46eab 100644
--- a/src/be/nikiroo/jvcard/Contact.java
+++ b/src/be/nikiroo/jvcard/Contact.java
@@ -1,12 +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.resources.StringUtils;
/**
* A contact is the information that represent a contact person or organisation.
@@ -14,12 +19,9 @@ import be.nikiroo.jvcard.parsers.Parser;
* @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
@@ -29,42 +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 informations (note: this is the actual list, be careful).
- *
- * @return the list of data anout this contact
- */
- public List getContent() {
- return datas;
- }
-
/**
* Return the preferred Data field with the given name, or NULL if none.
*
@@ -77,12 +47,9 @@ public class Contact {
for (Data data : getData(name)) {
if (first == null)
first = data;
- for (TypeInfo type : data.getTypes()) {
- if (type.getName().equals("TYPE")
- && type.getValue().equals("pref")) {
- return data;
- }
- }
+
+ if (data.isPreferred())
+ return data;
}
return first;
@@ -113,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);
}
@@ -132,13 +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 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
+ *
+ *
+ *
+ *
+ * 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, 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:
*
@@ -146,71 +164,169 @@ public class Contact {
* - @n: limit the size to a fixed value 'n'
* - @+: expand the size of this field as much as possible
*
+ *
*
- * Example: "N@10|FN@20|NICK@+|PHOTO@x"
+ *
+ * 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: "'Contact: |N@10|FN@20|NICK@+|PHOTO@x"
+ *
*
* @param format
* the format to use
* @param separator
* the separator {@link String} to use between fields
+ * @param padding
+ * 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
+ *
* @return the {@link String} representation
*/
- public String toString(String format, String separator, int width) {
+ public String toString(String format, String separator, String padding,
+ int width, boolean unicode, boolean removeAccents) {
StringBuilder builder = new StringBuilder();
- String[] formatFields = format.split("\\|");
- if (width > -1 && separator != null && separator.length() > 0
- && formatFields.length > 1) {
- int swidth = (formatFields.length - 1) * separator.length();
- if (swidth >= width) {
- int num = width / separator.length();
- int remainder = width % separator.length();
-
- if (remainder > 0)
- num++;
-
- while (builder.length() < width) {
- if (builder.length() + separator.length() <= width)
- builder.append(separator);
- else
- builder.append(separator
- .substring(0, (builder.length() + separator
- .length())
- - width));
- }
+ for (String str : toStringArray(format, separator, padding, width,
+ unicode)) {
+ builder.append(str);
+ }
- return builder.toString();
- }
+ return builder.toString();
+ }
- width -= swidth;
+ /**
+ * 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:
+ *
+ * - @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
+ *
+ *
+ *
+ *
+ * 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
+ * @param padding
+ * 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
+ *
+ * @return the {@link String} representation
+ */
+ public String[] toStringArray(String format, String separator,
+ String padding, int width, boolean unicode) {
+ if (width > -1) {
+ int numOfFields = format.split("\\|").length;
+ if (separator != null)
+ width -= (numOfFields - 1) * separator.length();
+ if (padding != null)
+ width -= (numOfFields) * (2 * padding.length());
+
+ if (width < 0)
+ width = 0;
}
- for (String str : toStringArray(format, width)) {
- builder.append(str);
+ List str = new LinkedList();
+
+ boolean first = true;
+ for (String s : toStringArray(format, width, unicode)) {
+ if (!first) {
+ str.add(separator);
+ }
+
+ if (padding != null)
+ str.add(padding + s + padding);
+ else
+ str.add(s);
+
+ first = false;
}
- return builder.toString();
+ return str.toArray(new String[] {});
}
/**
* 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. See {@link Contact#toString} for more
- * information about the format.
+ * optionally parametrised. 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
+ *
+ *
+ *
+ *
+ * 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 width
* 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) {
+ public String[] toStringArray(String format, int width, boolean unicode) {
List str = new LinkedList();
String[] formatFields = format.split("\\|");
@@ -221,6 +337,10 @@ public class Contact {
int totalSize = 0;
if (width == 0) {
+ for (int i = 0; i < formatFields.length; i++) {
+ str.add("");
+ }
+
return str.toArray(new String[] {});
}
@@ -230,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];
@@ -242,21 +365,52 @@ 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);
- if (value == null)
+ 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 {
+ value = StringUtils.sanitize(value, unicode);
+ }
if (size > -1) {
- value = fixedString(value, size);
+ value = StringUtils.padString(value, size);
}
expandedFields[i] = expand;
@@ -306,14 +460,12 @@ public class Contact {
if (expandedFields[i]) {
if (remainder > 0) {
values[i] = values[i]
- + new String(new char[remainder]).replace(
- '\0', ' ');
+ + StringUtils.padString("", remainder);
remainder = 0;
}
if (padPerItem > 0) {
values[i] = values[i]
- + new String(new char[padPerItem]).replace(
- '\0', ' ');
+ + StringUtils.padString("", padPerItem);
}
}
}
@@ -330,74 +482,6 @@ public class Contact {
return str.toArray(new String[] {});
}
- /**
- * Fix the size of the given {@link String} either with space-padding or by
- * shortening it.
- *
- * @param string
- * the {@link String} to fix
- * @param size
- * the size of the resulting {@link String}
- *
- * @return the fixed {@link String} of size size
- */
- static private String fixedString(String string, int size) {
- int length = string.length();
-
- if (length > size)
- string = string.substring(0, size);
- else if (length < size)
- string = string
- + new String(new char[size - length]).replace('\0', ' ');
-
- return string;
- }
-
- /**
- * 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.
- *
- * @param list
- * @param add
- * @param currentSize
- * @param maxSize
- * @return
- */
- static private int addToList(List list, String add,
- int currentSize, int maxSize) {
- if (add == null || add.length() == 0) {
- if (add != null)
- list.add(add);
- return 0;
- }
-
- if (maxSize > -1) {
- if (currentSize < maxSize) {
- if (currentSize + add.length() >= maxSize) {
- add = add.substring(0, maxSize - currentSize);
- }
- } else {
- add = "";
- }
- }
-
- if (add.length() > 0) {
- list.add(add);
- }
-
- return add.length();
- }
-
- /**
- * Return a {@link String} representation of this contact, in vCard 2.1,
- * without BKeys.
- *
- * @return the {@link String} representation
- */
- public String toString() {
- return toString(Format.VCard21, -1);
- }
-
/**
* Update the information from this contact with the information in the
* given contact. Non present fields will be removed, new fields will be
@@ -410,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);
@@ -421,11 +505,29 @@ public class Contact {
}
}
- this.datas = newDatas;
+ replaceListContent(newDatas);
this.nextBKey = vc.nextBKey;
+ }
+
+ @Override
+ public String getId() {
+ return "" + getPreferredDataValue("UID");
+ }
- setParent(parent);
- setDirty();
+ @Override
+ public String getState() {
+ return getId();
+ }
+
+ /**
+ * Return a {@link String} representation of this contact, in vCard 2.1,
+ * without BKeys.
+ *
+ * @return the {@link String} representation
+ */
+ @Override
+ public String toString() {
+ return toString(Format.VCard21, -1);
}
/**
@@ -444,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++);
@@ -452,24 +554,80 @@ public class Contact {
}
}
- public boolean isDirty() {
- return dirty;
+ /**
+ * 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
+ */
+ static private List load(List content) {
+ List datas = new ArrayList();
+
+ 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;
+ }
+
+ 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;
}
/**
- * Notify that this element has unsaved changes, and notify its parent of
- * the same if any.
+ * 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.
+ *
+ * @param list
+ * @param add
+ * @param currentSize
+ * @param maxSize
+ * @return
*/
- protected void setDirty() {
- this.dirty = true;
- if (this.parent != null)
- this.parent.setDirty();
- }
+ static private int addToList(List list, String add,
+ int currentSize, int maxSize) {
+ if (add == null || add.length() == 0) {
+ if (add != null)
+ list.add(add);
+ return 0;
+ }
- public void setParent(Card parent) {
- this.parent = parent;
- for (Data data : datas) {
- data.setParent(this);
+ if (maxSize > -1) {
+ if (currentSize < maxSize) {
+ if (currentSize + add.length() >= maxSize) {
+ add = add.substring(0, maxSize - currentSize);
+ }
+ } else {
+ add = "";
+ }
}
+
+ list.add(add);
+ return add.length();
}
}