package hudson.plugins.mercurial;
import hudson.model.User;
import hudson.scm.ChangeLogSet;
import hudson.scm.EditType;
import hudson.scm.ChangeLogSet.AffectedFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.kohsuke.stapler.export.Exported;
/**
* Represents a change set.
*
* <p>
* The object should be treated like an immutable object.
* The setters are only provided for digester.
*/
public class MercurialChangeSet extends ChangeLogSet.Entry {
private String node;
private String author;
private long rev;
private String date;
private String msg;
private boolean merge;
private List<String> added = Collections.emptyList();
private List<String> deleted = Collections.emptyList();
private List<String> modified = Collections.emptyList();
/**
* Lazily computed.
*/
private volatile List<String> affectedPaths;
/**
* Commit message.
*/
@Exported
public String getMsg() {
return msg;
}
/**
* Gets the user who made this change.
*/
@Exported
public User getAuthor() {
return User.get(author);
}
/**
* Gets the globally unique changeset ID.
*/
@Exported
public String getNode() {
return node;
}
/**
* Short node ID that hg CLI uses.
* The first 12 characters of {@link #getNode()}.
*/
public String getShortNode() {
// TODO: consider getting this value using the changelog template
// via {node|short}
// this will ensure that Hudson is consistent with the Mercurial project
// should they change the length of short nodes.
return node.substring(0,12);
}
/**
* Gets repository revision number, which is local in the current repository.
*/
@Exported
public long getRev() {
return rev;
}
@Exported
public String getDate() {
return date;
}
/** {@inheritDoc} */
@Override
public Collection<String> getAffectedPaths() {
if(affectedPaths==null) {
List<String> r = new ArrayList<String>(added.size()+modified.size()+deleted.size());
r.addAll(added);
r.addAll(modified);
r.addAll(deleted);
affectedPaths = r;
}
return affectedPaths;
}
/** {@inheritDoc} */
@Override
public Collection<? extends AffectedFile> getAffectedFiles() {
final List<MercurialAffectedFile> affected = new ArrayList<MercurialAffectedFile>(added.size() + modified.size()
+ deleted.size());
for (EditType editType : EditType.ALL) {
for (String path : getPaths(editType)) {
affected.add(new MercurialAffectedFile(editType, path));
}
}
return affected;
}
/**
* Gets all the files that were added.
*/
@Exported
public List<String> getAddedPaths() {
return added;
}
/**
* Gets all the files that were deleted.
*/
@Exported
public List<String> getDeletedPaths() {
return deleted;
}
/**
* Gets all the files that were modified.
*/
@Exported
public List<String> getModifiedPaths() {
return modified;
}
/**
* Checks if this is a merge changeset.
*/
@Exported
public boolean isMerge() {
return merge;
}
public List<String> getPaths(EditType kind) {
if(kind==EditType.ADD)
return getAddedPaths();
if(kind==EditType.EDIT)
return getModifiedPaths();
if(kind==EditType.DELETE)
return getDeletedPaths();
return null;
}
/**
* Returns all three variations of {@link EditType}.
* Placed here to simplify access from views.
*/
public List<EditType> getEditTypes() {
// return EditType.ALL;
return Arrays.asList(EditType.ADD,EditType.EDIT,EditType.DELETE);
}
protected @Override void setParent(ChangeLogSet parent) {
super.setParent(parent);
}
//
// used by Digester
//
@Deprecated
public void setMsg(String msg) {
this.msg = msg;
}
@Deprecated
public void setNode(String node) {
this.node = node;
}
@Deprecated
public void setUser(String author) {
this.author = author;
}
@Deprecated
public String getUser() {
return author;
}
@Deprecated
public void setAuthor(String author) {
this.author = author;
}
@Deprecated
public void setRev(long rev) {
this.rev = rev;
}
@Deprecated
public void setDate(String date) {
this.date = date;
}
@Deprecated
public void setAdded(String list) {
if (merge) {
return;
}
added = toList(list);
}
@Deprecated
public void setDeleted(String list) {
if (merge) {
return;
}
deleted = toList(list);
}
@Deprecated
public void setFiles(String list) {
if (merge) {
return;
}
modified = toList(list);
if(!added.isEmpty() || !deleted.isEmpty()) {
modified = new ArrayList<String>(modified);
modified.removeAll(added);
modified.removeAll(deleted);
}
}
@Deprecated
public void setParents(String parents) {
// Possible values for parents when not using --debug:
// "" - commit made in succession
// "6019:b70a530bdb93 " - commit with older parent
// "6021:df659eb23360 6027:b7f44f01a632 " - merge
// Possible values for parents when using --debug:
// "6031:36a60bd5b70715aea20bb3b4da56cd27c5fade20 -1:0000000000000000000000000000000000000000 " - commit
// "6029:dd3267698d84458686b3c5682ce027438900ffbd 6030:cee68264ed92444e59a9bd5cf9519702b092363e " - merge
// Would be nicer if --debug did not matter: http://www.selenic.com/mercurial/bts/issue1435
merge = parents.indexOf(':') != parents.lastIndexOf(':') && !parents.contains("-1");
if (merge) {
added = Collections.emptyList();
deleted = Collections.emptyList();
modified = Collections.emptyList();
}
}
private List<String> toList(String list) {
list = list.trim();
if(list.length()==0) return Collections.emptyList();
return Arrays.asList(list.split(" "));
}
/** |xmlescape handles a few cases that |escape does not */
static final String CHANGELOG_TEMPLATE =
"<changeset node='{node}' author='{author|xmlescape}' rev='{rev}' date='{date}'>" +
"<msg>{desc|xmlescape}</msg><added>{file_adds|stringify|xmlescape}</added><deleted>{file_dels|stringify|xmlescape}</deleted>" +
"<files>{files|stringify|xmlescape}</files><parents>{parents}</parents></changeset>\\n";
}