1
2
3
4
5
6
7
8
9
10 package org.jenkinsci.plugins.darcs;
11
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.logging.Logger;
17 import org.xml.sax.Attributes;
18 import org.xml.sax.SAXParseException;
19 import org.xml.sax.helpers.DefaultHandler;
20
21
22
23
24
25
26 class DarcsSaxHandler extends DefaultHandler {
27
28
29
30
31 private static final Logger LOGGER = Logger.getLogger(DarcsSaxHandler.class.getName());
32
33
34
35 private static final String ATTR_TRUE = "True";
36
37
38
39 private static final String ATTR_FALSE = "False";
40
41
42
43
44 private enum DarcsChangelogTag {
45
46
47
48
49 CHANGELOG("changelog"),
50
51
52
53 PATCH("patch"),
54
55
56
57 NAME("name"),
58
59
60
61 COMMENT("comment"),
62
63
64
65 SUMMARY("summary"),
66
67
68
69 MODIFY_FILE("modify_file"),
70
71
72
73 ADD_FILE("add_file"),
74
75
76
77 REMOVE_FILE("remove_file"),
78
79
80
81 MOVE_FILE("move"),
82
83
84
85 ADDED_LINES("added_lines"),
86
87
88
89 REMOVED_LINES("removed_lines"),
90
91
92
93 ADD_DIRECTORY("add_directory"),
94
95
96
97 REMOVE_DIRECTORY("remove_directory");
98
99
100
101 private static final Map<String, DarcsChangelogTag> LOOKUP = new HashMap<String, DarcsChangelogTag>();
102
103 static {
104 for (final DarcsChangelogTag tag : DarcsChangelogTag.values()) {
105 LOOKUP.put(tag.getTagName().toLowerCase(), tag);
106 }
107 }
108
109
110
111 private final String tagName;
112
113
114
115
116
117
118 private DarcsChangelogTag(final String tagName) {
119 this.tagName = tagName;
120 }
121
122
123
124
125
126
127 public String getTagName() {
128 return tagName;
129 }
130
131
132
133
134
135
136
137 static DarcsChangelogTag forTagName(final String tagName) {
138 if (LOOKUP.containsKey(tagName.toLowerCase())) {
139 return LOOKUP.get(tagName);
140 }
141
142 return null;
143 }
144 }
145
146
147
148
149 private enum DarcsPatchTagAttribute {
150
151
152
153
154 AUTHOR("author"),
155
156
157
158 DATE("date"),
159
160
161
162 LOCAL_DATE("local_date"),
163
164
165
166 HASH("hash"),
167
168
169
170 INVERTED("inverted");
171
172
173
174 private final String name;
175
176
177
178
179
180
181 private DarcsPatchTagAttribute(String name) {
182 this.name = name;
183 }
184
185
186
187
188
189
190 public String getName() {
191 return name;
192 }
193 }
194
195
196
197
198 private enum DarcsMoveTagAttribute {
199
200 FROM("from"),
201
202 TO("to");
203
204
205
206 private final String name;
207
208
209
210
211
212
213 private DarcsMoveTagAttribute(String name) {
214 this.name = name;
215 }
216
217
218
219
220
221
222 public String getName() {
223 return name;
224 }
225 }
226
227
228
229 private DarcsChangelogTag currentTag;
230
231
232
233 private DarcsChangeSet currentChangeSet;
234
235
236
237 private boolean ready;
238
239
240
241 private final List<DarcsChangeSet> changeSets = new ArrayList<DarcsChangeSet>();
242
243
244
245 private StringBuilder literalBuffer = new StringBuilder();
246
247
248
249
250 public DarcsSaxHandler() {
251 super();
252 }
253
254
255
256
257
258
259 public boolean isReady() {
260 return ready;
261 }
262
263
264
265
266
267
268 public List<DarcsChangeSet> getChangeSets() {
269 return changeSets;
270 }
271
272 @Override
273 public void endDocument() {
274 ready = true;
275 }
276
277
278
279
280
281
282
283
284 private void recognizeTag(final String tagName) {
285 final DarcsChangelogTag tag = DarcsChangelogTag.forTagName(tagName);
286
287 if (null == tag) {
288 LOGGER.warning(String.format("Unrecognized tag <%s>!", tagName));
289 } else {
290 currentTag = tag;
291 }
292 }
293
294 @Override
295 public void startElement(final String uri, final String name, final String qName, final Attributes atts) {
296 if (DarcsChangelogTag.MODIFY_FILE == currentTag) {
297 currentChangeSet.getModifiedPaths().add(literalBuffer.toString());
298 }
299
300 recognizeTag(qName);
301
302 if (DarcsChangelogTag.PATCH == currentTag) {
303 currentChangeSet = new DarcsChangeSet();
304 currentChangeSet.setAuthor(atts.getValue(DarcsPatchTagAttribute.AUTHOR.getName()));
305 currentChangeSet.setDate(atts.getValue(DarcsPatchTagAttribute.DATE.getName()));
306 currentChangeSet.setLocalDate(atts.getValue(DarcsPatchTagAttribute.LOCAL_DATE.getName()));
307 currentChangeSet.setHash(atts.getValue(DarcsPatchTagAttribute.HASH.getName()));
308
309 if (ATTR_TRUE.equalsIgnoreCase(atts.getValue(DarcsPatchTagAttribute.INVERTED.getName()))) {
310 currentChangeSet.setInverted(true);
311 } else if (ATTR_FALSE.equalsIgnoreCase(atts.getValue(DarcsPatchTagAttribute.INVERTED.getName()))) {
312 currentChangeSet.setInverted(false);
313 }
314 } else if (DarcsChangelogTag.MOVE_FILE == currentTag) {
315 currentChangeSet.getDeletedPaths().add(atts.getValue(DarcsMoveTagAttribute.FROM.getName()));
316 currentChangeSet.getAddedPaths().add(atts.getValue(DarcsMoveTagAttribute.TO.getName()));
317 }
318
319 literalBuffer = new StringBuilder();
320 }
321
322 @Override
323 public void endElement(final String uri, final String name, final String qName) {
324 recognizeTag(qName);
325
326 switch (currentTag) {
327 case PATCH:
328 changeSets.add(currentChangeSet);
329 break;
330 case NAME:
331 currentChangeSet.setName(literalBuffer.toString());
332 break;
333 case COMMENT:
334 final String comment = stripIgnoreThisFromComment(literalBuffer.toString());
335 currentChangeSet.setComment(comment);
336 break;
337 case ADD_FILE:
338 case ADD_DIRECTORY:
339 currentChangeSet.getAddedPaths().add(literalBuffer.toString());
340 break;
341 case REMOVE_FILE:
342 case REMOVE_DIRECTORY:
343 currentChangeSet.getDeletedPaths().add(literalBuffer.toString());
344 break;
345 default:
346 LOGGER.info(String.format("Ignored tag <%s>!", currentTag));
347 }
348
349 currentTag = null;
350 }
351
352
353
354
355
356
357
358
359
360 static String stripIgnoreThisFromComment(final String comment) {
361 if (comment.startsWith("Ignore-this:")) {
362 final int end = comment.indexOf("\n");
363
364 if (-1 == end) {
365 return "";
366 }
367
368 return comment.substring(end + 1);
369 }
370
371 return comment;
372 }
373
374
375
376
377
378
379
380 private boolean isWhiteSpace(final char c) {
381 switch (c) {
382 case '\n':
383 case '\r':
384 case '\t':
385 case ' ':
386 return true;
387 default:
388 return false;
389 }
390 }
391
392
393
394
395
396
397
398
399
400 private boolean skipWhiteSpace() {
401 return DarcsChangelogTag.NAME != currentTag && DarcsChangelogTag.COMMENT != currentTag;
402 }
403
404 @Override
405 public void characters(final char[] ch, final int start, final int length) {
406 for (int i = start; i < start + length; i++) {
407 if (isWhiteSpace(ch[i]) && skipWhiteSpace()) {
408 continue;
409 }
410
411 literalBuffer.append(ch[i]);
412 }
413 }
414
415 @Override
416 public void error(final SAXParseException saxpe) {
417 LOGGER.warning(saxpe.toString());
418 }
419
420 @Override
421 public void fatalError(final SAXParseException saxpe) {
422 LOGGER.warning(saxpe.toString());
423 }
424
425 @Override
426 public void warning(final SAXParseException saxpe) {
427 LOGGER.warning(saxpe.toString());
428 }
429 }