1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sourceforge.javadpkg.impl;
20
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Date;
27 import java.util.List;
28
29 import net.sourceforge.javadpkg.ChangeLog;
30 import net.sourceforge.javadpkg.ChangeLogConstants;
31 import net.sourceforge.javadpkg.ChangeLogParser;
32 import net.sourceforge.javadpkg.ChangeLogUrgency;
33 import net.sourceforge.javadpkg.ChangeLogUrgencyParser;
34 import net.sourceforge.javadpkg.ChangeLogVersionEntryDetail;
35 import net.sourceforge.javadpkg.Context;
36 import net.sourceforge.javadpkg.ParseException;
37 import net.sourceforge.javadpkg.control.PackageMaintainer;
38 import net.sourceforge.javadpkg.control.PackageMaintainerParser;
39 import net.sourceforge.javadpkg.control.PackageName;
40 import net.sourceforge.javadpkg.control.PackageNameParser;
41 import net.sourceforge.javadpkg.control.PackageVersion;
42 import net.sourceforge.javadpkg.control.PackageVersionParser;
43 import net.sourceforge.javadpkg.control.impl.PackageMaintainerParserImpl;
44 import net.sourceforge.javadpkg.control.impl.PackageNameParserImpl;
45 import net.sourceforge.javadpkg.control.impl.PackageVersionParserImpl;
46 import net.sourceforge.javadpkg.io.DataSource;
47
48
49
50
51
52
53
54
55
56
57 public class ChangeLogParserImpl implements ChangeLogParser, ChangeLogConstants {
58
59
60
61 private PackageNameParser packageNameParser;
62
63 private PackageVersionParser packageVersionParser;
64
65 private ChangeLogUrgencyParser changeLogUrgencyParser;
66
67 private PackageMaintainerParser packageMaintainerParser;
68
69
70
71
72
73
74
75 public ChangeLogParserImpl() {
76 super();
77
78 this.packageNameParser = new PackageNameParserImpl();
79 this.packageVersionParser = new PackageVersionParserImpl();
80 this.changeLogUrgencyParser = new ChangeLogUrgencyParserImpl();
81 this.packageMaintainerParser = new PackageMaintainerParserImpl();
82 }
83
84
85 @Override
86 public ChangeLog parseChangeLog(DataSource source, Context context) throws IOException, ParseException {
87 ChangeLogImpl changeLog;
88 String line = null;
89 int lineNumber = 0;
90 ChangeLogVersionEntryImpl entry = null;
91 StringBuilder sb = null;
92 ChangeLogVersionEntryDetail detail;
93
94
95 if (source == null)
96 throw new IllegalArgumentException("Argument source is null.");
97 if (context == null)
98 throw new IllegalArgumentException("Argument context is null.");
99
100 changeLog = new ChangeLogImpl();
101 try {
102 try (BufferedReader in = new BufferedReader(new InputStreamReader(source.getInputStream(), UTF_8_CHARSET))) {
103 while ((line = in.readLine()) != null) {
104 lineNumber++;
105
106
107 if ("#".equals(line) || line.startsWith("# ")) {
108 continue;
109 } else
110
111 if (entry == null) {
112
113 if (line.isEmpty() || line.trim().isEmpty()) {
114 continue;
115 }
116
117 entry = this.parseInitialLine(line, context);
118 if (entry == null) {
119 break;
120 }
121 changeLog.addEntry(entry);
122 }
123
124 else if (line.startsWith(TERMINATION_PREFIX)) {
125
126 if (sb != null) {
127
128 detail = new ChangeLogVersionEntryDetailImpl(sb.toString());
129 entry.addDetail(detail);
130 sb = null;
131 }
132 this.parseTerminationLine(line, entry, context);
133 entry = null;
134 }
135
136 else if (line.startsWith(DETAIL_START_PREFIX)) {
137
138 if (sb != null) {
139
140 detail = new ChangeLogVersionEntryDetailImpl(sb.toString());
141 entry.addDetail(detail);
142 sb = null;
143 }
144 sb = this.parseDetailLine(line, null, context);
145 }
146
147 else if (line.startsWith(DETAIL_LINE_PREFIX)) {
148 if (sb == null) {
149 context.addWarning(new ChangeLogUnsupportedLineWarning(line));
150 } else {
151 sb = this.parseDetailLine(line, sb, context);
152 }
153 }
154
155 else if (!line.isEmpty()) {
156 context.addWarning(new ChangeLogUnsupportedLineWarning(line));
157 }
158 }
159
160 if (entry != null)
161 throw new ParseException("End of last version entry in the change log is missing.");
162 if (lineNumber == 0)
163 throw new ParseException("Change log is empty.");
164 }
165 } catch (IOException e) {
166 throw new IOException("An error occurred while parsing source |" + source.getName() + "|: " + e.getMessage(), e);
167 } catch (ParseException e) {
168 throw new ParseException(
169 "Line |" + line + "| (Line number: " + lineNumber + ") couldn't be parsed: " + e.getMessage(), e);
170 }
171 return changeLog;
172 }
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188 private ChangeLogVersionEntryImpl parseInitialLine(String line, Context context) throws ParseException {
189 ChangeLogVersionEntryImpl entry;
190 int index, lastIndex;
191 String value;
192 PackageName packageName;
193 PackageVersion version;
194 List<String> distributions;
195 ChangeLogUrgency urgency;
196
197
198
199 index = line.indexOf(' ');
200 if (index == -1) {
201 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
202 "Didn't find first whitespace in the first line of the change log entry."));
203 return null;
204 }
205 value = line.substring(0, index);
206 try {
207 packageName = this.packageNameParser.parsePackageName(value, context);
208 } catch (ParseException e) {
209 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
210 "Couldn't parser package name |" + value + "| of first line of change log entry: " + e.getMessage()));
211 return null;
212 }
213
214
215 index++;
216 if (line.length() <= index) {
217 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
218 "First line of change log entry is too short. Only found the package name."));
219 return null;
220 }
221 if (line.charAt(index) != '(') {
222 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
223 "Couldn't find the beginning of version of the package in the first line of change log entry."));
224 return null;
225 }
226 lastIndex = ++index;
227 index = line.indexOf(')', lastIndex);
228 if (index == -1) {
229 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
230 "Couldn't find the end of version of the package in the first line of change log entry."));
231 return null;
232 }
233 value = line.substring(lastIndex, index);
234 try {
235 version = this.packageVersionParser.parsePackageVersion(value, context);
236 } catch (ParseException e) {
237 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
238 "Couldn't parser version |" + value + "| of first line of change log entry: " + e.getMessage()));
239 return null;
240 }
241 lastIndex = index + 1;
242
243
244 index = line.indexOf(';', lastIndex);
245 if (index == -1) {
246 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
247 "Couldn't find the semi-colon between the distributions and the urgency in the first line of change log entry."));
248 return null;
249 }
250 value = line.substring(lastIndex, index).trim();
251 distributions = new ArrayList<>(Arrays.asList(value.split(" ")));
252 lastIndex = index + 1;
253
254
255 value = line.substring(lastIndex).trim();
256 if (!value.startsWith("urgency=")) {
257 context.addWarning(
258 new ChangeLogInitialLineUnsupportedWarning(line, "Couldn't the urgency prefix |urgency=| in the part |"
259 + value + "| after the semi-colon in the first line of change log entry."));
260 return null;
261 }
262 value = value.substring(8);
263 try {
264 urgency = this.changeLogUrgencyParser.parseChangeLogUrgency(value, context);
265 } catch (ParseException e) {
266 context.addWarning(new ChangeLogInitialLineUnsupportedWarning(line,
267 "Couldn't parser urgency |" + value + "| of first line of change log entry: " + e.getMessage()));
268 return null;
269 }
270
271 entry = new ChangeLogVersionEntryImpl();
272 entry.setPackageName(packageName);
273 entry.setVersion(version);
274 entry.setDistributions(distributions);
275 entry.setUrgency(urgency);
276 return entry;
277 }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295 private StringBuilder parseDetailLine(String line, StringBuilder detail, Context context) throws ParseException {
296 StringBuilder sb;
297 String value;
298
299
300 if (detail == null) {
301 sb = new StringBuilder();
302 } else {
303 sb = detail;
304 }
305 value = line.substring(4);
306 if (sb.length() > 0) {
307 sb.append('\n');
308 }
309
310 if (!".".equals(value)) {
311 sb.append(value);
312 }
313
314 return sb;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 private void parseTerminationLine(String line, ChangeLogVersionEntryImpl entry, Context context) throws ParseException {
333 int index;
334 String rest, value;
335 PackageMaintainer maintainer;
336 Date date;
337
338
339 rest = line.substring(TERMINATION_PREFIX.length());
340 index = rest.indexOf(MAINTAINER_TIMESTAMP_SEPARATOR);
341 if (index == -1)
342 throw new ParseException("Couldn't parse last line |" + line
343 + "| of change log entry as the separator between maintainer and timestamp couldn't be found.");
344
345
346 value = rest.substring(0, index);
347 try {
348 maintainer = this.packageMaintainerParser.parsePackageMaintainer(value, context);
349 } catch (ParseException e) {
350 throw new ParseException(
351 "Couldn't parser maintainer |" + value + "| of last line of change log entry: " + e.getMessage(), e);
352 }
353
354
355 value = rest.substring(index + 2);
356 try {
357 date = TIMESTAMP_FORMAT.parse(value);
358 } catch (java.text.ParseException e) {
359 throw new ParseException(
360 "Couldn't parse timestamp |" + value + "| of last line of change log entry: " + e.getMessage(), e);
361 }
362
363 entry.setMaintainer(maintainer);
364 entry.setDate(date);
365 }
366
367
368 @Override
369 public ChangeLog parseChangeLogHtml(DataSource source, Context context) throws IOException, ParseException {
370
371 throw new ParseException("Not yet implemented");
372 }
373
374
375 }