1 package be
.nikiroo
.jvcard
.parsers
;
4 import java
.io
.FileWriter
;
5 import java
.io
.IOException
;
6 import java
.util
.Iterator
;
7 import java
.util
.LinkedList
;
10 import be
.nikiroo
.jvcard
.Card
;
11 import be
.nikiroo
.jvcard
.Contact
;
12 import be
.nikiroo
.jvcard
.Data
;
13 import be
.nikiroo
.jvcard
.TypeInfo
;
15 public class Vcard21Parser
{
17 * Load the given data from under the given {@link Format}.
20 * the input to load from
22 * the {@link Format} to load as
24 * @return the list of elements
26 public static List
<Contact
> parseContact(Iterable
<String
> textData
) {
27 Iterator
<String
> lines
= textData
.iterator();
28 List
<Contact
> contacts
= new LinkedList
<Contact
>();
29 List
<String
> datas
= null;
31 String nextRawLine
= null;
32 if (lines
.hasNext()) {
33 nextRawLine
= lines
.next();
34 while (lines
.hasNext() && isContinuation(nextRawLine
)) {
35 // BAD INPUT FILE. IGNORE.
37 .println("VCARD Parser warning: CONTINUATION line seen before any data line");
38 nextRawLine
= lines
.next();
42 while (nextRawLine
!= null) {
43 StringBuilder rawLine
= new StringBuilder(nextRawLine
.trim());
45 nextRawLine
= lines
.next();
49 while (isContinuation(nextRawLine
)) {
50 rawLine
.append(nextRawLine
.trim());
52 nextRawLine
= lines
.next();
57 String line
= rawLine
.toString();
58 if (line
.equals("BEGIN:VCARD")) {
59 datas
= new LinkedList
<String
>();
60 } else if (line
.equals("END:VCARD")) {
62 // BAD INPUT FILE. IGNORE.
64 .println("VCARD Parser warning: END:VCARD seen before any VCARD:BEGIN");
66 contacts
.add(new Contact(parseData(datas
)));
70 // BAD INPUT FILE. IGNORE.
72 .println("VCARD Parser warning: data seen before any VCARD:BEGIN");
83 * Load the given data from under the given {@link Format}.
86 * the input to load from
88 * the {@link Format} to load as
90 * @return the list of elements
92 public static List
<Data
> parseData(Iterable
<String
> textData
) {
93 List
<Data
> datas
= new LinkedList
<Data
>();
95 for (String line
: textData
) {
96 List
<TypeInfo
> types
= new LinkedList
<TypeInfo
>();
101 if (line
.contains(":")) {
102 int colIndex
= line
.indexOf(':');
103 String rest
= line
.substring(0, colIndex
);
104 value
= line
.substring(colIndex
+ 1);
106 if (rest
.contains(";")) {
107 String tab
[] = rest
.split(";");
110 for (int i
= 1; i
< tab
.length
; i
++) {
111 if (tab
[i
].contains("=")) {
112 int equIndex
= tab
[i
].indexOf('=');
113 String tname
= tab
[i
].substring(0, equIndex
);
114 String tvalue
= tab
[i
].substring(equIndex
+ 1);
115 types
.add(new TypeInfo(tname
, tvalue
));
117 types
.add(new TypeInfo(tab
[i
], ""));
127 if (name
.contains(".")) {
128 int dotIndex
= name
.indexOf('.');
129 group
= name
.substring(0, dotIndex
);
130 name
= name
.substring(dotIndex
+ 1);
133 datas
.add(new Data(types
, name
, value
, group
));
140 * Write the given {@link Card} in the {@link Appendable}.
143 * the {@link Appendable}
145 * the {@link Card} to write
147 * @throws IOException
148 * in case of IO error
150 public static void write(Appendable writer
, Card card
) throws IOException
{
151 for (Contact contact
: card
) {
152 write(writer
, contact
, -1);
157 * Write the given {@link Contact} in the {@link Appendable}.
160 * the {@link Appendable}
162 * the {@link Contact} to write
163 * @param startingBKey
164 * the starting BKey number (all the other will follow) or -1 for
167 * @throws IOException
168 * in case of IO error
170 public static void write(Appendable writer
, Contact contact
,
171 int startingBKey
) throws IOException
{
173 writer
.append("BEGIN:VCARD\r\n");
174 writer
.append("VERSION:2.1\r\n");
175 for (Data data
: contact
) {
178 writer
.append("END:VCARD\r\n");
182 * Write the given {@link Data} in the {@link Appendable}.
185 * the {@link Appendable}
187 * the {@link Data} to write
189 * @throws IOException
190 * in case of IO error
192 public static void write(Appendable writer
, Data data
) throws IOException
{
193 StringBuilder dataBuilder
= new StringBuilder();
194 if (data
.getGroup() != null && !data
.getGroup().trim().equals("")) {
195 dataBuilder
.append(data
.getGroup().trim());
196 dataBuilder
.append('.');
198 dataBuilder
.append(data
.getName());
199 for (TypeInfo type
: data
) {
200 dataBuilder
.append(';');
201 dataBuilder
.append(type
.getName());
202 if (type
.getValue() != null && !type
.getValue().trim().equals("")) {
203 dataBuilder
.append('=');
204 dataBuilder
.append(type
.getRawValue());
207 dataBuilder
.append(':');
210 dataBuilder
.append(data
.getRawValue());
212 // RFC says: Content lines SHOULD be folded to a maximum width of 75
213 // octets -> since it is SHOULD, we will just cut it as 74/75 chars
214 // depending if the last one fits in one char (note: chars != octet)
216 for (int index
= 0; index
< dataBuilder
.length(); previous
= index
) {
219 index
--; // the space takes 1
220 if (dataBuilder
.length() > index
) {
221 char car
= dataBuilder
.charAt(index
- 1);
222 // RFC forbids cutting a character in 2
223 if (Character
.isHighSurrogate(car
)) {
228 index
= Math
.min(index
, dataBuilder
.length());
231 writer
.append(dataBuilder
, previous
, index
);
232 writer
.append("\r\n");
237 * Clone the given {@link Card} by exporting then importing it again in VCF.
240 * the {@link Card} to clone
242 * @return the clone {@link Contact}
244 public static Card
clone(Card c
) {
246 File tmp
= File
.createTempFile("clone", ".vcf");
247 c
.saveAs(tmp
, Format
.VCard21
);
249 Card clone
= new Card(tmp
, Format
.VCard21
);
254 } catch (IOException e
) {
262 * Clone the given {@link Contact} by exporting then importing it again in
266 * the {@link Contact} to clone
268 * @return the clone {@link Contact}
270 public static Contact
clone(Contact c
) {
272 File tmp
= File
.createTempFile("clone", ".vcf");
273 FileWriter writer
= new FileWriter(tmp
);
274 write(writer
, c
, -1);
277 Card clone
= new Card(tmp
, Format
.VCard21
);
281 return clone
.remove(0);
282 } catch (IOException e
) {
290 * Check if the given line is a continuation line or not.
295 * @return TRUE if the line is a continuation line
297 private static boolean isContinuation(String line
) {
298 if (line
!= null && line
.length() > 0)
299 return (line
.charAt(0) == ' ' || line
.charAt(0) == '\t');