08c2a515e563cdfb39aadc7ab84795b1ec1153c6
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
;
10 import java
.util
.UUID
;
12 import be
.nikiroo
.jvcard
.parsers
.Format
;
13 import be
.nikiroo
.jvcard
.parsers
.Parser
;
14 import be
.nikiroo
.jvcard
.resources
.StringUtils
;
17 * A contact is the information that represent a contact person or organisation.
22 public class Contact
extends BaseClass
<Data
> {
23 private int nextBKey
= 1;
24 private Map
<Integer
, Data
> binaries
;
27 * Create a new Contact from the given information. Note that the BKeys data
31 * the information about the contact
33 public Contact(List
<Data
> content
) {
39 * Return the preferred Data field with the given name, or NULL if none.
42 * the name to look for
43 * @return the Data field, or NULL
45 public Data
getPreferredData(String name
) {
47 for (Data data
: getData(name
)) {
50 for (int index
= 0; index
< data
.size(); index
++) {
51 TypeInfo type
= data
.get(index
);
52 if (type
.getName().equals("TYPE")
53 && type
.getValue().equals("pref")) {
63 * Return the value of the preferred data field with this name, or NULL if
64 * none (you cannot differentiate a NULL value and no value).
67 * the name to look for
68 * @return the value (which can be NULL), or NULL
70 public String
getPreferredDataValue(String name
) {
71 Data data
= getPreferredData(name
);
72 if (data
!= null && data
.getValue() != null)
73 return data
.getValue().trim();
78 * Get the Data fields that share the given name.
82 * @return a list of Data fields with this name
84 public List
<Data
> getData(String name
) {
85 List
<Data
> found
= new LinkedList
<Data
>();
87 for (Data data
: this) {
88 if (data
.getName().equals(name
))
96 * Return a {@link String} representation of this contact.
99 * the {@link Format} to use
100 * @param startingBKey
101 * the starting BKey or -1 for no BKeys
102 * @return the {@link String} representation
104 public String
toString(Format format
, int startingBKey
) {
107 StringBuilder builder
= new StringBuilder();
108 for (String line
: Parser
.toStrings(this, format
, startingBKey
)) {
109 builder
.append(line
);
110 builder
.append("\r\n");
113 return builder
.toString();
117 * Return a {@link String} representation of this contact formated
118 * accordingly to the given format.
120 * The format is basically a list of field names separated by a pipe and
121 * optionally parametrised. The parameters allows you to:
123 * <li>@x: show only a present/not present info</li>
124 * <li>@n: limit the size to a fixed value 'n'</li>
125 * <li>@+: expand the size of this field as much as possible</li>
128 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
133 * @return the {@link String} representation
135 public String
toString(String format
) {
136 return toString(format
, "|", null, -1, true, false);
140 * Return a {@link String} representation of this contact formated
141 * accordingly to the given format.
143 * The format is basically a list of field names separated by a pipe and
144 * optionally parametrised. The parameters allows you to:
146 * <li>@x: (the 'x' is the letter 'x') show only a present/not present info</li>
147 * <li>@n: limit the size to a fixed value 'n'</li>
148 * <li>@+: expand the size of this field as much as possible</li>
151 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
156 * the separator {@link String} to use between fields
158 * the {@link String} to use for left and right padding
160 * a fixed width or -1 for "as long as needed"
163 * allow Uniode or only ASCII characters
165 * @return the {@link String} representation
167 public String
toString(String format
, String separator
, String padding
,
168 int width
, boolean unicode
, boolean removeAccents
) {
169 StringBuilder builder
= new StringBuilder();
171 for (String str
: toStringArray(format
, separator
, padding
, width
,
176 return builder
.toString();
180 * Return a {@link String} representation of this contact formated
181 * accordingly to the given format, part by part.
183 * The format is basically a list of field names separated by a pipe and
184 * optionally parametrised. The parameters allows you to:
186 * <li>@x: show only a present/not present info</li>
187 * <li>@n: limit the size to a fixed value 'n'</li>
188 * <li>@+: expand the size of this field as much as possible</li>
191 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
196 * the separator {@link String} to use between fields
198 * the {@link String} to use for left and right padding
200 * a fixed width or -1 for "as long as needed"
203 * allow Uniode or only ASCII characters
205 * @return the {@link String} representation
207 public String
[] toStringArray(String format
, String separator
,
208 String padding
, int width
, boolean unicode
) {
210 int numOfFields
= format
.split("\\|").length
;
211 if (separator
!= null)
212 width
-= (numOfFields
- 1) * separator
.length();
214 width
-= (numOfFields
) * (2 * padding
.length());
220 List
<String
> str
= new LinkedList
<String
>();
222 boolean first
= true;
223 for (String s
: toStringArray(format
, width
, unicode
)) {
229 str
.add(padding
+ s
+ padding
);
236 return str
.toArray(new String
[] {});
240 * Return a {@link String} representation of this contact formated
241 * accordingly to the given format, part by part.
243 * The format is basically a list of field names separated by a pipe and
244 * optionally parametrised. The parameters allows you to:
246 * <li>@x: show only a present/not present info</li>
247 * <li>@n: limit the size to a fixed value 'n'</li>
248 * <li>@+: expand the size of this field as much as possible</li>
251 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
256 * a fixed width or -1 for "as long as needed"
258 * allow Uniode or only ASCII characters
260 * @return the {@link String} representation
262 public String
[] toStringArray(String format
, int width
, boolean unicode
) {
263 List
<String
> str
= new LinkedList
<String
>();
265 String
[] formatFields
= format
.split("\\|");
266 String
[] values
= new String
[formatFields
.length
];
267 Boolean
[] expandedFields
= new Boolean
[formatFields
.length
];
268 Boolean
[] fixedsizeFields
= new Boolean
[formatFields
.length
];
269 int numOfFieldsToExpand
= 0;
273 for (int i
= 0; i
< formatFields
.length
; i
++) {
277 return str
.toArray(new String
[] {});
280 for (int i
= 0; i
< formatFields
.length
; i
++) {
281 String field
= formatFields
[i
];
284 boolean binary
= false;
285 boolean expand
= false;
287 if (field
.contains("@")) {
288 String
[] opts
= field
.split("@");
291 for (int io
= 1; io
< opts
.length
; io
++) {
292 String opt
= opts
[io
];
293 if (opt
.equals("x")) {
295 } else if (opt
.equals("+")) {
297 numOfFieldsToExpand
++;
300 size
= Integer
.parseInt(opt
);
301 } catch (Exception e
) {
307 String value
= getPreferredDataValue(field
);
311 value
= StringUtils
.sanitize(value
, unicode
);
315 value
= StringUtils
.padString(value
, size
);
318 expandedFields
[i
] = expand
;
319 fixedsizeFields
[i
] = (size
> -1);
322 if (value
!= null && !value
.equals(""))
329 totalSize
+= value
.length();
333 if (width
> -1 && totalSize
> width
) {
334 int toDo
= totalSize
- width
;
335 for (int i
= fixedsizeFields
.length
- 1; toDo
> 0 && i
>= 0; i
--) {
336 if (!fixedsizeFields
[i
]) {
337 int valueLength
= values
[i
].length();
338 if (valueLength
> 0) {
339 if (valueLength
>= toDo
) {
340 values
[i
] = values
[i
].substring(0, valueLength
351 totalSize
= width
+ toDo
;
354 if (width
> -1 && numOfFieldsToExpand
> 0) {
355 int availablePadding
= width
- totalSize
;
357 if (availablePadding
> 0) {
358 int padPerItem
= availablePadding
/ numOfFieldsToExpand
;
359 int remainder
= availablePadding
% numOfFieldsToExpand
;
361 for (int i
= 0; i
< values
.length
; i
++) {
362 if (expandedFields
[i
]) {
364 values
[i
] = values
[i
]
365 + StringUtils
.padString("", remainder
);
368 if (padPerItem
> 0) {
369 values
[i
] = values
[i
]
370 + StringUtils
.padString("", padPerItem
);
380 for (int i
= 0; i
< values
.length
; i
++) {
381 currentSize
+= addToList(str
, values
[i
], currentSize
, width
);
384 return str
.toArray(new String
[] {});
388 * Update the information from this contact with the information in the
389 * given contact. Non present fields will be removed, new fields will be
390 * added, BKey'ed fields will be completed with the binary information known
394 * the contact with the newer information and optional BKeys
396 public void updateFrom(Contact vc
) {
399 List
<Data
> newDatas
= new LinkedList
<Data
>(vc
);
400 for (int i
= 0; i
< newDatas
.size(); i
++) {
401 Data data
= newDatas
.get(i
);
402 int bkey
= Parser
.getBKey(data
);
404 if (binaries
.containsKey(bkey
)) {
405 newDatas
.set(i
, binaries
.get(bkey
));
410 replaceListContent(newDatas
);
411 this.nextBKey
= vc
.nextBKey
;
415 public String
getId() {
416 return "" + getPreferredDataValue("UID");
420 public String
getState() {
425 * Return a {@link String} representation of this contact, in vCard 2.1,
428 * @return the {@link String} representation
431 public String
toString() {
432 return toString(Format
.VCard21
, -1);
436 * Mark all the binary fields with a BKey number.
439 * force the marking, and reset all the numbers.
441 protected void updateBKeys(boolean force
) {
443 binaries
= new HashMap
<Integer
, Data
>();
447 if (binaries
== null) {
448 binaries
= new HashMap
<Integer
, Data
>();
451 for (Data data
: this) {
452 if (data
.isBinary() && (data
.getB64Key() <= 0 || force
)) {
453 binaries
.put(nextBKey
, data
);
454 data
.resetB64Key(nextBKey
++);
460 * Load the data from the given {@link File} under the given {@link Format}.
463 * the {@link File} to load from
465 * the {@link Format} to load as
467 * @return the list of elements
468 * @throws IOException
469 * in case of IO error
471 static private List
<Data
> load(List
<Data
> content
) {
472 List
<Data
> datas
= new ArrayList
<Data
>();
477 if (content
!= null) {
478 for (Data data
: content
) {
479 if (data
.getName().equals("N")) {
481 } else if (data
.getName().equals("FN")) {
483 } else if (data
.getName().equals("UID")) {
487 if (!data
.getName().equals("VERSION")) {
494 if (!n
) // required since vCard 3.0, supported in 2.1
495 datas
.add(new Data(null, "N", "", null));
496 if (!fn
) // not required anymore but still supported in 4.0
497 datas
.add(new Data(null, "FN", "", null));
498 if (!uid
) // supported by vCard, required by this program
499 datas
.add(new Data(null, "UID", UUID
.randomUUID().toString(), null));
505 * Add a {@link String} to the given {@link List}, but make sure it does not
506 * exceed the maximum size, and truncate it if needed to fit.
514 static private int addToList(List
<String
> list
, String add
,
515 int currentSize
, int maxSize
) {
516 if (add
== null || add
.length() == 0) {
523 if (currentSize
< maxSize
) {
524 if (currentSize
+ add
.length() >= maxSize
) {
525 add
= add
.substring(0, maxSize
- currentSize
);