2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.netbeans.lib.cvsclient.command.log;
45
import java.text.SimpleDateFormat;
46
import java.text.ParseException;
48
import org.netbeans.lib.cvsclient.command.*;
49
import org.netbeans.lib.cvsclient.event.*;
50
import org.netbeans.lib.cvsclient.util.*;
53
* Handles the building of a log information object and the firing of
54
* events when complete objects are built.
55
* @author Milos Kleint
57
public class LogBuilder implements Builder {
58
private static final String LOGGING_DIR = ": Logging "; //NOI18N
59
private static final String RCS_FILE = "RCS file: "; //NOI18N
60
private static final String WORK_FILE = "Working file: "; //NOI18N
61
private static final String REV_HEAD = "head: "; //NOI18N
62
private static final String BRANCH = "branch: "; //NOI18N
63
private static final String LOCKS = "locks: "; //NOI18N
64
private static final String ACCESS_LIST = "access list: "; //NOI18N
65
private static final String SYM_NAME = "symbolic names:"; //NOI18N
66
private static final String KEYWORD_SUBST = "keyword substitution: "; //NOI18N
67
private static final String TOTAL_REV = "total revisions: "; //NOI18N
68
private static final String SEL_REV = ";\tselected revisions: "; //NOI18N
69
private static final String DESCRIPTION = "description:"; //NOI18N
70
private static final String REVISION = "revision "; //NOI18N
71
private static final String DATE = "date: "; //NOI18N
72
private static final String BRANCHES = "branches: "; //NOI18N
73
private static final String AUTHOR = "author: "; //NOI18N
74
private static final String STATE = "state: "; //NOI18N
75
private static final String LINES = "lines: "; //NOI18N
76
private static final String COMMITID = "commitid: "; //NOI18N
77
private static final String SPLITTER = "----------------------------"; //NOI18N
78
private static final String FINAL_SPLIT = "============================================================================="; //NOI18N
79
private static final String ERROR = ": nothing known about "; //NOI18N
80
private static final String NO_FILE = "no file"; //NOI18N
82
* The event manager to use
84
protected EventManager eventManager;
85
protected BasicCommand logCommand;
87
* The log object that is currently being built
89
protected LogInformation logInfo;
90
protected LogInformation.Revision revision;
92
* The directory in which the file being processed lives. This is
93
* relative to the local directory
95
protected String fileDirectory;
96
private boolean addingSymNames;
97
private boolean addingDescription;
98
private boolean addingLogMessage;
99
private StringBuffer tempBuffer = null;
101
private List messageList;
103
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); //NOI18N
105
public LogBuilder(EventManager eventMan, BasicCommand command) {
106
logCommand = command;
107
eventManager = eventMan;
108
addingSymNames = false;
109
addingDescription = false;
110
addingLogMessage = false;
113
messageList = new ArrayList(500);
116
public void outputDone() {
117
if (logInfo != null) {
118
eventManager.fireCVSEvent(new FileInfoEvent(this, logInfo));
124
public void parseLine(String line, boolean isErrorMessage) {
125
if (line.equals(FINAL_SPLIT)) {
126
if (addingDescription) {
127
addingDescription = false;
128
logInfo.setDescription(tempBuffer.toString());
130
if (addingLogMessage) {
131
addingLogMessage = false;
132
revision.setMessage(CommandUtils.findUniqueString(tempBuffer.toString(), messageList));
134
if (revision != null) {
135
logInfo.addRevision(revision);
138
// fire the event and exit
139
if (logInfo != null) {
140
eventManager.fireCVSEvent(new FileInfoEvent(this, logInfo));
146
if (addingLogMessage) {
147
// first check for the branches tag
148
if (line.startsWith(BRANCHES)) {
149
processBranches(line.substring(BRANCHES.length()));
152
processLogMessage(line);
156
if (addingSymNames) {
157
processSymbolicNames(line);
159
if (addingDescription) {
160
processDescription(line);
162
// revision stuff first -> will be the most common to parse
163
if (line.startsWith(REVISION)) {
164
processRevisionStart(line);
166
if (line.startsWith(DATE)) {
167
processRevisionDate(line);
170
if (line.startsWith(KEYWORD_SUBST)) {
171
logInfo.setKeywordSubstitution(line.substring(KEYWORD_SUBST.length()).trim().intern());
172
addingSymNames = false;
176
if (line.startsWith(DESCRIPTION)) {
177
tempBuffer = new StringBuffer(line.substring(DESCRIPTION.length()));
178
addingDescription = true;
181
if (line.indexOf(LOGGING_DIR) >= 0) {
182
fileDirectory = line.substring(line.indexOf(LOGGING_DIR) + LOGGING_DIR.length()).trim();
185
if (line.startsWith(RCS_FILE)) {
186
processRcsFile(line.substring(RCS_FILE.length()));
189
if (line.startsWith(WORK_FILE)) {
190
processWorkingFile(line.substring(WORK_FILE.length()));
193
if (line.startsWith(REV_HEAD)) {
194
logInfo.setHeadRevision(line.substring(REV_HEAD.length()).trim().intern());
197
if (line.startsWith(BRANCH)) {
198
logInfo.setBranch(line.substring(BRANCH.length()).trim().intern());
200
if (line.startsWith(LOCKS)) {
201
logInfo.setLocks(line.substring(LOCKS.length()).trim().intern());
203
if (line.startsWith(ACCESS_LIST)) {
204
logInfo.setAccessList(line.substring(ACCESS_LIST.length()).trim().intern());
206
if (line.startsWith(SYM_NAME)) {
207
addingSymNames = true;
209
if (line.startsWith(TOTAL_REV)) {
210
int ind = line.indexOf(';');
212
// no selected revisions here..
213
logInfo.setTotalRevisions(line.substring(TOTAL_REV.length()).trim().intern());
214
logInfo.setSelectedRevisions("0"); //NOI18N
217
String total = line.substring(0, ind);
218
String select = line.substring(ind, line.length());
219
logInfo.setTotalRevisions(total.substring(TOTAL_REV.length()).trim().intern());
220
logInfo.setSelectedRevisions(select.substring(SEL_REV.length()).trim().intern());
225
private String findUniqueString(String name, List list) {
229
int index = list.indexOf(name);
231
return (String)list.get(index);
234
String newName = name;
240
private void processRcsFile(String line) {
241
if (logInfo != null) {
242
//do fire logcreated event;
244
logInfo = new LogInformation();
245
logInfo.setRepositoryFilename(line.trim());
248
private void processWorkingFile(String line) {
249
String fileName = line.trim();
250
if (fileName.startsWith(NO_FILE)) {
251
fileName = fileName.substring(8);
254
logInfo.setFile(createFile(line));
257
private void processBranches(String line) {
258
int ind = line.lastIndexOf(';');
260
line = line.substring(0, ind);
262
revision.setBranches(line.trim());
265
private void processLogMessage(String line) {
266
if (line.startsWith(SPLITTER)) {
267
addingLogMessage = false;
268
revision.setMessage(findUniqueString(tempBuffer.toString(), messageList));
271
tempBuffer.append(line + "\n"); //NOI18N
274
private void processSymbolicNames(String line) {
275
if (!line.startsWith(KEYWORD_SUBST)) {
277
int index = line.indexOf(':');
279
String symName = line.substring(0, index).trim();
280
String revName = line.substring(index + 1, line.length()).trim();
281
logInfo.addSymbolicName(symName.intern(), revName.intern());
286
private void processDescription(String line) {
287
if (line.startsWith(SPLITTER)) {
288
addingDescription = false;
289
logInfo.setDescription(tempBuffer.toString());
292
tempBuffer.append(line);
295
private void processRevisionStart(String line) {
296
if (revision != null) {
297
logInfo.addRevision(revision);
299
revision = logInfo.createNewRevision(
300
line.substring(REVISION.length()).intern());
303
private void processRevisionDate(String line) {
304
StringTokenizer tokenizer = new StringTokenizer(line, ";", false); //NOI18N
305
while (tokenizer.hasMoreTokens()) {
306
String token = tokenizer.nextToken().trim();
307
if (token.startsWith(DATE)) {
308
String dateString = token.substring(DATE.length());
311
// some servers use dashes to separate date components, so replace with slashes
312
// also add a default GMT timezone at the end, if the server already put one in this one will be ignored by the parser
313
dateString = dateString.replace('/', '-') + " +0000"; //NOI18N
314
date = dateFormat.parse(dateString);
315
} catch (ParseException e) {
316
BugLog.getInstance().bug("Couldn't parse date " + dateString); //NOI18N
318
revision.setDate(date, dateString);
320
else if (token.startsWith(AUTHOR)) revision.setAuthor(token.substring(AUTHOR.length()));
321
else if (token.startsWith(STATE)) revision.setState(token.substring(STATE.length()));
322
else if (token.startsWith(LINES)) revision.setLines(token.substring(LINES.length()));
323
else if (token.startsWith(COMMITID)) revision.setCommitID(token.substring(COMMITID.length()));
325
addingLogMessage = true;
326
tempBuffer = new StringBuffer();
330
* @param fileName relative URL-path to command execution directory
332
protected File createFile(String fileName) {
333
StringBuffer path = new StringBuffer();
334
path.append(logCommand.getLocalDirectory());
335
path.append(File.separator);
337
path.append(fileName.replace('/', File.separatorChar)); // NOI18N
338
return new File(path.toString());
341
public void parseEnhancedMessage(String key, Object value) {