21ed69b86f66e532adee8bc06a5bf317d00f9021
1 package be
.nikiroo
.jvcard
;
3 import java
.util
.HashMap
;
4 import java
.util
.LinkedList
;
8 import be
.nikiroo
.jvcard
.parsers
.Format
;
9 import be
.nikiroo
.jvcard
.parsers
.Parser
;
12 * A contact is the information that represent a contact person or organisation.
17 public class Contact
{
18 private List
<Data
> datas
;
19 private int nextBKey
= 1;
20 private Map
<Integer
, Data
> binaries
;
21 private boolean dirty
;
25 * Create a new Contact from the given information. Note that the BKeys data
29 * the information about the contact
31 public Contact(List
<Data
> content
) {
32 this.datas
= new LinkedList
<Data
>();
36 for (Data data
: content
) {
37 if (data
.getName().equals("N")) {
39 } else if (data
.getName().equals("FN")) {
43 if (!data
.getName().equals("VERSION")) {
50 datas
.add(new Data(null, "N", "", null));
53 datas
.add(new Data(null, "FN", "", null));
60 * Return the informations (note: this is the actual list, be careful).
62 * @return the list of data anout this contact
64 public List
<Data
> getContent() {
69 * Return the preferred Data field with the given name, or NULL if none.
72 * the name to look for
73 * @return the Data field, or NULL
75 public Data
getPreferredData(String name
) {
77 for (Data data
: getData(name
)) {
80 for (TypeInfo type
: data
.getTypes()) {
81 if (type
.getName().equals("TYPE")
82 && type
.getValue().equals("pref")) {
92 * Return the value of the preferred data field with this name, or NULL if
93 * none (you cannot differentiate a NULL value and no value).
96 * the name to look for
97 * @return the value (which can be NULL), or NULL
99 public String
getPreferredDataValue(String name
) {
100 Data data
= getPreferredData(name
);
101 if (data
!= null && data
.getValue() != null)
102 return data
.getValue().trim();
107 * Get the Data fields that share the given name.
110 * the name to ook for
111 * @return a list of Data fields with this name
113 public List
<Data
> getData(String name
) {
114 List
<Data
> found
= new LinkedList
<Data
>();
116 for (Data data
: datas
) {
117 if (data
.getName().equals(name
))
125 * Return a {@link String} representation of this contact.
128 * the {@link Format} to use
129 * @param startingBKey
130 * the starting BKey or -1 for no BKeys
131 * @return the {@link String} representation
133 public String
toString(Format format
, int startingBKey
) {
135 return Parser
.toString(this, format
, startingBKey
);
139 * Return a {@link String} representation of this contact formated
140 * accordingly to the given format.
142 * The format is basically a list of field names separated by a pipe and
143 * optionally parametrised. The parameters allows you to:
145 * <li>@x: show only a present/not present info</li>
146 * <li>@n: limit the size to a fixed value 'n'</li>
147 * <li>@+: expand the size of this field as much as possible</li>
150 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
155 * @return the {@link String} representation
157 public String
toString(String format
) {
158 return toString(format
, "|", null, -1);
162 * Return a {@link String} representation of this contact formated
163 * accordingly to the given format.
165 * The format is basically a list of field names separated by a pipe and
166 * optionally parametrised. The parameters allows you to:
168 * <li>@x: show only a present/not present info</li>
169 * <li>@n: limit the size to a fixed value 'n'</li>
170 * <li>@+: expand the size of this field as much as possible</li>
173 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
178 * the separator {@link String} to use between fields
180 * the {@link String} to use for left and right padding
182 * a fixed width or -1 for "as long as needed"
184 * @return the {@link String} representation
186 public String
toString(String format
, String separator
, String padding
,
188 StringBuilder builder
= new StringBuilder();
190 for (String str
: toStringArray(format
, separator
, padding
, width
)) {
194 return builder
.toString();
198 * Return a {@link String} representation of this contact formated
199 * accordingly to the given format, part by part.
201 * The format is basically a list of field names separated by a pipe and
202 * optionally parametrised. The parameters allows you to:
204 * <li>@x: show only a present/not present info</li>
205 * <li>@n: limit the size to a fixed value 'n'</li>
206 * <li>@+: expand the size of this field as much as possible</li>
209 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
214 * the separator {@link String} to use between fields
216 * the {@link String} to use for left and right padding
218 * a fixed width or -1 for "as long as needed"
220 * @return the {@link String} representation
222 public String
[] toStringArray(String format
, String separator
,
223 String padding
, int width
) {
225 int numOfFields
= format
.split("\\|").length
;
226 if (separator
!= null)
227 width
-= (numOfFields
- 1) * separator
.length();
229 width
-= (numOfFields
) * (2 * padding
.length());
235 List
<String
> str
= new LinkedList
<String
>();
237 boolean first
= true;
238 for (String s
: toStringArray(format
, width
)) {
244 str
.add(padding
+ s
+ padding
);
251 return str
.toArray(new String
[] {});
255 * Return a {@link String} representation of this contact formated
256 * accordingly to the given format, part by part.
258 * The format is basically a list of field names separated by a pipe and
259 * optionally parametrised. The parameters allows you to:
261 * <li>@x: show only a present/not present info</li>
262 * <li>@n: limit the size to a fixed value 'n'</li>
263 * <li>@+: expand the size of this field as much as possible</li>
266 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
271 * a fixed width or -1 for "as long as needed"
273 * @return the {@link String} representation
275 public String
[] toStringArray(String format
, int width
) {
276 List
<String
> str
= new LinkedList
<String
>();
278 String
[] formatFields
= format
.split("\\|");
279 String
[] values
= new String
[formatFields
.length
];
280 Boolean
[] expandedFields
= new Boolean
[formatFields
.length
];
281 Boolean
[] fixedsizeFields
= new Boolean
[formatFields
.length
];
282 int numOfFieldsToExpand
= 0;
286 for (int i
= 0; i
< formatFields
.length
; i
++) {
290 return str
.toArray(new String
[] {});
293 for (int i
= 0; i
< formatFields
.length
; i
++) {
294 String field
= formatFields
[i
];
297 boolean binary
= false;
298 boolean expand
= false;
300 if (field
.contains("@")) {
301 String
[] opts
= field
.split("@");
304 for (int io
= 1; io
< opts
.length
; io
++) {
305 String opt
= opts
[io
];
306 if (opt
.equals("x")) {
308 } else if (opt
.equals("+")) {
310 numOfFieldsToExpand
++;
313 size
= Integer
.parseInt(opt
);
314 } catch (Exception e
) {
320 String value
= getPreferredDataValue(field
);
325 value
= fixedString(value
, size
);
328 expandedFields
[i
] = expand
;
329 fixedsizeFields
[i
] = (size
> -1);
332 if (value
!= null && !value
.equals(""))
339 totalSize
+= value
.length();
343 if (width
> -1 && totalSize
> width
) {
344 int toDo
= totalSize
- width
;
345 for (int i
= fixedsizeFields
.length
- 1; toDo
> 0 && i
>= 0; i
--) {
346 if (!fixedsizeFields
[i
]) {
347 int valueLength
= values
[i
].length();
348 if (valueLength
> 0) {
349 if (valueLength
>= toDo
) {
350 values
[i
] = values
[i
].substring(0, valueLength
361 totalSize
= width
+ toDo
;
364 if (width
> -1 && numOfFieldsToExpand
> 0) {
365 int availablePadding
= width
- totalSize
;
367 if (availablePadding
> 0) {
368 int padPerItem
= availablePadding
/ numOfFieldsToExpand
;
369 int remainder
= availablePadding
% numOfFieldsToExpand
;
371 for (int i
= 0; i
< values
.length
; i
++) {
372 if (expandedFields
[i
]) {
374 values
[i
] = values
[i
] + fixedString("", remainder
);
377 if (padPerItem
> 0) {
378 values
[i
] = values
[i
] + fixedString("", padPerItem
);
388 for (int i
= 0; i
< values
.length
; i
++) {
389 currentSize
+= addToList(str
, values
[i
], currentSize
, width
);
392 return str
.toArray(new String
[] {});
396 * Fix the size of the given {@link String} either with space-padding or by
400 * the {@link String} to fix
402 * the size of the resulting {@link String}
404 * @return the fixed {@link String} of size <i>size</i>
406 static private String
fixedString(String string
, int size
) {
407 int length
= string
.length();
410 string
= string
.substring(0, size
);
411 } else if (length
< size
) {
413 + new String(new char[size
- length
]).replace('\0', ' ');
420 * Add a {@link String} to the given {@link List}, but make sure it does not
421 * exceed the maximum size, and truncate it if needed to fit.
429 static private int addToList(List
<String
> list
, String add
,
430 int currentSize
, int maxSize
) {
431 if (add
== null || add
.length() == 0) {
438 if (currentSize
< maxSize
) {
439 if (currentSize
+ add
.length() >= maxSize
) {
440 add
= add
.substring(0, maxSize
- currentSize
);
452 * Return a {@link String} representation of this contact, in vCard 2.1,
455 * @return the {@link String} representation
457 public String
toString() {
458 return toString(Format
.VCard21
, -1);
462 * Update the information from this contact with the information in the
463 * given contact. Non present fields will be removed, new fields will be
464 * added, BKey'ed fields will be completed with the binary information known
468 * the contact with the newer information and optional BKeys
470 public void updateFrom(Contact vc
) {
473 List
<Data
> newDatas
= new LinkedList
<Data
>(vc
.datas
);
474 for (int i
= 0; i
< newDatas
.size(); i
++) {
475 Data data
= newDatas
.get(i
);
476 int bkey
= Parser
.getBKey(data
);
478 if (binaries
.containsKey(bkey
)) {
479 newDatas
.set(i
, binaries
.get(bkey
));
484 this.datas
= newDatas
;
485 this.nextBKey
= vc
.nextBKey
;
492 * Mark all the binary fields with a BKey number.
495 * force the marking, and reset all the numbers.
497 protected void updateBKeys(boolean force
) {
499 binaries
= new HashMap
<Integer
, Data
>();
503 if (binaries
== null) {
504 binaries
= new HashMap
<Integer
, Data
>();
507 for (Data data
: datas
) {
508 if (data
.isBinary() && (data
.getB64Key() <= 0 || force
)) {
509 binaries
.put(nextBKey
, data
);
510 data
.resetB64Key(nextBKey
++);
515 public boolean isDirty() {
520 * Notify that this element has unsaved changes, and notify its parent of
523 protected void setDirty() {
525 if (this.parent
!= null)
526 this.parent
.setDirty();
529 public void setParent(Card parent
) {
530 this.parent
= parent
;
531 for (Data data
: datas
) {
532 data
.setParent(this);