948fd8447965a87d06663db2b4513d6bea022d92
1 package be
.nikiroo
.jvcard
;
4 import java
.io
.IOException
;
5 import java
.util
.ArrayList
;
6 import java
.util
.HashMap
;
7 import java
.util
.LinkedList
;
11 import be
.nikiroo
.jvcard
.parsers
.Format
;
12 import be
.nikiroo
.jvcard
.parsers
.Parser
;
13 import be
.nikiroo
.jvcard
.tui
.StringUtils
;
16 * A contact is the information that represent a contact person or organisation.
21 public class Contact
extends BaseClass
<Data
> {
22 private int nextBKey
= 1;
23 private Map
<Integer
, Data
> binaries
;
26 * Create a new Contact from the given information. Note that the BKeys data
30 * the information about the contact
32 public Contact(List
<Data
> content
) {
38 * Return the preferred Data field with the given name, or NULL if none.
41 * the name to look for
42 * @return the Data field, or NULL
44 public Data
getPreferredData(String name
) {
46 for (Data data
: getData(name
)) {
49 for (int index
= 0; index
< data
.size(); index
++) {
50 TypeInfo type
= data
.get(index
);
51 if (type
.getName().equals("TYPE")
52 && type
.getValue().equals("pref")) {
62 * Return the value of the preferred data field with this name, or NULL if
63 * none (you cannot differentiate a NULL value and no value).
66 * the name to look for
67 * @return the value (which can be NULL), or NULL
69 public String
getPreferredDataValue(String name
) {
70 Data data
= getPreferredData(name
);
71 if (data
!= null && data
.getValue() != null)
72 return data
.getValue().trim();
77 * Get the Data fields that share the given name.
81 * @return a list of Data fields with this name
83 public List
<Data
> getData(String name
) {
84 List
<Data
> found
= new LinkedList
<Data
>();
86 for (Data data
: this) {
87 if (data
.getName().equals(name
))
95 * Return a {@link String} representation of this contact.
98 * the {@link Format} to use
100 * the starting BKey or -1 for no BKeys
101 * @return the {@link String} representation
103 public String
toString(Format format
, int startingBKey
) {
105 return Parser
.toString(this, format
, startingBKey
);
109 * Return a {@link String} representation of this contact formated
110 * accordingly to the given format.
112 * The format is basically a list of field names separated by a pipe and
113 * optionally parametrised. The parameters allows you to:
115 * <li>@x: show only a present/not present info</li>
116 * <li>@n: limit the size to a fixed value 'n'</li>
117 * <li>@+: expand the size of this field as much as possible</li>
120 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
125 * @return the {@link String} representation
127 public String
toString(String format
) {
128 return toString(format
, "|", null, -1, true, false);
132 * Return a {@link String} representation of this contact formated
133 * accordingly to the given format.
135 * The format is basically a list of field names separated by a pipe and
136 * optionally parametrised. The parameters allows you to:
138 * <li>@x: (the 'x' is the letter 'x') show only a present/not present info</li>
139 * <li>@n: limit the size to a fixed value 'n'</li>
140 * <li>@+: expand the size of this field as much as possible</li>
143 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
148 * the separator {@link String} to use between fields
150 * the {@link String} to use for left and right padding
152 * a fixed width or -1 for "as long as needed"
155 * allow Uniode or only ASCII characters
157 * @return the {@link String} representation
159 public String
toString(String format
, String separator
, String padding
,
160 int width
, boolean unicode
, boolean removeAccents
) {
161 StringBuilder builder
= new StringBuilder();
163 for (String str
: toStringArray(format
, separator
, padding
, width
,
168 return builder
.toString();
172 * Return a {@link String} representation of this contact formated
173 * accordingly to the given format, part by part.
175 * The format is basically a list of field names separated by a pipe and
176 * optionally parametrised. The parameters allows you to:
178 * <li>@x: show only a present/not present info</li>
179 * <li>@n: limit the size to a fixed value 'n'</li>
180 * <li>@+: expand the size of this field as much as possible</li>
183 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
188 * the separator {@link String} to use between fields
190 * the {@link String} to use for left and right padding
192 * a fixed width or -1 for "as long as needed"
195 * allow Uniode or only ASCII characters
197 * @return the {@link String} representation
199 public String
[] toStringArray(String format
, String separator
,
200 String padding
, int width
, boolean unicode
) {
202 int numOfFields
= format
.split("\\|").length
;
203 if (separator
!= null)
204 width
-= (numOfFields
- 1) * separator
.length();
206 width
-= (numOfFields
) * (2 * padding
.length());
212 List
<String
> str
= new LinkedList
<String
>();
214 boolean first
= true;
215 for (String s
: toStringArray(format
, width
, unicode
)) {
221 str
.add(padding
+ s
+ padding
);
228 return str
.toArray(new String
[] {});
232 * Return a {@link String} representation of this contact formated
233 * accordingly to the given format, part by part.
235 * The format is basically a list of field names separated by a pipe and
236 * optionally parametrised. The parameters allows you to:
238 * <li>@x: show only a present/not present info</li>
239 * <li>@n: limit the size to a fixed value 'n'</li>
240 * <li>@+: expand the size of this field as much as possible</li>
243 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
248 * a fixed width or -1 for "as long as needed"
250 * allow Uniode or only ASCII characters
252 * @return the {@link String} representation
254 public String
[] toStringArray(String format
, int width
, boolean unicode
) {
255 List
<String
> str
= new LinkedList
<String
>();
257 String
[] formatFields
= format
.split("\\|");
258 String
[] values
= new String
[formatFields
.length
];
259 Boolean
[] expandedFields
= new Boolean
[formatFields
.length
];
260 Boolean
[] fixedsizeFields
= new Boolean
[formatFields
.length
];
261 int numOfFieldsToExpand
= 0;
265 for (int i
= 0; i
< formatFields
.length
; i
++) {
269 return str
.toArray(new String
[] {});
272 for (int i
= 0; i
< formatFields
.length
; i
++) {
273 String field
= formatFields
[i
];
276 boolean binary
= false;
277 boolean expand
= false;
279 if (field
.contains("@")) {
280 String
[] opts
= field
.split("@");
283 for (int io
= 1; io
< opts
.length
; io
++) {
284 String opt
= opts
[io
];
285 if (opt
.equals("x")) {
287 } else if (opt
.equals("+")) {
289 numOfFieldsToExpand
++;
292 size
= Integer
.parseInt(opt
);
293 } catch (Exception e
) {
299 String value
= getPreferredDataValue(field
);
303 value
= StringUtils
.sanitize(value
, unicode
);
307 value
= StringUtils
.padString(value
, size
);
310 expandedFields
[i
] = expand
;
311 fixedsizeFields
[i
] = (size
> -1);
314 if (value
!= null && !value
.equals(""))
321 totalSize
+= value
.length();
325 if (width
> -1 && totalSize
> width
) {
326 int toDo
= totalSize
- width
;
327 for (int i
= fixedsizeFields
.length
- 1; toDo
> 0 && i
>= 0; i
--) {
328 if (!fixedsizeFields
[i
]) {
329 int valueLength
= values
[i
].length();
330 if (valueLength
> 0) {
331 if (valueLength
>= toDo
) {
332 values
[i
] = values
[i
].substring(0, valueLength
343 totalSize
= width
+ toDo
;
346 if (width
> -1 && numOfFieldsToExpand
> 0) {
347 int availablePadding
= width
- totalSize
;
349 if (availablePadding
> 0) {
350 int padPerItem
= availablePadding
/ numOfFieldsToExpand
;
351 int remainder
= availablePadding
% numOfFieldsToExpand
;
353 for (int i
= 0; i
< values
.length
; i
++) {
354 if (expandedFields
[i
]) {
356 values
[i
] = values
[i
]
357 + StringUtils
.padString("", remainder
);
360 if (padPerItem
> 0) {
361 values
[i
] = values
[i
]
362 + StringUtils
.padString("", padPerItem
);
372 for (int i
= 0; i
< values
.length
; i
++) {
373 currentSize
+= addToList(str
, values
[i
], currentSize
, width
);
376 return str
.toArray(new String
[] {});
380 * Update the information from this contact with the information in the
381 * given contact. Non present fields will be removed, new fields will be
382 * added, BKey'ed fields will be completed with the binary information known
386 * the contact with the newer information and optional BKeys
388 public void updateFrom(Contact vc
) {
391 List
<Data
> newDatas
= new LinkedList
<Data
>(vc
);
392 for (int i
= 0; i
< newDatas
.size(); i
++) {
393 Data data
= newDatas
.get(i
);
394 int bkey
= Parser
.getBKey(data
);
396 if (binaries
.containsKey(bkey
)) {
397 newDatas
.set(i
, binaries
.get(bkey
));
402 replaceListContent(newDatas
);
403 this.nextBKey
= vc
.nextBKey
;
407 * Return a {@link String} representation of this contact, in vCard 2.1,
410 * @return the {@link String} representation
413 public String
toString() {
414 return toString(Format
.VCard21
, -1);
418 * Mark all the binary fields with a BKey number.
421 * force the marking, and reset all the numbers.
423 protected void updateBKeys(boolean force
) {
425 binaries
= new HashMap
<Integer
, Data
>();
429 if (binaries
== null) {
430 binaries
= new HashMap
<Integer
, Data
>();
433 for (Data data
: this) {
434 if (data
.isBinary() && (data
.getB64Key() <= 0 || force
)) {
435 binaries
.put(nextBKey
, data
);
436 data
.resetB64Key(nextBKey
++);
442 * Load the data from the given {@link File} under the given {@link Format}.
445 * the {@link File} to load from
447 * the {@link Format} to load as
449 * @return the list of elements
450 * @throws IOException
451 * in case of IO error
453 static private List
<Data
> load(List
<Data
> content
) {
454 List
<Data
> datas
= new ArrayList
<Data
>();
458 if (content
!= null) {
459 for (Data data
: content
) {
460 if (data
.getName().equals("N")) {
462 } else if (data
.getName().equals("FN")) {
466 if (!data
.getName().equals("VERSION")) {
474 datas
.add(new Data(null, "N", "", null));
477 datas
.add(new Data(null, "FN", "", null));
484 * Add a {@link String} to the given {@link List}, but make sure it does not
485 * exceed the maximum size, and truncate it if needed to fit.
493 static private int addToList(List
<String
> list
, String add
,
494 int currentSize
, int maxSize
) {
495 if (add
== null || add
.length() == 0) {
502 if (currentSize
< maxSize
) {
503 if (currentSize
+ add
.length() >= maxSize
) {
504 add
= add
.substring(0, maxSize
- currentSize
);