/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.netbeans.lib.cvsclient.command.log; import java.io.File; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.StringTokenizer; import org.netbeans.lib.cvsclient.command.BasicCommand; import org.netbeans.lib.cvsclient.command.Builder; import org.netbeans.lib.cvsclient.command.CommandUtils; import org.netbeans.lib.cvsclient.event.EventManager; import org.netbeans.lib.cvsclient.event.FileInfoEvent; import org.netbeans.lib.cvsclient.util.BugLog; /** * Handles the building of a log information object and the firing of events when complete objects are built. * * @author Milos Kleint */ public class LogBuilder implements Builder { private static final String LOGGING_DIR = ": Logging "; // NOI18N private static final String RCS_FILE = "RCS file: "; // NOI18N private static final String WORK_FILE = "Working file: "; // NOI18N private static final String REV_HEAD = "head: "; // NOI18N private static final String BRANCH = "branch: "; // NOI18N private static final String LOCKS = "locks: "; // NOI18N private static final String ACCESS_LIST = "access list: "; // NOI18N private static final String SYM_NAME = "symbolic names:"; // NOI18N private static final String KEYWORD_SUBST = "keyword substitution: "; // NOI18N private static final String TOTAL_REV = "total revisions: "; // NOI18N private static final String SEL_REV = ";\tselected revisions: "; // NOI18N private static final String DESCRIPTION = "description:"; // NOI18N private static final String REVISION = "revision "; // NOI18N private static final String DATE = "date: "; // NOI18N private static final String BRANCHES = "branches: "; // NOI18N private static final String AUTHOR = "author: "; // NOI18N private static final String STATE = "state: "; // NOI18N private static final String LINES = "lines: "; // NOI18N private static final String COMMITID = "commitid: "; // NOI18N private static final String SPLITTER = "----------------------------"; // NOI18N private static final String FINAL_SPLIT = "============================================================================="; // NOI18N private static final String ERROR = ": nothing known about "; // NOI18N private static final String NO_FILE = "no file"; // NOI18N /** * The event manager to use */ protected EventManager eventManager; protected BasicCommand logCommand; /** * The log object that is currently being built */ protected LogInformation logInfo; protected LogInformation.Revision revision; /** * The directory in which the file being processed lives. This is relative to the local directory */ protected String fileDirectory; private boolean addingSymNames; private boolean addingDescription; private boolean addingLogMessage; private StringBuffer tempBuffer = null; private List messageList; private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); // NOI18N public LogBuilder(EventManager eventMan, BasicCommand command) { logCommand = command; eventManager = eventMan; addingSymNames = false; addingDescription = false; addingLogMessage = false; logInfo = null; revision = null; messageList = new ArrayList(500); } @Override public void outputDone() { if (logInfo != null) { eventManager.fireCVSEvent(new FileInfoEvent(this, logInfo)); logInfo = null; messageList = null; } } @Override public void parseLine(String line, boolean isErrorMessage) { if (line.equals(FINAL_SPLIT)) { if (addingDescription) { addingDescription = false; logInfo.setDescription(tempBuffer.toString()); } if (addingLogMessage) { addingLogMessage = false; revision.setMessage(CommandUtils.findUniqueString(tempBuffer.toString(), messageList)); } if (revision != null) { logInfo.addRevision(revision); revision = null; } // fire the event and exit if (logInfo != null) { eventManager.fireCVSEvent(new FileInfoEvent(this, logInfo)); logInfo = null; tempBuffer = null; } return; } if (addingLogMessage) { // first check for the branches tag if (line.startsWith(BRANCHES)) { processBranches(line.substring(BRANCHES.length())); } else { processLogMessage(line); return; } } if (addingSymNames) { processSymbolicNames(line); } if (addingDescription) { processDescription(line); } // revision stuff first -> will be the most common to parse if (line.startsWith(REVISION)) { processRevisionStart(line); } if (line.startsWith(DATE)) { processRevisionDate(line); } if (line.startsWith(KEYWORD_SUBST)) { logInfo.setKeywordSubstitution(line.substring(KEYWORD_SUBST.length()).trim().intern()); addingSymNames = false; return; } if (line.startsWith(DESCRIPTION)) { tempBuffer = new StringBuffer(line.substring(DESCRIPTION.length())); addingDescription = true; } if (line.indexOf(LOGGING_DIR) >= 0) { fileDirectory = line.substring(line.indexOf(LOGGING_DIR) + LOGGING_DIR.length()).trim(); return; } if (line.startsWith(RCS_FILE)) { processRcsFile(line.substring(RCS_FILE.length())); return; } if (line.startsWith(WORK_FILE)) { processWorkingFile(line.substring(WORK_FILE.length())); return; } if (line.startsWith(REV_HEAD)) { logInfo.setHeadRevision(line.substring(REV_HEAD.length()).trim().intern()); return; } if (line.startsWith(BRANCH)) { logInfo.setBranch(line.substring(BRANCH.length()).trim().intern()); } if (line.startsWith(LOCKS)) { logInfo.setLocks(line.substring(LOCKS.length()).trim().intern()); } if (line.startsWith(ACCESS_LIST)) { logInfo.setAccessList(line.substring(ACCESS_LIST.length()).trim().intern()); } if (line.startsWith(SYM_NAME)) { addingSymNames = true; } if (line.startsWith(TOTAL_REV)) { int ind = line.indexOf(';'); if (ind < 0) { // no selected revisions here.. logInfo.setTotalRevisions(line.substring(TOTAL_REV.length()).trim().intern()); logInfo.setSelectedRevisions("0"); // NOI18N } else { String total = line.substring(0, ind); String select = line.substring(ind, line.length()); logInfo.setTotalRevisions(total.substring(TOTAL_REV.length()).trim().intern()); logInfo.setSelectedRevisions(select.substring(SEL_REV.length()).trim().intern()); } } } private String findUniqueString(String name, List list) { if (name == null) { return null; } int index = list.indexOf(name); if (index >= 0) { return (String) list.get(index); } else { String newName = name; list.add(newName); return newName; } } private void processRcsFile(String line) { if (logInfo != null) { // do fire logcreated event; } logInfo = new LogInformation(); logInfo.setRepositoryFilename(line.trim()); } private void processWorkingFile(String line) { String fileName = line.trim(); if (fileName.startsWith(NO_FILE)) { fileName = fileName.substring(8); } logInfo.setFile(createFile(line)); } private void processBranches(String line) { int ind = line.lastIndexOf(';'); if (ind > 0) { line = line.substring(0, ind); } revision.setBranches(line.trim()); } private void processLogMessage(String line) { if (line.startsWith(SPLITTER)) { addingLogMessage = false; revision.setMessage(findUniqueString(tempBuffer.toString(), messageList)); return; } tempBuffer.append(line + "\n"); // NOI18N } private void processSymbolicNames(String line) { if (!line.startsWith(KEYWORD_SUBST)) { line = line.trim(); int index = line.indexOf(':'); if (index > 0) { String symName = line.substring(0, index).trim(); String revName = line.substring(index + 1, line.length()).trim(); logInfo.addSymbolicName(symName.intern(), revName.intern()); } } } private void processDescription(String line) { if (line.startsWith(SPLITTER)) { addingDescription = false; logInfo.setDescription(tempBuffer.toString()); return; } tempBuffer.append(line); } private void processRevisionStart(String line) { if (revision != null) { logInfo.addRevision(revision); } revision = logInfo.createNewRevision(line.substring(REVISION.length()).intern()); } private void processRevisionDate(String line) { StringTokenizer tokenizer = new StringTokenizer(line, ";", false); // NOI18N while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken().trim(); if (token.startsWith(DATE)) { String dateString = token.substring(DATE.length()); Date date = null; try { // some servers use dashes to separate date components, so replace with slashes // also add a default GMT timezone at the end, if the server already put one in this one will be ignored by the parser dateString = dateString.replace('/', '-') + " +0000"; // NOI18N date = dateFormat.parse(dateString); } catch (ParseException e) { BugLog.getInstance().bug("Couldn't parse date " + dateString); // NOI18N } revision.setDate(date, dateString); } else if (token.startsWith(AUTHOR)) { revision.setAuthor(token.substring(AUTHOR.length())); } else if (token.startsWith(STATE)) { revision.setState(token.substring(STATE.length())); } else if (token.startsWith(LINES)) { revision.setLines(token.substring(LINES.length())); } else if (token.startsWith(COMMITID)) { revision.setCommitID(token.substring(COMMITID.length())); } } addingLogMessage = true; tempBuffer = new StringBuffer(); } /** * @param fileName * relative URL-path to command execution directory */ protected File createFile(String fileName) { StringBuffer path = new StringBuffer(); path.append(logCommand.getLocalDirectory()); path.append(File.separator); path.append(fileName.replace('/', File.separatorChar)); // NOI18N return new File(path.toString()); } @Override public void parseEnhancedMessage(String key, Object value) { } }