Coverage Report - net.sf.statcvs.input.CvsFileBlockParser
 
Classes in this File Line Coverage Branch Coverage Complexity
CvsFileBlockParser
89%
54/61
86%
24/28
2.556
 
 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: CvsFileBlockParser.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.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  
  * Parses the information of one file from a CVS logfile
 36  
  * {@link net.sf.statcvs.util.LookaheadReader}. A {@link Builder} must be
 37  
  * specified which constructs some representation of that file. The lookahead
 38  
  * reader must be positioned on the first line of the file's section in the
 39  
  * log ("RCS file: ...").
 40  
  * 
 41  
  * @author Anja Jentzsch
 42  
  * @author Richard Cyganiak
 43  
  * @author Tammo van Lessen
 44  
  * @version $Id: CvsFileBlockParser.java,v 1.47 2008/04/02 11:22:15 benoitx Exp $
 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  
      * Default Constructor CvsFileBlockParser.
 56  
      * @param logReader reader
 57  
      * @param builder a <tt>Builder</tt> for the creation process
 58  
      * @param isFirstFile Is this the first file of the log?
 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  
      * Parses one file from the input reader.
 68  
      * 
 69  
      * @throws LogSyntaxException on syntax error
 70  
      * @throws IOException on read/write error
 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  
      * Returns <tt>true</tt> if the log was generated
 101  
      * with the "-N" switch of "cvs log"
 102  
      * 
 103  
      * @return Returns <tt>true</tt> if the log was generated
 104  
      * with the "-N" switch of "cvs log"
 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); // ignore this line
 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  
             // ignore locks lines until "access list:" is reached
 147  
         }
 148  
         String line;
 149  
         do {
 150  216
             line = this.logReader.nextLine();
 151  
             // ignore access list lines until next section is reached
 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  
             // ignore description lines
 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  
 }