Add more warnings source to 1.6) and fix warnings
[jvcard.git] / src / be / nikiroo / jvcard / parsers / Vcard21Parser.java
CommitLineData
a3b510ab
NR
1package be.nikiroo.jvcard.parsers;
2
59597d59
NR
3import java.io.File;
4import java.io.FileWriter;
5import java.io.IOException;
bcb54330 6import java.util.Iterator;
a3b510ab
NR
7import java.util.LinkedList;
8import java.util.List;
9
10import be.nikiroo.jvcard.Card;
11import be.nikiroo.jvcard.Contact;
12import be.nikiroo.jvcard.Data;
13import be.nikiroo.jvcard.TypeInfo;
14
15public class Vcard21Parser {
0b6140e4
NR
16 /**
17 * Load the given data from under the given {@link Format}.
18 *
19 * @param lines
20 * the input to load from
21 * @param format
22 * the {@link Format} to load as
23 *
24 * @return the list of elements
25 */
26 public static List<Contact> parseContact(Iterable<String> textData) {
bcb54330 27 Iterator<String> lines = textData.iterator();
a3b510ab 28 List<Contact> contacts = new LinkedList<Contact>();
0b6140e4 29 List<String> datas = null;
a3b510ab 30
bcb54330
NR
31 String nextRawLine = null;
32 if (lines.hasNext()) {
33 nextRawLine = lines.next();
34 while (lines.hasNext() && isContinuation(nextRawLine)) {
35 // BAD INPUT FILE. IGNORE.
36 System.err
37 .println("VCARD Parser warning: CONTINUATION line seen before any data line");
38 nextRawLine = lines.next();
39 }
40 }
41
42 while (nextRawLine != null) {
43 StringBuilder rawLine = new StringBuilder(nextRawLine.trim());
44 if (lines.hasNext())
45 nextRawLine = lines.next();
46 else
47 nextRawLine = null;
48
49 while (isContinuation(nextRawLine)) {
50 rawLine.append(nextRawLine.trim());
51 if (lines.hasNext())
52 nextRawLine = lines.next();
53 else
54 nextRawLine = null;
55 }
56
57 String line = rawLine.toString();
a3b510ab 58 if (line.equals("BEGIN:VCARD")) {
0b6140e4 59 datas = new LinkedList<String>();
a3b510ab
NR
60 } else if (line.equals("END:VCARD")) {
61 if (datas == null) {
62 // BAD INPUT FILE. IGNORE.
63 System.err
64 .println("VCARD Parser warning: END:VCARD seen before any VCARD:BEGIN");
65 } else {
0b6140e4 66 contacts.add(new Contact(parseData(datas)));
a3b510ab
NR
67 }
68 } else {
69 if (datas == null) {
70 // BAD INPUT FILE. IGNORE.
71 System.err
72 .println("VCARD Parser warning: data seen before any VCARD:BEGIN");
73 } else {
0b6140e4
NR
74 datas.add(line);
75 }
76 }
77 }
78
79 return contacts;
80 }
81
82 /**
83 * Load the given data from under the given {@link Format}.
84 *
85 * @param lines
86 * the input to load from
87 * @param format
88 * the {@link Format} to load as
89 *
90 * @return the list of elements
91 */
92 public static List<Data> parseData(Iterable<String> textData) {
93 List<Data> datas = new LinkedList<Data>();
94
95 for (String line : textData) {
96 List<TypeInfo> types = new LinkedList<TypeInfo>();
97 String name = "";
98 String value = "";
99 String group = "";
100
101 if (line.contains(":")) {
102 int colIndex = line.indexOf(':');
103 String rest = line.substring(0, colIndex);
104 value = line.substring(colIndex + 1);
105
106 if (rest.contains(";")) {
107 String tab[] = rest.split(";");
108 name = tab[0];
109
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));
a3b510ab 116 } else {
0b6140e4 117 types.add(new TypeInfo(tab[i], ""));
a3b510ab 118 }
a3b510ab 119 }
0b6140e4
NR
120 } else {
121 name = rest;
a3b510ab 122 }
0b6140e4
NR
123 } else {
124 name = line;
125 }
126
127 if (name.contains(".")) {
128 int dotIndex = name.indexOf('.');
129 group = name.substring(0, dotIndex);
130 name = name.substring(dotIndex + 1);
a3b510ab 131 }
0b6140e4
NR
132
133 datas.add(new Data(types, name, value, group));
a3b510ab
NR
134 }
135
0b6140e4 136 return datas;
a3b510ab
NR
137 }
138
cf77cb35 139 /**
59597d59 140 * Write the given {@link Card} in the {@link Appendable}.
cf77cb35 141 *
59597d59
NR
142 * @param writer
143 * the {@link Appendable}
cf77cb35 144 * @param card
59597d59 145 * the {@link Card} to write
cf77cb35 146 *
59597d59
NR
147 * @throws IOException
148 * in case of IO error
0b6140e4 149 */
59597d59 150 public static void write(Appendable writer, Card card) throws IOException {
0b6140e4 151 for (Contact contact : card) {
59597d59 152 write(writer, contact, -1);
0b6140e4 153 }
0b6140e4
NR
154 }
155
156 /**
59597d59 157 * Write the given {@link Contact} in the {@link Appendable}.
0b6140e4 158 *
59597d59
NR
159 * @param writer
160 * the {@link Appendable}
161 * @param contact
162 * the {@link Contact} to write
cf77cb35 163 * @param startingBKey
d5260eeb
NR
164 * the starting BKey number (all the others will follow) or -1
165 * for no BKey
cf77cb35 166 *
59597d59
NR
167 * @throws IOException
168 * in case of IO error
cf77cb35 169 */
59597d59
NR
170 public static void write(Appendable writer, Contact contact,
171 int startingBKey) throws IOException {
d5260eeb 172 // TODO: bkey handling?
59597d59
NR
173 writer.append("BEGIN:VCARD\r\n");
174 writer.append("VERSION:2.1\r\n");
1c03abaf 175 for (Data data : contact) {
59597d59 176 write(writer, data);
a3b510ab 177 }
59597d59 178 writer.append("END:VCARD\r\n");
a3b510ab
NR
179 }
180
cf77cb35 181 /**
59597d59 182 * Write the given {@link Data} in the {@link Appendable}.
cf77cb35 183 *
59597d59
NR
184 * @param writer
185 * the {@link Appendable}
0b6140e4 186 * @param data
59597d59 187 * the {@link Data} to write
cf77cb35 188 *
59597d59
NR
189 * @throws IOException
190 * in case of IO error
cf77cb35 191 */
59597d59 192 public static void write(Appendable writer, Data data) throws IOException {
0b6140e4
NR
193 StringBuilder dataBuilder = new StringBuilder();
194 if (data.getGroup() != null && !data.getGroup().trim().equals("")) {
195 dataBuilder.append(data.getGroup().trim());
196 dataBuilder.append('.');
197 }
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('=');
aecb3399 204 dataBuilder.append(type.getRawValue());
0b6140e4
NR
205 }
206 }
207 dataBuilder.append(':');
208
209 // TODO: bkey!
aecb3399 210 dataBuilder.append(data.getRawValue());
0b6140e4
NR
211
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)
59597d59
NR
215 int previous = 0;
216 for (int index = 0; index < dataBuilder.length(); previous = index) {
217 index += 74;
218 if (previous > 0)
219 index--; // the space takes 1
220 if (dataBuilder.length() > index) {
221 char car = dataBuilder.charAt(index - 1);
0b6140e4
NR
222 // RFC forbids cutting a character in 2
223 if (Character.isHighSurrogate(car)) {
59597d59 224 index++;
0b6140e4
NR
225 }
226 }
227
59597d59
NR
228 index = Math.min(index, dataBuilder.length());
229 if (previous > 0)
230 writer.append(' ');
231 writer.append(dataBuilder, previous, index);
232 writer.append("\r\n");
a3b510ab 233 }
a3b510ab 234 }
bcb54330 235
5ad0e17e
NR
236 /**
237 * Clone the given {@link Card} by exporting then importing it again in VCF.
238 *
239 * @param c
240 * the {@link Card} to clone
241 *
242 * @return the clone {@link Contact}
243 */
244 public static Card clone(Card c) {
59597d59
NR
245 try {
246 File tmp = File.createTempFile("clone", ".vcf");
247 c.saveAs(tmp, Format.VCard21);
248
249 Card clone = new Card(tmp, Format.VCard21);
250 clone.unlink();
251 tmp.delete();
252
253 return clone;
254 } catch (IOException e) {
255 e.printStackTrace();
256 }
257
258 return null;
5ad0e17e
NR
259 }
260
261 /**
262 * Clone the given {@link Contact} by exporting then importing it again in
263 * VCF.
264 *
265 * @param c
266 * the {@link Contact} to clone
267 *
268 * @return the clone {@link Contact}
269 */
270 public static Contact clone(Contact c) {
59597d59
NR
271 try {
272 File tmp = File.createTempFile("clone", ".vcf");
273 FileWriter writer = new FileWriter(tmp);
274 write(writer, c, -1);
275 writer.close();
276
277 Card clone = new Card(tmp, Format.VCard21);
278 clone.unlink();
279 tmp.delete();
280
281 return clone.remove(0);
282 } catch (IOException e) {
283 e.printStackTrace();
284 }
285
286 return null;
5ad0e17e
NR
287 }
288
bcb54330
NR
289 /**
290 * Check if the given line is a continuation line or not.
291 *
292 * @param line
293 * the line to check
294 *
295 * @return TRUE if the line is a continuation line
296 */
297 private static boolean isContinuation(String line) {
298 if (line != null && line.length() > 0)
299 return (line.charAt(0) == ' ' || line.charAt(0) == '\t');
300 return false;
301 }
a3b510ab 302}