1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
|
23 | |
|
24 | |
package net.sf.statcvs.input; |
25 | |
|
26 | |
import java.io.IOException; |
27 | |
import java.util.HashMap; |
28 | |
import java.util.Map; |
29 | |
import java.util.logging.Logger; |
30 | |
|
31 | |
import net.sf.statcvs.util.CvsLogUtils; |
32 | |
import net.sf.statcvs.util.LookaheadReader; |
33 | |
|
34 | |
|
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
public class CvsFileBlockParser { |
47 | 32 | private static Logger logger = Logger.getLogger(CvsFileBlockParser.class.getName()); |
48 | |
private final LookaheadReader logReader; |
49 | |
private final CvsLogBuilder builder; |
50 | 200 | private boolean isLogWithoutSymbolicNames = false; |
51 | |
private final boolean isFirstFile; |
52 | 200 | private final Map revBySymNames = new HashMap(); |
53 | |
|
54 | |
|
55 | |
|
56 | |
|
57 | |
|
58 | |
|
59 | |
|
60 | 200 | public CvsFileBlockParser(final LookaheadReader logReader, final CvsLogBuilder builder, final boolean isFirstFile) { |
61 | 200 | this.logReader = logReader; |
62 | 200 | this.builder = builder; |
63 | 200 | this.isFirstFile = isFirstFile; |
64 | 200 | } |
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | |
|
71 | |
|
72 | |
public void parse() throws LogSyntaxException, IOException { |
73 | 200 | final String rcsFile = parseSingleLine(this.logReader.getCurrentLine(), "RCS file: "); |
74 | 200 | final String workingFile = parseSingleLine(this.logReader.nextLine(), "Working file: "); |
75 | 200 | final boolean isInAttic = CvsLogUtils.isInAttic(rcsFile, workingFile); |
76 | 200 | requireLine(this.logReader.nextLine(), "head:"); |
77 | 200 | requireLine(this.logReader.nextLine(), "branch:"); |
78 | 200 | requireLine(this.logReader.nextLine(), "locks:"); |
79 | 200 | parseLocksAndAccessList(); |
80 | 200 | parseSymbolicNames(); |
81 | 200 | final String keywordSubst = parseSingleLine(this.logReader.getCurrentLine(), "keyword substitution: "); |
82 | 200 | boolean isBinary = false; |
83 | |
try { |
84 | 200 | isBinary = CvsLogUtils.isBinaryKeywordSubst(keywordSubst); |
85 | 0 | } catch (final IllegalArgumentException unknownKeywordSubst) { |
86 | 0 | logger.warning("unknown keyword substitution '" + keywordSubst + "' in line " + this.logReader.getLineNumber()); |
87 | 200 | } |
88 | 200 | requireLine(this.logReader.nextLine(), "total revisions:"); |
89 | 200 | parseDescription(); |
90 | 200 | if (this.isFirstFile) { |
91 | 64 | this.builder.buildModule(CvsLogUtils.getModuleName(rcsFile, workingFile)); |
92 | |
} |
93 | 200 | this.builder.buildFile(workingFile, isBinary, isInAttic, this.revBySymNames); |
94 | 200 | if (!CvsRevisionParser.FILE_DELIMITER.equals(this.logReader.getCurrentLine())) { |
95 | 184 | new CvsRevisionParser(this.logReader, this.builder).parse(); |
96 | |
} |
97 | 192 | } |
98 | |
|
99 | |
|
100 | |
|
101 | |
|
102 | |
|
103 | |
|
104 | |
|
105 | |
|
106 | |
public boolean isLogWithoutSymbolicNames() { |
107 | 0 | return this.isLogWithoutSymbolicNames; |
108 | |
} |
109 | |
|
110 | |
private String parseSingleLine(final String line, final String lineStart) throws LogSyntaxException { |
111 | |
|
112 | 1600 | if (!line.startsWith(lineStart)) { |
113 | 0 | throw new LogSyntaxException("line " + this.logReader.getLineNumber() + ": expected '" + lineStart + "' but found '" + line + "'"); |
114 | |
} |
115 | |
|
116 | 1600 | return line.substring(lineStart.length()); |
117 | |
} |
118 | |
|
119 | |
private void requireLine(final String line, final String lineStart) throws LogSyntaxException { |
120 | |
|
121 | 1000 | parseSingleLine(line, lineStart); |
122 | 1000 | } |
123 | |
|
124 | |
private void parseSymbolicNames() throws IOException { |
125 | 200 | if (this.logReader.getCurrentLine().startsWith("keyword substitution: ")) { |
126 | 8 | return; |
127 | |
} |
128 | |
String line; |
129 | 192 | if (this.logReader.getCurrentLine().equals("symbolic names:")) { |
130 | 192 | line = this.logReader.nextLine(); |
131 | |
} else { |
132 | 0 | this.isLogWithoutSymbolicNames = true; |
133 | 0 | line = this.logReader.getCurrentLine(); |
134 | |
} |
135 | 224 | while (line != null && !line.startsWith("keyword substitution: ")) { |
136 | 32 | final int firstColon = line.indexOf(':'); |
137 | 32 | final String tagName = line.substring(1, firstColon); |
138 | 32 | final String tagRevision = line.substring(firstColon + 2); |
139 | 32 | this.revBySymNames.put(tagName, tagRevision); |
140 | 32 | line = this.logReader.nextLine(); |
141 | 28 | } |
142 | 192 | } |
143 | |
|
144 | |
private void parseLocksAndAccessList() throws IOException { |
145 | 216 | while (!"access list:".equals(this.logReader.nextLine())) { |
146 | |
|
147 | |
} |
148 | |
String line; |
149 | |
do { |
150 | 216 | line = this.logReader.nextLine(); |
151 | |
|
152 | 216 | } while (!line.equals("symbolic names:") && !line.startsWith("keyword substitution: ")); |
153 | 200 | } |
154 | |
|
155 | |
private void parseDescription() throws LogSyntaxException, IOException { |
156 | 200 | final String line = this.logReader.nextLine(); |
157 | 200 | if (line.equals(CvsRevisionParser.FILE_DELIMITER)) { |
158 | 0 | throw new LogSyntaxException("line " + this.logReader.getLineNumber() + ": missing description; please don't use the -h switch of 'cvs log'!"); |
159 | |
} |
160 | 200 | requireLine(this.logReader.getCurrentLine(), "description:"); |
161 | 248 | while (!isDescriptionDelimiter(this.logReader.nextLine())) { |
162 | |
|
163 | |
} |
164 | 200 | } |
165 | |
|
166 | |
private boolean isDescriptionDelimiter(final String line) { |
167 | 248 | return CvsRevisionParser.REVISION_DELIMITER.equals(line) || CvsRevisionParser.FILE_DELIMITER.equals(line); |
168 | |
} |
169 | |
} |