1 | |
package net.sf.statsvn.input; |
2 | |
|
3 | |
import java.text.ParseException; |
4 | |
import java.util.ArrayList; |
5 | |
import java.util.Date; |
6 | |
import java.util.HashMap; |
7 | |
|
8 | |
import net.sf.statsvn.output.SvnConfigurationOptions; |
9 | |
import net.sf.statsvn.util.XMLUtil; |
10 | |
|
11 | |
import org.xml.sax.Attributes; |
12 | |
import org.xml.sax.SAXException; |
13 | |
import org.xml.sax.SAXParseException; |
14 | |
import org.xml.sax.helpers.DefaultHandler; |
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
|
23 | |
|
24 | |
|
25 | |
public class SvnXmlLogFileHandler extends DefaultHandler { |
26 | |
|
27 | |
private static final String INVALID_SVN_LOG_FILE = "Invalid SVN log file."; |
28 | |
|
29 | |
private static final String AUTHOR = "author"; |
30 | |
|
31 | |
private static final String DATE = "date"; |
32 | |
|
33 | |
private static final String FATAL_ERROR_MESSAGE = INVALID_SVN_LOG_FILE; |
34 | |
|
35 | |
private static final String LOG = "log"; |
36 | |
|
37 | |
private static final String LOGENTRY = "logentry"; |
38 | |
|
39 | |
private static final String MSG = "msg"; |
40 | |
|
41 | |
private static final String PATH = "path"; |
42 | |
|
43 | |
private static final String PATHS = "paths"; |
44 | |
|
45 | |
private final SvnLogBuilder builder; |
46 | |
|
47 | |
private ArrayList currentFilenames; |
48 | |
|
49 | |
private RevisionData currentRevisionData; |
50 | |
|
51 | |
private ArrayList currentRevisions; |
52 | |
|
53 | 30 | private String lastElement = ""; |
54 | |
|
55 | 30 | private String pathAction = ""; |
56 | |
|
57 | 30 | private String stringData = ""; |
58 | |
|
59 | 30 | private String copyfromRev = ""; |
60 | |
|
61 | 30 | private String copyfromPath = ""; |
62 | |
|
63 | |
private final RepositoryFileManager repositoryFileManager; |
64 | |
|
65 | 30 | private final HashMap tagsMap = new HashMap(); |
66 | |
|
67 | 30 | private final HashMap tagsDateMap = new HashMap(); |
68 | |
|
69 | |
|
70 | |
|
71 | |
|
72 | |
|
73 | |
|
74 | |
|
75 | |
|
76 | |
|
77 | 30 | public SvnXmlLogFileHandler(final SvnLogBuilder builder, final RepositoryFileManager repositoryFileManager) { |
78 | 30 | this.builder = builder; |
79 | 30 | this.repositoryFileManager = repositoryFileManager; |
80 | 30 | } |
81 | |
|
82 | |
|
83 | |
|
84 | |
|
85 | |
|
86 | |
public void characters(final char[] ch, final int start, final int length) throws SAXException { |
87 | 538500 | super.characters(ch, start, length); |
88 | 538500 | stringData += new String(ch, start, length); |
89 | 538500 | } |
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
|
95 | |
|
96 | |
|
97 | |
|
98 | |
|
99 | |
private void checkLastElement(final String last) throws SAXException { |
100 | 506340 | if (!lastElement.equals(last)) { |
101 | 0 | fatalError(FATAL_ERROR_MESSAGE); |
102 | |
} |
103 | 506340 | } |
104 | |
|
105 | |
|
106 | |
|
107 | |
|
108 | |
|
109 | |
|
110 | |
|
111 | |
private void endAuthor() throws SAXException { |
112 | 18120 | checkLastElement(LOGENTRY); |
113 | 18120 | currentRevisionData.setLoginName(stringData); |
114 | 18120 | } |
115 | |
|
116 | |
|
117 | |
|
118 | |
|
119 | |
|
120 | |
|
121 | |
|
122 | |
|
123 | |
|
124 | |
|
125 | |
private void endDate() throws SAXException { |
126 | 18120 | checkLastElement(LOGENTRY); |
127 | |
Date dt; |
128 | |
try { |
129 | 18120 | dt = XMLUtil.parseXsdDateTime(stringData); |
130 | 18120 | currentRevisionData.setDate(dt); |
131 | 0 | } catch (final ParseException e) { |
132 | 0 | warning("Invalid date specified."); |
133 | 18120 | } |
134 | 18120 | } |
135 | |
|
136 | |
|
137 | |
|
138 | |
|
139 | |
|
140 | |
|
141 | |
|
142 | |
|
143 | |
public void endElement(final String uri, final String localName, final String qName) throws SAXException { |
144 | 253170 | super.endElement(uri, localName, qName); |
145 | 253170 | String eName = localName; |
146 | 253170 | if ("".equals(eName)) { |
147 | 253170 | eName = qName; |
148 | |
} |
149 | 253170 | if (eName.equals(LOG)) { |
150 | 30 | endLog(); |
151 | 253140 | } else if (eName.equals(LOGENTRY)) { |
152 | 18120 | endLogEntry(); |
153 | 235020 | } else if (eName.equals(AUTHOR)) { |
154 | 18120 | endAuthor(); |
155 | 216900 | } else if (eName.equals(DATE)) { |
156 | 18120 | endDate(); |
157 | 198780 | } else if (eName.equals(MSG)) { |
158 | 18120 | endMsg(); |
159 | 180660 | } else if (eName.equals(PATHS)) { |
160 | 18120 | endPaths(); |
161 | 162540 | } else if (eName.equals(PATH)) { |
162 | 162540 | endPath(); |
163 | |
} else { |
164 | 0 | fatalError(INVALID_SVN_LOG_FILE); |
165 | |
} |
166 | 253170 | } |
167 | |
|
168 | |
|
169 | |
|
170 | |
|
171 | |
|
172 | |
|
173 | |
|
174 | |
private void endLog() throws SAXException { |
175 | 30 | checkLastElement(LOG); |
176 | 30 | lastElement = ""; |
177 | 30 | } |
178 | |
|
179 | |
|
180 | |
|
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | |
private void endLogEntry() throws SAXException { |
187 | 18120 | checkLastElement(LOGENTRY); |
188 | 18120 | lastElement = LOG; |
189 | |
|
190 | 180660 | for (int i = 0; i < currentFilenames.size(); i++) { |
191 | 162540 | if (currentFilenames.get(i) == null) { |
192 | 0 | continue; |
193 | |
} |
194 | 162540 | final RevisionData revisionData = (RevisionData) currentRevisions.get(i); |
195 | 162540 | revisionData.setComment(currentRevisionData.getComment()); |
196 | 162540 | revisionData.setDate(currentRevisionData.getDate()); |
197 | 162540 | revisionData.setLoginName(currentRevisionData.getLoginName()); |
198 | 162540 | final String currentFilename = currentFilenames.get(i).toString(); |
199 | |
|
200 | 162540 | final boolean isBinary = repositoryFileManager.isBinary(currentFilename); |
201 | 162540 | builder.buildFile(currentFilename, isBinary, revisionData.isDeletion(), tagsMap, tagsDateMap); |
202 | 162540 | builder.buildRevision(revisionData); |
203 | |
} |
204 | 18120 | } |
205 | |
|
206 | |
|
207 | |
|
208 | |
|
209 | |
|
210 | |
|
211 | |
|
212 | |
private void endMsg() throws SAXException { |
213 | 18120 | checkLastElement(LOGENTRY); |
214 | 18120 | currentRevisionData.setComment(stringData); |
215 | 18120 | } |
216 | |
|
217 | |
|
218 | |
|
219 | |
|
220 | |
|
221 | |
|
222 | |
|
223 | |
|
224 | |
private void endPath() throws SAXException { |
225 | 162540 | checkLastElement(PATHS); |
226 | |
|
227 | |
|
228 | |
|
229 | 162540 | final String filename = repositoryFileManager.absoluteToRelativePath(stringData); |
230 | 162540 | final RevisionData data = currentRevisionData.createCopy(); |
231 | 162540 | if (!pathAction.equals("D")) { |
232 | 152490 | data.setStateExp(true); |
233 | 152490 | if (pathAction.equals("A") || pathAction.equals("R")) { |
234 | 38100 | data.setStateAdded(true); |
235 | |
} |
236 | |
} else { |
237 | 10050 | data.setStateDead(true); |
238 | |
} |
239 | |
|
240 | 162540 | final String tagsStr = SvnConfigurationOptions.getTagsDirectory(); |
241 | 162540 | if (copyfromRev != null && filename == null && stringData != null && stringData.indexOf(tagsStr) >= 0) { |
242 | 0 | String tag = stringData.substring(stringData.indexOf(tagsStr) + tagsStr.length()); |
243 | 0 | if (tag.indexOf("/") >= 0) { |
244 | 0 | tag = tag.substring(0, tag.indexOf("/")); |
245 | |
} |
246 | |
|
247 | 0 | if (!tagsMap.containsKey(tag) && builder.matchesTagPatterns(tag)) { |
248 | 0 | SvnConfigurationOptions.getTaskLogger().info("= TAG " + tag + " rev:" + copyfromRev + " stringData [" + stringData + "]"); |
249 | 0 | tagsMap.put(tag, copyfromRev); |
250 | 0 | tagsDateMap.put(tag, currentRevisionData.getDate()); |
251 | |
} |
252 | |
} |
253 | |
|
254 | 162540 | data.setCopyfromPath(copyfromPath); |
255 | 162540 | data.setCopyfromRevision(copyfromRev); |
256 | |
|
257 | 162540 | currentRevisions.add(data); |
258 | 162540 | currentFilenames.add(filename); |
259 | 162540 | } |
260 | |
|
261 | |
|
262 | |
|
263 | |
|
264 | |
|
265 | |
|
266 | |
|
267 | |
private void endPaths() throws SAXException { |
268 | 18120 | checkLastElement(PATHS); |
269 | 18120 | lastElement = LOGENTRY; |
270 | 18120 | } |
271 | |
|
272 | |
|
273 | |
|
274 | |
|
275 | |
|
276 | |
|
277 | |
|
278 | |
|
279 | |
|
280 | |
private void fatalError(final String message) throws SAXException { |
281 | 0 | fatalError(new SAXParseException(message, null)); |
282 | 0 | } |
283 | |
|
284 | |
|
285 | |
|
286 | |
|
287 | |
|
288 | |
|
289 | |
|
290 | |
private void startAuthorDateMsg() throws SAXException { |
291 | 54360 | checkLastElement(LOGENTRY); |
292 | 54360 | } |
293 | |
|
294 | |
|
295 | |
|
296 | |
|
297 | |
|
298 | |
|
299 | |
|
300 | |
|
301 | |
public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException { |
302 | 253170 | super.startElement(uri, localName, qName, attributes); |
303 | 253170 | stringData = ""; |
304 | 253170 | String eName = localName; |
305 | 253170 | if ("".equals(eName)) { |
306 | 253170 | eName = qName; |
307 | |
} |
308 | 253170 | if (eName.equals(LOG)) { |
309 | 30 | startLog(); |
310 | 253140 | } else if (eName.equals(LOGENTRY)) { |
311 | 18120 | startLogEntry(attributes); |
312 | 235020 | } else if (eName.equals(AUTHOR) || eName.equals(DATE) || eName.equals(MSG)) { |
313 | 54360 | startAuthorDateMsg(); |
314 | 180660 | } else if (eName.equals(PATHS)) { |
315 | 18120 | startPaths(); |
316 | 162540 | } else if (eName.equals(PATH)) { |
317 | 162540 | startPath(attributes); |
318 | |
} else { |
319 | 0 | fatalError(INVALID_SVN_LOG_FILE); |
320 | |
} |
321 | 253170 | } |
322 | |
|
323 | |
|
324 | |
|
325 | |
|
326 | |
|
327 | |
|
328 | |
|
329 | |
private void startLog() throws SAXException { |
330 | 30 | checkLastElement(""); |
331 | 30 | lastElement = LOG; |
332 | |
|
333 | |
try { |
334 | 30 | repositoryFileManager.loadInfo(); |
335 | 30 | builder.buildModule(repositoryFileManager.getModuleName()); |
336 | 0 | } catch (final Exception e) { |
337 | 0 | throw new SAXException(e); |
338 | 30 | } |
339 | |
|
340 | 30 | } |
341 | |
|
342 | |
|
343 | |
|
344 | |
|
345 | |
|
346 | |
|
347 | |
|
348 | |
|
349 | |
private void startLogEntry(final Attributes attributes) throws SAXException { |
350 | 18120 | checkLastElement(LOG); |
351 | 18120 | lastElement = LOGENTRY; |
352 | 18120 | currentRevisionData = new RevisionData(); |
353 | 18120 | currentRevisions = new ArrayList(); |
354 | 18120 | currentFilenames = new ArrayList(); |
355 | 18120 | if (attributes != null && attributes.getValue("revision") != null) { |
356 | 18120 | currentRevisionData.setRevisionNumber(attributes.getValue("revision")); |
357 | |
} else { |
358 | 0 | fatalError(INVALID_SVN_LOG_FILE); |
359 | |
} |
360 | 18120 | } |
361 | |
|
362 | |
|
363 | |
|
364 | |
|
365 | |
|
366 | |
|
367 | |
|
368 | |
private void startPath(final Attributes attributes) throws SAXException { |
369 | 162540 | checkLastElement(PATHS); |
370 | 162540 | if (attributes != null && attributes.getValue("action") != null) { |
371 | 162540 | pathAction = attributes.getValue("action"); |
372 | |
} else { |
373 | 0 | fatalError(INVALID_SVN_LOG_FILE); |
374 | |
} |
375 | |
|
376 | 162540 | copyfromPath = attributes.getValue("copyfrom-path"); |
377 | 162540 | copyfromRev = attributes.getValue("copyfrom-rev"); |
378 | |
|
379 | 162540 | } |
380 | |
|
381 | |
|
382 | |
|
383 | |
|
384 | |
|
385 | |
|
386 | |
|
387 | |
private void startPaths() throws SAXException { |
388 | 18120 | checkLastElement(LOGENTRY); |
389 | 18120 | lastElement = PATHS; |
390 | 18120 | } |
391 | |
|
392 | |
|
393 | |
|
394 | |
|
395 | |
|
396 | |
|
397 | |
|
398 | |
|
399 | |
|
400 | |
private void warning(final String message) throws SAXException { |
401 | 0 | SvnConfigurationOptions.getTaskLogger().info(message); |
402 | 0 | } |
403 | |
} |