View Javadoc
1 /* 2 Bloof - visualize the evolution of your software project 3 Copyright ( C ) 2003 Lukasz Pekacki <lukasz@pekacki.de> 4 http://bloof.sf.net/ 5 6 This program is free software; you can redistribute it and/or modify it 7 under the terms of the GNU General Public License. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along with 15 this program; if not, write to the Free Software Foundation, Inc., 16 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 $RCSfile: LogParser.java,v $ 19 Created on $Date: 2003/09/06 08:35:09 $ 20 */ 21 22 package net.sf.bloof.scm.cvsplugin; 23 24 import net.sf.bloof.Bloof; 25 import net.sf.bloof.scm.FileState; 26 27 import java.io.BufferedReader; 28 import java.io.IOException; 29 import java.io.Reader; 30 import java.text.ParseException; 31 import java.util.Date; 32 import java.util.NoSuchElementException; 33 import java.util.StringTokenizer; 34 import java.util.logging.Logger; 35 36 37 38 /*** 39 * Parser for a CVS log file 40 * @author Lukasz Pekacki <pekacki@users.sourceforge.net> 41 * @version $Id: LogParser.java,v 1.12 2003/09/06 08:35:09 pekacki Exp $ 42 */ 43 public class LogParser implements Runnable { 44 45 /*** 46 * Constructs a log parser for the given input 47 * @param aLogReader Reader on the the CVS log file 48 * @param aRiter RevisonIterator that receives parsed Revisions 49 * */ 50 public LogParser(Reader aLogReader, RevisionIterator aRiter) { 51 mRiter = aRiter; 52 mLogReader = new BufferedReader(aLogReader); 53 } 54 /*** 55 * Gets the name of the file 56 * @param aRcsFile the full RCS name of the file 57 * @return String the name of the file 58 */ 59 private String extractFileName(String aRcsFile) { 60 int beginIndex = aRcsFile.lastIndexOf(SLASH) + 1; 61 int endIndex = aRcsFile.lastIndexOf(KOMMA); 62 return aRcsFile.substring(beginIndex, endIndex); 63 } 64 /*** 65 * Extracts the path to the file 66 * @param aRcsFile Path string of the RCS File 67 * @return String representing the path 68 */ 69 private String extractPath(String aRcsFile) { 70 String resultPath = CvsPlugin.normalizePath(aRcsFile); 71 int endIndex = resultPath.lastIndexOf(KOMMA); 72 return resultPath.substring(0, endIndex); 73 } 74 /*** 75 * Finishes parsing and closes the stream 76 * */ 77 private void finishParsing() { 78 mParsingFinished = true; 79 mRiter.parserHasFinished(); 80 try { 81 sLogger.fine("Closing reader"); 82 mLogReader.close(); 83 } catch (IOException e) { 84 throw new NoSuchElementException("Parsing log file failed. Could not close stream."); 85 } 86 } 87 private String getComment() throws IOException, LogSyntaxException { 88 StringBuffer comment = new StringBuffer(); 89 String line = null; 90 boolean found = false; 91 do { 92 line = readCvsMessageLine(); 93 if (line == null) { 94 finishParsing(); 95 return comment.toString(); 96 } 97 98 line.trim(); 99 if (line.startsWith(REV_DELIM)) { 100 found = true; 101 } else if (line.matches(".*?" + FILE_DELIM)) { 102 found = true; 103 mCurrentFileHasMoreRevisionds = false; 104 } else { 105 comment.append(line); 106 } 107 } while (!found); 108 return comment.toString(); 109 } 110 111 /*** 112 * Returns the value of an line start tag, e.g. 113 * head: 1.2; in this case "1.2" will be returned 114 * */ 115 private String getValue(String aLine, String aLineStart) 116 throws IOException, LogSyntaxException { 117 118 if (!aLine.startsWith(aLineStart)) { 119 finishParsing(); 120 throw new LogSyntaxException("Expected: " + aLineStart); 121 } 122 123 return aLine.substring(aLineStart.length()); 124 } 125 126 private String ignoreLineTill(String aPattern) throws IOException, LogSyntaxException { 127 String line = null; 128 do { 129 line = readCvsMessageLine(); 130 if (line == null || mParsingFinished) { 131 finishParsing(); 132 return null; 133 } 134 135 line.trim(); 136 } while (!line.startsWith(aPattern)); 137 if (line == null || mParsingFinished) { 138 finishParsing(); 139 throw new LogSyntaxException("Expected pattern:" + aPattern + " not found"); 140 } 141 return line; 142 } 143 144 /*** 145 * Parses the next file in the log; fills the working revision 146 * with necessary entries 147 * @throws LogSyntaxException 148 * @throws IOException 149 */ 150 private void parseNextFileHead() throws LogSyntaxException, IOException { 151 mLookingForNextFile = true; 152 String line = ignoreLineTill(RCS_KEY); 153 if (line == null) { 154 finishParsing(); 155 return; 156 } 157 mLookingForNextFile = false; 158 String rcsFile = getValue(line, RCS_KEY); 159 line = ignoreLineTill(KEYSUBS_KEY); 160 String keywordSubs = getValue(line, KEYSUBS_KEY); 161 if (keywordSubs.equals(BINARY_KEYSUB)) { 162 parseNextFileHead(); 163 return; 164 } 165 line = ignoreLineTill(REVISON_SELECTION); 166 int selectedRevs = parseSelectedRevisions(line); 167 if (selectedRevs == 0) { 168 parseNextFileHead(); 169 return; 170 } 171 ignoreLineTill(REV_DELIM); 172 mCurrentFileHasMoreRevisionds = true; 173 mCurrentFileName = extractFileName(rcsFile); 174 mCurrentPath = extractPath(rcsFile); 175 } 176 177 /*** 178 * Parses the next Revision and puts it into the RevisionBuffer. 179 * PRE: A file head is correctly parsed and has more revisions: 180 * mFileHasRevisions == true && mParsingFinished == false 181 * */ 182 private Revision parseNextRevision() throws LogSyntaxException, IOException { 183 if (!mCurrentFileHasMoreRevisionds) { 184 parseNextFileHead(); 185 } 186 if (mParsingFinished) { 187 return null; 188 } 189 Revision r = new Revision(); 190 r.setPath(mCurrentPath); 191 r.setFileName(mCurrentFileName); 192 String line = ignoreLineTill(REVISION_KEY); 193 if (line == null) { 194 finishParsing(); 195 throw new LogSyntaxException("Could not find Revision Line"); 196 } 197 String revisionName = getValue(line, REVISION_KEY); 198 r.setRevisionName(revisionName); 199 /* 200 * ignore lines till next: 201 * <DATE>; author: <NAME>; state: <STATE>; lines: <ADDED> <REMOVED> 202 * */ 203 line = ignoreLineTill(DATE_KEY); 204 int endOfDateIndex = line.indexOf(';', 6); 205 String dateString = line.substring(6, endOfDateIndex) + " GMT"; 206 Date creationDate = null; 207 try { 208 creationDate = CvsPlugin.convertFromLogTime(dateString); 209 } catch (ParseException e) { 210 throw new LogSyntaxException("Unexpected date format"); 211 } 212 r.setDate(creationDate); 213 // get the author name 214 int endOfAuthorIndex = line.indexOf(';', endOfDateIndex + 1); 215 r.setAuthor(line.substring(endOfDateIndex + 11, endOfAuthorIndex)); 216 // get the file state ( because this revision might be "dead" ) 217 String fileStateTag = 218 line.substring(endOfAuthorIndex + 10, line.indexOf(';', endOfAuthorIndex + 1)); 219 if (fileStateTag.equalsIgnoreCase("dead")) { 220 r.setFileState(FileState.DEAD); 221 } else { 222 r.setFileState(FileState.LIVING); 223 } 224 int beginOfLinesIndex = line.indexOf("lines:", endOfAuthorIndex + 1); 225 if (beginOfLinesIndex >= 0) { 226 StringTokenizer st = new StringTokenizer(line.substring(beginOfLinesIndex + 8)); 227 r.setAdded(Integer.parseInt(st.nextToken())); 228 r.setRemoved(-Integer.parseInt(st.nextToken())); 229 } // get revision comment and check if this revision is last revision of this file 230 r.setComment(getComment()); 231 return r; 232 } 233 private int parseSelectedRevisions(String aLine) { 234 return Integer.parseInt( 235 aLine.substring(aLine.indexOf(SELECTED_REVS) + SELECTED_REVS.length()).trim()); 236 } 237 238 239 240 /*** 241 * Reads a line from the cvs stream, cuts status prefixed and 242 * checks for finishing prefix 243 * */ 244 private String readCvsMessageLine() throws IOException { 245 String line = mLogReader.readLine(); 246 //sLogger.fine("Read:"+line); 247 if (line == null) { 248 finishParsing(); 249 } else { 250 line = removePrefixes(line); 251 if (mLookingForNextFile) { 252 if (line.startsWith(OK_MARKER)) { 253 finishParsing(); 254 return null; 255 } 256 if (line.startsWith(ERROR_MARKER)) { 257 finishParsing(); 258 Bloof.fail("Syntax Error on parsing cvs line:" + line); 259 return null; 260 } 261 return line; 262 263 } else { 264 return line; 265 } 266 } 267 268 return line; 269 } 270 271 private String removePrefixes(String aCvsLogLine) { 272 if (aCvsLogLine.startsWith(MESSAGE_PREFIX)) { 273 String returnLine = aCvsLogLine.substring(MESSAGE_PREFIX.length()); 274 return returnLine; 275 } 276 if (aCvsLogLine.startsWith(E_PREFIX)) { 277 String returnLine = aCvsLogLine.substring(E_PREFIX.length()); 278 return returnLine; 279 } 280 return aCvsLogLine; 281 282 } 283 284 /*** 285 * @see java.lang.Runnable#run( ) 286 */ 287 public void run() { 288 while (!mParsingFinished) { 289 try { 290 Revision r = parseNextRevision(); 291 if (r != null) { 292 mRiter.addRevision(r); 293 } 294 } catch (LogSyntaxException e) { 295 Bloof.fail("Syntax Error on parsing cvs log:" + e.toString()); 296 } catch (IOException e) { 297 Bloof.fail("IO Error on parsing cvs log:" + e.toString()); 298 mParsingFinished = true; 299 } 300 } 301 mRiter.parserHasFinished(); 302 } 303 private static final String BINARY_KEYSUB = "b"; 304 private static final String DATE_KEY = "date: "; 305 private static final String E_PREFIX = "E "; 306 /*** 307 * File delimiter String 308 * */ 309 public static final String FILE_DELIM = 310 "============================================================================="; 311 private static final String KEYSUBS_KEY = "keyword substitution: "; 312 private static final String KOMMA = ","; 313 private static final String MESSAGE_PREFIX = "M "; 314 private static final String OK_MARKER = "ok", ERROR_MARKER = "error"; 315 private static final String RCS_KEY = "RCS file: "; 316 /*** 317 * Revision delimiter String 318 * */ 319 public static final String REV_DELIM = "----------------------------"; 320 private static final String REVISION_KEY = "revision "; 321 private static final String REVISON_SELECTION = "total revisions: "; 322 private static final String SELECTED_REVS = "selected revisions:"; 323 /*** 324 * CVS logfile constants 325 */ 326 private static final String SLASH = "/"; 327 private static Logger sLogger = Logger.getLogger(LogParser.class.getName()); 328 private boolean mCurrentFileHasMoreRevisionds = false; 329 // private ScmAccess mAccess; 330 private BufferedReader mLogReader; 331 private boolean mLookingForNextFile = true; 332 private boolean mParsingFinished = false; 333 /*** 334 * Control flow members 335 * */ 336 private String mCurrentFileName, mCurrentPath; 337 private RevisionIterator mRiter; 338 339 }

This page was automatically generated by Maven