Coverage Report - net.sf.statcvs.input.CvsRevisionParser
 
Classes in this File Line Coverage Branch Coverage Complexity
CvsRevisionParser
92%
69/75
76%
26/34
4.857
 
 1  
 /*
 2  
     StatCvs - CVS statistics generation 
 3  
     Copyright (C) 2002  Lukasz Pekacki <lukasz@pekacki.de>
 4  
     http://statcvs.sf.net/
 5  
     
 6  
     This library is free software; you can redistribute it and/or
 7  
     modify it under the terms of the GNU Lesser General Public
 8  
     License as published by the Free Software Foundation; either
 9  
     version 2.1 of the License, or (at your option) any later version.
 10  
 
 11  
     This library is distributed in the hope that it will be useful,
 12  
     but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14  
     Lesser General Public License for more details.
 15  
 
 16  
     You should have received a copy of the GNU Lesser General Public
 17  
     License along with this library; if not, write to the Free Software
 18  
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 19  
     
 20  
         $RCSfile: CvsRevisionParser.java,v $ 
 21  
         Created on $Date: 2008/04/02 11:22:15 $ 
 22  
 */
 23  
 
 24  
 package net.sf.statcvs.input;
 25  
 
 26  
 import java.io.IOException;
 27  
 import java.text.ParseException;
 28  
 import java.text.SimpleDateFormat;
 29  
 import java.util.Date;
 30  
 import java.util.Locale;
 31  
 import java.util.StringTokenizer;
 32  
 import java.util.logging.Logger;
 33  
 
 34  
 import net.sf.statcvs.util.LookaheadReader;
 35  
 
 36  
 /**
 37  
  * Parses all revisions of one file.
 38  
  * 
 39  
  * @author Anja Jentzsch
 40  
  * @author Richard Cyganiak
 41  
  * @version $Id: CvsRevisionParser.java,v 1.41 2008/04/02 11:22:15 benoitx Exp $
 42  
  */
 43  
 public class CvsRevisionParser {
 44  
 
 45  32
     private static Logger logger = Logger.getLogger(CvsRevisionParser.class.getName());
 46  
 
 47  
     /**
 48  
      * Revision Delimiter in CVS log file
 49  
      */
 50  
     public static final String REVISION_DELIMITER = "----------------------------";
 51  
     /**
 52  
      * File Delimiter in CVS log file
 53  
      */
 54  
     public static final String FILE_DELIMITER = "======================================" + "=======================================";
 55  
 
 56  
     private static final String OLD_LOG_TIMESTAMP_FORMAT = "yyyy/MM/dd HH:mm:ss zzz";
 57  
     private static final String NEW_LOG_TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
 58  16
     private static final Locale LOG_TIMESTAMP_LOCALE = Locale.US;
 59  16
     private static SimpleDateFormat oldLogTimeFormat = new SimpleDateFormat(OLD_LOG_TIMESTAMP_FORMAT, LOG_TIMESTAMP_LOCALE);
 60  16
     private static SimpleDateFormat newLogTimeFormat = new SimpleDateFormat(NEW_LOG_TIMESTAMP_FORMAT, LOG_TIMESTAMP_LOCALE);
 61  
     private final LookaheadReader logReader;
 62  
     private final CvsLogBuilder builder;
 63  184
     private boolean fileDone = false;
 64  
     private RevisionData revision;
 65  
 
 66  
     /**
 67  
      * Default Constructor CvsRevisionParser.
 68  
      * @param logReader the reader
 69  
      * @param builder a <tt>Builder</tt> for the creation process
 70  
      */
 71  184
     public CvsRevisionParser(final LookaheadReader logReader, final CvsLogBuilder builder) {
 72  184
         this.logReader = logReader;
 73  184
         this.builder = builder;
 74  184
     }
 75  
 
 76  
     /**
 77  
      * Parses the list of revisions for one file
 78  
      * @throws LogSyntaxException on syntax error in the log
 79  
      * @throws IOException on read error
 80  
      */
 81  
     public void parse() throws LogSyntaxException, IOException {
 82  184
         this.logReader.nextLine();
 83  
         do {
 84  224
             revision = new RevisionData();
 85  224
             parseRevision();
 86  216
             builder.buildRevision(revision);
 87  216
         } while (!fileDone);
 88  176
     }
 89  
 
 90  
     private void parseRevision() throws IOException, LogSyntaxException {
 91  224
         if (!isNewRevisionLine(logReader.getCurrentLine())) {
 92  0
             throw new LogSyntaxException("expected 'revision' but found '" + logReader.getCurrentLine() + "' in line " + logReader.getLineNumber());
 93  
         }
 94  224
         final String revNo = logReader.getCurrentLine().substring("revision ".length());
 95  224
         revision.setRevisionNumber(revNo);
 96  224
         parseDateLine(this.logReader.nextLine());
 97  224
         if (this.logReader.nextLine().startsWith("branches:")) {
 98  16
             this.logReader.nextLine();
 99  
         }
 100  224
         final StringBuffer comment = new StringBuffer();
 101  
         while (true) {
 102  456
             final String line = logReader.getCurrentLine();
 103  456
             if (REVISION_DELIMITER.equals(line)) {
 104  48
                 final String next = this.logReader.nextLine();
 105  48
                 if (isNewRevisionLine(next)) {
 106  40
                     revision.setComment(comment.toString());
 107  40
                     return;
 108  
                 }
 109  58
             } else if (FILE_DELIMITER.equals(line)) {
 110  176
                 if (!this.logReader.hasNextLine() || "".equals(this.logReader.nextLine())) {
 111  176
                     this.revision.setComment(comment.toString());
 112  176
                     this.fileDone = true;
 113  176
                     return;
 114  
                 }
 115  
             } else {
 116  232
                 this.logReader.nextLine();
 117  
             }
 118  232
             if (comment.length() != 0) {
 119  16
                 comment.append('\n');
 120  
             }
 121  232
             comment.append(line);
 122  203
         }
 123  
     }
 124  
 
 125  
     private void parseDateLine(final String line) throws LogSyntaxException {
 126  
 
 127  
         // date: 2000/06/19 04:56:21;  author: somebody;  state: Exp;  lines: +114 -45
 128  
 
 129  
         // get the creation date
 130  224
         final int endOfDateIndex = line.indexOf(';', 6);
 131  224
         final String dateString = line.substring(6, endOfDateIndex) + " GMT";
 132  224
         final Date date = convertFromLogTime(dateString);
 133  224
         if (date == null) {
 134  0
             throw new LogSyntaxException("unexpected date format in line " + logReader.getLineNumber());
 135  
         }
 136  224
         revision.setDate(date);
 137  
 
 138  
         // get the author name
 139  224
         final int endOfAuthorIndex = line.indexOf(';', endOfDateIndex + 1);
 140  224
         revision.setLoginName(line.substring(endOfDateIndex + 11, endOfAuthorIndex));
 141  
 
 142  
         // get the file state (because this revision might be "dead")
 143  224
         final String fileState = line.substring(endOfAuthorIndex + 10, line.indexOf(';', endOfAuthorIndex + 1));
 144  224
         if (isDeadState(fileState)) {
 145  8
             revision.setStateDead();
 146  8
             return;
 147  
         }
 148  216
         revision.setStateExp();
 149  
 
 150  
         // is this an initial revision?
 151  216
         final int beginOfLinesIndex = line.indexOf("lines:", endOfAuthorIndex + 1);
 152  216
         if (beginOfLinesIndex < 0) {
 153  176
             return;
 154  
         }
 155  
 
 156  
         // get lines added and lines removed
 157  40
         final StringTokenizer st = new StringTokenizer(line.substring(beginOfLinesIndex + 8));
 158  40
         final int linesAdded = Integer.parseInt(st.nextToken());
 159  40
         String removed = st.nextToken();
 160  40
         if (removed.indexOf(';') >= 0) {
 161  8
             removed = removed.substring(0, removed.indexOf(';'));
 162  
         }
 163  40
         final int linesRemoved = -Integer.parseInt(removed);
 164  40
         revision.setLines(linesAdded, linesRemoved);
 165  40
     }
 166  
 
 167  
     private boolean isNewRevisionLine(final String line) {
 168  272
         return line.startsWith("revision ");
 169  
     }
 170  
 
 171  
     private boolean isDeadState(final String state) {
 172  224
         if ("dead".equals(state)) {
 173  8
             return true;
 174  
         }
 175  216
         if ("Exp".equals(state) || "Stab".equals(state) || "Rel".equals(state)) {
 176  216
             return false;
 177  
         }
 178  0
         logger.warning("unknown file state '" + state + "' at line " + this.logReader.getLineNumber());
 179  0
         return false;
 180  
     }
 181  
 
 182  
     /**
 183  
      * Returns a date from a given modTime String of a cvs logfile
 184  
      * @param modTime modTime String of a cvs logfile
 185  
      * @return Date date from a given modTime String of a cvs logfile
 186  
      */
 187  
     private static Date convertFromLogTime(final String modTime) {
 188  
         try {
 189  224
             return oldLogTimeFormat.parse(modTime);
 190  8
         } catch (final ParseException e) {
 191  
             // try other format
 192  
         }
 193  
         try {
 194  8
             return newLogTimeFormat.parse(modTime);
 195  0
         } catch (final ParseException e) {
 196  0
             return null;
 197  
         }
 198  
     }
 199  
 }