View Javadoc

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      private static Logger logger = Logger.getLogger(CvsFileBlockParser.class.getName());
48      private final LookaheadReader logReader;
49      private final CvsLogBuilder builder;
50      private boolean isLogWithoutSymbolicNames = false;
51      private final boolean isFirstFile;
52      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      public CvsFileBlockParser(final LookaheadReader logReader, final CvsLogBuilder builder, final boolean isFirstFile) {
61          this.logReader = logReader;
62          this.builder = builder;
63          this.isFirstFile = isFirstFile;
64      }
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          final String rcsFile = parseSingleLine(this.logReader.getCurrentLine(), "RCS file: ");
74          final String workingFile = parseSingleLine(this.logReader.nextLine(), "Working file: ");
75          final boolean isInAttic = CvsLogUtils.isInAttic(rcsFile, workingFile);
76          requireLine(this.logReader.nextLine(), "head:");
77          requireLine(this.logReader.nextLine(), "branch:");
78          requireLine(this.logReader.nextLine(), "locks:");
79          parseLocksAndAccessList();
80          parseSymbolicNames();
81          final String keywordSubst = parseSingleLine(this.logReader.getCurrentLine(), "keyword substitution: ");
82          boolean isBinary = false;
83          try {
84              isBinary = CvsLogUtils.isBinaryKeywordSubst(keywordSubst);
85          } catch (final IllegalArgumentException unknownKeywordSubst) {
86              logger.warning("unknown keyword substitution '" + keywordSubst + "' in line " + this.logReader.getLineNumber());
87          }
88          requireLine(this.logReader.nextLine(), "total revisions:");
89          parseDescription();
90          if (this.isFirstFile) {
91              this.builder.buildModule(CvsLogUtils.getModuleName(rcsFile, workingFile));
92          }
93          this.builder.buildFile(workingFile, isBinary, isInAttic, this.revBySymNames);
94          if (!CvsRevisionParser.FILE_DELIMITER.equals(this.logReader.getCurrentLine())) {
95              new CvsRevisionParser(this.logReader, this.builder).parse();
96          }
97      }
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         return this.isLogWithoutSymbolicNames;
108     }
109 
110     private String parseSingleLine(final String line, final String lineStart) throws LogSyntaxException {
111 
112         if (!line.startsWith(lineStart)) {
113             throw new LogSyntaxException("line " + this.logReader.getLineNumber() + ": expected '" + lineStart + "' but found '" + line + "'");
114         }
115 
116         return line.substring(lineStart.length());
117     }
118 
119     private void requireLine(final String line, final String lineStart) throws LogSyntaxException {
120 
121         parseSingleLine(line, lineStart); // ignore this line
122     }
123 
124     private void parseSymbolicNames() throws IOException {
125         if (this.logReader.getCurrentLine().startsWith("keyword substitution: ")) {
126             return;
127         }
128         String line;
129         if (this.logReader.getCurrentLine().equals("symbolic names:")) {
130             line = this.logReader.nextLine();
131         } else {
132             this.isLogWithoutSymbolicNames = true;
133             line = this.logReader.getCurrentLine();
134         }
135         while (line != null && !line.startsWith("keyword substitution: ")) {
136             final int firstColon = line.indexOf(':');
137             final String tagName = line.substring(1, firstColon);
138             final String tagRevision = line.substring(firstColon + 2);
139             this.revBySymNames.put(tagName, tagRevision);
140             line = this.logReader.nextLine();
141         }
142     }
143 
144     private void parseLocksAndAccessList() throws IOException {
145         while (!"access list:".equals(this.logReader.nextLine())) {
146             // ignore locks lines until "access list:" is reached
147         }
148         String line;
149         do {
150             line = this.logReader.nextLine();
151             // ignore access list lines until next section is reached
152         } while (!line.equals("symbolic names:") && !line.startsWith("keyword substitution: "));
153     }
154 
155     private void parseDescription() throws LogSyntaxException, IOException {
156         final String line = this.logReader.nextLine();
157         if (line.equals(CvsRevisionParser.FILE_DELIMITER)) {
158             throw new LogSyntaxException("line " + this.logReader.getLineNumber() + ": missing description; please don't use the -h switch of 'cvs log'!");
159         }
160         requireLine(this.logReader.getCurrentLine(), "description:");
161         while (!isDescriptionDelimiter(this.logReader.nextLine())) {
162             // ignore description lines
163         }
164     }
165 
166     private boolean isDescriptionDelimiter(final String line) {
167         return CvsRevisionParser.REVISION_DELIMITER.equals(line) || CvsRevisionParser.FILE_DELIMITER.equals(line);
168     }
169 }