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  24
     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  12
     private static final Locale LOG_TIMESTAMP_LOCALE = Locale.US;
 59  12
     private static SimpleDateFormat oldLogTimeFormat = new SimpleDateFormat(OLD_LOG_TIMESTAMP_FORMAT, LOG_TIMESTAMP_LOCALE);
 60  12
     private static SimpleDateFormat newLogTimeFormat = new SimpleDateFormat(NEW_LOG_TIMESTAMP_FORMAT, LOG_TIMESTAMP_LOCALE);
 61  
     private final LookaheadReader logReader;
 62  
     private final CvsLogBuilder builder;
 63  138
     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  138
     public CvsRevisionParser(final LookaheadReader logReader, final CvsLogBuilder builder) {
 72  138
         this.logReader = logReader;
 73  138
         this.builder = builder;
 74  138
     }
 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  138
         this.logReader.nextLine();
 83  
         do {
 84  168
             revision = new RevisionData();
 85  168
             parseRevision();
 86  162
             builder.buildRevision(revision);
 87  162
         } while (!fileDone);
 88  132
     }
 89  
 
 90  
     private void parseRevision() throws IOException, LogSyntaxException {
 91  168
         if (!isNewRevisionLine(logReader.getCurrentLine())) {
 92  0
             throw new LogSyntaxException("expected 'revision' but found '" + logReader.getCurrentLine() + "' in line " + logReader.getLineNumber());
 93  
         }
 94  168
         final String revNo = logReader.getCurrentLine().substring("revision ".length());
 95  168
         revision.setRevisionNumber(revNo);
 96  168
         parseDateLine(this.logReader.nextLine());
 97  168
         if (this.logReader.nextLine().startsWith("branches:")) {
 98  12
             this.logReader.nextLine();
 99  
         }
 100  168
         final StringBuffer comment = new StringBuffer();
 101  
         while (true) {
 102  342
             final String line = logReader.getCurrentLine();
 103  342
             if (REVISION_DELIMITER.equals(line)) {
 104  36
                 final String next = this.logReader.nextLine();
 105  36
                 if (isNewRevisionLine(next)) {
 106  30
                     revision.setComment(comment.toString());
 107  30
                     return;
 108  
                 }
 109  56
             } else if (FILE_DELIMITER.equals(line)) {
 110  132
                 if (!this.logReader.hasNextLine() || "".equals(this.logReader.nextLine())) {
 111  132
                     this.revision.setComment(comment.toString());
 112  132
                     this.fileDone = true;
 113  132
                     return;
 114  
                 }
 115  
             } else {
 116  174
                 this.logReader.nextLine();
 117  
             }
 118  174
             if (comment.length() != 0) {
 119  12
                 comment.append('\n');
 120  
             }
 121  174
             comment.append(line);
 122  145
         }
 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  168
         final int endOfDateIndex = line.indexOf(';', 6);
 131  168
         final String dateString = line.substring(6, endOfDateIndex) + " GMT";
 132  168
         final Date date = convertFromLogTime(dateString);
 133  168
         if (date == null) {
 134  0
             throw new LogSyntaxException("unexpected date format in line " + logReader.getLineNumber());
 135  
         }
 136  168
         revision.setDate(date);
 137  
 
 138  
         // get the author name
 139  168
         final int endOfAuthorIndex = line.indexOf(';', endOfDateIndex + 1);
 140  168
         revision.setLoginName(line.substring(endOfDateIndex + 11, endOfAuthorIndex));
 141  
 
 142  
         // get the file state (because this revision might be "dead")
 143  168
         final String fileState = line.substring(endOfAuthorIndex + 10, line.indexOf(';', endOfAuthorIndex + 1));
 144  168
         if (isDeadState(fileState)) {
 145  6
             revision.setStateDead();
 146  6
             return;
 147  
         }
 148  162
         revision.setStateExp();
 149  
 
 150  
         // is this an initial revision?
 151  162
         final int beginOfLinesIndex = line.indexOf("lines:", endOfAuthorIndex + 1);
 152  162
         if (beginOfLinesIndex < 0) {
 153  132
             return;
 154  
         }
 155  
 
 156  
         // get lines added and lines removed
 157  30
         final StringTokenizer st = new StringTokenizer(line.substring(beginOfLinesIndex + 8));
 158  30
         final int linesAdded = Integer.parseInt(st.nextToken());
 159  30
         String removed = st.nextToken();
 160  30
         if (removed.indexOf(';') >= 0) {
 161  6
             removed = removed.substring(0, removed.indexOf(';'));
 162  
         }
 163  30
         final int linesRemoved = -Integer.parseInt(removed);
 164  30
         revision.setLines(linesAdded, linesRemoved);
 165  30
     }
 166  
 
 167  
     private boolean isNewRevisionLine(final String line) {
 168  204
         return line.startsWith("revision ");
 169  
     }
 170  
 
 171  
     private boolean isDeadState(final String state) {
 172  168
         if ("dead".equals(state)) {
 173  6
             return true;
 174  
         }
 175  162
         if ("Exp".equals(state) || "Stab".equals(state) || "Rel".equals(state)) {
 176  162
             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  168
             return oldLogTimeFormat.parse(modTime);
 190  6
         } catch (final ParseException e) {
 191  
             // try other format
 192  
         }
 193  
         try {
 194  6
             return newLogTimeFormat.parse(modTime);
 195  0
         } catch (final ParseException e) {
 196  0
             return null;
 197  
         }
 198  
     }
 199  
 }