1 package be
.nikiroo
.jvcard
.parsers
;
3 import java
.util
.Iterator
;
4 import java
.util
.LinkedList
;
7 import be
.nikiroo
.jvcard
.Card
;
8 import be
.nikiroo
.jvcard
.Contact
;
9 import be
.nikiroo
.jvcard
.Data
;
10 import be
.nikiroo
.jvcard
.TypeInfo
;
12 public class Vcard21Parser
{
13 public static List
<Contact
> parse(Iterable
<String
> textData
) {
14 Iterator
<String
> lines
= textData
.iterator();
15 List
<Contact
> contacts
= new LinkedList
<Contact
>();
16 List
<Data
> datas
= null;
18 String nextRawLine
= null;
19 if (lines
.hasNext()) {
20 nextRawLine
= lines
.next();
21 while (lines
.hasNext() && isContinuation(nextRawLine
)) {
22 // BAD INPUT FILE. IGNORE.
24 .println("VCARD Parser warning: CONTINUATION line seen before any data line");
25 nextRawLine
= lines
.next();
29 while (nextRawLine
!= null) {
30 StringBuilder rawLine
= new StringBuilder(nextRawLine
.trim());
32 nextRawLine
= lines
.next();
36 while (isContinuation(nextRawLine
)) {
37 rawLine
.append(nextRawLine
.trim());
39 nextRawLine
= lines
.next();
44 String line
= rawLine
.toString();
45 if (line
.equals("BEGIN:VCARD")) {
46 datas
= new LinkedList
<Data
>();
47 } else if (line
.equals("END:VCARD")) {
49 // BAD INPUT FILE. IGNORE.
51 .println("VCARD Parser warning: END:VCARD seen before any VCARD:BEGIN");
53 contacts
.add(new Contact(datas
));
57 // BAD INPUT FILE. IGNORE.
59 .println("VCARD Parser warning: data seen before any VCARD:BEGIN");
61 List
<TypeInfo
> types
= new LinkedList
<TypeInfo
>();
66 if (line
.contains(":")) {
67 int colIndex
= line
.indexOf(':');
68 String rest
= line
.substring(0, colIndex
);
69 value
= line
.substring(colIndex
+ 1);
71 if (rest
.contains(";")) {
72 String tab
[] = rest
.split(";");
75 for (int i
= 1; i
< tab
.length
; i
++) {
76 if (tab
[i
].contains("=")) {
77 int equIndex
= tab
[i
].indexOf('=');
79 .substring(0, equIndex
);
80 String tvalue
= tab
[i
]
81 .substring(equIndex
+ 1);
82 types
.add(new TypeInfo(tname
, tvalue
));
84 types
.add(new TypeInfo(tab
[i
], ""));
94 if (name
.contains(".")) {
95 int dotIndex
= name
.indexOf('.');
96 group
= name
.substring(0, dotIndex
);
97 name
= name
.substring(dotIndex
+ 1);
100 datas
.add(new Data(types
, name
, value
, group
));
109 public static String
toString(Contact contact
, int startingBKey
) {
110 StringBuilder builder
= new StringBuilder();
112 builder
.append("BEGIN:VCARD");
113 builder
.append("\r\n");
114 builder
.append("VERSION:2.1");
115 builder
.append("\r\n");
116 for (Data data
: contact
) {
117 StringBuilder dataBuilder
= new StringBuilder();
118 if (data
.getGroup() != null && !data
.getGroup().trim().equals("")) {
119 dataBuilder
.append(data
.getGroup().trim());
120 dataBuilder
.append('.');
122 dataBuilder
.append(data
.getName());
123 for (TypeInfo type
: data
) {
124 dataBuilder
.append(';');
125 dataBuilder
.append(type
.getName());
126 if (type
.getValue() != null
127 && !type
.getValue().trim().equals("")) {
128 dataBuilder
.append('=');
129 dataBuilder
.append(type
.getValue());
132 dataBuilder
.append(':');
135 dataBuilder
.append(data
.getValue());
137 // RFC says: Content lines SHOULD be folded to a maximum width of 75
138 // octets -> since it is SHOULD, we will just cut it as 74/75 chars
139 // depending if the last one fits in one char (note: chars != octet)
140 boolean continuation
= false;
141 while (dataBuilder
.length() > 0) {
144 stop
--; // the space takes 1
145 if (dataBuilder
.length() > stop
) {
146 char car
= dataBuilder
.charAt(stop
- 1);
147 // RFC forbids cutting a character in 2
148 if (Character
.isHighSurrogate(car
)) {
153 stop
= Math
.min(stop
, dataBuilder
.length());
156 builder
.append(dataBuilder
, 0, stop
);
157 builder
.append("\r\n");
158 dataBuilder
.delete(0, stop
);
163 builder
.append("END:VCARD");
164 builder
.append("\r\n");
166 return builder
.toString();
169 public static String
toString(Card card
) {
170 StringBuilder builder
= new StringBuilder();
172 for (Contact contact
: card
) {
173 builder
.append(toString(contact
, -1));
176 builder
.append("\r\n");
178 return builder
.toString();
182 * Check if the given line is a continuation line or not.
187 * @return TRUE if the line is a continuation line
189 private static boolean isContinuation(String line
) {
190 if (line
!= null && line
.length() > 0)
191 return (line
.charAt(0) == ' ' || line
.charAt(0) == '\t');