/*******************************************************************************
* Copyright (c) 2012 xored software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* xored software, Inc. - initial API and implementation (Ivan Lobachev)
******************************************************************************/
package com.xored.glance.ui.ccvs;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSTag;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.Policy;
import org.eclipse.team.internal.ccvs.core.client.Command;
import org.eclipse.team.internal.ccvs.core.client.Session;
import org.eclipse.team.internal.ccvs.core.client.Update;
import org.eclipse.team.internal.ccvs.core.client.Command.GlobalOption;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
@SuppressWarnings("restriction")
public class HistoryFetcher {
private static final String HISTORY_PATTERN = "[ARM]\\s*(\\S*)\\s*(\\S*)\\s*(\\S*)\\s*(\\S*)\\s*(\\S*)\\s*(\\S*)\\s*(\\S*)\\s*(.*)";
public HistoryFetcher(/* ICVSRemoteFolder parentFolder */) {
}
private List<String> downloadHistory(Session session,
IProgressMonitor progress, ICVSRemoteFolder parentFolder,
String lastDate) throws CVSException {
final List<String> res = new ArrayList<String>();
LocalOption[] options;
if (lastDate != null && lastDate.length() > 0) {
options = new LocalOption[] { HistoryCommand.ADDED_REMOVED_ENTRIES,
HistoryCommand.ALL_USERS,
HistoryCommand.makeDateOption(lastDate) };
} else {
options = new LocalOption[] { HistoryCommand.ADDED_REMOVED_ENTRIES,
HistoryCommand.ALL_USERS };
}
(new HistoryCommand()).execute(session, Command.NO_GLOBAL_OPTIONS,
options, new ICVSResource[] { parentFolder },
new ICommandOutputListener() {
public IStatus errorLine(String line,
ICVSRepositoryLocation location,
ICVSFolder commandRoot, IProgressMonitor monitor) {
// System.out.println(line);
return OK;
}
public IStatus messageLine(String line,
ICVSRepositoryLocation location,
ICVSFolder commandRoot, IProgressMonitor monitor) {
// System.out.println(line);
if (line.startsWith("A ") || line.startsWith("R ")
|| line.startsWith("M ")) {
res.add(line);
}
return OK;
}
}, Policy.subMonitorFor(progress, 90));
return res;
}
protected List<String> getModules(Session session,
IProgressMonitor progress, ICVSRemoteFolder parentFolder)
throws CVSException {
final List<String> res = new ArrayList<String>();
// Build the local options
final List localOptions = new ArrayList();
localOptions.add(Update.RETRIEVE_ABSENT_DIRECTORIES);
Command.UPDATE.execute(session,
new GlobalOption[] { Command.DO_NOT_CHANGE },
(LocalOption[]) localOptions
.toArray(new LocalOption[localOptions.size()]),
new ICVSResource[] { parentFolder },
new ICommandOutputListener() {
public IStatus errorLine(String line,
ICVSRepositoryLocation location,
ICVSFolder commandRoot, IProgressMonitor monitor) {
// cvs update: New directory `CVSROOT' -- ignored
String prefix = "cvs update: New directory ";
String moduleName = line;
if (line.startsWith(prefix)) {
moduleName = line.substring(prefix.length() + 1,
line.lastIndexOf("'"));
res.add(moduleName);
}
return OK;
}
public IStatus messageLine(String line,
ICVSRepositoryLocation location,
ICVSFolder commandRoot, IProgressMonitor monitor) {
return OK;
}
}, Policy.subMonitorFor(progress, 90));
return res;
}
private CVSHistoryTree createHistory(Session session,
IProgressMonitor progress, ICVSRemoteFolder parentFolder,
List<String> entries) throws CVSException {
final CVSHistoryTree tree = new CVSHistoryTree(parentFolder
.getRepository(), parentFolder.getTag());
Pattern p = Pattern.compile(HISTORY_PATTERN);
CVSTag tag = parentFolder.getTag();
if (tag == null) {
tag = new CVSTag();
}
for (String entry : entries) {
Matcher m = p.matcher(entry);
if (m.find()) {
if (entry.startsWith("A ")) {
tree.createFile(m.group(6), tag.getName() + "/"
+ m.group(7), m.group(5));
} else if (entry.startsWith("R ")) {
tree.removeFile(m.group(6), tag.getName() + "/"
+ m.group(7));
} else if (entry.startsWith("M ")) {
tree.updateFileRevision(m.group(6), tag.getName() + "/"
+ m.group(7), m.group(5));
}
}
}
return tree;
}
public CVSHistoryTree fetchHistory(IProgressMonitor progress,
ICVSRemoteFolder parentFolder) throws CVSException {
progress.beginTask(null, 100);
Session session = new Session(parentFolder.getRepository(),
parentFolder, false /* output to console */);
session
.open(Policy.subMonitorFor(progress, 10), false /* read-only */);
try {
String fileName = GlanceCVSPlugin.getDefault().getStateLocation()
.append(
parentFolder.getRepository().toString().replace(
"/", "_").replace(":", "-")).toString();
Date date = getCacheDate(fileName);
List<String> cacheEntries = new ArrayList<String>();
if (date != null) {
cacheEntries = readHistoryFromFile(fileName);
}
List<String> downloadedEntries = downloadHistory(session, progress,
parentFolder, dateToString(date));
Date lastDate = getLastEntryDate(downloadedEntries);
// lastDate.to
if (lastDate != null) {
writeCacheDate(fileName, lastDate);
}
writeHistoryToFile(fileName, downloadedEntries);
cacheEntries.addAll(downloadedEntries);
CVSHistoryTree historyTree = createHistory(session, progress,
parentFolder, cacheEntries);
List<String> modulesList = getModules(session, progress,
parentFolder);
ICVSHistoryNode[] nodes = historyTree.getChild("HEAD")
.getNodeChildren();
for (ICVSHistoryNode node : nodes) {
if (!modulesList.contains(node.getElementName())) {
node.removed();
}
}
return historyTree;
} finally {
session.close();
}
}
private Date getCacheDate(String fileName) {
Date res = null;
try {
File file = new File(fileName + "_date.txt");
if (!file.exists()) {
return null;
}
BufferedReader reader = new BufferedReader(new FileReader(file));
res = parseDate(reader.readLine());
res = new Date(res.getTime() + 60000);
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
private void writeCacheDate(String fileName, Date date) {
try {
File file = new File(fileName + "_date.txt");
file.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write(dateToString(date));
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeHistoryToFile(String fileName, List<String> entries) {
try {
RandomAccessFile raf = new RandomAccessFile(fileName + "cont.txt",
"rw");
raf.seek(raf.length());
for (String entry : entries) {
raf.writeUTF(entry + "\n");
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private List<String> readHistoryFromFile(String fileName) {
List<String> res = new ArrayList<String>();
try {
RandomAccessFile raf = new RandomAccessFile(fileName + "cont.txt",
"rw");
raf.seek(0);
String line;
while (raf.getFilePointer() < raf.length()) {
line = raf.readUTF();
res.add(line);
}
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
private Date getLastEntryDate(List<String> entries) {
String lastDate = "";
String curDate = "";
Pattern p = Pattern.compile("[ARM]\\s*(\\S*\\s*\\S*\\s*\\S*)");
for (String entry : entries) {
Matcher m = p.matcher(entry);
if (m.find()) {
curDate = m.group(1);
if (lastDate.compareTo(curDate) < 0) {
lastDate = curDate;
}
}
}
return parseDate(lastDate);
}
private String dateToString(Date date) {
if (date == null) {
return null;
}
return (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z")).format(date);
}
private Date parseDate(String strDate) {
ArrayList<SimpleDateFormat> dateFormats = new ArrayList<SimpleDateFormat>();
dateFormats.add(new SimpleDateFormat("yyyy-MM-dd HH:mm Z"));
dateFormats.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"));
dateFormats.add(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy-MM-dd hh:mm"));
dateFormats.add(new SimpleDateFormat("yyyy MM dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy.MM.dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy-MMM-dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy MMM dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy.MMM.dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy/MMM/dd hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd-MM-yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd MM yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd.MM.yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd/MM/yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd-MMM-yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd MMM yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("dd.MMM.yyyy hh:mm:ss"));
dateFormats.add(new SimpleDateFormat("yyyy-MM-dd"));
dateFormats.add(new SimpleDateFormat("yyyy MM dd"));
dateFormats.add(new SimpleDateFormat("yyyy.MM.dd"));
dateFormats.add(new SimpleDateFormat("yyyy-MMM-dd"));
dateFormats.add(new SimpleDateFormat("yyyy MMM dd"));
dateFormats.add(new SimpleDateFormat("yyyy.MMM.dd"));
dateFormats.add(new SimpleDateFormat("dd-MM-yyyy"));
dateFormats.add(new SimpleDateFormat("dd MM yyyy"));
dateFormats.add(new SimpleDateFormat("dd.MM.yyyy"));
dateFormats.add(new SimpleDateFormat("dd-MMM-yy"));
dateFormats.add(new SimpleDateFormat("dd MMM yy"));
dateFormats.add(new SimpleDateFormat("dd.MMM.yy"));
dateFormats.add(new SimpleDateFormat("dd/MMM/yy"));
Date myDate = null;
for (SimpleDateFormat myFormat : dateFormats) {
try {
myDate = myFormat.parse(strDate);
break;
} catch (Exception e) {
}
}
return myDate;
}
}