package com.jkristian;
import com.jkristian.cli.CommandLine;
import com.jkristian.cli.UnixCommand;
import com.jkristian.io.LineReader;
import com.jkristian.io.ProcessInputStream;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class p4d2p
{
public static void main(String[] argv) throws IOException
{
UnixCommand command = new UnixCommand(p4d2p.class, "hi::o:p:v", argv);
try {
if (command.hasOption("h")) {
command.writeError(command.getHelp());
} else {
command.setEditInPlace(command.getOption("i"));
p4d2p self = new p4d2p(command);
for (File file; (file = command.next()) != null;) {
if (command.hasOption("v"))
command.writeError(p4d2p.class.getName() + " -v " + file + " ...\n");
copyAll(command.getInputLineReader(), self);
self.close();
}
}
} finally {
command.flush();
}
}
private static void copyAll(LineReader in, p4d2p out) throws IOException
{
StringBuffer line = new StringBuffer();
while (in.readLine(line) >= 0) {
out.writeLine(line);
line.setLength(0);
}
}
protected p4d2p(UnixCommand command) throws IOException
{
this.command = command;
this.options = command.getOptions();
this.verbose = command.hasOption("v");
this.out = command.getOutputWriter();
}
protected UnixCommand command;
protected Properties options;
protected boolean verbose;
protected Writer out;
protected String epoch = "1970-01-01 00:00:00Z";
protected DateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); // ISO
// 8601
protected String now = timeFormat.format(new Date());
protected String oldFile = null;
protected String oldNote;
protected String newFile;
protected String newNote;
protected List movedFiles = new ArrayList();
protected static Pattern P4_DESCRIBE_MOVE = Pattern.compile(
"^\\.\\.\\. (//.+?\\#\\d+ (add|branch|delete))$", Pattern.MULTILINE);
protected static Pattern P4_MOVED_FILE = Pattern.compile("//(.+?)(\\#\\d+) (\\w*)$",
Pattern.MULTILINE);
protected static Pattern P4_DESCRIBE_HEADER = Pattern.compile(
"^==== //(.+?)(\\#\\d+) .*?====( \\(.+\\))?$", Pattern.MULTILINE);
protected static Pattern P4_DIFF_HEADER = Pattern.compile(
"^==== //(.+?)(\\#\\d+) +- +(.+?) +====( \\(.+\\))?$", Pattern.MULTILINE);
protected static Pattern P4_DIFF2_HEADER = Pattern.compile(
"^==== //(.+?)(\\#\\d+) +\\(.+?\\) +- +(.+?)(\\#\\d+) +\\(.+?\\) +==== +\\w+$",
Pattern.MULTILINE);
protected static Pattern VERSION = Pattern.compile("\\#(\\d+)");
protected static Pattern UNIFIED_DIFF = Pattern.compile("^\\@\\@\\s.*\\s\\@\\@$",
Pattern.MULTILINE);
protected static Pattern CONTEXT_DIFF = Pattern.compile("^\\*+$", Pattern.MULTILINE);
protected static Pattern DEFAULT_DIFF = Pattern.compile("^\\d+(,\\d+)?[acd]\\d+",
Pattern.MULTILINE);
public void writeLine(StringBuffer line) throws IOException
{
Matcher m;
if ((m = P4_DESCRIBE_MOVE.matcher(line)).find()) { // add, branch or
// delete
movedFiles.add(m.group(1));
} else if ((m = P4_DIFF_HEADER.matcher(line)).find()) {
oldFile = m.group(1);
newFile = m.group(3);
oldNote = " " + options.getProperty("o", m.group(2));
long lastModified = (new File(newFile)).lastModified();
newNote = " " + ((lastModified == 0) ? now : timeFormat.format(new Date(lastModified)));
} else if ((m = P4_DIFF2_HEADER.matcher(line)).find()) {
oldFile = m.group(1);
newFile = m.group(3);
oldNote = " " + options.getProperty("o", m.group(2));
newNote = " " + m.group(4);
} else if ((m = P4_DESCRIBE_HEADER.matcher(line)).find()) {
newFile = m.group(1);
newNote = " " + m.group(2);
oldFile = newFile;
oldNote = options.getProperty("o");
oldNote = " "
+ ((oldNote != null) ? oldNote : ("#" + (Long.parseLong((m = VERSION
.matcher(newNote)).find() ? m.group(1) : null) - 1)));
} else if (oldFile != null) {
if ((m = UNIFIED_DIFF.matcher(line)).find()) { // the preferred
// format for `patch`
out.write("Index: //" + oldFile + "\n");
out.write("--- " + oldFile + oldNote + "\n");
out.write("+++ " + oldFile + newNote + "\n");
oldFile = null;
} else if ((m = CONTEXT_DIFF.matcher(line)).find()) {
out.write("Index: //" + oldFile + "\n");
out.write("*** " + oldFile + oldNote + "\n");
out.write("--- " + oldFile + newNote + "\n");
oldFile = null;
} else if ((m = DEFAULT_DIFF.matcher(line)).find()) {
if (verbose)
command.writeError(command.getResource("diff.default"));
out.write("Index: " + oldFile + "\n");
out.write("diff -r //" + oldFile + " " + newFile + "\n");
oldFile = null;
}
}
out.write(line.toString());
}
public void flush() throws IOException
{
}
public void close() throws IOException
{
if (!movedFiles.isEmpty()) {
String verb;
List file = null;
for (Iterator iter = movedFiles.iterator(); iter.hasNext();) {
String movedFile = iter.next() + "";
Matcher m = P4_MOVED_FILE.matcher(movedFile);
m.find();
newFile = m.group(1);
newNote = m.group(2);
verb = m.group(3);
oldFile = newFile;
if ("delete".equals(verb)) {
(m = VERSION.matcher(newNote)).find();
oldNote = "#" + (Long.parseLong(m.group(1)) - 1);
newNote = epoch;
file = p4print("//" + oldFile + oldNote);
} else { // add or branch
oldNote = epoch;
file = p4print("//" + newFile + newNote);
}
if (!file.isEmpty()) {
oldNote = " " + options.getProperty("o", oldNote);
newNote = " " + newNote;
long fileSize = file.size();
String lines = (fileSize <= 1) ? "" : ("," + fileSize);
out.write("\nIndex: //" + newFile + "\n");
out.write("--- " + oldFile + oldNote + "\n");
out.write("+++ " + newFile + newNote + "\n");
String prefix;
if ("delete".equals(verb)) {
out.write("@@ -1" + lines + " +0,0 @@\n");
prefix = "-";
} else { // add or branch
out.write("@@ -0,0 +1" + lines + " @@\n");
prefix = "+";
}
StringBuffer lastLine = null;
for (int i = 0; i < fileSize; ++i) {
out.write(prefix + (lastLine = (StringBuffer) (file.get(i))));
}
if (lastLine.charAt(lastLine.length() - 1) != '\n') {
out.write("\n\\ No newline at end of file\n");
}
}
}
movedFiles.clear();
}
oldFile = null;
}
protected List p4print(String name) throws IOException
{
CommandLine cmd = (new CommandLine(options.getProperty("p", "p4 print -q"))).append(name);
// Sadly, executing `p4 print` will consume some input.
// Which is one reason not to emit files immediately
// upon reading their names from the input stream.
ProcessInputStream p = new ProcessInputStream(cmd.exec());
List file = (new LineReader(p)).readAllLines();
if (p.exitValue() != 0) {
command.writeError("exit " + p.exitValue() + " from " + cmd + "\n");
}
return file;
}
}