Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
PerforceChangeLogConsumer |
|
| 4.5;4,5 |
1 | package org.apache.maven.scm.provider.perforce.command.changelog; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
20 | */ | |
21 | ||
22 | import org.apache.maven.scm.ChangeFile; | |
23 | import org.apache.maven.scm.ChangeSet; | |
24 | import org.apache.maven.scm.ScmException; | |
25 | import org.apache.maven.scm.log.ScmLogger; | |
26 | import org.apache.maven.scm.util.AbstractConsumer; | |
27 | import org.apache.regexp.RE; | |
28 | import org.apache.regexp.RESyntaxException; | |
29 | ||
30 | import java.util.ArrayList; | |
31 | import java.util.Date; | |
32 | import java.util.LinkedHashMap; | |
33 | import java.util.List; | |
34 | import java.util.Map; | |
35 | ||
36 | /** | |
37 | * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> | |
38 | * @version $Id: PerforceChangeLogConsumer.java 1057019 2011-01-09 20:13:07Z olamy $ | |
39 | */ | |
40 | public class PerforceChangeLogConsumer | |
41 | extends AbstractConsumer | |
42 | { | |
43 | /** | |
44 | * Date formatter for perforce timestamp | |
45 | */ | |
46 | private static final String PERFORCE_TIMESTAMP_PATTERN = "yyyy/MM/dd HH:mm:ss"; | |
47 | ||
48 | 0 | private List<ChangeSet> entries = new ArrayList<ChangeSet>(); |
49 | ||
50 | /** | |
51 | * State machine constant: expecting revision and/or file information | |
52 | */ | |
53 | private static final int GET_REVISION = 1; | |
54 | ||
55 | /** | |
56 | * State machine constant: eat the first blank line | |
57 | */ | |
58 | private static final int GET_COMMENT_BEGIN = 2; | |
59 | ||
60 | /** | |
61 | * State machine constant: expecting comments | |
62 | */ | |
63 | private static final int GET_COMMENT = 3; | |
64 | ||
65 | /** | |
66 | * The comment section ends with a blank line | |
67 | */ | |
68 | private static final String COMMENT_DELIMITER = ""; | |
69 | ||
70 | /** | |
71 | * A file line begins with two slashes | |
72 | */ | |
73 | private static final String FILE_BEGIN_TOKEN = "//"; | |
74 | ||
75 | /** | |
76 | * Current status of the parser | |
77 | */ | |
78 | 0 | private int status = GET_REVISION; |
79 | ||
80 | /** | |
81 | * The current log entry being processed by the parser | |
82 | */ | |
83 | private ChangeSet currentChange; | |
84 | ||
85 | /** | |
86 | * the current file being processed by the parser | |
87 | */ | |
88 | private String currentFile; | |
89 | ||
90 | /** | |
91 | * The location of files within the Perforce depot that we are processing | |
92 | * e.g. //depot/projects/foo/bar | |
93 | */ | |
94 | private String repoPath; | |
95 | ||
96 | /** | |
97 | * The regular expression used to match header lines | |
98 | */ | |
99 | private RE revisionRegexp; | |
100 | ||
101 | private Date startDate; | |
102 | ||
103 | private Date endDate; | |
104 | ||
105 | private String userDatePattern; | |
106 | ||
107 | private static final String PATTERN = "^\\.\\.\\. #(\\d+) " + // revision number | |
108 | "change (\\d+) .* " + // changelist number | |
109 | "on (.*) " + // date | |
110 | "by (.*)@"; // author | |
111 | ||
112 | public PerforceChangeLogConsumer( String path, Date startDate, Date endDate, String userDatePattern, | |
113 | ScmLogger logger ) | |
114 | { | |
115 | 0 | super( logger ); |
116 | ||
117 | 0 | this.startDate = startDate; |
118 | 0 | this.endDate = endDate; |
119 | 0 | this.userDatePattern = userDatePattern; |
120 | 0 | this.repoPath = path; |
121 | ||
122 | try | |
123 | { | |
124 | 0 | revisionRegexp = new RE( PATTERN ); |
125 | } | |
126 | 0 | catch ( RESyntaxException ignored ) |
127 | { | |
128 | 0 | if ( getLogger().isErrorEnabled() ) |
129 | { | |
130 | 0 | getLogger().error( "Could not create regexp to parse perforce log file", ignored ); |
131 | } | |
132 | 0 | } |
133 | 0 | } |
134 | ||
135 | // ---------------------------------------------------------------------- | |
136 | // | |
137 | // ---------------------------------------------------------------------- | |
138 | ||
139 | public List<ChangeSet> getModifications() throws ScmException | |
140 | { | |
141 | ||
142 | // Here there are one entry for each couple (changelist,file). We merge | |
143 | // entries to have only one entry per changelist | |
144 | ||
145 | // Date > ChangeSet | |
146 | 0 | Map<Date,ChangeSet> groupedEntries = new LinkedHashMap<Date,ChangeSet>(); |
147 | 0 | for ( int i = 0; i < entries.size(); i++ ) |
148 | { | |
149 | 0 | ChangeSet cs = (ChangeSet) entries.get( i ); |
150 | 0 | ChangeSet hit = (ChangeSet) groupedEntries.get( cs.getDate() ); |
151 | 0 | if ( hit != null ) |
152 | { | |
153 | 0 | if ( cs.getFiles().size() != 1 ) |
154 | { | |
155 | 0 | throw new ScmException( "Merge of entries failed. Bad entry size: " + cs.getFiles().size() ); |
156 | } | |
157 | 0 | hit.addFile( (ChangeFile) cs.getFiles().get( 0 ) ); |
158 | } | |
159 | else | |
160 | { | |
161 | 0 | groupedEntries.put( cs.getDate(), cs ); |
162 | } | |
163 | } | |
164 | ||
165 | 0 | List<ChangeSet> result = new ArrayList<ChangeSet>(); |
166 | 0 | result.addAll( groupedEntries.values() ); |
167 | ||
168 | 0 | return result; |
169 | ||
170 | } | |
171 | ||
172 | // ---------------------------------------------------------------------- | |
173 | // StreamConsumer Implementation | |
174 | // ---------------------------------------------------------------------- | |
175 | ||
176 | /** {@inheritDoc} */ | |
177 | public void consumeLine( String line ) | |
178 | { | |
179 | 0 | switch ( status ) |
180 | { | |
181 | case GET_REVISION: | |
182 | 0 | processGetRevision( line ); |
183 | 0 | break; |
184 | case GET_COMMENT_BEGIN: | |
185 | 0 | status = GET_COMMENT; |
186 | 0 | break; |
187 | case GET_COMMENT: | |
188 | 0 | processGetComment( line ); |
189 | 0 | break; |
190 | default: | |
191 | 0 | throw new IllegalStateException( "Unknown state: " + status ); |
192 | } | |
193 | 0 | } |
194 | ||
195 | // ---------------------------------------------------------------------- | |
196 | // | |
197 | // ---------------------------------------------------------------------- | |
198 | ||
199 | /** | |
200 | * Add a change log entry to the list (if it's not already there) | |
201 | * with the given file. | |
202 | * | |
203 | * @param entry a {@link ChangeSet} to be added to the list if another | |
204 | * with the same key (p4 change number) doesn't exist already. | |
205 | * @param file a {@link ChangeFile} to be added to the entry | |
206 | */ | |
207 | private void addEntry( ChangeSet entry, ChangeFile file ) | |
208 | { | |
209 | // ---------------------------------------------------------------------- | |
210 | // Check that we are inside the requested date range | |
211 | // ---------------------------------------------------------------------- | |
212 | ||
213 | 0 | if ( startDate != null && entry.getDate().before( startDate ) ) |
214 | { | |
215 | 0 | return; |
216 | } | |
217 | ||
218 | 0 | if ( endDate != null && entry.getDate().after( endDate ) ) |
219 | { | |
220 | 0 | return; |
221 | } | |
222 | ||
223 | // ---------------------------------------------------------------------- | |
224 | // | |
225 | // ---------------------------------------------------------------------- | |
226 | ||
227 | 0 | entry.addFile( file ); |
228 | ||
229 | 0 | entries.add( entry ); |
230 | 0 | } |
231 | ||
232 | /** | |
233 | * Most of the relevant info is on the revision line matching the | |
234 | * 'pattern' string. | |
235 | * | |
236 | * @param line A line of text from the perforce log output | |
237 | */ | |
238 | private void processGetRevision( String line ) | |
239 | { | |
240 | 0 | if ( line.startsWith( FILE_BEGIN_TOKEN ) ) |
241 | { | |
242 | 0 | currentFile = line.substring( repoPath.length() + 1 ); |
243 | 0 | return; |
244 | } | |
245 | ||
246 | 0 | if ( !revisionRegexp.match( line ) ) |
247 | { | |
248 | 0 | return; |
249 | } | |
250 | ||
251 | 0 | currentChange = new ChangeSet(); |
252 | 0 | currentChange.setDate( parseDate( revisionRegexp.getParen( 3 ), userDatePattern, PERFORCE_TIMESTAMP_PATTERN ) ); |
253 | 0 | currentChange.setAuthor( revisionRegexp.getParen( 4 ) ); |
254 | ||
255 | 0 | status = GET_COMMENT_BEGIN; |
256 | 0 | } |
257 | ||
258 | /** | |
259 | * Process the current input line in the GET_COMMENT state. This | |
260 | * state gathers all of the comments that are part of a log entry. | |
261 | * | |
262 | * @param line a line of text from the perforce log output | |
263 | */ | |
264 | private void processGetComment( String line ) | |
265 | { | |
266 | 0 | if ( line.equals( COMMENT_DELIMITER ) ) |
267 | { | |
268 | 0 | addEntry( currentChange, new ChangeFile( currentFile, revisionRegexp.getParen( 1 ) ) ); |
269 | ||
270 | 0 | status = GET_REVISION; |
271 | } | |
272 | else | |
273 | { | |
274 | 0 | currentChange.setComment( currentChange.getComment() + line + "\n" ); |
275 | } | |
276 | 0 | } |
277 | } |