553ca7680f486f34b1a38e9753dff0b4f06edb99
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
;
10 import be
.nikiroo
.jvcard
.tui
.StringUtils
;
13 * A contact is the information that represent a contact person or organisation.
18 public class Contact
{
19 private List
<Data
> datas
;
20 private int nextBKey
= 1;
21 private Map
<Integer
, Data
> binaries
;
22 private boolean dirty
;
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
) {
33 this.datas
= new LinkedList
<Data
>();
37 for (Data data
: content
) {
38 if (data
.getName().equals("N")) {
40 } else if (data
.getName().equals("FN")) {
44 if (!data
.getName().equals("VERSION")) {
51 datas
.add(new Data(null, "N", "", null));
54 datas
.add(new Data(null, "FN", "", null));
61 * Return the informations (note: this is the actual list, be careful).
63 * @return the list of data anout this contact
65 public List
<Data
> getContent() {
70 * Return the preferred Data field with the given name, or NULL if none.
73 * the name to look for
74 * @return the Data field, or NULL
76 public Data
getPreferredData(String name
) {
78 for (Data data
: getData(name
)) {
81 for (TypeInfo type
: data
.getTypes()) {
82 if (type
.getName().equals("TYPE")
83 && type
.getValue().equals("pref")) {
93 * Return the value of the preferred data field with this name, or NULL if
94 * none (you cannot differentiate a NULL value and no value).
97 * the name to look for
98 * @return the value (which can be NULL), or NULL
100 public String
getPreferredDataValue(String name
) {
101 Data data
= getPreferredData(name
);
102 if (data
!= null && data
.getValue() != null)
103 return data
.getValue().trim();
108 * Get the Data fields that share the given name.
111 * the name to ook for
112 * @return a list of Data fields with this name
114 public List
<Data
> getData(String name
) {
115 List
<Data
> found
= new LinkedList
<Data
>();
117 for (Data data
: datas
) {
118 if (data
.getName().equals(name
))
126 * Return a {@link String} representation of this contact.
129 * the {@link Format} to use
130 * @param startingBKey
131 * the starting BKey or -1 for no BKeys
132 * @return the {@link String} representation
134 public String
toString(Format format
, int startingBKey
) {
136 return Parser
.toString(this, format
, startingBKey
);
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: 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 * @return the {@link String} representation
158 public String
toString(String format
) {
159 return toString(format
, "|", null, -1, true, false);
163 * Return a {@link String} representation of this contact formated
164 * accordingly to the given format.
166 * The format is basically a list of field names separated by a pipe and
167 * optionally parametrised. The parameters allows you to:
169 * <li>@x: show only a present/not present info</li>
170 * <li>@n: limit the size to a fixed value 'n'</li>
171 * <li>@+: expand the size of this field as much as possible</li>
174 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
179 * the separator {@link String} to use between fields
181 * the {@link String} to use for left and right padding
183 * a fixed width or -1 for "as long as needed"
186 * allow Uniode or only ASCII characters
188 * @return the {@link String} representation
190 public String
toString(String format
, String separator
, String padding
,
191 int width
, boolean unicode
, boolean removeAccents
) {
192 StringBuilder builder
= new StringBuilder();
194 for (String str
: toStringArray(format
, separator
, padding
, width
,
199 return builder
.toString();
203 * Return a {@link String} representation of this contact formated
204 * accordingly to the given format, part by part.
206 * The format is basically a list of field names separated by a pipe and
207 * optionally parametrised. The parameters allows you to:
209 * <li>@x: show only a present/not present info</li>
210 * <li>@n: limit the size to a fixed value 'n'</li>
211 * <li>@+: expand the size of this field as much as possible</li>
214 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
219 * the separator {@link String} to use between fields
221 * the {@link String} to use for left and right padding
223 * a fixed width or -1 for "as long as needed"
226 * allow Uniode or only ASCII characters
228 * @return the {@link String} representation
230 public String
[] toStringArray(String format
, String separator
,
231 String padding
, int width
, boolean unicode
) {
233 int numOfFields
= format
.split("\\|").length
;
234 if (separator
!= null)
235 width
-= (numOfFields
- 1) * separator
.length();
237 width
-= (numOfFields
) * (2 * padding
.length());
243 List
<String
> str
= new LinkedList
<String
>();
245 boolean first
= true;
246 for (String s
: toStringArray(format
, width
, unicode
)) {
252 str
.add(padding
+ s
+ padding
);
259 return str
.toArray(new String
[] {});
263 * Return a {@link String} representation of this contact formated
264 * accordingly to the given format, part by part.
266 * The format is basically a list of field names separated by a pipe and
267 * optionally parametrised. The parameters allows you to:
269 * <li>@x: show only a present/not present info</li>
270 * <li>@n: limit the size to a fixed value 'n'</li>
271 * <li>@+: expand the size of this field as much as possible</li>
274 * Example: "N@10|FN@20|NICK@+|PHOTO@x"
279 * a fixed width or -1 for "as long as needed"
281 * allow Uniode or only ASCII characters
283 * @return the {@link String} representation
285 public String
[] toStringArray(String format
, int width
, boolean unicode
) {
286 List
<String
> str
= new LinkedList
<String
>();
288 String
[] formatFields
= format
.split("\\|");
289 String
[] values
= new String
[formatFields
.length
];
290 Boolean
[] expandedFields
= new Boolean
[formatFields
.length
];
291 Boolean
[] fixedsizeFields
= new Boolean
[formatFields
.length
];
292 int numOfFieldsToExpand
= 0;
296 for (int i
= 0; i
< formatFields
.length
; i
++) {
300 return str
.toArray(new String
[] {});
303 for (int i
= 0; i
< formatFields
.length
; i
++) {
304 String field
= formatFields
[i
];
307 boolean binary
= false;
308 boolean expand
= false;
310 if (field
.contains("@")) {
311 String
[] opts
= field
.split("@");
314 for (int io
= 1; io
< opts
.length
; io
++) {
315 String opt
= opts
[io
];
316 if (opt
.equals("x")) {
318 } else if (opt
.equals("+")) {
320 numOfFieldsToExpand
++;
323 size
= Integer
.parseInt(opt
);
324 } catch (Exception e
) {
330 String value
= getPreferredDataValue(field
);
334 value
= StringUtils
.sanitize(value
, unicode
);
338 value
= StringUtils
.padString(value
, size
);
341 expandedFields
[i
] = expand
;
342 fixedsizeFields
[i
] = (size
> -1);
345 if (value
!= null && !value
.equals(""))
352 totalSize
+= value
.length();
356 if (width
> -1 && totalSize
> width
) {
357 int toDo
= totalSize
- width
;
358 for (int i
= fixedsizeFields
.length
- 1; toDo
> 0 && i
>= 0; i
--) {
359 if (!fixedsizeFields
[i
]) {
360 int valueLength
= values
[i
].length();
361 if (valueLength
> 0) {
362 if (valueLength
>= toDo
) {
363 values
[i
] = values
[i
].substring(0, valueLength
374 totalSize
= width
+ toDo
;
377 if (width
> -1 && numOfFieldsToExpand
> 0) {
378 int availablePadding
= width
- totalSize
;
380 if (availablePadding
> 0) {
381 int padPerItem
= availablePadding
/ numOfFieldsToExpand
;
382 int remainder
= availablePadding
% numOfFieldsToExpand
;
384 for (int i
= 0; i
< values
.length
; i
++) {
385 if (expandedFields
[i
]) {
387 values
[i
] = values
[i
]
388 + StringUtils
.padString("", remainder
);
391 if (padPerItem
> 0) {
392 values
[i
] = values
[i
]
393 + StringUtils
.padString("", padPerItem
);
403 for (int i
= 0; i
< values
.length
; i
++) {
404 currentSize
+= addToList(str
, values
[i
], currentSize
, width
);
407 return str
.toArray(new String
[] {});
411 * Add a {@link String} to the given {@link List}, but make sure it does not
412 * exceed the maximum size, and truncate it if needed to fit.
420 static private int addToList(List
<String
> list
, String add
,
421 int currentSize
, int maxSize
) {
422 if (add
== null || add
.length() == 0) {
429 if (currentSize
< maxSize
) {
430 if (currentSize
+ add
.length() >= maxSize
) {
431 add
= add
.substring(0, maxSize
- currentSize
);
443 * Return a {@link String} representation of this contact, in vCard 2.1,
446 * @return the {@link String} representation
448 public String
toString() {
449 return toString(Format
.VCard21
, -1);
453 * Update the information from this contact with the information in the
454 * given contact. Non present fields will be removed, new fields will be
455 * added, BKey'ed fields will be completed with the binary information known
459 * the contact with the newer information and optional BKeys
461 public void updateFrom(Contact vc
) {
464 List
<Data
> newDatas
= new LinkedList
<Data
>(vc
.datas
);
465 for (int i
= 0; i
< newDatas
.size(); i
++) {
466 Data data
= newDatas
.get(i
);
467 int bkey
= Parser
.getBKey(data
);
469 if (binaries
.containsKey(bkey
)) {
470 newDatas
.set(i
, binaries
.get(bkey
));
475 this.datas
= newDatas
;
476 this.nextBKey
= vc
.nextBKey
;
483 * Mark all the binary fields with a BKey number.
486 * force the marking, and reset all the numbers.
488 protected void updateBKeys(boolean force
) {
490 binaries
= new HashMap
<Integer
, Data
>();
494 if (binaries
== null) {
495 binaries
= new HashMap
<Integer
, Data
>();
498 for (Data data
: datas
) {
499 if (data
.isBinary() && (data
.getB64Key() <= 0 || force
)) {
500 binaries
.put(nextBKey
, data
);
501 data
.resetB64Key(nextBKey
++);
506 public boolean isDirty() {
511 * Notify that this element has unsaved changes, and notify its parent of
514 protected void setDirty() {
516 if (this.parent
!= null)
517 this.parent
.setDirty();
520 void setParent(Card parent
) {
521 this.parent
= parent
;
522 for (Data data
: datas
) {
523 data
.setParent(this);
528 * Delete this {@link Contact} from its parent {@link Card} if any.
530 * @return TRUE in case of success
532 public boolean delete() {
533 if (parent
!= null) {
534 List
<Contact
> list
= parent
.getContactsList();
535 for (int i
= 0; i
< list
.size(); i
++) {
536 if (list
.get(i
) == this) {
548 * Notify this element <i>and all its descendants</i> that it is in pristine
549 * state (as opposed to dirty).
553 for (Data data
: datas
) {