package hudson.plugins.perforce;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.scm.ChangeLogSet;
import java.io.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.nio.charset.Charset;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;
import com.tek42.perforce.model.Changelist;
import org.kohsuke.stapler.framework.io.WriterOutputStream;
/**
* @author Mike Wille
*/
public class PerforceChangeLogSet extends ChangeLogSet<PerforceChangeLogEntry> {
private List<PerforceChangeLogEntry> history = null;
public PerforceChangeLogSet(AbstractBuild<?, ?> build, List<PerforceChangeLogEntry> logs) {
super(build);
this.history = Collections.unmodifiableList(logs);
}
public List<PerforceChangeLogEntry> getHistory() {
return history;
}
/*
* @see hudson.scm.ChangeLogSet#isEmptySet()
*/
@Override
public boolean isEmptySet() {
return history.size() == 0;
}
/*
* @see java.lang.Iterable#iterator()
*/
public Iterator<PerforceChangeLogEntry> iterator() {
return history.iterator();
}
/**
* Parses the change log stream and returns a Perforce change log set.
*
* @param build
* the build for the change log
* @param changeLogStream
* input stream containing the change log
* @return the change log set
*/
@SuppressWarnings("unchecked")
public static PerforceChangeLogSet parse(AbstractBuild build, InputStream changeLogStream) throws IOException, SAXException {
ArrayList<PerforceChangeLogEntry> changeLogEntries = new ArrayList<PerforceChangeLogEntry>();
SAXReader reader = new SAXReader();
Document changeDoc = null;
PerforceChangeLogSet changeLogSet = new PerforceChangeLogSet(build, changeLogEntries);
try {
changeDoc = reader.read(changeLogStream);
Node historyNode = changeDoc.selectSingleNode("/changelog");
if (historyNode == null)
return changeLogSet;
List<Node> entries = historyNode.selectNodes("entry");
if (entries == null)
return changeLogSet;
for (Node node : entries) {
Changelist change = new Changelist();
if (node.selectSingleNode("changenumber") != null)
change.setChangeNumber(new Integer(node.selectSingleNode("changenumber").getStringValue()));
if (node.selectSingleNode("date") != null)
change.setDate(stringDateToJavaDate(node.selectSingleNode("date").getStringValue()));
if (node.selectSingleNode("description") != null)
change.setDescription(node.selectSingleNode("description").getStringValue());
if (node.selectSingleNode("user") != null)
change.setUser(node.selectSingleNode("user").getStringValue());
if (node.selectSingleNode("workspace") != null)
change.setWorkspace(node.selectSingleNode("workspace").getStringValue());
List<Node> fileNodes = node.selectSingleNode("files").selectNodes("file");
List<Changelist.FileEntry> files = new ArrayList<Changelist.FileEntry>();
for (Node fnode : fileNodes) {
Changelist.FileEntry file = new Changelist.FileEntry();
file.setFilename(fnode.selectSingleNode("name").getStringValue());
file.setRevision(fnode.selectSingleNode("rev").getStringValue());
file.setAction(Changelist.FileEntry.Action.valueOf(fnode.selectSingleNode("action")
.getStringValue()));
files.add(file);
}
change.setFiles(files);
List<Node> jobNodes = node.selectSingleNode("jobs").selectNodes("job");
List<Changelist.JobEntry> jobs = new ArrayList<Changelist.JobEntry>();
for (Node jnode : jobNodes) {
Changelist.JobEntry job = new Changelist.JobEntry();
job.setJob(jnode.selectSingleNode("name").getStringValue());
job.setDescription(jnode.selectSingleNode("description").getStringValue());
job.setStatus(jnode.selectSingleNode("status").getStringValue());
jobs.add(job);
}
change.setJobs(jobs);
PerforceChangeLogEntry entry = new PerforceChangeLogEntry(changeLogSet);
entry.setChange(change);
changeLogEntries.add(entry);
}
} catch(Exception e) {
throw new IOException("Failed to parse changelog file: " + e.getMessage());
}
return changeLogSet;
}
/**
* Stores the history objects to the output stream as xml
*
* @param outputStream
* the stream to write to
* @param changes
* the history objects to store
* @throws IOException
*/
public static void saveToChangeLog(OutputStream outputStream, List<Changelist> changes) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter(outputStream, Charset.forName("UTF-8"));
WriterOutputStream stream1 = new WriterOutputStream(writer);
PrintStream stream = new PrintStream(stream1);
stream.println("<?xml version='1.0' encoding='UTF-8'?>");
stream.println("<changelog>");
for (Changelist change : changes) {
stream.println("\t<entry>");
stream.println("\t\t<changenumber>" + change.getChangeNumber() + "</changenumber>");
stream.println("\t\t<date>" + Util.xmlEscape(javaDateToStringDate(change.getDate())) + "</date>");
stream.println("\t\t<description>" + Util.xmlEscape(change.getDescription()) + "</description>");
stream.println("\t\t<user>" + Util.xmlEscape(change.getUser()) + "</user>");
stream.println("\t\t<workspace>" + Util.xmlEscape(change.getWorkspace()) + "</workspace>");
stream.println("\t\t<files>");
for (Changelist.FileEntry entry : change.getFiles()) {
stream.println("\t\t\t<file>");
stream.println("\t\t\t\t<name>" + Util.xmlEscape(entry.getFilename()) + "</name>");
stream.println("\t\t\t\t<rev>" + Util.xmlEscape(entry.getRevision()) + "</rev>");
stream.println("\t\t\t\t<action>" + entry.getAction() + "</action>");
stream.println("\t\t\t</file>");
}
stream.println("\t\t</files>");
stream.println("\t\t<jobs>");
for (Changelist.JobEntry entry : change.getJobs()) {
stream.println("\t\t\t<job>");
stream.println("\t\t\t\t<name>" + Util.xmlEscape(entry.getJob()) + "</name>");
stream.println("\t\t\t\t<description>" + Util.xmlEscape(entry.getDescription()) + "</description>");
stream.println("\t\t\t\t<status>" + Util.xmlEscape(entry.getStatus()) + "</status>");
stream.println("\t\t\t</job>");
}
stream.println("\t\t</jobs>");
stream.println("\t</entry>");
}
stream.println("</changelog>");
stream.close();
}
/**
* This takes a java.util.Date and converts it to a string.
*
* @return A string representation of the date
*/
public static String javaDateToStringDate(java.util.Date newDate) {
if (newDate == null)
return "";
GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
cal.clear();
cal.setTime(newDate);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DAY_OF_MONTH);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int min = cal.get(Calendar.MINUTE);
int sec = cal.get(Calendar.SECOND);
String date = year + "-" + putZero(month) + "-" + putZero(day);
if (hour + min + sec > 0)
date += " " + putZero(hour) + ":" + putZero(min) + ":" + putZero(sec);
return date;
}
/**
* Returns a java.util.Date object set to the time specified in newDate. The
* format expected is the format of: YYYY-MM-DD HH:MM:SS
*
* @param newDate
* the string date to convert
* @return A java.util.Date based off of the string format.
*/
protected static java.util.Date stringDateToJavaDate(String newDate) {
// when we have a null from the database, give it zeros first.
if (newDate == null || newDate.equals("")) {
return null;
}
String[] parts = newDate.split(" ");
String[] date = parts[0].split("-");
String[] time = null;
if (parts.length > 1) {
time = parts[1].split(":");
time[2] = time[2].replaceAll("\\.0", "");
} else {
time = "00:00:00".split(":");
}
GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
cal.clear();
cal.set(new Integer(date[0]).intValue(), (new Integer(date[1]).intValue() - 1),
new Integer(date[2]).intValue(), new Integer(time[0]).intValue(), new Integer(time[1]).intValue(),
new Integer(time[2]).intValue());
return cal.getTime();
}
private static String putZero(int i) {
if (i < 10) {
return "0" + i;
}
return i + "";
}
}