1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.scm.provider.svn.svnexe.command.changelog;
20
21 import java.util.ArrayList;
22 import java.util.Date;
23 import java.util.List;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26
27 import org.apache.maven.scm.ChangeFile;
28 import org.apache.maven.scm.ChangeSet;
29 import org.apache.maven.scm.ScmFileStatus;
30 import org.apache.maven.scm.provider.svn.SvnChangeSet;
31 import org.apache.maven.scm.util.AbstractConsumer;
32
33
34
35
36 public class SvnChangeLogConsumer extends AbstractConsumer {
37
38
39
40 private static final String SVN_TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss zzzzzzzzz";
41
42
43
44
45 private static final int GET_HEADER = 1;
46
47
48
49
50 private static final int GET_FILE = 2;
51
52
53
54
55 private static final int GET_COMMENT = 3;
56
57
58
59
60 private static final Pattern FILE_PATTERN = Pattern.compile("^\\s\\s\\s([A-Z])\\s(.+)$");
61
62
63
64
65
66 private static final Pattern ORIG_FILE_PATTERN = Pattern.compile("\\([A-Za-z]+ (.+):(\\d+)\\)");
67
68
69
70
71 private static final String FILE_END_TOKEN = "";
72
73
74
75
76 private static final String COMMENT_END_TOKEN =
77 "------------------------------------" + "------------------------------------";
78
79
80
81
82 private int status = GET_HEADER;
83
84
85
86
87 private final List<ChangeSet> entries = new ArrayList<>();
88
89
90
91
92 private SvnChangeSet currentChange;
93
94
95
96
97 private String currentRevision;
98
99
100
101
102 private StringBuilder currentComment;
103
104
105
106
107 private static final Pattern HEADER_REG_EXP = Pattern.compile("^(.+) \\| (.+) \\| (.+) \\|.*$");
108
109 private static final int REVISION_GROUP = 1;
110
111 private static final int AUTHOR_GROUP = 2;
112
113 private static final int DATE_GROUP = 3;
114
115 private static final Pattern REVISION_REG_EXP1 = Pattern.compile("rev (\\d+):");
116
117 private static final Pattern REVISION_REG_EXP2 = Pattern.compile("r(\\d+)");
118
119 private static final Pattern DATE_REG_EXP = Pattern.compile("(\\d+-\\d+-\\d+ " +
120 "\\d+:\\d+:\\d+) "
121 +
122 "([\\-+])(\\d\\d)(\\d\\d)");
123
124 private final String userDateFormat;
125
126
127
128
129 public SvnChangeLogConsumer(String userDateFormat) {
130 this.userDateFormat = userDateFormat;
131 }
132
133 public List<ChangeSet> getModifications() {
134 return entries;
135 }
136
137
138
139
140
141
142
143
144 public void consumeLine(String line) {
145 if (logger.isDebugEnabled()) {
146 logger.debug(line);
147 }
148 switch (status) {
149 case GET_HEADER:
150 processGetHeader(line);
151 break;
152 case GET_FILE:
153 processGetFile(line);
154 break;
155 case GET_COMMENT:
156 processGetComment(line);
157 break;
158 default:
159 throw new IllegalStateException("Unknown state: " + status);
160 }
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 private void processGetHeader(String line) {
177 Matcher matcher = HEADER_REG_EXP.matcher(line);
178 if (!matcher.matches()) {
179
180 return;
181 }
182
183 currentRevision = getRevision(matcher.group(REVISION_GROUP));
184
185 currentChange = new SvnChangeSet();
186
187 currentChange.setAuthor(matcher.group(AUTHOR_GROUP));
188
189 currentChange.setDate(getDate(matcher.group(DATE_GROUP)));
190
191 currentChange.setRevision(currentRevision);
192
193 status = GET_FILE;
194 }
195
196
197
198
199
200
201
202 private String getRevision(final String revisionOutput) {
203 Matcher matcher;
204 if ((matcher = REVISION_REG_EXP1.matcher(revisionOutput)).matches()) {
205 return matcher.group(1);
206 } else if ((matcher = REVISION_REG_EXP2.matcher(revisionOutput)).matches()) {
207 return matcher.group(1);
208 } else {
209 throw new IllegalOutputException(revisionOutput);
210 }
211 }
212
213
214
215
216
217
218
219
220
221 private void processGetFile(String line) {
222 Matcher matcher = FILE_PATTERN.matcher(line);
223 if (matcher.matches()) {
224 final String fileinfo = matcher.group(2);
225 String name = fileinfo;
226 String originalName = null;
227 String originalRev = null;
228 final int n = fileinfo.indexOf(" (");
229 if (n > 1 && fileinfo.endsWith(")")) {
230 final String origFileInfo = fileinfo.substring(n);
231 Matcher matcher2 = ORIG_FILE_PATTERN.matcher(origFileInfo);
232 if (matcher2.find()) {
233
234 name = fileinfo.substring(0, n);
235 originalName = matcher2.group(1);
236 originalRev = matcher2.group(2);
237 }
238 }
239 final String actionStr = matcher.group(1);
240 final ScmFileStatus action;
241 if ("A".equals(actionStr)) {
242
243 action = originalRev == null ? ScmFileStatus.ADDED : ScmFileStatus.COPIED;
244 } else if ("D".equals(actionStr)) {
245 action = ScmFileStatus.DELETED;
246 } else if ("M".equals(actionStr)) {
247 action = ScmFileStatus.MODIFIED;
248 } else if ("R".equals(actionStr)) {
249 action = ScmFileStatus.UPDATED;
250 } else {
251 action = ScmFileStatus.UNKNOWN;
252 }
253 if (logger.isDebugEnabled()) {
254 logger.debug(actionStr + " : " + name);
255 }
256 final ChangeFile changeFile = new ChangeFile(name, currentRevision);
257 changeFile.setAction(action);
258 changeFile.setOriginalName(originalName);
259 changeFile.setOriginalRevision(originalRev);
260 currentChange.addFile(changeFile);
261
262 status = GET_FILE;
263 } else if (line.equals(FILE_END_TOKEN)) {
264
265
266 currentComment = new StringBuilder();
267
268 status = GET_COMMENT;
269 }
270 }
271
272
273
274
275
276
277
278 private void processGetComment(String line) {
279 if (line.equals(COMMENT_END_TOKEN)) {
280 currentChange.setComment(currentComment.toString());
281
282 entries.add(currentChange);
283
284 status = GET_HEADER;
285 } else {
286 currentComment.append(line).append('\n');
287 }
288 }
289
290
291
292
293
294
295
296
297 private Date getDate(final String dateOutput) {
298 Matcher matcher = DATE_REG_EXP.matcher(dateOutput);
299 if (!matcher.find()) {
300 throw new IllegalOutputException(dateOutput);
301 }
302
303 final StringBuilder date = new StringBuilder();
304 date.append(matcher.group(1));
305 date.append(" GMT");
306 date.append(matcher.group(2));
307 date.append(matcher.group(3));
308 date.append(':');
309 date.append(matcher.group(4));
310
311 return parseDate(date.toString(), userDateFormat, SVN_TIMESTAMP_PATTERN);
312 }
313 }